SDL: Update sample processing bookkeeping when recovering the AAudio audio device

From 806e11ac005752c5628e394fac225806ac6d00ba Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 30 Sep 2023 15:04:21 -0700
Subject: [PATCH] Update sample processing bookkeeping when recovering the
 AAudio audio device

---
 src/audio/aaudio/SDL_aaudio.c | 73 +++++++++++++++++------------------
 1 file changed, 36 insertions(+), 37 deletions(-)

diff --git a/src/audio/aaudio/SDL_aaudio.c b/src/audio/aaudio/SDL_aaudio.c
index 5b4e6efb7248..4ea18c8afc0f 100644
--- a/src/audio/aaudio/SDL_aaudio.c
+++ b/src/audio/aaudio/SDL_aaudio.c
@@ -171,48 +171,40 @@ static void AAUDIO_WaitDevice(SDL_AudioDevice *device)
 
 static int BuildAAudioStream(SDL_AudioDevice *device);
 
-static int RecoverAAudioDeviceIfFailed(SDL_AudioDevice *device)
+static int RecoverAAudioDevice(SDL_AudioDevice *device)
 {
     struct SDL_PrivateAudioData *hidden = device->hidden;
-    const aaudio_result_t err = (aaudio_result_t) SDL_AtomicGet(&hidden->error_callback_triggered);
-    if (err) {
-        SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "aaudio: Audio device triggered error %d (%s)", (int) err, ctx.AAudio_convertResultToText(err));
 
-        // attempt to build a new stream, in case there's a new default device.
-        ctx.AAudioStream_requestStop(hidden->stream);
-        ctx.AAudioStream_close(hidden->stream);
-        hidden->stream = NULL;
+    // attempt to build a new stream, in case there's a new default device.
+    ctx.AAudioStream_requestStop(hidden->stream);
+    ctx.AAudioStream_close(hidden->stream);
+    hidden->stream = NULL;
 
-        SDL_aligned_free(hidden->mixbuf);
-        hidden->mixbuf = NULL;
+    SDL_aligned_free(hidden->mixbuf);
+    hidden->mixbuf = NULL;
 
-        SDL_DestroySemaphore(hidden->semaphore);
-        hidden->semaphore = NULL;
+    SDL_DestroySemaphore(hidden->semaphore);
+    hidden->semaphore = NULL;
 
-        const int prev_sample_frames = device->sample_frames;
-        SDL_AudioSpec prevspec;
-        SDL_copyp(&prevspec, &device->spec);
+    const int prev_sample_frames = device->sample_frames;
+    SDL_AudioSpec prevspec;
+    SDL_copyp(&prevspec, &device->spec);
 
-        if (BuildAAudioStream(device) == -1) {
-            return -1;  // oh well, we tried.
-        }
-
-        // we don't know the new device spec until we open the new device, so we saved off the old one and force it back
-        // so SDL_AudioDeviceFormatChanged can set up all the important state if necessary and then set it back to the new spec.
-        const int new_sample_frames = device->sample_frames;
-        SDL_AudioSpec newspec;
-        SDL_copyp(&newspec, &device->spec);
+    if (BuildAAudioStream(device) < 0) {
+        return -1;  // oh well, we tried.
+    }
 
-        device->sample_frames = prev_sample_frames;
-        SDL_copyp(&device->spec, &prevspec);
-        if (SDL_AudioDeviceFormatChangedAlreadyLocked(device, &newspec, new_sample_frames) == -1) {
-            return -1;  // ugh
-        }
+    // we don't know the new device spec until we open the new device, so we saved off the old one and force it back
+    // so SDL_AudioDeviceFormatChanged can set up all the important state if necessary and then set it back to the new spec.
+    const int new_sample_frames = device->sample_frames;
+    SDL_AudioSpec newspec;
+    SDL_copyp(&newspec, &device->spec);
 
-        // we're recovering from PlayDevice, so wait until the data callback fires so we know we fed the pending buffer to the device.
-        SDL_WaitSemaphore(device->hidden->semaphore);
+    device->sample_frames = prev_sample_frames;
+    SDL_copyp(&device->spec, &prevspec);
+    if (SDL_AudioDeviceFormatChangedAlreadyLocked(device, &newspec, new_sample_frames) < 0) {
+        return -1;  // ugh
     }
-
     return 0;
 }
 
@@ -222,12 +214,17 @@ static int AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int b
     struct SDL_PrivateAudioData *hidden = device->hidden;
 
     // AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here.
-    if (RecoverAAudioDeviceIfFailed(device) == -1) {
-        return -1;  // oh well, we went down hard.
-    }
+    const aaudio_result_t err = (aaudio_result_t) SDL_AtomicGet(&hidden->error_callback_triggered);
+    if (err) {
+        SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "aaudio: Audio device triggered error %d (%s)", (int) err, ctx.AAudio_convertResultToText(err));
 
-    SDL_MemoryBarrierRelease();
-    hidden->processed_bytes += buflen;
+        if (RecoverAAudioDevice(device) < 0) {
+            return -1;  // oh well, we went down hard.
+        }
+    } else {
+        SDL_MemoryBarrierRelease();
+        hidden->processed_bytes += buflen;
+    }
     return 0;
 }
 
@@ -358,6 +355,8 @@ static int BuildAAudioStream(SDL_AudioDevice *device)
     if (hidden->mixbuf == NULL) {
         return SDL_OutOfMemory();
     }
+    hidden->processed_bytes = 0;
+    hidden->callback_bytes = 0;
 
     hidden->semaphore = SDL_CreateSemaphore(iscapture ? 0 : hidden->num_buffers);
     if (!hidden->semaphore) {