SDL: aaudio: Disconnect playing devices if error callback fires.

https://github.com/libsdl-org/SDL/commit/2507c1d68b5ab234417cd45d0f065229aa61e06a

From 2507c1d68b5ab234417cd45d0f065229aa61e06a Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Sat, 29 Jul 2023 19:53:38 -0400
Subject: [PATCH] aaudio: Disconnect playing devices if error callback fires.

---
 src/audio/aaudio/SDL_aaudio.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/src/audio/aaudio/SDL_aaudio.c b/src/audio/aaudio/SDL_aaudio.c
index 1ac0b0b3479e..f49599eb39b6 100644
--- a/src/audio/aaudio/SDL_aaudio.c
+++ b/src/audio/aaudio/SDL_aaudio.c
@@ -39,6 +39,7 @@ struct SDL_PrivateAudioData
     AAudioStream *stream;
     Uint8 *mixbuf;    // Raw mixing buffer
     SDL_Semaphore *semaphore;
+    SDL_AtomicInt error_callback_triggered;
     int resume;  // Resume device if it was paused automatically
 };
 
@@ -73,8 +74,12 @@ static int AAUDIO_LoadFunctions(AAUDIO_Data *data)
 static void AAUDIO_errorCallback(AAudioStream *stream, void *userData, aaudio_result_t error)
 {
     LOGI("SDL AAUDIO_errorCallback: %d - %s", error, ctx.AAudio_convertResultToText(error));
-    // !!! FIXME: you MUST NOT close the audio stream from this callback, so we cannot call SDL_AudioDeviceDisconnected here.
-    // !!! FIXME: but we should flag the device and kill it in WaitDevice/PlayDevice.
+
+    // You MUST NOT close the audio stream from this callback, so we cannot call SDL_AudioDeviceDisconnected here.
+    // Just flag the device so we can kill it in WaitDevice/PlayDevice instead.
+    SDL_AudioDevice *device = (SDL_AudioDevice *) userData;
+    SDL_AtomicSet(&device->hidden->error_callback_triggered, 1);
+    SDL_PostSemaphore(device->hidden->semaphore);  // in case we're blocking in WaitDevice.
 }
 
 static aaudio_data_callback_result_t AAUDIO_dataCallback(AAudioStream *stream, void *userData, void *audioData, int32_t numFrames);
@@ -104,6 +109,8 @@ static int AAUDIO_OpenDevice(SDL_AudioDevice *device)
         return SDL_OutOfMemory();
     }
 
+    SDL_AtomicSet(&hidden->error_callback_triggered, 0);
+
     AAudioStreamBuilder *builder = NULL;
     res = ctx.AAudio_createStreamBuilder(&builder);
     if (res != AAUDIO_OK) {
@@ -264,7 +271,11 @@ static void AAUDIO_WaitDevice(SDL_AudioDevice *device)
 
 static void AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
 {
-    // AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice.
+    // AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here.
+    if (SDL_AtomicGet(&device->hidden->error_callback_triggered)) {
+        SDL_AtomicSet(&device->hidden->error_callback_triggered, 0);
+        SDL_AudioDeviceDisconnected(device);
+    }
 }
 
 // no need for a FlushCapture implementation, just don't read mixbuf until the next iteration.