2013-10-06 06:22:08 +00:00
/*
2015-10-22 05:13:26 +00:00
* Apple // emulator for *ix
2015-01-31 21:57:10 +00:00
*
* This software package is subject to the GNU General Public License
2015-10-22 05:13:26 +00:00
* version 3 or later ( your choice ) as published by the Free Software
2015-01-31 21:57:10 +00:00
* Foundation .
*
2015-10-22 05:13:26 +00:00
* Copyright 2013 - 2015 Aaron Culliney
2015-01-31 21:57:10 +00:00
*
*/
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
/* Apple //e speaker support. Source inspired/derived from AppleWin.
*
2015-07-02 05:54:09 +00:00
* - ~ 23 //e cycles per PC sample (played back at 44.100kHz)
2013-10-06 06:22:08 +00:00
*
2015-01-31 21:57:10 +00:00
* The soundcard output drives how much 6502 emulation is done in real - time . If the soundcard buffer is running out of
* sample - data , then more 6502 cycles need to be executed to top - up the buffer , and vice - versa .
*
* This is in contrast to the AY8910 voices ( mockingboard / phasor ) , which can simply generate more data if their buffers
* are running low .
2013-10-06 06:22:08 +00:00
*/
# include "common.h"
2014-01-23 04:42:34 +00:00
2015-07-02 05:54:09 +00:00
# define DEBUG_SPEAKER 0
2015-01-31 21:57:10 +00:00
# if DEBUG_SPEAKER
# define SPEAKER_LOG(...) LOG(__VA_ARGS__)
2013-10-06 06:22:08 +00:00
# else
2015-01-31 21:57:10 +00:00
# define SPEAKER_LOG(...)
2013-10-06 06:22:08 +00:00
# endif
2015-07-03 03:37:06 +00:00
# define SPKR_SILENT_STEP 1
2015-07-02 05:54:09 +00:00
static unsigned long bufferTotalSize = 0 ;
static unsigned long bufferSizeIdealMin = 0 ;
static unsigned long bufferSizeIdealMax = 0 ;
2015-07-11 21:06:09 +00:00
static unsigned long channelsSampleRateHz = 0 ;
2013-10-06 06:22:08 +00:00
2015-07-02 05:54:09 +00:00
static bool speaker_isAvailable = false ;
static int16_t * samples_buffer = NULL ; // holds max 1 second of samples
static int16_t * remainder_buffer = NULL ; // holds enough to create one sample (averaged)
2017-09-22 00:30:09 +00:00
static unsigned long samples_buffer_idx = 0 ;
2015-01-31 21:57:10 +00:00
static unsigned int remainder_buffer_size = 0 ;
2015-07-02 05:54:09 +00:00
static unsigned long remainder_buffer_size_max = 0 ;
2015-01-31 21:57:10 +00:00
static unsigned int remainder_buffer_idx = 0 ;
2013-10-06 06:22:08 +00:00
2016-03-26 05:34:33 +00:00
static long speaker_volume = 0 ;
2015-01-31 21:57:10 +00:00
static int16_t speaker_amplitude = SPKR_DATA_INIT ;
static int16_t speaker_data = 0 ;
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
static double cycles_per_sample = 0.0 ;
2016-01-17 19:54:22 +00:00
static unsigned long cycles_last_update = 0 ;
static unsigned long cycles_quiet_time = 0 ;
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
static bool speaker_accessed_since_last_flush = false ;
static bool speaker_recently_active = false ;
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
static bool speaker_going_silent = false ;
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
static int samples_adjustment_counter = 0 ;
2013-10-06 06:22:08 +00:00
2015-06-17 04:18:52 +00:00
static AudioBuffer_s * speakerBuffer = NULL ;
2013-10-06 06:22:08 +00:00
2016-07-24 00:34:59 +00:00
# if SPEAKER_TRACING
static FILE * speaker_trace_fp = NULL ;
static unsigned long cycles_trace_toggled = 0 ;
# endif
2015-01-31 21:57:10 +00:00
// --------------------------------------------------------------------------------------------------------------------
2013-10-06 06:22:08 +00:00
2016-03-26 05:34:33 +00:00
static void speaker_prefsChanged ( const char * domain ) {
2016-04-14 02:45:55 +00:00
long lVal = 0 ;
speaker_volume = prefs_parseLongValue ( domain , PREF_SPEAKER_VOLUME , & lVal , /*base:*/ 10 ) ? lVal : 5 ; // expected range 0-10
2016-03-26 05:34:33 +00:00
if ( speaker_volume < 0 ) {
speaker_volume = 0 ;
}
if ( speaker_volume > 10 ) {
speaker_volume = 10 ;
}
float samplesScale = speaker_volume / 10.f ;
speaker_amplitude = ( int16_t ) ( SPKR_DATA_INIT * samplesScale ) ;
}
static __attribute__ ( ( constructor ) ) void _init_speaker ( void ) {
prefs_registerListener ( PREF_DOMAIN_AUDIO , & speaker_prefsChanged ) ;
}
2015-01-31 21:57:10 +00:00
/*
* Because disk image loading is slow ( AKA close - to - original - //e-speed), we may auto-switch to "fullspeed" for faster
* loading when all the following heuristics hold true :
* - Disk motor is on
* - Speaker has not been toggled in some time ( is not " active " )
* - The graphics state is not " dirty "
*
* In fullspeed mode we output only quiet samples ( zero - amplitude ) at such a rate as to prevent the streaming audio from
* either underflowing or overflowing .
*
* We will also auto - switch back to the last configured " scaled " speed when the speaker is toggled .
*/
static void _speaker_init_timing ( void ) {
// 46.28 //e cycles for 22.05kHz sample rate
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
// AppleWin NOTE : use integer value: Better for MJ Mahon's RT.SYNTH.DSK (integer multiples of 1.023MHz Clk)
2017-07-15 23:25:00 +00:00
cycles_per_sample = ( unsigned int ) ( cycles_persec_target / ( double ) audio_getCurrentBackend ( ) - > systemSettings . sampleRateHz ) ;
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
unsigned int last_remainder_buffer_size = remainder_buffer_size ;
remainder_buffer_size = ( unsigned int ) cycles_per_sample ;
if ( ( double ) remainder_buffer_size ! = cycles_per_sample ) {
+ + remainder_buffer_size ;
2013-10-06 06:22:08 +00:00
}
2015-07-02 05:54:09 +00:00
assert ( remainder_buffer_size < = remainder_buffer_size_max ) ;
2015-01-31 21:57:10 +00:00
if ( last_remainder_buffer_size = = remainder_buffer_size ) {
// no change ... insure seamless remainder_buffer
} else {
SPEAKER_LOG ( " changing remainder buffer size " ) ;
remainder_buffer_idx = 0 ;
2013-10-06 06:22:08 +00:00
}
2015-01-31 21:57:10 +00:00
if ( cycles_last_update > cycles_count_total ) {
SPEAKER_LOG ( " resetting speaker cycles counter " ) ;
cycles_last_update = 0 ;
2013-10-06 06:22:08 +00:00
}
2017-07-15 23:25:00 +00:00
LOG ( " Speaker initialize timing ... cycles_persec_target:%f cycles_per_sample:%f speaker sampleRateHz:%lu " , cycles_persec_target , cycles_per_sample , audio_getCurrentBackend ( ) - > systemSettings . sampleRateHz ) ;
2015-07-11 21:06:09 +00:00
2015-01-31 21:57:10 +00:00
if ( is_fullspeed ) {
remainder_buffer_idx = 0 ;
samples_buffer_idx = 0 ;
2013-10-06 06:22:08 +00:00
}
}
2015-01-31 21:57:10 +00:00
/*
* Adds to the samples_buffer the number of samples since the last invocation of this function .
*
* Speaker output square wave example :
* _______ ____ _____________________ ! . + speaker_amplitude
* silence _ .
* threshold _ .
* _ remainder _ .
* average _ .
* _______ ____________ ______ ___ . 0
*
* - When the speaker is accessed by the emulated program , the output ( speaker_data ) is toggled between the
* positive amplitude or zero
* - Evenly - divisible samples since last function call ( cycles_diff / cycles_per_sample ) are put directly into the
* samples_buffer for output to audio system backend
* - ( + ) Fractional samples are put into a remainder_buffer to be averaged and then transfered to the sample_buffer
* when there is enough data for 1 whole sample ( possibly on a subsequent invocation of this function )
* - ( + ) If the speaker has not been toggled with output at + amplitude for a certain number of machine cycles , we
* gradually step the samples down to the zero bound of true quiet . ( This is done to avoid glitching when
* pausing / unpausing emulation for GUI / menus and auto - switching between full and configured speeds )
*/
static void _speaker_update ( /*bool toggled*/ ) {
2013-10-06 06:22:08 +00:00
2016-01-17 19:54:22 +00:00
do {
if ( is_fullspeed ) {
break ;
}
if ( UNLIKELY ( cycles_last_update > cycles_count_total ) ) {
LOG ( " ignoring cycles_count_total overflow ... " ) ;
break ; // ignore cycles_count_total overflow ...
}
2013-10-06 06:22:08 +00:00
2016-01-17 19:54:22 +00:00
unsigned long cycles_diff = cycles_count_total - cycles_last_update ;
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
if ( remainder_buffer_idx ) {
// top-off previous previous fractional remainder cycles
while ( cycles_diff & & ( remainder_buffer_idx < remainder_buffer_size ) ) {
remainder_buffer [ remainder_buffer_idx ] = speaker_data ;
+ + remainder_buffer_idx ;
- - cycles_diff ;
}
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
if ( remainder_buffer_idx = = remainder_buffer_size ) {
// now have a complete extra sample from (previous + now) ... calculate mean value
remainder_buffer_idx = 0 ;
int sample_mean = 0 ;
for ( unsigned int i = 0 ; i < remainder_buffer_size ; i + + ) {
sample_mean + = ( int ) remainder_buffer [ i ] ;
}
2013-10-06 06:22:08 +00:00
2016-09-10 17:32:33 +00:00
assert ( remainder_buffer_size > 0 ) ;
2015-01-31 21:57:10 +00:00
sample_mean / = ( int ) remainder_buffer_size ;
2013-10-06 06:22:08 +00:00
2015-07-11 21:06:09 +00:00
if ( samples_buffer_idx < channelsSampleRateHz ) {
2015-01-31 21:57:10 +00:00
samples_buffer [ samples_buffer_idx + + ] = ( int16_t ) sample_mean ;
2015-07-11 21:06:09 +00:00
if ( NUM_CHANNELS = = 2 ) {
samples_buffer [ samples_buffer_idx + + ] = ( int16_t ) sample_mean ;
}
2015-01-31 21:57:10 +00:00
}
}
2013-10-06 06:22:08 +00:00
}
2016-01-17 19:54:22 +00:00
const unsigned long samples_count = ( unsigned long ) ( ( double ) cycles_diff / cycles_per_sample ) ;
unsigned long num_samples = samples_count ;
const unsigned long cycles_remainder = ( unsigned long ) ( ( double ) cycles_diff - ( double ) num_samples * cycles_per_sample ) ;
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
// populate samples_buffer with whole samples
2015-07-11 21:06:09 +00:00
while ( num_samples & & ( samples_buffer_idx < channelsSampleRateHz ) ) {
2015-01-31 21:57:10 +00:00
samples_buffer [ samples_buffer_idx + + ] = speaker_data ;
2015-07-11 21:06:09 +00:00
if ( NUM_CHANNELS = = 2 ) {
samples_buffer [ samples_buffer_idx + + ] = speaker_data ;
}
2015-07-12 20:01:25 +00:00
# if !defined(ANDROID)
2015-01-31 21:57:10 +00:00
if ( speaker_going_silent & & speaker_data ) {
2015-07-03 03:37:06 +00:00
speaker_data - = SPKR_SILENT_STEP ;
2015-01-31 21:57:10 +00:00
}
2015-07-12 20:01:25 +00:00
# endif
2015-01-31 21:57:10 +00:00
- - num_samples ;
}
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
if ( cycles_remainder > 0 ) {
// populate remainder_buffer with fractional samples
assert ( remainder_buffer_idx = = 0 & & " should have already dealt with remainder buffer " ) ;
2016-01-17 19:54:22 +00:00
if ( UNLIKELY ( cycles_remainder > = remainder_buffer_size ) ) {
LOG ( " OOPS, overflow in cycles_remainder:%lu " , cycles_remainder ) ;
} else {
while ( remainder_buffer_idx < cycles_remainder ) {
remainder_buffer [ remainder_buffer_idx ] = speaker_data ;
+ + remainder_buffer_idx ;
}
2015-01-31 21:57:10 +00:00
}
}
2013-10-06 06:22:08 +00:00
2015-10-21 03:47:08 +00:00
if ( UNLIKELY ( samples_buffer_idx > channelsSampleRateHz ) ) {
2015-10-24 21:29:52 +00:00
////assert(samples_buffer_idx == channelsSampleRateHz && "should be at exactly the end, no further");
2015-10-21 03:47:08 +00:00
if ( UNLIKELY ( samples_buffer_idx > channelsSampleRateHz ) ) {
2016-01-17 19:54:22 +00:00
LOG ( " OOPS, possible overflow in speaker samples buffer ... samples_buffer_idx:%lu channelsSampleRateHz:%lu " , ( unsigned long ) samples_buffer_idx , channelsSampleRateHz ) ;
2015-10-21 03:47:08 +00:00
}
2015-01-31 21:57:10 +00:00
}
2016-01-17 19:54:22 +00:00
} while ( 0 ) ;
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
cycles_last_update = cycles_count_total ;
2013-10-06 06:22:08 +00:00
}
2015-01-31 21:57:10 +00:00
/*
* Submits " quiet " samples to the audio system backend when CPU thread is running fullspeed , to keep the audio streaming
* topped up .
*
* 20150131 NOTE : it seems that there are still cases where we glitch OpenAL ( seemingly on transitioning back to
* regular speed sample submissions ) . I have not been able to fully isolate the cause , but this has been minimized by
* always trending the samples to the zero value when there has been a sufficient amount of speaker silence .
*/
static void _submit_samples_buffer_fullspeed ( void ) {
samples_adjustment_counter = 0 ;
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
unsigned long bytes_queued = 0 ;
2015-06-20 07:01:17 +00:00
long err = speakerBuffer - > GetCurrentPosition ( speakerBuffer , & bytes_queued ) ;
2015-01-31 21:57:10 +00:00
if ( err ) {
return ;
}
2015-10-21 03:47:08 +00:00
////assert(bytes_queued <= bufferTotalSize); -- wtf failing on Desktop on launch
2013-10-06 06:22:08 +00:00
2015-07-02 05:54:09 +00:00
if ( bytes_queued > = bufferSizeIdealMax ) {
2015-01-31 21:57:10 +00:00
return ;
}
2013-10-06 06:22:08 +00:00
2017-09-22 00:30:09 +00:00
unsigned long num_samples_pad = ( bufferSizeIdealMax - bytes_queued ) / sizeof ( int16_t ) ;
2015-01-31 21:57:10 +00:00
if ( num_samples_pad = = 0 ) {
return ;
}
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
unsigned long system_buffer_size = 0 ;
int16_t * system_samples_buffer = NULL ;
2015-06-20 07:01:17 +00:00
if ( speakerBuffer - > Lock ( speakerBuffer , num_samples_pad * sizeof ( int16_t ) , & system_samples_buffer , & system_buffer_size ) ) {
2015-01-31 21:57:10 +00:00
return ;
}
2015-09-07 18:05:22 +00:00
if ( num_samples_pad > system_buffer_size / sizeof ( int16_t ) ) {
num_samples_pad = system_buffer_size / sizeof ( int16_t ) ;
}
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
//SPEAKER_LOG("bytes_queued:%d enqueueing %d quiet samples", bytes_queued, num_samples_pad);
for ( unsigned int i = 0 ; i < num_samples_pad ; i + + ) {
system_samples_buffer [ i ] = speaker_data ;
}
2013-10-06 06:22:08 +00:00
2015-06-20 07:01:17 +00:00
speakerBuffer - > Unlock ( speakerBuffer , system_buffer_size ) ;
2013-10-06 06:22:08 +00:00
}
2015-01-31 21:57:10 +00:00
// Submits samples from the samples_buffer to the audio system backend when running at a normal scaled-speed. This also
// generates cycles feedback to the main CPU timing routine depending on the needs of the streaming audio (more or less
// data).
2017-09-22 00:30:09 +00:00
static unsigned long _submit_samples_buffer ( const unsigned long num_channel_samples ) {
2013-10-06 06:22:08 +00:00
2015-07-11 21:06:09 +00:00
assert ( num_channel_samples ) ;
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
unsigned long bytes_queued = 0 ;
2015-06-20 07:01:17 +00:00
long err = speakerBuffer - > GetCurrentPosition ( speakerBuffer , & bytes_queued ) ;
2015-01-31 21:57:10 +00:00
if ( err ) {
2015-07-11 21:06:09 +00:00
return num_channel_samples ;
2015-01-31 21:57:10 +00:00
}
2015-10-21 03:47:08 +00:00
////assert(bytes_queued <= bufferTotalSize); -- this is failing on desktop FIXME TODO ...
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
//
// calculate CPU cycles feedback adjustment to prevent system audio buffer under/overflow
//
2013-10-06 06:22:08 +00:00
2015-07-02 05:54:09 +00:00
if ( bytes_queued < bufferSizeIdealMin ) {
2015-06-17 05:39:35 +00:00
samples_adjustment_counter + = SOUNDCORE_ERROR_INC ; // need moar data
2015-07-02 05:54:09 +00:00
} else if ( bytes_queued > bufferSizeIdealMax ) {
2015-06-17 05:39:35 +00:00
samples_adjustment_counter - = SOUNDCORE_ERROR_INC ; // need less data
2015-01-31 21:57:10 +00:00
} else {
samples_adjustment_counter = 0 ; // Acceptable amount of data in buffer
}
2013-10-06 06:22:08 +00:00
2015-06-17 05:39:35 +00:00
if ( samples_adjustment_counter < - SOUNDCORE_ERROR_MAX ) {
samples_adjustment_counter = - SOUNDCORE_ERROR_MAX ;
} else if ( samples_adjustment_counter > SOUNDCORE_ERROR_MAX ) {
samples_adjustment_counter = SOUNDCORE_ERROR_MAX ;
2015-01-31 21:57:10 +00:00
}
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
cycles_speaker_feedback = ( int ) ( samples_adjustment_counter * cycles_per_sample ) ;
2013-10-06 06:22:08 +00:00
2015-07-02 05:54:09 +00:00
//SPEAKER_LOG("feedback:%d samples_adjustment_counter:%d bytes_queued:%lu", cycles_speaker_feedback, samples_adjustment_counter, bytes_queued);
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
//
// copy samples to audio system backend
//
2013-10-06 06:22:08 +00:00
2017-09-22 00:30:09 +00:00
const unsigned long bytes_free = bufferTotalSize - bytes_queued ;
2015-07-11 21:06:09 +00:00
unsigned long requested_samples = num_channel_samples ;
unsigned long requested_buffer_size = num_channel_samples * sizeof ( int16_t ) ;
2013-10-06 06:22:08 +00:00
2015-07-11 21:06:09 +00:00
if ( requested_buffer_size > bytes_free ) {
requested_samples = bytes_free / sizeof ( int16_t ) ;
requested_buffer_size = bytes_free ;
2015-01-31 21:57:10 +00:00
}
2013-10-06 06:22:08 +00:00
2015-07-11 21:06:09 +00:00
if ( requested_buffer_size ) {
2015-01-31 21:57:10 +00:00
unsigned long system_buffer_size = 0 ;
int16_t * system_samples_buffer = NULL ;
2013-10-06 06:22:08 +00:00
2015-11-30 06:14:55 +00:00
const unsigned long maxSpeakerBytes = channelsSampleRateHz * sizeof ( int16_t ) ;
2015-07-11 21:06:09 +00:00
unsigned long curr_buffer_size = requested_buffer_size ;
unsigned long samples_idx = 0 ;
unsigned long counter = 0 ;
do {
if ( speakerBuffer - > Lock ( speakerBuffer , curr_buffer_size , & system_samples_buffer , & system_buffer_size ) ) {
2017-07-16 00:34:43 +00:00
LOG ( " Problem locking speaker buffer " ) ;
2015-07-11 21:06:09 +00:00
break ;
}
2013-10-06 06:22:08 +00:00
2015-10-31 06:14:20 +00:00
if ( system_buffer_size > maxSpeakerBytes ) {
2017-07-16 00:19:31 +00:00
LOG ( " AVOIDING BUFOVER... " ) ;
2015-10-31 06:14:20 +00:00
system_buffer_size = maxSpeakerBytes ;
2015-11-30 06:14:55 +00:00
requested_buffer_size = maxSpeakerBytes ;
2015-10-31 06:14:20 +00:00
}
2015-07-11 21:06:09 +00:00
memcpy ( system_samples_buffer , & samples_buffer [ samples_idx ] , system_buffer_size ) ;
2013-10-06 06:22:08 +00:00
2015-07-11 21:06:09 +00:00
err = speakerBuffer - > Unlock ( speakerBuffer , system_buffer_size ) ;
if ( err ) {
2017-07-16 00:34:43 +00:00
LOG ( " Problem unlocking speaker buffer " ) ;
2015-07-11 21:06:09 +00:00
break ;
}
curr_buffer_size - = system_buffer_size ;
samples_idx + = system_buffer_size ;
+ + counter ;
} while ( samples_idx < requested_buffer_size & & counter < 2 ) ;
2013-10-06 06:22:08 +00:00
}
2015-07-11 21:06:09 +00:00
return requested_samples ;
2013-10-06 06:22:08 +00:00
}
2015-01-31 21:57:10 +00:00
// --------------------------------------------------------------------------------------------------------------------
// speaker public API functions
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
void speaker_destroy ( void ) {
2015-07-26 20:38:43 +00:00
assert ( pthread_self ( ) = = cpu_thread_id ) ;
2015-07-02 05:54:09 +00:00
speaker_isAvailable = false ;
2015-06-17 06:32:19 +00:00
audio_destroySoundBuffer ( & speakerBuffer ) ;
2015-07-02 05:54:09 +00:00
FREE ( samples_buffer ) ;
FREE ( remainder_buffer ) ;
2013-10-06 06:22:08 +00:00
}
2015-01-31 21:57:10 +00:00
void speaker_init ( void ) {
2015-07-26 20:38:43 +00:00
assert ( pthread_self ( ) = = cpu_thread_id ) ;
2015-07-02 05:54:09 +00:00
long err = 0 ;
speaker_isAvailable = false ;
do {
2015-07-11 21:37:41 +00:00
err = audio_createSoundBuffer ( & speakerBuffer ) ;
2015-07-02 05:54:09 +00:00
if ( err ) {
break ;
}
2017-07-15 23:25:00 +00:00
assert ( audio_getCurrentBackend ( ) - > systemSettings . bytesPerSample = = sizeof ( int16_t ) ) ;
2015-07-11 21:06:09 +00:00
assert ( NUM_CHANNELS = = 2 | | NUM_CHANNELS = = 1 ) ;
if ( NUM_CHANNELS = = 2 ) {
2017-07-15 23:25:00 +00:00
bufferTotalSize = audio_getCurrentBackend ( ) - > systemSettings . stereoBufferSizeSamples * audio_getCurrentBackend ( ) - > systemSettings . bytesPerSample * NUM_CHANNELS ;
2015-07-11 21:06:09 +00:00
} else {
2017-07-15 23:25:00 +00:00
bufferTotalSize = audio_getCurrentBackend ( ) - > systemSettings . monoBufferSizeSamples * audio_getCurrentBackend ( ) - > systemSettings . bytesPerSample ;
2015-07-11 21:06:09 +00:00
}
2015-07-02 05:54:09 +00:00
bufferSizeIdealMin = bufferTotalSize / 4 ;
bufferSizeIdealMax = bufferTotalSize / 2 ;
2017-07-15 23:25:00 +00:00
channelsSampleRateHz = audio_getCurrentBackend ( ) - > systemSettings . sampleRateHz * NUM_CHANNELS ;
LOG ( " Speaker initializing with %lu buffer size (bytes), sample rate of %lu " , bufferTotalSize , audio_getCurrentBackend ( ) - > systemSettings . sampleRateHz ) ;
2015-07-02 05:54:09 +00:00
2017-07-15 23:25:00 +00:00
remainder_buffer_size_max = ( ( CLK_6502_INT * ( unsigned long ) CPU_SCALE_FASTEST ) / audio_getCurrentBackend ( ) - > systemSettings . sampleRateHz ) + 1 ;
2015-07-02 05:54:09 +00:00
2015-12-31 06:16:57 +00:00
samples_buffer = CALLOC ( 1 , channelsSampleRateHz * sizeof ( int16_t ) ) ;
2015-07-02 05:54:09 +00:00
if ( ! samples_buffer ) {
err = - 1 ;
break ;
}
2015-09-16 05:18:43 +00:00
samples_buffer_idx = bufferSizeIdealMax ;
2015-07-02 05:54:09 +00:00
2015-12-31 06:16:57 +00:00
remainder_buffer = MALLOC ( remainder_buffer_size_max * sizeof ( int16_t ) ) ;
2015-07-02 05:54:09 +00:00
if ( ! remainder_buffer ) {
err = - 1 ;
break ;
}
2015-06-26 04:32:37 +00:00
_speaker_init_timing ( ) ;
2015-07-02 05:54:09 +00:00
speaker_isAvailable = true ;
} while ( 0 ) ;
if ( err ) {
LOG ( " Error creating speaker subsystem, regular sound will be disabled! " ) ;
if ( samples_buffer ) {
FREE ( samples_buffer ) ;
}
if ( remainder_buffer ) {
FREE ( remainder_buffer ) ;
}
2015-06-26 04:32:37 +00:00
}
2013-10-06 06:22:08 +00:00
}
2015-01-31 21:57:10 +00:00
void speaker_reset ( void ) {
2015-07-02 05:54:09 +00:00
if ( speaker_isAvailable ) {
2015-06-26 04:32:37 +00:00
_speaker_init_timing ( ) ;
}
2013-10-06 06:22:08 +00:00
}
2015-01-31 21:57:10 +00:00
void speaker_flush ( void ) {
2015-10-04 21:21:28 +00:00
SCOPE_TRACE_AUDIO ( " speaker_flush " ) ;
2015-07-02 05:54:09 +00:00
if ( ! speaker_isAvailable ) {
2015-06-26 04:32:37 +00:00
return ;
}
2015-01-31 21:57:10 +00:00
assert ( pthread_self ( ) = = cpu_thread_id ) ;
if ( is_fullspeed ) {
cycles_quiet_time = cycles_count_total ;
speaker_going_silent = false ;
speaker_accessed_since_last_flush = false ;
} else {
if ( speaker_accessed_since_last_flush ) {
cycles_quiet_time = 0 ;
speaker_going_silent = false ;
speaker_accessed_since_last_flush = false ;
} else {
if ( ! cycles_quiet_time ) {
cycles_quiet_time = cycles_count_total ;
}
const unsigned int cycles_diff = ( unsigned int ) ( cycles_persec_target / 10 ) ; // 0.1sec of cycles
if ( ( cycles_count_total - cycles_quiet_time ) > cycles_diff * 2 ) {
// After 0.2sec of //e cycles time set inactive flag (allows auto-switch to full speed for fast disk access)
speaker_recently_active = false ;
2015-07-02 05:54:09 +00:00
} else if ( ( speaker_data ! = 0 ) & & ( cycles_count_total - cycles_quiet_time > cycles_diff ) ) {
2015-07-05 01:11:58 +00:00
# if defined(ANDROID)
// OpenSLES seems to be able to pause output without the nasty pops that I hear with OpenAL on Linux
// desktop. So this speaker_going_silent hack is not needed. There is also a noticeable glitch in
// OpenSLES when this codepath is enabled.
2015-07-11 21:37:41 +00:00
//
// Furthermore, the simple mixer in soundcore-opensles.c now requires signed 16bit samples
2015-07-05 01:11:58 +00:00
# else
2015-01-31 21:57:10 +00:00
// After 0.1sec of //e cycles time start reducing samples to zero (if they aren't there already). This
// process attempts to mask the extraneous clicks when freezing/restarting emulation (GUI access) and
// glitching from the audio system backend
speaker_going_silent = true ;
SPEAKER_LOG ( " speaker going silent " ) ;
2015-07-05 01:11:58 +00:00
# endif
2015-01-31 21:57:10 +00:00
}
2013-10-06 06:22:08 +00:00
}
2015-01-31 21:57:10 +00:00
}
_speaker_update ( /*toggled:false*/ ) ;
2017-09-22 00:30:09 +00:00
unsigned long samples_used = 0 ;
2015-01-31 21:57:10 +00:00
if ( is_fullspeed ) {
assert ( ! samples_buffer_idx & & " should be all quiet samples " ) ;
_submit_samples_buffer_fullspeed ( ) ;
} else if ( samples_buffer_idx ) {
samples_used = _submit_samples_buffer ( samples_buffer_idx ) ;
}
assert ( samples_used < = samples_buffer_idx ) ;
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
if ( samples_used ) {
2015-07-11 21:06:09 +00:00
size_t unsubmitted_size = samples_buffer_idx - samples_used ;
if ( unsubmitted_size ) {
memmove ( samples_buffer , & samples_buffer [ samples_used ] , unsubmitted_size ) ;
}
2015-01-31 21:57:10 +00:00
samples_buffer_idx - = samples_used ;
}
2013-10-06 06:22:08 +00:00
}
2015-07-12 20:00:39 +00:00
bool speaker_isActive ( void ) {
2015-06-17 04:18:52 +00:00
return speaker_recently_active ;
2013-10-06 06:22:08 +00:00
}
2015-07-12 20:00:39 +00:00
double speaker_cyclesPerSample ( void ) {
2015-01-31 21:57:10 +00:00
return cycles_per_sample ;
2013-10-06 06:22:08 +00:00
}
2015-01-31 21:57:10 +00:00
// --------------------------------------------------------------------------------------------------------------------
// VM system entry point
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
GLUE_C_READ ( speaker_toggle )
2013-10-06 06:22:08 +00:00
{
2015-01-31 21:57:10 +00:00
assert ( pthread_self ( ) = = cpu_thread_id ) ;
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
timing_checkpoint_cycles ( ) ;
2013-10-06 06:22:08 +00:00
2016-07-24 00:34:59 +00:00
# if SPEAKER_TRACING
// output cycle count delta when toggled
if ( speaker_trace_fp ) {
unsigned long cycles_trace_delta = cycles_count_total - cycles_trace_toggled ;
fprintf ( speaker_trace_fp , " %lu \n " , cycles_trace_delta ) ;
cycles_trace_toggled = cycles_count_total ;
}
# endif
2015-01-31 21:57:10 +00:00
# if DIRECT_SPEAKER_ACCESS
# error this used to be implemented ...
// AFAIK ... this requires an actual speaker device and ability to access it (usually requiring this program to be
// running setuid-operator or (gasp) setuid-root ... maybe
2013-10-23 04:22:43 +00:00
# else
2015-01-31 21:57:10 +00:00
speaker_accessed_since_last_flush = true ;
speaker_recently_active = true ;
2013-10-06 06:22:08 +00:00
2015-07-26 20:38:43 +00:00
# if !defined(MOBILE_DEVICE)
if ( timing_shouldAutoAdjustSpeed ( ) ) {
2015-01-31 21:57:10 +00:00
is_fullspeed = false ;
}
2015-07-26 20:38:43 +00:00
# endif
2013-10-06 06:22:08 +00:00
2015-07-02 05:54:09 +00:00
if ( speaker_isAvailable ) {
2015-06-26 04:32:37 +00:00
_speaker_update ( /*toggled:true*/ ) ;
}
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
if ( ! is_fullspeed ) {
2015-07-03 03:37:06 +00:00
if ( speaker_data = = speaker_amplitude ) {
2015-07-11 21:37:41 +00:00
# ifdef ANDROID
speaker_data = - speaker_amplitude ;
# else
2015-01-31 21:57:10 +00:00
speaker_data = 0 ;
2015-07-11 21:37:41 +00:00
# endif
2015-01-31 21:57:10 +00:00
} else {
speaker_data = speaker_amplitude ;
}
}
# endif
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
return floating_bus ( ) ;
2013-10-06 06:22:08 +00:00
}
2016-07-24 00:34:59 +00:00
# if SPEAKER_TRACING
// --------------------------------------------------------------------------------------------------------------------
// Speaker audio tracing (binary samples output)
void speaker_traceBegin ( const char * trace_file ) {
if ( trace_file ) {
speaker_trace_fp = fopen ( trace_file , " w " ) ;
}
}
void speaker_traceFlush ( void ) {
if ( speaker_trace_fp ) {
fflush ( speaker_trace_fp ) ;
}
}
void speaker_traceEnd ( void ) {
if ( speaker_trace_fp ) {
fflush ( speaker_trace_fp ) ;
fclose ( speaker_trace_fp ) ;
speaker_trace_fp = NULL ;
}
}
# endif