SDL: audio: Let apps save an audio stream from destruction during SDL_Quit(). (#13244)

From 274aa0242e7bc91e5927663fd663581147496a78 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Mon, 23 Jun 2025 00:06:15 -0400
Subject: [PATCH] audio: Let apps save an audio stream from destruction during
 SDL_Quit(). (#13244)

This is a special-case piece of functionality, generally these are expected
to go away during shutdown, but maybe someone is switching between audio
subsystems or something...
---
 include/SDL3/SDL_audio.h | 12 ++++++++++++
 src/audio/SDL_audio.c    | 13 ++++++++++---
 2 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/include/SDL3/SDL_audio.h b/include/SDL3/SDL_audio.h
index b91586fd6ac5c..90e0018b90561 100644
--- a/include/SDL3/SDL_audio.h
+++ b/include/SDL3/SDL_audio.h
@@ -1064,6 +1064,15 @@ extern SDL_DECLSPEC SDL_AudioStream * SDLCALL SDL_CreateAudioStream(const SDL_Au
 /**
  * Get the properties associated with an audio stream.
  *
+ * The application can hang any data it wants here, but
+ * the following properties are understood by SDL:
+ *
+ * - `SDL_PROP_AUDIOSTREAM_KEEP_ON_SHUTDOWN_BOOLEAN`: if true, the stream will
+ *   not be automatically destroyed during SDL_Quit(). This property is
+ *   ignored for streams created through SDL_OpenAudioDeviceStream(). Streams
+ *   bound to devices that aren't destroyed will still be unbound.
+ *   Default false. (since SDL 3.4.0)
+ *
  * \param stream the SDL_AudioStream to query.
  * \returns a valid property ID on success or 0 on failure; call
  *          SDL_GetError() for more information.
@@ -1074,6 +1083,9 @@ extern SDL_DECLSPEC SDL_AudioStream * SDLCALL SDL_CreateAudioStream(const SDL_Au
  */
 extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetAudioStreamProperties(SDL_AudioStream *stream);
 
+#define SDL_PROP_AUDIOSTREAM_KEEP_ON_SHUTDOWN_BOOLEAN "SDL.audiostream.keep_on_shutdown"
+
+
 /**
  * Query the current format of an audio stream.
  *
diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index 90d9f9bba6321..e672e0dac0ff4 100644
--- a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -1073,9 +1073,16 @@ void SDL_QuitAudio(void)
 
     current_audio.impl.DeinitializeStart();
 
-    // Destroy any audio streams that still exist...
-    while (current_audio.existing_streams) {
-        SDL_DestroyAudioStream(current_audio.existing_streams);
+    // Destroy any audio streams that still exist...unless app asked to keep it.
+    SDL_AudioStream *next = NULL;
+    for (SDL_AudioStream *i = current_audio.existing_streams; i; i = next) {
+        next = i->next;
+        if (i->simplified || !SDL_GetBooleanProperty(i->props, SDL_PROP_AUDIOSTREAM_KEEP_ON_SHUTDOWN_BOOLEAN, false)) {
+            SDL_DestroyAudioStream(i);
+        } else {
+            i->prev = NULL;
+            i->next = NULL;
+        }
     }
 
     SDL_LockRWLockForWriting(current_audio.device_hash_lock);