Browse Source

SDL_audio.c: Fix crash if we switch from direct output to streaming, and if the buffersizes change.

jfmu 1 year ago
parent
commit
eb1b9b418c
1 changed files with 31 additions and 2 deletions
  1. 31 2
      src/audio/SDL_audio.c

+ 31 - 2
src/audio/SDL_audio.c

@@ -677,6 +677,7 @@ static int SDLCALL SDL_RunAudio(void *userdata)
     SDL_AudioCallback callback = device->callbackspec.callback;
     int data_len = 0;
     Uint8 *data;
+    Uint8 *device_buf_keepsafe = NULL;
 
     SDL_assert(!device->iscapture);
 
@@ -703,8 +704,16 @@ static int SDLCALL SDL_RunAudio(void *userdata)
 
         /* Fill the current buffer with sound */
         if (!device->stream && SDL_AtomicGet(&device->enabled)) {
-            SDL_assert(data_len == device->spec.size);
             data = current_audio.impl.GetDeviceBuf(device);
+
+            if (device->stream && SDL_AtomicGet(&device->enabled)) {
+                /* Oops. Audio device reset and now we suddenly use a stream, */
+                /* so save this devicebuf for later, to prevent de-sync */
+                if (data != NULL) {
+                    device_buf_keepsafe = data;
+                }
+                data = NULL;
+            }
         } else {
             /* if the device isn't enabled, we still write to the
                work_buffer, so the app's callback will fire with
@@ -735,7 +744,19 @@ static int SDLCALL SDL_RunAudio(void *userdata)
 
             while (SDL_AudioStreamAvailable(device->stream) >= ((int)device->spec.size)) {
                 int got;
-                data = SDL_AtomicGet(&device->enabled) ? current_audio.impl.GetDeviceBuf(device) : NULL;
+                if (SDL_AtomicGet(&device->enabled)) {
+                    /* if device reset occured - a switch from direct output to streaming */
+                    /* use the already aquired device buffer */
+                    if (device_buf_keepsafe) {
+                        data = device_buf_keepsafe;
+                        device_buf_keepsafe = NULL;
+                    } else {
+                        /* else - normal flow, just acquire the device buffer here */
+                        data = current_audio.impl.GetDeviceBuf(device);
+                    }
+                } else {
+                    data = NULL;
+                }
                 got = SDL_AudioStreamGet(device->stream, data ? data : device->work_buffer, device->spec.size);
                 SDL_assert((got <= 0) || (got == device->spec.size));
 
@@ -750,6 +771,14 @@ static int SDLCALL SDL_RunAudio(void *userdata)
                     current_audio.impl.WaitDevice(device);
                 }
             }
+
+            /* it seems resampling was not fast enough, device_buf_keepsafe was not released yet, so play silence here */
+            if (device_buf_keepsafe) {
+                SDL_memset(device_buf_keepsafe, device->spec.silence, device->spec.size);
+                current_audio.impl.PlayDevice(device);
+                current_audio.impl.WaitDevice(device);
+                device_buf_keepsafe = NULL;
+            }
         } else if (data == device->work_buffer) {
             /* nothing to do; pause like we queued a buffer to play. */
             const Uint32 delay = ((device->spec.samples * 1000) / device->spec.freq);