sdl12-compat: SDL_PauseAudio should work within the callback

From ea08a24b3f5eba381c0ce962914c76106116eeaa Mon Sep 17 00:00:00 2001
From: David Gow <[EMAIL REDACTED]>
Date: Tue, 22 Jun 2021 20:53:03 +0800
Subject: [PATCH] SDL_PauseAudio should work within the callback

In SDL 1.2, SDL_PauseAudio() works from within the audio callback,
whereas currently in sdl12-compat, it deadlocks due to SDL_LockAudio()
being called.

Move the callback paused state out of the callback structure (whose
allocation is protected by SDL_LockAudio()) and into a separate atomic
variable. SDL_PauseAudio() can then simply set that value.

This fixes some SDL 1.2 Ren'py games, which will deadlock somewhat
randomly either at startup or when a new piece of music plays, due to
SDL_PauseAudio() being called from within the callback.
---
 src/SDL12_compat.c | 15 +++++----------
 src/SDL20_syms.h   |  3 +++
 2 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/src/SDL12_compat.c b/src/SDL12_compat.c
index 8daf96e..f13f26d 100644
--- a/src/SDL12_compat.c
+++ b/src/SDL12_compat.c
@@ -6086,7 +6086,6 @@ typedef struct
     SDL_AudioSpec device_format;
 
     SDL_bool app_callback_opened;
-    SDL_bool app_callback_paused;
     SDL_AudioSpec app_callback_format;
     SDL_AudioStream *app_callback_stream;
 
@@ -6107,7 +6106,7 @@ typedef struct
 } AudioCallbackWrapperData;
 
 static AudioCallbackWrapperData *audio_cbdata = NULL;
-
+static SDL_atomic_t audio_callback_paused;
 
 
 static void
@@ -6721,7 +6720,7 @@ AudioCallbackWrapper(void *userdata, Uint8 *stream, int len)
     AudioCallbackWrapperData *data = (AudioCallbackWrapperData *) userdata;
     SDL_bool must_mix = SDL_FALSE;
 
-    if (data->app_callback_opened && !data->app_callback_paused) {
+    if (data->app_callback_opened && !SDL20_AtomicGet(&audio_callback_paused)) {
         while (SDL20_AudioStreamAvailable(data->app_callback_stream) < len) {
             SDL20_memset(data->mix_buffer, data->app_callback_format.silence, data->app_callback_format.size);  /* SDL2 doesn't clear the stream before calling in here, but 1.2 expects it. */
             data->app_callback_format.callback(data->app_callback_format.userdata, data->mix_buffer, data->app_callback_format.size);
@@ -6903,7 +6902,7 @@ SDL_OpenAudio(SDL_AudioSpec *want, SDL_AudioSpec *obtained)
 
     SDL20_memcpy(&audio_cbdata->app_callback_format, want, sizeof (SDL_AudioSpec));
     audio_cbdata->app_callback_opened = SDL_TRUE;
-    audio_cbdata->app_callback_paused = SDL_TRUE;  /* app callback always starts paused after open. */
+    SDL20_AtomicSet(&audio_callback_paused, SDL_TRUE);  /* app callback always starts paused after open. */
 
     FIXME("Cleanup from failures in here");
     SDL_assert(audio_cbdata->app_callback_stream == NULL);
@@ -6921,11 +6920,7 @@ SDL_OpenAudio(SDL_AudioSpec *want, SDL_AudioSpec *obtained)
 DECLSPEC void SDLCALL
 SDL_PauseAudio(int pause_on)
 {
-    SDL20_LockAudio();
-    if (audio_cbdata && audio_cbdata->app_callback_opened) {
-        audio_cbdata->app_callback_paused = pause_on ? SDL_TRUE : SDL_FALSE;
-    }
-    SDL20_UnlockAudio();
+    SDL20_AtomicSet(&audio_callback_paused, pause_on ? SDL_TRUE : SDL_FALSE);
 }
 
 DECLSPEC SDL_AudioStatus SDLCALL
@@ -6934,7 +6929,7 @@ SDL_GetAudioStatus(void)
     SDL_AudioStatus retval = SDL_AUDIO_STOPPED;
     SDL20_LockAudio();
     if (audio_cbdata && audio_cbdata->app_callback_opened) {
-        retval = audio_cbdata->app_callback_paused ? SDL_AUDIO_PAUSED : SDL_AUDIO_PLAYING;
+        retval = SDL20_AtomicGet(&audio_callback_paused) ? SDL_AUDIO_PAUSED : SDL_AUDIO_PLAYING;
     }
     SDL20_UnlockAudio();
     return retval;
diff --git a/src/SDL20_syms.h b/src/SDL20_syms.h
index baf6a02..0937d8a 100644
--- a/src/SDL20_syms.h
+++ b/src/SDL20_syms.h
@@ -188,6 +188,9 @@ SDL20_SYM_PASSTHROUGH(int,CondBroadcast,(SDL_cond *a),(a),return)
 SDL20_SYM_PASSTHROUGH(int,CondWait,(SDL_cond *a, SDL_mutex *b),(a,b),return)
 SDL20_SYM_PASSTHROUGH(int,CondWaitTimeout,(SDL_cond *a, SDL_mutex *b, Uint32 c),(a,b,c),return)
 
+SDL20_SYM(int,AtomicGet,(SDL_atomic_t *a),(a),return)
+SDL20_SYM(void,AtomicSet,(SDL_atomic_t *a, int b),(a,b),)
+
 SDL20_SYM(SDL_AudioSpec *,LoadWAV_RW,(SDL_RWops *a, int b, SDL_AudioSpec *c, Uint8 **d, Uint32 *e),(a,b,c,d,e),return)
 SDL20_SYM(int,OpenAudio,(SDL_AudioSpec *a, SDL_AudioSpec *b),(a,b),return)
 SDL20_SYM(void,CloseAudio,(void),(),)