Fixed bug 4171 - SDL_GetQueuedAudioSize is broken with WASAPI
authorSam Lantinga
Tue, 04 Jun 2019 17:32:15 -0700
changeset 1275904552ff8c83d
parent 12758 3c6a6dad3487
child 12760 728c6562100f
Fixed bug 4171 - SDL_GetQueuedAudioSize is broken with WASAPI

Cameron Gutman

I was trying to use SDL_GetQueuedAudioSize() to ensure my audio latency didn't get too high while streaming data in from the network. If I get more than N frames of audio queued, I know that the network is giving me more data than I can play and I need to drop some to keep latency low.

This doesn't work well on WASAPI out of the box, due to the addition of GetPendingBytes() to the amount of queued data. As a terrible hack, I loop 100 times calling SDL_Delay(10) and SDL_GetQueuedAudioSize() before I ever call SDL_QueueAudio() to get a "baseline" amount that I then subtract from SDL_GetQueuedAudioSize() later. However, because this value isn't actually a constant, this hack can cause SDL_GetQueuedAudioSize() - baselineSize to be < 0. This means I have no accurate way of determining how much data is actually queued in SDL's audio buffer queue.

The SDL_GetQueuedAudioSize() documentation says: "This is the number of bytes that have been queued for playback with SDL_QueueAudio(), but have not yet been sent to the hardware." Yet, SDL_GetQueuedAudioSize() returns > 0 value when SDL_QueueAudio() has never been called.

Based on that documentation, I believe the current behavior contradicts the documented behavior of this function and should be changed in line with Boris's patch.

I understand that exposing the IAudioClient::GetCurrentPadding() value is useful, but a solution there needs to take into account what of that data is silence inserted by SDL and what is actual data queued by the user with SDL_QueueAudio(). Until that happens, I think the best approach is to remove the GetPendingBytes() call until SDL is able to keep track of queued data to make sense of it. This would make SDL_GetQueuedAudioSize() possible to use accurately with WASAPI.
src/audio/SDL_audio.c
src/audio/SDL_sysaudio.h
src/audio/wasapi/SDL_wasapi.c
     1.1 --- a/src/audio/SDL_audio.c	Tue May 28 17:39:13 2019 -0400
     1.2 +++ b/src/audio/SDL_audio.c	Tue Jun 04 17:32:15 2019 -0700
     1.3 @@ -248,12 +248,6 @@
     1.4  {                               /* no-op. */
     1.5  }
     1.6  
     1.7 -static int
     1.8 -SDL_AudioGetPendingBytes_Default(_THIS)
     1.9 -{
    1.10 -    return 0;
    1.11 -}
    1.12 -
    1.13  static Uint8 *
    1.14  SDL_AudioGetDeviceBuf_Default(_THIS)
    1.15  {
    1.16 @@ -361,7 +355,6 @@
    1.17      FILL_STUB(BeginLoopIteration);
    1.18      FILL_STUB(WaitDevice);
    1.19      FILL_STUB(PlayDevice);
    1.20 -    FILL_STUB(GetPendingBytes);
    1.21      FILL_STUB(GetDeviceBuf);
    1.22      FILL_STUB(CaptureFromDevice);
    1.23      FILL_STUB(FlushCapture);
    1.24 @@ -654,11 +647,9 @@
    1.25      }
    1.26  
    1.27      /* Nothing to do unless we're set up for queueing. */
    1.28 -    if (device->callbackspec.callback == SDL_BufferQueueDrainCallback) {
    1.29 -        current_audio.impl.LockDevice(device);
    1.30 -        retval = ((Uint32) SDL_CountDataQueue(device->buffer_queue)) + current_audio.impl.GetPendingBytes(device);
    1.31 -        current_audio.impl.UnlockDevice(device);
    1.32 -    } else if (device->callbackspec.callback == SDL_BufferQueueFillCallback) {
    1.33 +    if (device->callbackspec.callback == SDL_BufferQueueDrainCallback ||
    1.34 +        device->callbackspec.callback == SDL_BufferQueueFillCallback)
    1.35 +    {
    1.36          current_audio.impl.LockDevice(device);
    1.37          retval = (Uint32) SDL_CountDataQueue(device->buffer_queue);
    1.38          current_audio.impl.UnlockDevice(device);
     2.1 --- a/src/audio/SDL_sysaudio.h	Tue May 28 17:39:13 2019 -0400
     2.2 +++ b/src/audio/SDL_sysaudio.h	Tue Jun 04 17:32:15 2019 -0700
     2.3 @@ -71,7 +71,6 @@
     2.4      void (*BeginLoopIteration)(_THIS);  /* Called by audio thread at top of loop */
     2.5      void (*WaitDevice) (_THIS);
     2.6      void (*PlayDevice) (_THIS);
     2.7 -    int (*GetPendingBytes) (_THIS);
     2.8      Uint8 *(*GetDeviceBuf) (_THIS);
     2.9      int (*CaptureFromDevice) (_THIS, void *buffer, int buflen);
    2.10      void (*FlushCapture) (_THIS);
     3.1 --- a/src/audio/wasapi/SDL_wasapi.c	Tue May 28 17:39:13 2019 -0400
     3.2 +++ b/src/audio/wasapi/SDL_wasapi.c	Tue Jun 04 17:32:15 2019 -0700
     3.3 @@ -161,21 +161,6 @@
     3.4      WASAPI_EnumerateEndpoints();
     3.5  }
     3.6  
     3.7 -static int
     3.8 -WASAPI_GetPendingBytes(_THIS)
     3.9 -{
    3.10 -    UINT32 frames = 0;
    3.11 -
    3.12 -    /* it's okay to fail here; we'll deal with failures in the audio thread. */
    3.13 -    /* FIXME: need a lock around checking this->hidden->client */
    3.14 -    if (this->hidden->client != NULL) {  /* definitely activated? */
    3.15 -        if (FAILED(IAudioClient_GetCurrentPadding(this->hidden->client, &frames))) {
    3.16 -            return 0;  /* oh well. */
    3.17 -        }
    3.18 -    }
    3.19 -    return ((int) frames) * this->hidden->framesize;
    3.20 -}
    3.21 -
    3.22  static SDL_INLINE SDL_bool
    3.23  WasapiFailed(_THIS, const HRESULT err)
    3.24  {
    3.25 @@ -765,7 +750,6 @@
    3.26      impl->OpenDevice = WASAPI_OpenDevice;
    3.27      impl->PlayDevice = WASAPI_PlayDevice;
    3.28      impl->WaitDevice = WASAPI_WaitDevice;
    3.29 -    impl->GetPendingBytes = WASAPI_GetPendingBytes;
    3.30      impl->GetDeviceBuf = WASAPI_GetDeviceBuf;
    3.31      impl->CaptureFromDevice = WASAPI_CaptureFromDevice;
    3.32      impl->FlushCapture = WASAPI_FlushCapture;