From f8fdb20d8f1d4fba4fd9f782addb9a3b987f861a Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 20 Sep 2023 10:47:11 -0400 Subject: [PATCH] audio: Destroy all existing SDL_AudioStreams on shutdown. --- src/audio/SDL_audio.c | 41 +++++++++++++++++++++++++++++++++++++++- src/audio/SDL_audiocvt.c | 4 ++++ src/audio/SDL_sysaudio.h | 13 ++++++++++--- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index f2a2e4d17e..625625f933 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -137,6 +137,42 @@ static int GetDefaultSampleFramesFromFreq(const int freq) } } +void OnAudioStreamCreated(SDL_AudioStream *stream) +{ + SDL_assert(SDL_GetCurrentAudioDriver() != NULL); + SDL_assert(stream != NULL); + + // this isn't really part of the "device list" but it's a convenient lock to use here. + SDL_LockRWLockForWriting(current_audio.device_list_lock); + if (current_audio.existing_streams) { + current_audio.existing_streams->prev = stream; + } + stream->prev = NULL; + stream->next = current_audio.existing_streams; + current_audio.existing_streams = stream; + SDL_UnlockRWLock(current_audio.device_list_lock); +} + +void OnAudioStreamDestroy(SDL_AudioStream *stream) +{ + SDL_assert(SDL_GetCurrentAudioDriver() != NULL); + SDL_assert(stream != NULL); + + // this isn't really part of the "device list" but it's a convenient lock to use here. + SDL_LockRWLockForWriting(current_audio.device_list_lock); + if (stream->prev) { + stream->prev->next = stream->next; + } + if (stream->next) { + stream->next->prev = stream->prev; + } + if (stream == current_audio.existing_streams) { + current_audio.existing_streams = stream->next; + } + SDL_UnlockRWLock(current_audio.device_list_lock); +} + + // device should be locked when calling this. static SDL_bool AudioDeviceCanUseSimpleCopy(SDL_AudioDevice *device) { @@ -657,7 +693,10 @@ void SDL_QuitAudio(void) return; } - // !!! FIXME: Destroy all known audio streams, too. + // Destroy any audio streams that still exist... + while (current_audio.existing_streams != NULL) { + SDL_DestroyAudioStream(current_audio.existing_streams); + } // merge device lists so we don't have to duplicate work below. SDL_LockRWLockForWriting(current_audio.device_list_lock); diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c index 018701d3e8..a5be406dbf 100644 --- a/src/audio/SDL_audiocvt.c +++ b/src/audio/SDL_audiocvt.c @@ -430,6 +430,8 @@ SDL_AudioStream *SDL_CreateAudioStream(const SDL_AudioSpec *src_spec, const SDL_ return NULL; } + OnAudioStreamCreated(retval); + if (SDL_SetAudioStreamFormat(retval, src_spec, dst_spec) == -1) { SDL_DestroyAudioStream(retval); return NULL; @@ -1152,6 +1154,8 @@ void SDL_DestroyAudioStream(SDL_AudioStream *stream) return; } + OnAudioStreamDestroy(stream); + const SDL_bool simplified = stream->simplified; if (simplified) { SDL_assert(stream->bound_device->simplified); diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h index 4212abce33..980dbc565f 100644 --- a/src/audio/SDL_sysaudio.h +++ b/src/audio/SDL_sysaudio.h @@ -102,7 +102,7 @@ extern SDL_AudioDevice *SDL_FindPhysicalAudioDeviceByCallback(SDL_bool (*callbac extern void SDL_UpdatedAudioDeviceFormat(SDL_AudioDevice *device); // Backends can call this to get a standardized name for a thread to power a specific audio device. -char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen); +extern char *SDL_GetAudioThreadName(SDL_AudioDevice *device, char *buf, size_t buflen); // These functions are the heart of the audio threads. Backends can call them directly if they aren't using the SDL-provided thread. @@ -115,9 +115,12 @@ extern void SDL_CaptureAudioThreadShutdown(SDL_AudioDevice *device); extern void SDL_AudioThreadFinalize(SDL_AudioDevice *device); // this gets used from the audio device threads. It has rules, don't use this if you don't know how to use it! -void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_format, int src_channels, - void *dst, SDL_AudioFormat dst_format, int dst_channels, void* scratch); +extern void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_format, int src_channels, + void *dst, SDL_AudioFormat dst_format, int dst_channels, void* scratch); +// Special case to let something in SDL_audiocvt.c access something in SDL_audio.c. Don't use this. +extern void OnAudioStreamCreated(SDL_AudioStream *stream); +extern void OnAudioStreamDestroy(SDL_AudioStream *stream); typedef struct SDL_AudioDriverImpl { @@ -151,6 +154,7 @@ typedef struct SDL_AudioDriver SDL_RWLock *device_list_lock; // A mutex for device detection SDL_AudioDevice *output_devices; // the list of currently-available audio output devices. SDL_AudioDevice *capture_devices; // the list of currently-available audio capture devices. + SDL_AudioStream *existing_streams; // a list of all existing SDL_AudioStreams. SDL_AudioDeviceID default_output_device_id; SDL_AudioDeviceID default_capture_device_id; SDL_AtomicInt output_device_count; @@ -191,6 +195,9 @@ struct SDL_AudioStream SDL_LogicalAudioDevice *bound_device; SDL_AudioStream *next_binding; SDL_AudioStream *prev_binding; + + SDL_AudioStream *prev; // linked list of all existing streams (so we can free them on shutdown). + SDL_AudioStream *next; // linked list of all existing streams (so we can free them on shutdown). }; /* Logical devices are an abstraction in SDL3; you can open the same physical