@@ -31,22 +31,22 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
3131#include "configdb.h"
3232
3333
34- int stereo_separation = AUDIO_DEFAULT_SEPARATION ;
35- int audio_volume = AUDIO_DEFAULT_VOLUME ;
36-
3734struct SidEmulation sid [NUMBER_OF_SIDS ];
3835
3936static opl3_chip opl3 ;
4037
4138static SDL_AudioDeviceID audio = 0 ; // SDL audio device
42- static int stereo_separation_orig = 100 ;
43- static int stereo_separation_other = 0 ;
4439static int system_sound_mix_freq ; // playback sample rate (in Hz) of the emulator itself
4540static int system_sid_cycles_per_sec ;
4641static double dma_audio_mixing_value ;
4742
4843Uint8 mixer_register = 0x00 ;
49- static Uint8 mixer_coeffs [0x100 ];
44+ static Uint8 mixer_bytes [0x100 ];
45+ static float mixer_floats [0x80 ];
46+ static int output_selection ; // $00 = HDMI/speakers, $C0 = headphones
47+ static int xemu_volume_int ;
48+ static float xemu_volume_float ;
49+ static bool mono_downmix ;
5050
5151#if NUMBER_OF_SIDS != 4
5252# error "Currently NUMBER_OF_SIDS macro must be set to 4!"
@@ -222,35 +222,6 @@ static inline void render_dma_audio ( int channel, Sint16 *buffer, int len )
222222}
223223
224224
225- void audio_set_stereo_parameters ( int vol , int sep )
226- {
227- if (sep == AUDIO_UNCHANGED_SEPARATION ) {
228- sep = stereo_separation ;
229- } else {
230- if (sep > 100 )
231- sep = 100 ;
232- else if (sep < -100 )
233- sep = -100 ;
234- stereo_separation = sep ;
235- }
236- if (vol == AUDIO_UNCHANGED_VOLUME ) {
237- vol = audio_volume ;
238- } else {
239- if (vol > 100 )
240- vol = 100 ;
241- else if (vol < 0 )
242- vol = 0 ;
243- audio_volume = vol ;
244- }
245- //sep = ((sep + 100) * 0x100) / 200;
246- sep = (sep + 100 ) / 2 ;
247- //sep = (sep + 100) * 0x100 / 200;
248- stereo_separation_orig = (sep * vol ) / 100 ;
249- stereo_separation_other = ((100 - sep ) * vol ) / 100 ;
250- DEBUGPRINT ("AUDIO: volume is set to %d%%, stereo separation is %d%% [component-A is %d, component-B is %d]" NL , audio_volume , stereo_separation , stereo_separation_orig , stereo_separation_other );
251- }
252-
253-
254225// 10 channels, consist of: 4 channel for audio DMA, 4 channel for SIDs (each SIDs are pre-mixed to one channel by sid.c), 2 OPL3 channel (OPL3 is pre-mixed into two channels in opl3.c)
255226#define MIXED_CHANNELS 10
256227
@@ -264,6 +235,9 @@ void audio_set_stereo_parameters ( int vol, int sep )
264235#define STREAMS (n ) (streams + ((n) * (AUDIO_BUFFER_SAMPLES_MAX)))
265236#define STREAMS_SAMPLE (n ,d ) ((int)(STREAMS(n)[d]))
266237
238+ static float scalers_left [MIXED_CHANNELS ];
239+ static float scalers_right [MIXED_CHANNELS ];
240+
267241
268242static void audio_callback ( void * userdata , Uint8 * stereo_out_stream , int len )
269243{
@@ -310,18 +284,16 @@ static void audio_callback ( void *userdata, Uint8 *stereo_out_stream, int len )
310284 }
311285 // Now mix the result ...
312286 for (int i = 0 , j = 0 ; i < len ; i ++ ) {
313- // mixing streams together
314- // Currently: put the first two SIDS to the left, the second two to the right, same for DMA audio channels, and OPL3 seems to need 2 channel
315- const register int orig_left = STREAMS_SAMPLE (0 , i ) + STREAMS_SAMPLE (1 , i ) + STREAMS_SAMPLE (4 , i ) + STREAMS_SAMPLE (5 , i ) + STREAMS_SAMPLE (8 , i );
316- const register int orig_right = STREAMS_SAMPLE (2 , i ) + STREAMS_SAMPLE (3 , i ) + STREAMS_SAMPLE (6 , i ) + STREAMS_SAMPLE (7 , i ) + STREAMS_SAMPLE (9 , i );
317- #if 1
318- // channel stereo separation (including inversion) + volume handling
319- int left = ((orig_left * stereo_separation_orig ) / 100 ) + ((orig_right * stereo_separation_other ) / 100 );
320- int right = ((orig_right * stereo_separation_orig ) / 100 ) + ((orig_left * stereo_separation_other ) / 100 );
321- #else
322- int left = orig_left ;
323- int right = orig_right ;
324- #endif
287+ // do the mixing stuff. NOTE: it seems on modern CPUs, using float/doubles can be even faster than integer math where I also need a division ...
288+ float fl_left = 0 , fl_right = 0 ;
289+ for (int c = 0 ; c < MIXED_CHANNELS ; c ++ ) {
290+ const float sample = (float )STREAMS_SAMPLE (c , i );
291+ fl_left += sample * scalers_left [c ];
292+ fl_right += sample * scalers_right [c ];
293+ }
294+ // convert to integer
295+ int left = (int )fl_left ;
296+ int right = (int )fl_right ;
325297 // do some ugly clipping ...
326298 if (left > 0x7FFF ) left = 0x7FFF ;
327299 else if (left < -0x8000 ) left = -0x8000 ;
@@ -355,6 +327,34 @@ void audio65_clear_regs ( void )
355327}
356328
357329
330+ #define GETMIXFL (n ) mixer_floats[((n) + output_selection) >> 1]
331+
332+
333+ static void recalulate_scalers ( void )
334+ {
335+ const float master_left = GETMIXFL (0x1E ) * xemu_volume_float ;
336+ const float master_right = GETMIXFL (0x3E ) * xemu_volume_float ;
337+ // Digi (audio DMA) channel 1-2 volume
338+ scalers_left [0 ] = scalers_left [1 ] = GETMIXFL (0x10 ) * master_left ;
339+ scalers_right [0 ] = scalers_right [1 ] = GETMIXFL (0x30 ) * master_right ;
340+ // Digi (audio DMA) channel 3-4 volume
341+ scalers_left [2 ] = scalers_left [3 ] = GETMIXFL (0x12 ) * master_left ;
342+ scalers_right [2 ] = scalers_right [3 ] = GETMIXFL (0x32 ) * master_right ;
343+ // SID 1-2 volume
344+ scalers_left [4 ] = scalers_left [5 ] = GETMIXFL (0x00 ) * master_left ;
345+ scalers_right [4 ] = scalers_right [5 ] = GETMIXFL (0x20 ) * master_right ;
346+ // SID 3-4 volume
347+ scalers_left [6 ] = scalers_left [7 ] = GETMIXFL (0x02 ) * master_left ;
348+ scalers_right [6 ] = scalers_right [7 ] = GETMIXFL (0x22 ) * master_right ;
349+ // OPL FM volume. NOTE: Xemu uses NukedOPL which emulates OPL3 [stereo], but MEGA65's OPL2(-ish?) implementation has only mono as OPL source
350+ scalers_left [8 ] = scalers_left [9 ] = GETMIXFL (0x1C ) * master_left ;
351+ scalers_right [8 ] = scalers_right [9 ] = GETMIXFL (0x3C ) * master_right ;
352+ if (XEMU_UNLIKELY (mono_downmix ))
353+ for (int c = 0 ; c < MIXED_CHANNELS ; c ++ )
354+ scalers_left [c ] = scalers_right [c ] = (scalers_left [c ] + scalers_right [c ]) / 2 ;
355+ }
356+
357+
358358void audio65_reset ( void )
359359{
360360 // We always initialize SIDs/OPL, even if no audio emulation is compiled in
@@ -389,34 +389,130 @@ void audio65_start ( void )
389389
390390Uint8 audio65_read_mixer_register ( void )
391391{
392- return mixer_coeffs [mixer_register ];
392+ return mixer_bytes [mixer_register ];
393+ }
394+
395+
396+ static inline void write_mixer_register ( const int reg , const Uint8 data )
397+ {
398+ mixer_bytes [reg ] = data ;
399+ mixer_floats [reg >> 1 ] = (float )((mixer_bytes [reg & 0xFE ] << 8 ) + mixer_bytes [reg | 0x01 ]) / (float )0xFFFFU ;
393400}
394401
395402
396403void audio65_write_mixer_register ( const Uint8 data )
397404{
398- mixer_coeffs [mixer_register ] = data ;
399- if (mixer_register == 0x1E || mixer_register == 0x1F ) { // HDMI-LEFT?
400- int vol = (mixer_coeffs [0x1E ] << 8 ) + mixer_coeffs [0x1F ];
401- vol = vol * 100 / 65536 ;
402- audio_set_stereo_parameters (vol , AUDIO_UNCHANGED_SEPARATION );
403- }
404- if (mixer_register == 0x3E || mixer_register == 0x3F ) { // HDMI-RIGHT?
405- int vol = (mixer_coeffs [0x3E ] << 8 ) + mixer_coeffs [0x3F ];
406- vol = vol * 100 / 65536 ;
407- audio_set_stereo_parameters (vol , AUDIO_UNCHANGED_SEPARATION );
405+ if (mixer_bytes [mixer_register ] != data ) {
406+ write_mixer_register (mixer_register , data );
407+ recalulate_scalers ();
408408 }
409409}
410410
411411
412- void audio65_init ( int sid_cycles_per_sec , int sound_mix_freq , int volume , int separation , unsigned int buffer_size )
412+ static void default_mixer_helper ( const int basereg , const Uint16 value_l , const Uint16 value_r )
413+ {
414+ write_mixer_register (basereg + 0x00 , value_l >> 8 );
415+ write_mixer_register (basereg + 0x01 , value_l & 0xFF );
416+ write_mixer_register (basereg + 0x20 , value_r >> 8 );
417+ write_mixer_register (basereg + 0x21 , value_r & 0xFF );
418+ // repeat the same for headphones output:
419+ write_mixer_register (basereg + 0xC0 , value_l >> 8 );
420+ write_mixer_register (basereg + 0xC1 , value_l & 0xFF );
421+ write_mixer_register (basereg + 0xE0 , value_r >> 8 );
422+ write_mixer_register (basereg + 0xE1 , value_r & 0xFF );
423+ }
424+
425+
426+ static void print_audio_info ( void )
427+ {
428+ DEBUGPRINT ("AUDIO: emu-volume = %d%%, mono-downmix = %s, output-reg-shift = $%02X" NL ,
429+ xemu_volume_int ,
430+ mono_downmix ? "ON" : "off" ,
431+ output_selection
432+ );
433+ }
434+
435+
436+ void audio65_reset_mixer ( void )
437+ {
438+ mixer_register = 0 ;
439+ for (int i = 0 ; i < 0x100 ; i ++ )
440+ write_mixer_register (i , 0 );
441+ default_mixer_helper (0x00 , 0xBEBE , 0x4040 ); // SID-L input
442+ default_mixer_helper (0x02 , 0x4040 , 0xBEBE ); // SID-R input
443+ default_mixer_helper (0x10 , 0xBEBE , 0x4040 ); // DIGI-L input
444+ default_mixer_helper (0x12 , 0x4040 , 0xBEBE ); // DIGI-R input
445+ default_mixer_helper (0x1C , 0xBEBE , 0xBEBE ); // OPL FM! On MEGA65 OPL channel is maybe muted by default, btw ...
446+ default_mixer_helper (0x1E , 0xFFFF , 0xFFFF ); // master volume
447+ recalulate_scalers ();
448+ }
449+
450+
451+ static void set_volume ( int vol )
452+ {
453+ if (vol < 0 ) vol = 0 ;
454+ else if (vol > 100 ) vol = 100 ;
455+ xemu_volume_int = vol ;
456+ xemu_volume_float = (float )vol / (float )100.0 ;
457+ }
458+
459+
460+ void audio65_set_volume ( int vol )
461+ {
462+ set_volume (vol );
463+ recalulate_scalers ();
464+ print_audio_info ();
465+ }
466+
467+
468+ int audio65_get_volume ( void )
469+ {
470+ return xemu_volume_int ;
471+ }
472+
473+
474+ void audio65_set_mono_downmix ( const bool status )
475+ {
476+ mono_downmix = status ;
477+ recalulate_scalers ();
478+ print_audio_info ();
479+ }
480+
481+
482+ bool audio65_get_mono_downmix ( void )
483+ {
484+ return mono_downmix ;
485+ }
486+
487+
488+ void audio65_set_output ( const int val )
489+ {
490+ output_selection = val ;
491+ recalulate_scalers ();
492+ print_audio_info ();
493+ }
494+
495+
496+ int audio65_get_output ( void )
497+ {
498+ return output_selection ;
499+ }
500+
501+
502+ void audio65_init ( int sid_cycles_per_sec , int sound_mix_freq , int volume , unsigned int buffer_size )
413503{
414504 static volatile int initialized = 0 ;
415505 if (initialized ) {
416506 ERROR_WINDOW ("Trying to reinitialize audio??\nRefusing to do so!!" );
417507 return ;
418508 }
419509 initialized = 1 ;
510+ // Setting these parameters should be followed by calling recalulate_scalers(). However, audio65_reset_mixer() at the end will call that anyway.
511+ output_selection = AUDIO_OUTPUT_SPEAKERS ;
512+ mono_downmix = false;
513+ set_volume (volume );
514+ audio65_reset_mixer ();
515+ print_audio_info ();
420516 for (int i = 0 ; i < NUMBER_OF_SIDS ; i ++ )
421517 UNLOCK_SID ("INIT" , i );
422518 UNLOCK_OPL ("INIT" );
@@ -451,7 +547,6 @@ void audio65_init ( int sid_cycles_per_sec, int sound_mix_freq, int volume, int
451547 //}
452548 } else
453549 ERROR_WINDOW ("Cannot open audio device!" );
454- audio_set_stereo_parameters (volume , separation );
455550#else
456551 DEBUGPRINT ("AUDIO: has been disabled at compilation time." NL );
457552#endif
0 commit comments