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)
2015-01-31 21:57:10 +00:00
static unsigned int samples_buffer_idx = 0 ;
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
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 ;
static unsigned long long cycles_last_update = 0 ;
static unsigned long 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
2015-01-31 21:57:10 +00:00
// --------------------------------------------------------------------------------------------------------------------
2013-10-06 06:22:08 +00:00
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)
2015-07-11 21:06:09 +00:00
cycles_per_sample = ( unsigned int ) ( cycles_persec_target / ( double ) audio_backend - > 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
}
2015-07-11 21:06:09 +00:00
LOG ( " Speaker initialize timing ... cycles_persec_target:%f cycles_per_sample:%f speaker sampleRateHz:%lu " , cycles_persec_target , cycles_per_sample , audio_backend - > systemSettings . sampleRateHz ) ;
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
2015-01-31 21:57:10 +00:00
if ( ! is_fullspeed ) {
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
unsigned long 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
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
}
2015-01-31 21:57:10 +00:00
const unsigned long long samples_count = ( unsigned long long ) ( ( double ) cycles_diff / cycles_per_sample ) ;
unsigned long long num_samples = samples_count ;
const unsigned long long cycles_remainder = ( unsigned long 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 " ) ;
assert ( cycles_remainder < remainder_buffer_size & & " otherwise there should have been another whole sample " ) ;
2013-10-06 06:22:08 +00:00
2015-01-31 21:57:10 +00:00
while ( remainder_buffer_idx < cycles_remainder ) {
remainder_buffer [ remainder_buffer_idx ] = speaker_data ;
+ + remainder_buffer_idx ;
}
}
2013-10-06 06:22:08 +00:00
2015-10-21 03:47:08 +00:00
if ( UNLIKELY ( samples_buffer_idx > channelsSampleRateHz ) ) {
//assert(samples_buffer_idx == channelsSampleRateHz && "should be at exactly the end, no further");
if ( UNLIKELY ( samples_buffer_idx > channelsSampleRateHz ) ) {
ERRLOG ( " OOPS, possible overflow in speaker samples buffer ... samples_buffer_idx:%lu channelsSampleRateHz:%lu " , samples_buffer_idx , channelsSampleRateHz ) ;
}
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_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
2015-07-02 05:54:09 +00:00
unsigned int 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).
2015-07-11 21:06:09 +00:00
static unsigned int _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
2015-07-02 05:54:09 +00:00
const unsigned int 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-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 ) ) {
ERRLOG ( " Problem locking speaker buffer " ) ;
break ;
}
2013-10-06 06:22:08 +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 ) {
ERRLOG ( " Problem unlocking speaker buffer " ) ;
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 ;
}
2015-07-11 21:06:09 +00:00
assert ( audio_backend - > systemSettings . bytesPerSample = = sizeof ( int16_t ) ) ;
assert ( NUM_CHANNELS = = 2 | | NUM_CHANNELS = = 1 ) ;
if ( NUM_CHANNELS = = 2 ) {
bufferTotalSize = audio_backend - > systemSettings . stereoBufferSizeSamples * audio_backend - > systemSettings . bytesPerSample * NUM_CHANNELS ;
} else {
bufferTotalSize = audio_backend - > systemSettings . monoBufferSizeSamples * audio_backend - > systemSettings . bytesPerSample ;
}
2015-07-02 05:54:09 +00:00
bufferSizeIdealMin = bufferTotalSize / 4 ;
bufferSizeIdealMax = bufferTotalSize / 2 ;
2015-07-11 21:06:09 +00:00
channelsSampleRateHz = audio_backend - > systemSettings . sampleRateHz * NUM_CHANNELS ;
LOG ( " Speaker initializing with %lu buffer size (bytes), sample rate of %lu " , bufferTotalSize , audio_backend - > systemSettings . sampleRateHz ) ;
2015-07-02 05:54:09 +00:00
2015-07-11 21:06:09 +00:00
remainder_buffer_size_max = ( ( CLK_6502_INT * ( unsigned long ) CPU_SCALE_FASTEST ) / audio_backend - > systemSettings . sampleRateHz ) + 1 ;
2015-07-02 05:54:09 +00:00
2015-09-16 05:18:43 +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
remainder_buffer = malloc ( remainder_buffer_size_max * sizeof ( int16_t ) ) ;
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*/ ) ;
unsigned int samples_used = 0 ;
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
void speaker_setVolumeZeroToTen ( unsigned long goesToTen ) {
float samplesScale = goesToTen / 10.f ;
2015-07-25 05:21:01 +00:00
speaker_amplitude = ( int16_t ) ( SPKR_DATA_INIT * samplesScale ) ;
2013-11-17 19:20:26 +00:00
}
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
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
}