Skip to content

Commit

Permalink
audio: Don't trust audio drivers to drain pending audio.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
icculus committed Oct 7, 2016
1 parent 551cdc8 commit f6a280a
Show file tree
Hide file tree
Showing 11 changed files with 19 additions and 130 deletions.
22 changes: 13 additions & 9 deletions src/audio/SDL_audio.c
Expand Up @@ -191,11 +191,6 @@ SDL_AudioGetDeviceBuf_Default(_THIS)
return NULL;
}

static void
SDL_AudioWaitDone_Default(_THIS)
{ /* no-op. */
}

static int
SDL_AudioCaptureFromDevice_Default(_THIS, void *buffer, int buflen)
{
Expand All @@ -207,6 +202,11 @@ SDL_AudioFlushCapture_Default(_THIS)
{ /* no-op. */
}

static void
SDL_AudioPrepareToClose_Default(_THIS)
{ /* no-op. */
}

static void
SDL_AudioCloseDevice_Default(_THIS)
{ /* no-op. */
Expand Down Expand Up @@ -292,9 +292,9 @@ finish_audio_entry_points_init(void)
FILL_STUB(PlayDevice);
FILL_STUB(GetPendingBytes);
FILL_STUB(GetDeviceBuf);
FILL_STUB(WaitDone);
FILL_STUB(CaptureFromDevice);
FILL_STUB(FlushCapture);
FILL_STUB(PrepareToClose);
FILL_STUB(CloseDevice);
FILL_STUB(LockDevice);
FILL_STUB(UnlockDevice);
Expand Down Expand Up @@ -715,6 +715,9 @@ SDL_FinalizeAudioDevice(SDL_AudioDevice *device)
return;
}

SDL_AtomicSet(&device->shutdown, 1); /* just in case. */
SDL_AtomicSet(&device->enabled, 0);

/* lock/unlock here so we don't race if the audio thread saw the shutdown
var without locking, and the thread that requested shutdown is now
trying to unlock the mutex while we destroy it. Threading is hard. */
Expand Down Expand Up @@ -811,9 +814,10 @@ SDL_RunAudio(void *devicep)
}
}

current_audio.impl.PrepareToClose(device);

/* Wait for the audio to drain. */
/* !!! FIXME: can we rename this WaitDrain? */
current_audio.impl.WaitDone(device);
SDL_Delay(((device->spec.samples * 1000) / device->spec.freq) * 2);

SDL_FinalizeAudioDevice(device);

Expand Down Expand Up @@ -1153,7 +1157,7 @@ close_audio_device(SDL_AudioDevice * device)

if (!device->iscapture) {
const SDL_AudioSpec *spec = &device->spec;
delay = ((spec.samples * 1000) / spec.freq) * 2;
delay = ((spec->samples * 1000) / spec->freq) * 2;
}

/* Lock to make sure an audio callback doesn't fire after we return.
Expand Down
2 changes: 1 addition & 1 deletion src/audio/SDL_sysaudio.h
Expand Up @@ -79,9 +79,9 @@ typedef struct SDL_AudioDriverImpl
void (*PlayDevice) (_THIS);
int (*GetPendingBytes) (_THIS);
Uint8 *(*GetDeviceBuf) (_THIS);
void (*WaitDone) (_THIS);
int (*CaptureFromDevice) (_THIS, void *buffer, int buflen);
void (*FlushCapture) (_THIS);
void (*PrepareToClose) (_THIS); /**< Called between run and draining wait for playback devices */
void (*CloseDevice) (_THIS);
void (*LockDevice) (_THIS);
void (*UnlockDevice) (_THIS);
Expand Down
8 changes: 0 additions & 8 deletions src/audio/arts/SDL_artsaudio.c
Expand Up @@ -185,13 +185,6 @@ ARTS_PlayDevice(_THIS)
#endif
}

static void
ARTS_WaitDone(_THIS)
{
/* !!! FIXME: camp here until buffer drains... SDL_Delay(???); */
}


static Uint8 *
ARTS_GetDeviceBuf(_THIS)
{
Expand Down Expand Up @@ -356,7 +349,6 @@ ARTS_Init(SDL_AudioDriverImpl * impl)
impl->WaitDevice = ARTS_WaitDevice;
impl->GetDeviceBuf = ARTS_GetDeviceBuf;
impl->CloseDevice = ARTS_CloseDevice;
impl->WaitDone = ARTS_WaitDone;
impl->Deinitialize = ARTS_Deinitialize;
impl->OnlyHasDefaultOutputDevice = 1;

Expand Down
17 changes: 0 additions & 17 deletions src/audio/directsound/SDL_directsound.c
Expand Up @@ -307,22 +307,6 @@ DSOUND_GetDeviceBuf(_THIS)
return (this->hidden->locked_buf);
}

static void
DSOUND_WaitDone(_THIS)
{
Uint8 *stream = DSOUND_GetDeviceBuf(this);

/* Wait for the playing chunk to finish */
if (stream != NULL) {
SDL_memset(stream, this->spec.silence, this->spec.size);
DSOUND_PlayDevice(this);
}
DSOUND_WaitDevice(this);

/* Stop the looping sound buffer */
IDirectSoundBuffer_Stop(this->hidden->mixbuf);
}

static int
DSOUND_CaptureFromDevice(_THIS, void *buffer, int buflen)
{
Expand Down Expand Up @@ -600,7 +584,6 @@ DSOUND_Init(SDL_AudioDriverImpl * impl)
impl->OpenDevice = DSOUND_OpenDevice;
impl->PlayDevice = DSOUND_PlayDevice;
impl->WaitDevice = DSOUND_WaitDevice;
impl->WaitDone = DSOUND_WaitDone;
impl->GetDeviceBuf = DSOUND_GetDeviceBuf;
impl->CaptureFromDevice = DSOUND_CaptureFromDevice;
impl->FlushCapture = DSOUND_FlushCapture;
Expand Down
8 changes: 0 additions & 8 deletions src/audio/fusionsound/SDL_fsaudio.c
Expand Up @@ -151,13 +151,6 @@ SDL_FS_PlayDevice(_THIS)
#endif
}

static void
SDL_FS_WaitDone(_THIS)
{
this->hidden->stream->Wait(this->hidden->stream,
this->hidden->mixsamples * FUSION_BUFFERS);
}


static Uint8 *
SDL_FS_GetDeviceBuf(_THIS)
Expand Down Expand Up @@ -319,7 +312,6 @@ SDL_FS_Init(SDL_AudioDriverImpl * impl)
impl->WaitDevice = SDL_FS_WaitDevice;
impl->GetDeviceBuf = SDL_FS_GetDeviceBuf;
impl->CloseDevice = SDL_FS_CloseDevice;
impl->WaitDone = SDL_FS_WaitDone;
impl->Deinitialize = SDL_FS_Deinitialize;
impl->OnlyHasDefaultOutputDevice = 1;

Expand Down
1 change: 0 additions & 1 deletion src/audio/psp/SDL_pspaudio.c
Expand Up @@ -152,7 +152,6 @@ PSPAUDIO_Init(SDL_AudioDriverImpl * impl)
impl->PlayDevice = PSPAUDIO_PlayDevice;
impl->WaitDevice = PSPAUDIO_WaitDevice;
impl->GetDeviceBuf = PSPAUDIO_GetDeviceBuf;
impl->WaitDone = PSPAUDIO_WaitDevice;
impl->CloseDevice = PSPAUDIO_CloseDevice;
impl->ThreadInit = PSPAUDIO_ThreadInit;

Expand Down
23 changes: 0 additions & 23 deletions src/audio/pulseaudio/SDL_pulseaudio.c
Expand Up @@ -368,28 +368,6 @@ PULSEAUDIO_PlayDevice(_THIS)
}
}

static void
PULSEAUDIO_WaitDone(_THIS)
{
if (SDL_AtomicGet(&this->enabled)) {
struct SDL_PrivateAudioData *h = this->hidden;
pa_operation *o = PULSEAUDIO_pa_stream_drain(h->stream, stream_operation_complete_no_op, NULL);
if (o) {
while (PULSEAUDIO_pa_operation_get_state(o) != PA_OPERATION_DONE) {
if (PULSEAUDIO_pa_context_get_state(h->context) != PA_CONTEXT_READY ||
PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY ||
PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) {
PULSEAUDIO_pa_operation_cancel(o);
break;
}
}
PULSEAUDIO_pa_operation_unref(o);
}
}
}



static Uint8 *
PULSEAUDIO_GetDeviceBuf(_THIS)
{
Expand Down Expand Up @@ -776,7 +754,6 @@ PULSEAUDIO_Init(SDL_AudioDriverImpl * impl)
impl->WaitDevice = PULSEAUDIO_WaitDevice;
impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
impl->CloseDevice = PULSEAUDIO_CloseDevice;
impl->WaitDone = PULSEAUDIO_WaitDone;
impl->Deinitialize = PULSEAUDIO_Deinitialize;
impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice;
impl->FlushCapture = PULSEAUDIO_FlushCapture;
Expand Down
19 changes: 0 additions & 19 deletions src/audio/qsa/SDL_qsa_audio.c
Expand Up @@ -708,24 +708,6 @@ QSA_DetectDevices(void)
}
}

static void
QSA_WaitDone(_THIS)
{
if (!this->hidden->iscapture) {
if (this->hidden->audio_handle != NULL) {
/* Wait till last fragment is played and stop channel */
snd_pcm_plugin_flush(this->hidden->audio_handle,
SND_PCM_CHANNEL_PLAYBACK);
}
} else {
if (this->hidden->audio_handle != NULL) {
/* Discard all unread data and stop channel */
snd_pcm_plugin_flush(this->hidden->audio_handle,
SND_PCM_CHANNEL_CAPTURE);
}
}
}

static void
QSA_Deinitialize(void)
{
Expand Down Expand Up @@ -759,7 +741,6 @@ QSA_Init(SDL_AudioDriverImpl * impl)
impl->PlayDevice = QSA_PlayDevice;
impl->GetDeviceBuf = QSA_GetDeviceBuf;
impl->CloseDevice = QSA_CloseDevice;
impl->WaitDone = QSA_WaitDone;
impl->Deinitialize = QSA_Deinitialize;
impl->LockDevice = NULL;
impl->UnlockDevice = NULL;
Expand Down
8 changes: 1 addition & 7 deletions src/audio/sndio/SDL_sndioaudio.c
Expand Up @@ -170,16 +170,11 @@ SNDIO_GetDeviceBuf(_THIS)
return this->hidden->mixbuf;
}

static void
SNDIO_WaitDone(_THIS)
{
SNDIO_sio_stop(this->hidden->dev);
}

static void
SNDIO_CloseDevice(_THIS)
{
if ( this->hidden->dev != NULL ) {
SNDIO_sio_stop(this->hidden->dev);
SNDIO_sio_close(this->hidden->dev);
}
SDL_free(this->hidden->mixbuf);
Expand Down Expand Up @@ -304,7 +299,6 @@ SNDIO_Init(SDL_AudioDriverImpl * impl)
impl->WaitDevice = SNDIO_WaitDevice;
impl->PlayDevice = SNDIO_PlayDevice;
impl->GetDeviceBuf = SNDIO_GetDeviceBuf;
impl->WaitDone = SNDIO_WaitDone;
impl->CloseDevice = SNDIO_CloseDevice;
impl->Deinitialize = SNDIO_Deinitialize;
impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: sndio can handle multiple devices. */
Expand Down
19 changes: 0 additions & 19 deletions src/audio/winmm/SDL_winmm.c
Expand Up @@ -135,24 +135,6 @@ WINMM_PlayDevice(_THIS)
this->hidden->next_buffer = (this->hidden->next_buffer + 1) % NUM_BUFFERS;
}

static void
WINMM_WaitDone(_THIS)
{
int i, left;

do {
left = NUM_BUFFERS;
for (i = 0; i < NUM_BUFFERS; ++i) {
if (this->hidden->wavebuf[i].dwFlags & WHDR_DONE) {
--left;
}
}
if (left > 0) {
SDL_Delay(100);
}
} while (left > 0);
}

static int
WINMM_CaptureFromDevice(_THIS, void *buffer, int buflen)
{
Expand Down Expand Up @@ -422,7 +404,6 @@ WINMM_Init(SDL_AudioDriverImpl * impl)
impl->OpenDevice = WINMM_OpenDevice;
impl->PlayDevice = WINMM_PlayDevice;
impl->WaitDevice = WINMM_WaitDevice;
impl->WaitDone = WINMM_WaitDone;
impl->GetDeviceBuf = WINMM_GetDeviceBuf;
impl->CaptureFromDevice = WINMM_CaptureFromDevice;
impl->FlushCapture = WINMM_FlushCapture;
Expand Down
22 changes: 4 additions & 18 deletions src/audio/xaudio2/SDL_xaudio2.c
Expand Up @@ -232,28 +232,14 @@ XAUDIO2_WaitDevice(_THIS)
}

static void
XAUDIO2_WaitDone(_THIS)
XAUDIO2_PrepareToClose(_THIS)
{
IXAudio2SourceVoice *source = this->hidden->source;
XAUDIO2_VOICE_STATE state;
SDL_assert(!SDL_AtomicGet(&this->enabled)); /* flag that stops playing. */
IXAudio2SourceVoice_Discontinuity(source);
#if SDL_XAUDIO2_WIN8
IXAudio2SourceVoice_GetState(source, &state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
#else
IXAudio2SourceVoice_GetState(source, &state);
#endif
while (state.BuffersQueued > 0) {
SDL_SemWait(this->hidden->semaphore);
#if SDL_XAUDIO2_WIN8
IXAudio2SourceVoice_GetState(source, &state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
#else
IXAudio2SourceVoice_GetState(source, &state);
#endif
if (source) {
IXAudio2SourceVoice_Discontinuity(source);
}
}


static void
XAUDIO2_CloseDevice(_THIS)
{
Expand Down Expand Up @@ -489,7 +475,7 @@ XAUDIO2_Init(SDL_AudioDriverImpl * impl)
impl->OpenDevice = XAUDIO2_OpenDevice;
impl->PlayDevice = XAUDIO2_PlayDevice;
impl->WaitDevice = XAUDIO2_WaitDevice;
impl->WaitDone = XAUDIO2_WaitDone;
impl->PrepareToClose = XAUDIO2_PrepareToClose;
impl->GetDeviceBuf = XAUDIO2_GetDeviceBuf;
impl->CloseDevice = XAUDIO2_CloseDevice;
impl->Deinitialize = XAUDIO2_Deinitialize;
Expand Down

0 comments on commit f6a280a

Please sign in to comment.