static int sound_init_blip( Blip_Buffer **buf, Blip_Synth **synth ) { *buf = new_Blip_Buffer(); blip_buffer_set_clock_rate( *buf, sound_get_effective_processor_speed() ); /* Allow up to 1s of playback buffer - this allows us to cope with slowing down to 2% of speed where a single Speccy frame generates just under 1s of sound */ if ( blip_buffer_set_sample_rate( *buf, settings_current.sound_freq, 1000 ) ) { sound_end(); ui_error( UI_ERROR_ERROR, "out of memory at %s:%d", __FILE__, __LINE__ ); return 0; } *synth = new_Blip_Synth(); blip_synth_set_volume( *synth, sound_get_volume( settings_current.volume_beeper ) ); blip_synth_set_output( *synth, *buf ); blip_buffer_set_bass_freq( *buf, speaker_type[ option_enumerate_sound_speaker_type() ].bass ); blip_synth_set_treble_eq( *synth, speaker_type[ option_enumerate_sound_speaker_type() ].treble ); return 1; }
void sound_init( const char *device ) { float hz; double treble; Blip_Synth **ay_left_synth; Blip_Synth **ay_mid_synth; Blip_Synth **ay_mid_synth_r; Blip_Synth **ay_right_synth; /* Allow sound as long as emulation speed is greater than 2% (less than that and a single Speccy frame generates more than a seconds worth of sound which is bigger than the maximum Blip_Buffer of 1 second) */ if( !( !sound_enabled && settings_current.sound && settings_current.emulation_speed > 1 ) ) return; /* only try for stereo if we need it */ sound_stereo_ay = option_enumerate_sound_stereo_ay(); if( settings_current.sound && sound_lowlevel_init( device, &settings_current.sound_freq, &sound_stereo_ay ) ) return; if( !sound_init_blip(&left_buf, &left_beeper_synth) ) return; if( sound_stereo_ay != SOUND_STEREO_AY_NONE && !sound_init_blip(&right_buf, &right_beeper_synth) ) return; treble = speaker_type[ option_enumerate_sound_speaker_type() ].treble; ay_a_synth = new_Blip_Synth(); blip_synth_set_volume( ay_a_synth, sound_get_volume( settings_current.volume_ay) ); blip_synth_set_treble_eq( ay_a_synth, treble ); ay_b_synth = new_Blip_Synth(); blip_synth_set_volume( ay_b_synth, sound_get_volume( settings_current.volume_ay) ); blip_synth_set_treble_eq( ay_b_synth, treble ); ay_c_synth = new_Blip_Synth(); blip_synth_set_volume( ay_c_synth, sound_get_volume( settings_current.volume_ay) ); blip_synth_set_treble_eq( ay_c_synth, treble ); left_specdrum_synth = new_Blip_Synth(); blip_synth_set_volume( left_specdrum_synth, sound_get_volume( settings_current.volume_specdrum ) ); blip_synth_set_output( left_specdrum_synth, left_buf ); blip_synth_set_treble_eq( left_specdrum_synth, treble ); /* important to override these settings if not using stereo * (it would probably be confusing to mess with the stereo * settings in settings_current though, which is why we make copies * rather than using the real ones). */ ay_a_synth_r = NULL; ay_b_synth_r = NULL; ay_c_synth_r = NULL; if( sound_stereo_ay != SOUND_STEREO_AY_NONE ) { /* Attach the Blip_Synth's we've already created as appropriate, and * create one more Blip_Synth for the middle channel's right buffer. */ if( sound_stereo_ay == SOUND_STEREO_AY_ACB ) { ay_left_synth = &ay_a_synth; ay_mid_synth = &ay_c_synth; ay_mid_synth_r = &ay_c_synth_r; ay_right_synth = &ay_b_synth; } else if ( sound_stereo_ay == SOUND_STEREO_AY_ABC ) { ay_left_synth = &ay_a_synth; ay_mid_synth = &ay_b_synth; ay_mid_synth_r = &ay_b_synth_r; ay_right_synth = &ay_c_synth; } else { ui_error( UI_ERROR_ERROR, "unknown AY stereo separation type: %d", sound_stereo_ay ); fuse_abort(); } blip_synth_set_output( *ay_left_synth, left_buf ); blip_synth_set_output( *ay_mid_synth, left_buf ); blip_synth_set_output( *ay_right_synth, right_buf ); *ay_mid_synth_r = new_Blip_Synth(); blip_synth_set_volume( *ay_mid_synth_r, sound_get_volume( settings_current.volume_ay ) ); blip_synth_set_output( *ay_mid_synth_r, right_buf ); blip_synth_set_treble_eq( *ay_mid_synth_r, treble ); right_specdrum_synth = new_Blip_Synth(); blip_synth_set_volume( right_specdrum_synth, sound_get_volume( settings_current.volume_specdrum ) ); blip_synth_set_output( right_specdrum_synth, right_buf ); blip_synth_set_treble_eq( right_specdrum_synth, treble ); } else { blip_synth_set_output( ay_a_synth, left_buf ); blip_synth_set_output( ay_b_synth, left_buf ); blip_synth_set_output( ay_c_synth, left_buf ); } sound_enabled = sound_enabled_ever = 1; sound_channels = ( sound_stereo_ay != SOUND_STEREO_AY_NONE ? 2 : 1 ); /* Adjust relative processor speed to deal with adjusting sound generation frequency against emulation speed (more flexible than adjusting generated sample rate) */ hz = ( float )sound_get_effective_processor_speed() / machine_current->timings.tstates_per_frame; /* Size of audio data we will get from running a single Spectrum frame */ sound_framesiz = ( float )settings_current.sound_freq / hz; sound_framesiz++; samples = (blip_sample_t *)libspectrum_calloc( sound_framesiz * sound_channels, sizeof(blip_sample_t) ); /* initialize movie settings... */ movie_init_sound( settings_current.sound_freq, sound_stereo_ay ); }
int sound_lowlevel_init( const char *device, int *freqptr, int *stereoptr ) { unsigned int exact_rate, periods; unsigned int val, n; snd_pcm_hw_params_t *hw_params; snd_pcm_sw_params_t *sw_params; snd_pcm_uframes_t avail_min = 0, sound_periodsize, bsize = 0; static int first_init = 1; static int init_running = 0; const char *option; char tmp; int err, dir, nperiods = NUM_FRAMES; float hz; if( init_running ) return 0; init_running = 1; /* select a default device if we weren't explicitly given one */ option = device; while( option && *option ) { tmp = '*'; if( ( err = sscanf( option, " buffer=%i %n%c", &val, &n, &tmp ) > 0 ) && ( tmp == ',' || strlen( option ) == n ) ) { if( val < 1 ) { fprintf( stderr, "Bad value for ALSA buffer size %i, using default\n", val ); } else { bsize = val; } } else if( ( err = sscanf( option, " frames=%i %n%c", &val, &n, &tmp ) > 0 ) && ( tmp == ',' || strlen( option ) == n ) ) { if( val < 1 ) { fprintf( stderr, "Bad value for ALSA buffer size %i frames, using default (%d)\n", val, NUM_FRAMES ); } else { nperiods = val; } } else if( ( err = sscanf( option, " avail=%i %n%c", &val, &n, &tmp ) > 0 ) && ( tmp == ',' || strlen( option ) == n ) ) { if( val < 1 ) { fprintf( stderr, "Bad value for ALSA avail_min size %i frames, using default\n", val ); } else { avail_min = val; } } else if( ( err = sscanf( option, " verbose %n%c", &n, &tmp ) == 1 ) && ( tmp == ',' || strlen( option ) == n ) ) { verb = 1; } else { /* try as device name */ while( isspace(*option) ) option++; if( *option == '\'' ) /* force device... */ option++; pcm_name = option; n = strlen( pcm_name ); } option += n + ( tmp == ',' ); } /* Open the sound device */ if( pcm_name == NULL || *pcm_name == '\0' ) pcm_name = "default"; if( snd_pcm_open( &pcm_handle, pcm_name , stream, 0 ) < 0 ) { if( strcmp( pcm_name, "default" ) == 0 ) { /* we try a last one: plughw:0,0 but what a weired ALSA conf.... */ if( snd_pcm_open( &pcm_handle, "plughw:0,0", stream, 0 ) < 0 ) { settings_current.sound = 0; ui_error( UI_ERROR_ERROR, "couldn't open sound device 'default' and 'plughw:0,0' check ALSA configuration." ); init_running = 0; return 1; } else { if( first_init ) fprintf( stderr, "Couldn't open sound device 'default', using 'plughw:0,0' check ALSA configuration.\n" ); } } settings_current.sound = 0; ui_error( UI_ERROR_ERROR, "couldn't open sound device '%s'.", pcm_name ); init_running = 0; return 1; } /* Allocate the snd_pcm_hw_params_t structure on the stack. */ snd_pcm_hw_params_alloca( &hw_params ); /* Init hw_params with full configuration space */ if( snd_pcm_hw_params_any( pcm_handle, hw_params ) < 0 ) { settings_current.sound = 0; ui_error( UI_ERROR_ERROR, "couldn't get configuration space on sound device '%s'.", pcm_name ); snd_pcm_close( pcm_handle ); init_running = 0; return 1; } if( snd_pcm_hw_params_set_access( pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED ) < 0) { settings_current.sound = 0; ui_error( UI_ERROR_ERROR, "couldn't set access interleaved on '%s'.", pcm_name ); snd_pcm_close( pcm_handle ); init_running = 0; return 1; } /* Set sample format */ if( snd_pcm_hw_params_set_format( pcm_handle, hw_params, #if defined WORDS_BIGENDIAN SND_PCM_FORMAT_S16_BE #else SND_PCM_FORMAT_S16_LE #endif ) < 0 ) { settings_current.sound = 0; ui_error( UI_ERROR_ERROR, "couldn't set format on '%s'.", pcm_name ); snd_pcm_close( pcm_handle ); init_running = 0; return 1; } ch = *stereoptr ? 2 : 1; if( snd_pcm_hw_params_set_channels( pcm_handle, hw_params, ch ) < 0 ) { fprintf( stderr, "Couldn't set %s to '%s'.\n", pcm_name, (*stereoptr ? "stereo" : "mono") ); ch = *stereoptr ? 1 : 2; /* try with opposite */ if( snd_pcm_hw_params_set_channels( pcm_handle, hw_params, ch ) < 0 ) { ui_error( UI_ERROR_ERROR, "couldn't set %s to '%s'.", pcm_name, (*stereoptr ? "stereo" : "mono") ); settings_current.sound = 0; snd_pcm_close( pcm_handle ); init_running = 0; return 1; } *stereoptr = *stereoptr ? 0 : 1; /* write back */ } framesize = ch << 1; /* we always use 16 bit sorry :-( */ /* Set sample rate. If the exact rate is not supported */ /* by the hardware, use nearest possible rate. */ exact_rate = *freqptr; if( snd_pcm_hw_params_set_rate_near( pcm_handle, hw_params, &exact_rate, NULL ) < 0) { settings_current.sound = 0; ui_error( UI_ERROR_ERROR, "couldn't set rate %d on '%s'.", *freqptr, pcm_name ); snd_pcm_close( pcm_handle ); init_running = 0; return 1; } if( first_init && *freqptr != exact_rate ) { fprintf( stderr, "The rate %d Hz is not supported by your hardware. " "Using %d Hz instead.\n", *freqptr, exact_rate ); *freqptr = exact_rate; } if( bsize != 0 ) { exact_periodsize = sound_periodsize = bsize / nperiods; if( bsize < 1 ) { fprintf( stderr, "bad value for ALSA buffer size %i, using default.\n", val ); bsize = 0; } } if( bsize == 0 ) { /* Adjust relative processor speed to deal with adjusting sound generation frequency against emulation speed (more flexible than adjusting generated sample rate) */ hz = (float)sound_get_effective_processor_speed() / machine_current->timings.tstates_per_frame; /* Amount of audio data we will accumulate before yielding back to the OS. Not much point having more than 100Hz playback, we probably get downgraded by the OS as being a hog too (unlimited Hz limits playback speed to about 2000% on my Mac, 100Hz allows up to 5000% for me) */ if( hz > 100.0 ) hz = 100.0; exact_periodsize = sound_periodsize = *freqptr / hz; } dir = -1; if( snd_pcm_hw_params_set_period_size_near( pcm_handle, hw_params, &exact_periodsize, &dir ) < 0 ) { settings_current.sound = 0; ui_error( UI_ERROR_ERROR, "couldn't set period size %d on '%s'.", (int)sound_periodsize, pcm_name ); snd_pcm_close( pcm_handle ); init_running = 0; return 1; } if( first_init && ( exact_periodsize < sound_periodsize / 1.5 || exact_periodsize > sound_periodsize * 1.5 ) ) { fprintf( stderr, "The period size %d is not supported by your hardware. " "Using %d instead.\n", (int)sound_periodsize, (int)exact_periodsize ); } periods = nperiods; /* Set number of periods. Periods used to be called fragments. */ if( snd_pcm_hw_params_set_periods_near( pcm_handle, hw_params, &periods, NULL ) < 0 ) { settings_current.sound = 0; ui_error( UI_ERROR_ERROR, "couldn't set periods on '%s'.", pcm_name ); snd_pcm_close( pcm_handle ); init_running = 0; return 1; } if( first_init && periods != nperiods ) { fprintf( stderr, "%d periods is not supported by your hardware. " "Using %d instead.\n", nperiods, periods ); } snd_pcm_hw_params_get_buffer_size( hw_params, &exact_bsize ); /* Apply HW parameter settings to */ /* PCM device and prepare device */ if( snd_pcm_hw_params( pcm_handle, hw_params ) < 0 ) { settings_current.sound = 0; ui_error( UI_ERROR_ERROR,"couldn't set hw_params on %s", pcm_name ); snd_pcm_close( pcm_handle ); init_running = 0; return 1; } snd_pcm_sw_params_alloca( &sw_params ); if( ( err = snd_pcm_sw_params_current( pcm_handle, sw_params ) ) < 0 ) { ui_error( UI_ERROR_ERROR,"couldn't get sw_params from %s: %s", pcm_name, snd_strerror ( err ) ); snd_pcm_close( pcm_handle ); init_running = 0; return 1; } if( ( err = snd_pcm_sw_params_set_start_threshold( pcm_handle, sw_params, exact_periodsize * ( nperiods - 1 ) ) ) < 0 ) { ui_error( UI_ERROR_ERROR,"couldn't set start_treshold on %s: %s", pcm_name, snd_strerror ( err ) ); snd_pcm_close( pcm_handle ); init_running = 0; return 1; } if( !avail_min ) avail_min = exact_periodsize >> 1; if( snd_pcm_sw_params_set_avail_min( pcm_handle, sw_params, avail_min ) < 0 ) { #if SND_LIB_VERSION < 0x10010 if( ( err = snd_pcm_sw_params_set_sleep_min( pcm_handle, sw_params, 1 ) ) < 0 ) { fprintf( stderr, "Unable to set minimal sleep 1 for %s: %s\n", pcm_name, snd_strerror ( err ) ); } #else fprintf( stderr, "Unable to set avail min %s: %s\n", pcm_name, snd_strerror( err ) ); #endif } #if SND_LIB_VERSION < 0x10010 if( ( err = snd_pcm_sw_params_set_xfer_align( pcm_handle, sw_params, 1 ) ) < 0 ) { ui_error( UI_ERROR_ERROR,"couldn't set xfer_allign on %s: %s", pcm_name, snd_strerror ( err ) ); init_running = 0; return 1; } #endif if( ( err = snd_pcm_sw_params( pcm_handle, sw_params ) ) < 0 ) { ui_error( UI_ERROR_ERROR,"couldn't set sw_params on %s: %s", pcm_name, snd_strerror ( err ) ); init_running = 0; return 1; } if( first_init ) snd_output_stdio_attach(&output, stdout, 0); first_init = 0; init_running = 0; return 0; /* success */ }
void sound_init( const char *device ) { int ret; float hz; /* Allow sound as long as emulation speed is greater than 2% (less than that and a single Speccy frame generates more than a seconds worth of sound which is bigger than the maximum Blip_Buffer of 1 second) */ if( !( !sound_enabled && settings_current.sound && settings_current.emulation_speed > 1 ) ) return; sound_stereo_ay = settings_current.stereo_ay; /* only try for stereo if we need it */ if( sound_stereo_ay ) sound_stereo = 1; ret = sound_lowlevel_init( device, &settings_current.sound_freq, &sound_stereo ); if( ret ) return; if( !sound_init_blip(&left_buf, &left_beeper_synth) ) return; if( sound_stereo && !sound_init_blip(&right_buf, &right_beeper_synth) ) return; ay_a_synth = new_Blip_Synth(); blip_synth_set_volume( ay_a_synth, sound_get_volume( settings_current.volume_ay) ); blip_synth_set_output( ay_a_synth, left_buf ); blip_synth_set_treble_eq( ay_a_synth, speaker_type[ settings_current.speaker_type ].treble ); ay_b_synth = new_Blip_Synth(); blip_synth_set_volume( ay_b_synth, sound_get_volume( settings_current.volume_ay) ); blip_synth_set_treble_eq( ay_b_synth, speaker_type[ settings_current.speaker_type ].treble ); ay_c_synth = new_Blip_Synth(); blip_synth_set_volume( ay_c_synth, sound_get_volume( settings_current.volume_ay) ); blip_synth_set_output( ay_c_synth, left_buf ); blip_synth_set_treble_eq( ay_c_synth, speaker_type[ settings_current.speaker_type ].treble ); /* important to override these settings if not using stereo * (it would probably be confusing to mess with the stereo * settings in settings_current though, which is why we make copies * rather than using the real ones). */ if( !sound_stereo ) { sound_stereo_ay = 0; } ay_a_synth_r = NULL; ay_b_synth_r = NULL; ay_c_synth_r = NULL; if( sound_stereo ) { ay_c_synth_r = new_Blip_Synth(); blip_synth_set_volume( ay_c_synth_r, sound_get_volume( settings_current.volume_ay ) ); blip_synth_set_output( ay_c_synth_r, right_buf ); if( sound_stereo_ay ) { /* stereo with ACB stereo. */ blip_synth_set_output( ay_b_synth, right_buf ); } else { ay_a_synth_r = new_Blip_Synth(); blip_synth_set_volume( ay_a_synth_r, sound_get_volume( settings_current.volume_ay ) ); blip_synth_set_output( ay_a_synth_r, right_buf ); blip_synth_set_treble_eq( ay_a_synth_r, speaker_type[ settings_current.speaker_type ].treble ); blip_synth_set_output( ay_b_synth, left_buf ); ay_b_synth_r = new_Blip_Synth(); blip_synth_set_volume( ay_b_synth_r, sound_get_volume( settings_current.volume_ay ) ); blip_synth_set_output( ay_b_synth_r, right_buf ); blip_synth_set_treble_eq( ay_b_synth_r, speaker_type[ settings_current.speaker_type ].treble ); } } else { blip_synth_set_output( ay_b_synth, left_buf ); } sound_enabled = sound_enabled_ever = 1; sound_channels = ( sound_stereo ? 2 : 1 ); /* Adjust relative processor speed to deal with adjusting sound generation frequency against emulation speed (more flexible than adjusting generated sample rate) */ hz = ( float )sound_get_effective_processor_speed() / machine_current->timings.tstates_per_frame; /* Size of audio data we will get from running a single Spectrum frame */ sound_framesiz = ( float )settings_current.sound_freq / hz; sound_framesiz++; samples = (blip_sample_t *)calloc( sound_framesiz * sound_channels, sizeof(blip_sample_t) ); }