From 9f4e6992fb3972c660fe92dbd7f73e3903fe900b Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 25 Oct 2021 16:09:02 +0000 Subject: [PATCH] simplify/fix sdl2 sound backend, fixes no audio in CLI - simplified ring-buffer mechanism - added proper locking for all variables accessed by 2 different threads - fixed oob writes that occassionally crashed SDL's "Alsa Hotplug thread" - make buffer sufficiently large to prebuffer enough samples to survive the occassional SDL_Delay(1) in the frontend. - fixed ignoring volume set by the SPU. - improved speed and robustness by not calling malloc over and over in SDL callback, and copying directly to the SDL buffer if volume is max (no need to use mixer to lower the volume in that case). --- desmume/src/frontend/posix/shared/sndsdl.cpp | 138 ++++++++++--------- 1 file changed, 70 insertions(+), 68 deletions(-) diff --git a/desmume/src/frontend/posix/shared/sndsdl.cpp b/desmume/src/frontend/posix/shared/sndsdl.cpp index 892c65819..31f3ca222 100644 --- a/desmume/src/frontend/posix/shared/sndsdl.cpp +++ b/desmume/src/frontend/posix/shared/sndsdl.cpp @@ -51,10 +51,9 @@ SNDSDLUnMuteAudio, SNDSDLSetVolume }; -static u16 *stereodata16; -static u32 soundoffset; -static volatile u32 soundpos; -static u32 soundlen; +static u8 *stereodata; +static u8 *mixerdata; +static u32 soundoff; static u32 soundbufsize; static SDL_AudioSpec audiofmt; int audio_volume; @@ -81,20 +80,26 @@ DWORD WINAPI SNDXBOXThread( LPVOID ) static void MixAudio(void *userdata, Uint8 *stream, int len) { int i; - Uint8 *soundbuf=(Uint8 *)stereodata16; - - Uint8 *stream_tmp=(Uint8 *)malloc(len); - for (i = 0; i < len; i++) - { - if (soundpos >= soundbufsize) - soundpos = 0; - - stream_tmp[i] = soundbuf[soundpos]; - soundpos++; + /* if the volume is max, we don't need to call the mixer and do additional + memory copying/cleaning, but can copy directly into the SDL buffer. */ + u8 *dest = audio_volume == SDL_MIX_MAXVOLUME ? stream : mixerdata; + SDL_LockAudio(); + if (len > soundoff) { + //dprintf(2, "bu: want %u, got %u\n", (unsigned)len, (unsigned)soundoff); + /* buffer underrun - rather than zeroing out SDL's audio buffer, we just + hand back the existing data, which reduces cracking */ + len = soundoff; + } + memcpy(dest, stereodata, len); + soundoff -= len; + /* move rest of prebuffered data to the beginning of the buffer */ + if (soundoff) + memmove(stereodata, stereodata+len, soundoff); + SDL_UnlockAudio(); + if(audio_volume != SDL_MIX_MAXVOLUME) { + memset(stream, 0, len); + SDL_MixAudio(stream, mixerdata, len, audio_volume); } - memset(stream, 0, len); - SDL_MixAudio(stream, stream_tmp, len, audio_volume); - free(stream_tmp); } ////////////////////////////////////////////////////////////////////////////// @@ -114,34 +119,37 @@ int SNDSDLInit(int buffersize) //samples should be a power of 2 according to SDL-doc //so normalize it to the nearest power of 2 here u32 normSamples = 512; - while (normSamples < audiofmt.samples) - normSamples <<= 1; + while (normSamples < audiofmt.samples) normSamples <<= 1; audiofmt.samples = normSamples; - - soundlen = audiofmt.freq / 60; // 60 for NTSC - soundbufsize = buffersize * sizeof(s16) * 2; - if (SDL_OpenAudio(&audiofmt, NULL) != 0) + SDL_AudioSpec obtained; + if (SDL_OpenAudio(&audiofmt, &obtained) != 0) { return -1; } - if ((stereodata16 = (u16 *)malloc(soundbufsize)) == NULL) + audiofmt = obtained; + + /* allocate multiple of the size needed by SDL to be able to efficiently pre-buffer. + we effectively need the engine to pre-buffer a couple milliseconds because the + frontend might call SDL_Delay() to achieve the desired framerate, during which + the buffer isn't updated by the SPU. */ + soundbufsize = audiofmt.size * 4; + soundoff = 0; + + if ((stereodata = (u8*)calloc(soundbufsize, 1)) == NULL || + (mixerdata = (u8*)calloc(soundbufsize, 1)) == NULL) return -1; - memset(stereodata16, 0, soundbufsize); - - soundpos = 0; - - SDL_PauseAudio(0); - #ifdef _XBOX - doterminate = false; + doterminate = false; terminated = false; CreateThread(0,0,SNDXBOXThread,0,0,0); #endif + SDL_PauseAudio(0); + return 0; } @@ -157,38 +165,22 @@ void SNDSDLDeInit() #endif SDL_CloseAudio(); - if (stereodata16) - free(stereodata16); + free(stereodata); + free(mixerdata); + stereodata = NULL; + mixerdata = NULL; } ////////////////////////////////////////////////////////////////////////////// void SNDSDLUpdateAudio(s16 *buffer, u32 num_samples) { - u32 copy1size=0, copy2size=0; + // dprintf(2, "up: %u\n", num_samples); + u8 *incoming = (u8*) buffer; + u32 n = num_samples * 4; SDL_LockAudio(); - - if ((soundbufsize - soundoffset) < (num_samples * sizeof(s16) * 2)) - { - copy1size = (soundbufsize - soundoffset); - copy2size = (num_samples * sizeof(s16) * 2) - copy1size; - } - else - { - copy1size = (num_samples * sizeof(s16) * 2); - copy2size = 0; - } - - memcpy((((u8 *)stereodata16)+soundoffset), buffer, copy1size); -// ScspConvert32uto16s((s32 *)leftchanbuffer, (s32 *)rightchanbuffer, (s16 *)(((u8 *)stereodata16)+soundoffset), copy1size / sizeof(s16) / 2); - - if (copy2size) - memcpy(stereodata16, ((u8 *)buffer)+copy1size, copy2size); -// ScspConvert32uto16s((s32 *)leftchanbuffer, (s32 *)rightchanbuffer, (s16 *)stereodata16, copy2size / sizeof(s16) / 2); - - soundoffset += copy1size + copy2size; - soundoffset %= soundbufsize; - + memcpy(stereodata+soundoff, incoming, n); + soundoff += n; SDL_UnlockAudio(); } @@ -196,14 +188,11 @@ void SNDSDLUpdateAudio(s16 *buffer, u32 num_samples) u32 SNDSDLGetAudioSpace() { - u32 freespace=0; - - if (soundoffset > soundpos) - freespace = soundbufsize - soundoffset + soundpos; - else - freespace = soundpos - soundoffset; - - return (freespace / sizeof(s16) / 2); + u32 tmp; + SDL_LockAudio(); + tmp = (soundbufsize - soundoff)/4; + SDL_UnlockAudio(); + return tmp; } ////////////////////////////////////////////////////////////////////////////// @@ -222,16 +211,29 @@ void SNDSDLUnMuteAudio() ////////////////////////////////////////////////////////////////////////////// +/* the engine's min/max is 0/100, SDLs 0/128 */ void SNDSDLSetVolume(int volume) { + u32 tmp = volume * SDL_MIX_MAXVOLUME; + SDL_LockAudio(); + audio_volume = tmp / 100; + SDL_UnlockAudio(); } ////////////////////////////////////////////////////////////////////////////// +/* these 2 are special functions that the GTK frontend calls directly. + and it has a slider where its max is 128. doh. */ + int SNDSDLGetAudioVolume() { - return audio_volume; + SDL_LockAudio(); + int tmp = audio_volume; + SDL_UnlockAudio(); + return tmp; } -void SNDSDLSetAudioVolume(int value) -{ - audio_volume = value; + +void SNDSDLSetAudioVolume(int volume) { + SDL_LockAudio(); + audio_volume = volume; + SDL_UnlockAudio(); }