Browse Source

audio: Optimize setting device formats during audio thread iteration.

The API entry point does a ton of unnecessary validation just to eventually
do a memcpy().
Ryan C. Gordon 1 year ago
parent
commit
36b0f11414
1 changed files with 25 additions and 7 deletions
  1. 25 7
      src/audio/SDL_audio.c

+ 25 - 7
src/audio/SDL_audio.c

@@ -707,6 +707,24 @@ static void MixFloat32Audio(float *dst, const float *src, const int buffer_size)
     }
     }
 }
 }
 
 
+static int GetAudioStreamDataInFormat(SDL_AudioStream *stream, void *voidbuf, int len, const SDL_AudioSpec *spec)
+{
+    // SDL_SetAudioStreamFormat is just doing this plus a lot of heavy validation we can skip.
+    SDL_LockMutex(stream->lock);
+    SDL_copyp(&stream->dst_spec, spec);
+    SDL_UnlockMutex(stream->lock);
+    return SDL_GetAudioStreamData(stream, voidbuf, len);
+}
+
+static int PutAudioStreamDataInFormat(SDL_AudioStream *stream, void *voidbuf, int len, const SDL_AudioSpec *spec)
+{
+    // SDL_SetAudioStreamFormat is just doing this plus a lot of heavy validation we can skip.
+    SDL_LockMutex(stream->lock);
+    SDL_copyp(&stream->src_spec, spec);
+    SDL_UnlockMutex(stream->lock);
+    return SDL_PutAudioStreamData(stream, voidbuf, len);
+}
+
 
 
 // Output device thread. This is split into chunks, so backends that need to control this directly can use the pieces they need without duplicating effort.
 // Output device thread. This is split into chunks, so backends that need to control this directly can use the pieces they need without duplicating effort.
 
 
@@ -746,8 +764,8 @@ SDL_bool SDL_OutputAudioThreadIterate(SDL_AudioDevice *device)
         if (simple_copy) {
         if (simple_copy) {
             SDL_LogicalAudioDevice *logdev = device->logical_devices;
             SDL_LogicalAudioDevice *logdev = device->logical_devices;
             SDL_AudioStream *stream = logdev->bound_streams;
             SDL_AudioStream *stream = logdev->bound_streams;
-            SDL_SetAudioStreamFormat(stream, NULL, &device->spec);
-            const int br = SDL_GetAudioStreamData(stream, device_buffer, buffer_size);
+
+            const int br = GetAudioStreamDataInFormat(stream, device_buffer, buffer_size, &device->spec);
             if (br < 0) {  // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow.
             if (br < 0) {  // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow.
                 retval = SDL_FALSE;
                 retval = SDL_FALSE;
                 SDL_memset(device_buffer, device->silence_value, buffer_size);  // just supply silence to the device before we die.
                 SDL_memset(device_buffer, device->silence_value, buffer_size);  // just supply silence to the device before we die.
@@ -781,13 +799,11 @@ SDL_bool SDL_OutputAudioThreadIterate(SDL_AudioDevice *device)
                 }
                 }
 
 
                 for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) {
                 for (SDL_AudioStream *stream = logdev->bound_streams; stream != NULL; stream = stream->next_binding) {
-                    SDL_SetAudioStreamFormat(stream, NULL, &outspec);
-
                     /* this will hold a lock on `stream` while getting. We don't explicitly lock the streams
                     /* this will hold a lock on `stream` while getting. We don't explicitly lock the streams
                        for iterating here because the binding linked list can only change while the device lock is held.
                        for iterating here because the binding linked list can only change while the device lock is held.
                        (we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind
                        (we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind
                        the same stream to different devices at the same time, though.) */
                        the same stream to different devices at the same time, though.) */
-                    const int br = SDL_GetAudioStreamData(stream, device->work_buffer, work_buffer_size);
+                    const int br = GetAudioStreamDataInFormat(stream, device->work_buffer, work_buffer_size, &outspec);
                     if (br < 0) {  // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow.
                     if (br < 0) {  // Probably OOM. Kill the audio device; the whole thing is likely dying soon anyhow.
                         retval = SDL_FALSE;
                         retval = SDL_FALSE;
                         break;
                         break;
@@ -903,8 +919,7 @@ SDL_bool SDL_CaptureAudioThreadIterate(SDL_AudioDevice *device)
                        for iterating here because the binding linked list can only change while the device lock is held.
                        for iterating here because the binding linked list can only change while the device lock is held.
                        (we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind
                        (we _do_ lock the stream during binding/unbinding to make sure that two threads can't try to bind
                        the same stream to different devices at the same time, though.) */
                        the same stream to different devices at the same time, though.) */
-                    SDL_SetAudioStreamFormat(stream, &outspec, NULL);
-                    if (SDL_PutAudioStreamData(stream, output_buffer, br) < 0) {
+                    if (PutAudioStreamDataInFormat(stream, output_buffer, br, &outspec) < 0) {
                         // oh crud, we probably ran out of memory. This is possibly an overreaction to kill the audio device, but it's likely the whole thing is going down in a moment anyhow.
                         // oh crud, we probably ran out of memory. This is possibly an overreaction to kill the audio device, but it's likely the whole thing is going down in a moment anyhow.
                         retval = SDL_FALSE;
                         retval = SDL_FALSE;
                         break;
                         break;
@@ -1492,6 +1507,9 @@ int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int
         return SDL_SetError("Cannot change stream bindings on device opened with SDL_OpenAudioDeviceStream");
         return SDL_SetError("Cannot change stream bindings on device opened with SDL_OpenAudioDeviceStream");
     }
     }
 
 
+    // !!! FIXME: We'll set the device's side's format below, but maybe we should refuse to bind a stream if the app's side doesn't have a format set yet.
+    // !!! FIXME: Actually, why do we allow there to be an invalid format, again?
+
     // make sure start of list is sane.
     // make sure start of list is sane.
     SDL_assert(!logdev->bound_streams || (logdev->bound_streams->prev_binding == NULL));
     SDL_assert(!logdev->bound_streams || (logdev->bound_streams->prev_binding == NULL));