audio: Don't trust audio drivers to drain pending audio.
authorRyan C. Gordon
Fri, 07 Oct 2016 15:13:46 -0400
changeset 10473d6427519ca66
parent 10472 a3d50ebe2a3b
child 10474 e723c663ba58
audio: Don't trust audio drivers to drain pending audio.

This tends to be a frequent spot where drivers hang, and the waits were
often unreliable in any case.

Instead, our audio thread now alerts the driver that we're done streaming audio
(which currently XAudio2 uses to alert the system not to warn about the
impending underflow) and then SDL_Delay()'s for a duration that's reasonable
to drain the DMA buffers before closing the device.
src/audio/SDL_audio.c
src/audio/SDL_sysaudio.h
src/audio/arts/SDL_artsaudio.c
src/audio/directsound/SDL_directsound.c
src/audio/fusionsound/SDL_fsaudio.c
src/audio/psp/SDL_pspaudio.c
src/audio/pulseaudio/SDL_pulseaudio.c
src/audio/qsa/SDL_qsa_audio.c
src/audio/sndio/SDL_sndioaudio.c
src/audio/winmm/SDL_winmm.c
src/audio/xaudio2/SDL_xaudio2.c
     1.1 --- a/src/audio/SDL_audio.c	Fri Oct 07 14:42:24 2016 -0400
     1.2 +++ b/src/audio/SDL_audio.c	Fri Oct 07 15:13:46 2016 -0400
     1.3 @@ -191,11 +191,6 @@
     1.4      return NULL;
     1.5  }
     1.6  
     1.7 -static void
     1.8 -SDL_AudioWaitDone_Default(_THIS)
     1.9 -{                               /* no-op. */
    1.10 -}
    1.11 -
    1.12  static int
    1.13  SDL_AudioCaptureFromDevice_Default(_THIS, void *buffer, int buflen)
    1.14  {
    1.15 @@ -208,6 +203,11 @@
    1.16  }
    1.17  
    1.18  static void
    1.19 +SDL_AudioPrepareToClose_Default(_THIS)
    1.20 +{                               /* no-op. */
    1.21 +}
    1.22 +
    1.23 +static void
    1.24  SDL_AudioCloseDevice_Default(_THIS)
    1.25  {                               /* no-op. */
    1.26  }
    1.27 @@ -292,9 +292,9 @@
    1.28      FILL_STUB(PlayDevice);
    1.29      FILL_STUB(GetPendingBytes);
    1.30      FILL_STUB(GetDeviceBuf);
    1.31 -    FILL_STUB(WaitDone);
    1.32      FILL_STUB(CaptureFromDevice);
    1.33      FILL_STUB(FlushCapture);
    1.34 +    FILL_STUB(PrepareToClose);
    1.35      FILL_STUB(CloseDevice);
    1.36      FILL_STUB(LockDevice);
    1.37      FILL_STUB(UnlockDevice);
    1.38 @@ -715,6 +715,9 @@
    1.39          return;
    1.40      }
    1.41  
    1.42 +    SDL_AtomicSet(&device->shutdown, 1);  /* just in case. */
    1.43 +    SDL_AtomicSet(&device->enabled, 0);
    1.44 +
    1.45      /* lock/unlock here so we don't race if the audio thread saw the shutdown
    1.46         var without locking, and the thread that requested shutdown is now
    1.47         trying to unlock the mutex while we destroy it. Threading is hard. */
    1.48 @@ -811,9 +814,10 @@
    1.49          }
    1.50      }
    1.51  
    1.52 +    current_audio.impl.PrepareToClose(device);
    1.53 +
    1.54      /* Wait for the audio to drain. */
    1.55 -    /* !!! FIXME: can we rename this WaitDrain? */
    1.56 -    current_audio.impl.WaitDone(device);
    1.57 +    SDL_Delay(((device->spec.samples * 1000) / device->spec.freq) * 2);
    1.58  
    1.59      SDL_FinalizeAudioDevice(device);
    1.60  
    1.61 @@ -1153,7 +1157,7 @@
    1.62  
    1.63          if (!device->iscapture) {
    1.64              const SDL_AudioSpec *spec = &device->spec;
    1.65 -            delay = ((spec.samples * 1000) / spec.freq) * 2;
    1.66 +            delay = ((spec->samples * 1000) / spec->freq) * 2;
    1.67          }
    1.68  
    1.69          /* Lock to make sure an audio callback doesn't fire after we return.
     2.1 --- a/src/audio/SDL_sysaudio.h	Fri Oct 07 14:42:24 2016 -0400
     2.2 +++ b/src/audio/SDL_sysaudio.h	Fri Oct 07 15:13:46 2016 -0400
     2.3 @@ -79,9 +79,9 @@
     2.4      void (*PlayDevice) (_THIS);
     2.5      int (*GetPendingBytes) (_THIS);
     2.6      Uint8 *(*GetDeviceBuf) (_THIS);
     2.7 -    void (*WaitDone) (_THIS);
     2.8      int (*CaptureFromDevice) (_THIS, void *buffer, int buflen);
     2.9      void (*FlushCapture) (_THIS);
    2.10 +    void (*PrepareToClose) (_THIS);  /**< Called between run and draining wait for playback devices */
    2.11      void (*CloseDevice) (_THIS);
    2.12      void (*LockDevice) (_THIS);
    2.13      void (*UnlockDevice) (_THIS);
     3.1 --- a/src/audio/arts/SDL_artsaudio.c	Fri Oct 07 14:42:24 2016 -0400
     3.2 +++ b/src/audio/arts/SDL_artsaudio.c	Fri Oct 07 15:13:46 2016 -0400
     3.3 @@ -185,13 +185,6 @@
     3.4  #endif
     3.5  }
     3.6  
     3.7 -static void
     3.8 -ARTS_WaitDone(_THIS)
     3.9 -{
    3.10 -    /* !!! FIXME: camp here until buffer drains... SDL_Delay(???); */
    3.11 -}
    3.12 -
    3.13 -
    3.14  static Uint8 *
    3.15  ARTS_GetDeviceBuf(_THIS)
    3.16  {
    3.17 @@ -356,7 +349,6 @@
    3.18      impl->WaitDevice = ARTS_WaitDevice;
    3.19      impl->GetDeviceBuf = ARTS_GetDeviceBuf;
    3.20      impl->CloseDevice = ARTS_CloseDevice;
    3.21 -    impl->WaitDone = ARTS_WaitDone;
    3.22      impl->Deinitialize = ARTS_Deinitialize;
    3.23      impl->OnlyHasDefaultOutputDevice = 1;
    3.24  
     4.1 --- a/src/audio/directsound/SDL_directsound.c	Fri Oct 07 14:42:24 2016 -0400
     4.2 +++ b/src/audio/directsound/SDL_directsound.c	Fri Oct 07 15:13:46 2016 -0400
     4.3 @@ -307,22 +307,6 @@
     4.4      return (this->hidden->locked_buf);
     4.5  }
     4.6  
     4.7 -static void
     4.8 -DSOUND_WaitDone(_THIS)
     4.9 -{
    4.10 -    Uint8 *stream = DSOUND_GetDeviceBuf(this);
    4.11 -
    4.12 -    /* Wait for the playing chunk to finish */
    4.13 -    if (stream != NULL) {
    4.14 -        SDL_memset(stream, this->spec.silence, this->spec.size);
    4.15 -        DSOUND_PlayDevice(this);
    4.16 -    }
    4.17 -    DSOUND_WaitDevice(this);
    4.18 -
    4.19 -    /* Stop the looping sound buffer */
    4.20 -    IDirectSoundBuffer_Stop(this->hidden->mixbuf);
    4.21 -}
    4.22 -
    4.23  static int
    4.24  DSOUND_CaptureFromDevice(_THIS, void *buffer, int buflen)
    4.25  {
    4.26 @@ -600,7 +584,6 @@
    4.27      impl->OpenDevice = DSOUND_OpenDevice;
    4.28      impl->PlayDevice = DSOUND_PlayDevice;
    4.29      impl->WaitDevice = DSOUND_WaitDevice;
    4.30 -    impl->WaitDone = DSOUND_WaitDone;
    4.31      impl->GetDeviceBuf = DSOUND_GetDeviceBuf;
    4.32      impl->CaptureFromDevice = DSOUND_CaptureFromDevice;
    4.33      impl->FlushCapture = DSOUND_FlushCapture;
     5.1 --- a/src/audio/fusionsound/SDL_fsaudio.c	Fri Oct 07 14:42:24 2016 -0400
     5.2 +++ b/src/audio/fusionsound/SDL_fsaudio.c	Fri Oct 07 15:13:46 2016 -0400
     5.3 @@ -151,13 +151,6 @@
     5.4  #endif
     5.5  }
     5.6  
     5.7 -static void
     5.8 -SDL_FS_WaitDone(_THIS)
     5.9 -{
    5.10 -    this->hidden->stream->Wait(this->hidden->stream,
    5.11 -                               this->hidden->mixsamples * FUSION_BUFFERS);
    5.12 -}
    5.13 -
    5.14  
    5.15  static Uint8 *
    5.16  SDL_FS_GetDeviceBuf(_THIS)
    5.17 @@ -319,7 +312,6 @@
    5.18      impl->WaitDevice = SDL_FS_WaitDevice;
    5.19      impl->GetDeviceBuf = SDL_FS_GetDeviceBuf;
    5.20      impl->CloseDevice = SDL_FS_CloseDevice;
    5.21 -    impl->WaitDone = SDL_FS_WaitDone;
    5.22      impl->Deinitialize = SDL_FS_Deinitialize;
    5.23      impl->OnlyHasDefaultOutputDevice = 1;
    5.24  
     6.1 --- a/src/audio/psp/SDL_pspaudio.c	Fri Oct 07 14:42:24 2016 -0400
     6.2 +++ b/src/audio/psp/SDL_pspaudio.c	Fri Oct 07 15:13:46 2016 -0400
     6.3 @@ -152,7 +152,6 @@
     6.4      impl->PlayDevice = PSPAUDIO_PlayDevice;
     6.5      impl->WaitDevice = PSPAUDIO_WaitDevice;
     6.6      impl->GetDeviceBuf = PSPAUDIO_GetDeviceBuf;
     6.7 -    impl->WaitDone = PSPAUDIO_WaitDevice;
     6.8      impl->CloseDevice = PSPAUDIO_CloseDevice;
     6.9      impl->ThreadInit = PSPAUDIO_ThreadInit;
    6.10  
     7.1 --- a/src/audio/pulseaudio/SDL_pulseaudio.c	Fri Oct 07 14:42:24 2016 -0400
     7.2 +++ b/src/audio/pulseaudio/SDL_pulseaudio.c	Fri Oct 07 15:13:46 2016 -0400
     7.3 @@ -368,28 +368,6 @@
     7.4      }
     7.5  }
     7.6  
     7.7 -static void
     7.8 -PULSEAUDIO_WaitDone(_THIS)
     7.9 -{
    7.10 -    if (SDL_AtomicGet(&this->enabled)) {
    7.11 -        struct SDL_PrivateAudioData *h = this->hidden;
    7.12 -        pa_operation *o = PULSEAUDIO_pa_stream_drain(h->stream, stream_operation_complete_no_op, NULL);
    7.13 -        if (o) {
    7.14 -            while (PULSEAUDIO_pa_operation_get_state(o) != PA_OPERATION_DONE) {
    7.15 -                if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
    7.16 -                    PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
    7.17 -                    PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
    7.18 -                    PULSEAUDIO_pa_operation_cancel(o);
    7.19 -                    break;
    7.20 -                }
    7.21 -            }
    7.22 -            PULSEAUDIO_pa_operation_unref(o);
    7.23 -        }
    7.24 -    }
    7.25 -}
    7.26 -
    7.27 -
    7.28 -
    7.29  static Uint8 *
    7.30  PULSEAUDIO_GetDeviceBuf(_THIS)
    7.31  {
    7.32 @@ -776,7 +754,6 @@
    7.33      impl->WaitDevice = PULSEAUDIO_WaitDevice;
    7.34      impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
    7.35      impl->CloseDevice = PULSEAUDIO_CloseDevice;
    7.36 -    impl->WaitDone = PULSEAUDIO_WaitDone;
    7.37      impl->Deinitialize = PULSEAUDIO_Deinitialize;
    7.38      impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice;
    7.39      impl->FlushCapture = PULSEAUDIO_FlushCapture;
     8.1 --- a/src/audio/qsa/SDL_qsa_audio.c	Fri Oct 07 14:42:24 2016 -0400
     8.2 +++ b/src/audio/qsa/SDL_qsa_audio.c	Fri Oct 07 15:13:46 2016 -0400
     8.3 @@ -709,24 +709,6 @@
     8.4  }
     8.5  
     8.6  static void
     8.7 -QSA_WaitDone(_THIS)
     8.8 -{
     8.9 -    if (!this->hidden->iscapture) {
    8.10 -        if (this->hidden->audio_handle != NULL) {
    8.11 -            /* Wait till last fragment is played and stop channel */
    8.12 -            snd_pcm_plugin_flush(this->hidden->audio_handle,
    8.13 -                                 SND_PCM_CHANNEL_PLAYBACK);
    8.14 -        }
    8.15 -    } else {
    8.16 -        if (this->hidden->audio_handle != NULL) {
    8.17 -            /* Discard all unread data and stop channel */
    8.18 -            snd_pcm_plugin_flush(this->hidden->audio_handle,
    8.19 -                                 SND_PCM_CHANNEL_CAPTURE);
    8.20 -        }
    8.21 -    }
    8.22 -}
    8.23 -
    8.24 -static void
    8.25  QSA_Deinitialize(void)
    8.26  {
    8.27      /* Clear devices array on shutdown */
    8.28 @@ -759,7 +741,6 @@
    8.29      impl->PlayDevice = QSA_PlayDevice;
    8.30      impl->GetDeviceBuf = QSA_GetDeviceBuf;
    8.31      impl->CloseDevice = QSA_CloseDevice;
    8.32 -    impl->WaitDone = QSA_WaitDone;
    8.33      impl->Deinitialize = QSA_Deinitialize;
    8.34      impl->LockDevice = NULL;
    8.35      impl->UnlockDevice = NULL;
     9.1 --- a/src/audio/sndio/SDL_sndioaudio.c	Fri Oct 07 14:42:24 2016 -0400
     9.2 +++ b/src/audio/sndio/SDL_sndioaudio.c	Fri Oct 07 15:13:46 2016 -0400
     9.3 @@ -171,15 +171,10 @@
     9.4  }
     9.5  
     9.6  static void
     9.7 -SNDIO_WaitDone(_THIS)
     9.8 -{
     9.9 -    SNDIO_sio_stop(this->hidden->dev);
    9.10 -}
    9.11 -
    9.12 -static void
    9.13  SNDIO_CloseDevice(_THIS)
    9.14  {
    9.15      if ( this->hidden->dev != NULL ) {
    9.16 +        SNDIO_sio_stop(this->hidden->dev);
    9.17          SNDIO_sio_close(this->hidden->dev);
    9.18      }
    9.19      SDL_free(this->hidden->mixbuf);
    9.20 @@ -304,7 +299,6 @@
    9.21      impl->WaitDevice = SNDIO_WaitDevice;
    9.22      impl->PlayDevice = SNDIO_PlayDevice;
    9.23      impl->GetDeviceBuf = SNDIO_GetDeviceBuf;
    9.24 -    impl->WaitDone = SNDIO_WaitDone;
    9.25      impl->CloseDevice = SNDIO_CloseDevice;
    9.26      impl->Deinitialize = SNDIO_Deinitialize;
    9.27      impl->OnlyHasDefaultOutputDevice = 1;  /* !!! FIXME: sndio can handle multiple devices. */
    10.1 --- a/src/audio/winmm/SDL_winmm.c	Fri Oct 07 14:42:24 2016 -0400
    10.2 +++ b/src/audio/winmm/SDL_winmm.c	Fri Oct 07 15:13:46 2016 -0400
    10.3 @@ -135,24 +135,6 @@
    10.4      this->hidden->next_buffer = (this->hidden->next_buffer + 1) % NUM_BUFFERS;
    10.5  }
    10.6  
    10.7 -static void
    10.8 -WINMM_WaitDone(_THIS)
    10.9 -{
   10.10 -    int i, left;
   10.11 -
   10.12 -    do {
   10.13 -        left = NUM_BUFFERS;
   10.14 -        for (i = 0; i < NUM_BUFFERS; ++i) {
   10.15 -            if (this->hidden->wavebuf[i].dwFlags & WHDR_DONE) {
   10.16 -                --left;
   10.17 -            }
   10.18 -        }
   10.19 -        if (left > 0) {
   10.20 -            SDL_Delay(100);
   10.21 -        }
   10.22 -    } while (left > 0);
   10.23 -}
   10.24 -
   10.25  static int
   10.26  WINMM_CaptureFromDevice(_THIS, void *buffer, int buflen)
   10.27  {
   10.28 @@ -422,7 +404,6 @@
   10.29      impl->OpenDevice = WINMM_OpenDevice;
   10.30      impl->PlayDevice = WINMM_PlayDevice;
   10.31      impl->WaitDevice = WINMM_WaitDevice;
   10.32 -    impl->WaitDone = WINMM_WaitDone;
   10.33      impl->GetDeviceBuf = WINMM_GetDeviceBuf;
   10.34      impl->CaptureFromDevice = WINMM_CaptureFromDevice;
   10.35      impl->FlushCapture = WINMM_FlushCapture;
    11.1 --- a/src/audio/xaudio2/SDL_xaudio2.c	Fri Oct 07 14:42:24 2016 -0400
    11.2 +++ b/src/audio/xaudio2/SDL_xaudio2.c	Fri Oct 07 15:13:46 2016 -0400
    11.3 @@ -232,28 +232,14 @@
    11.4  }
    11.5  
    11.6  static void
    11.7 -XAUDIO2_WaitDone(_THIS)
    11.8 +XAUDIO2_PrepareToClose(_THIS)
    11.9  {
   11.10      IXAudio2SourceVoice *source = this->hidden->source;
   11.11 -    XAUDIO2_VOICE_STATE state;
   11.12 -    SDL_assert(!SDL_AtomicGet(&this->enabled));  /* flag that stops playing. */
   11.13 -    IXAudio2SourceVoice_Discontinuity(source);
   11.14 -#if SDL_XAUDIO2_WIN8
   11.15 -    IXAudio2SourceVoice_GetState(source, &state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
   11.16 -#else
   11.17 -    IXAudio2SourceVoice_GetState(source, &state);
   11.18 -#endif
   11.19 -    while (state.BuffersQueued > 0) {
   11.20 -        SDL_SemWait(this->hidden->semaphore);
   11.21 -#if SDL_XAUDIO2_WIN8
   11.22 -        IXAudio2SourceVoice_GetState(source, &state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
   11.23 -#else
   11.24 -        IXAudio2SourceVoice_GetState(source, &state);
   11.25 -#endif
   11.26 +    if (source) {
   11.27 +        IXAudio2SourceVoice_Discontinuity(source);
   11.28      }
   11.29  }
   11.30  
   11.31 -
   11.32  static void
   11.33  XAUDIO2_CloseDevice(_THIS)
   11.34  {
   11.35 @@ -489,7 +475,7 @@
   11.36      impl->OpenDevice = XAUDIO2_OpenDevice;
   11.37      impl->PlayDevice = XAUDIO2_PlayDevice;
   11.38      impl->WaitDevice = XAUDIO2_WaitDevice;
   11.39 -    impl->WaitDone = XAUDIO2_WaitDone;
   11.40 +    impl->PrepareToClose = XAUDIO2_PrepareToClose;
   11.41      impl->GetDeviceBuf = XAUDIO2_GetDeviceBuf;
   11.42      impl->CloseDevice = XAUDIO2_CloseDevice;
   11.43      impl->Deinitialize = XAUDIO2_Deinitialize;