SDL: Refactored SDL_audiocvt.c

From a62e62f97a5595dc27e1eaf69dfa63e2f8855e13 Mon Sep 17 00:00:00 2001
From: Brick <[EMAIL REDACTED]>
Date: Wed, 6 Sep 2023 18:38:01 +0100
Subject: [PATCH] Refactored SDL_audiocvt.c

---
 VisualC-GDK/SDL/SDL.vcxproj           |   4 +
 VisualC-GDK/SDL/SDL.vcxproj.filters   |  12 +
 VisualC-WinRT/SDL-UWP.vcxproj         |   4 +
 VisualC-WinRT/SDL-UWP.vcxproj.filters |  12 +
 VisualC/SDL/SDL.vcxproj               |   4 +
 VisualC/SDL/SDL.vcxproj.filters       |  12 +
 src/audio/SDL_audiocvt.c              | 978 +++++---------------------
 src/audio/SDL_audioqueue.c            | 516 ++++++++++++++
 src/audio/SDL_audioqueue.h            |  77 ++
 src/audio/SDL_audioresample.c         | 332 +++++++++
 src/audio/SDL_audioresample.h         |  43 ++
 src/audio/SDL_sysaudio.h              |   4 +-
 test/testaudiostreamdynamicresample.c |  32 +-
 test/testautomation_audio.c           |  12 +-
 14 files changed, 1211 insertions(+), 831 deletions(-)
 create mode 100644 src/audio/SDL_audioqueue.c
 create mode 100644 src/audio/SDL_audioqueue.h
 create mode 100644 src/audio/SDL_audioresample.c
 create mode 100644 src/audio/SDL_audioresample.h

diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj
index 05556a850f58..0545d3c4224a 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -370,6 +370,8 @@
     <ClInclude Include="..\..\src\audio\SDL_audio_c.h" />
     <ClInclude Include="..\..\src\audio\SDL_audiodev_c.h" />
     <ClInclude Include="..\..\src\audio\SDL_sysaudio.h" />
+    <ClInclude Include="..\..\src\audio\SDL_audioqueue.h" />
+    <ClInclude Include="..\..\src\audio\SDL_audioresample.h" />
     <ClInclude Include="..\..\src\audio\SDL_wave.h" />
     <ClInclude Include="..\..\src\audio\wasapi\SDL_wasapi.h" />
     <ClInclude Include="..\..\src\core\gdk\SDL_gdk.h" />
@@ -549,6 +551,8 @@
     <ClCompile Include="..\..\src\audio\SDL_audiocvt.c" />
     <ClCompile Include="..\..\src\audio\SDL_audiodev.c" />
     <ClCompile Include="..\..\src\audio\SDL_audiotypecvt.c" />
+    <ClCompile Include="..\..\src\audio\SDL_audioqueue.c" />
+    <ClCompile Include="..\..\src\audio\SDL_audioresample.c" />
     <ClCompile Include="..\..\src\audio\SDL_mixer.c" />
     <ClCompile Include="..\..\src\audio\SDL_wave.c" />
     <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c" />
diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters
index 863cb5f6e6d6..62f79c6b1fa7 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj.filters
+++ b/VisualC-GDK/SDL/SDL.vcxproj.filters
@@ -419,6 +419,12 @@
     <ClInclude Include="..\..\src\audio\SDL_sysaudio.h">
       <Filter>audio</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\audio\SDL_audioqueue.h">
+      <Filter>audio</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\audio\SDL_audioresample.h">
+      <Filter>audio</Filter>
+    </ClInclude>    
     <ClInclude Include="..\..\src\core\windows\SDL_hid.h">
       <Filter>core\windows</Filter>
     </ClInclude>
@@ -854,6 +860,12 @@
     <ClCompile Include="..\..\src\audio\SDL_audiotypecvt.c">
       <Filter>audio</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\audio\SDL_audioqueue.c">
+      <Filter>audio</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\audio\SDL_audioresample.c">
+      <Filter>audio</Filter>
+    </ClCompile>    
     <ClCompile Include="..\..\src\audio\SDL_wave.c">
       <Filter>audio</Filter>
     </ClCompile>
diff --git a/VisualC-WinRT/SDL-UWP.vcxproj b/VisualC-WinRT/SDL-UWP.vcxproj
index c6b9b05d438a..93e3b3ccca77 100644
--- a/VisualC-WinRT/SDL-UWP.vcxproj
+++ b/VisualC-WinRT/SDL-UWP.vcxproj
@@ -94,6 +94,8 @@
     <ClInclude Include="..\src\audio\SDL_audiodev_c.h" />
     <ClInclude Include="..\src\audio\SDL_audio_c.h" />
     <ClInclude Include="..\src\audio\SDL_sysaudio.h" />
+    <ClInclude Include="..\src\audio\SDL_audioqueue.h" />
+    <ClInclude Include="..\src\audio\SDL_audioresample.h" />
     <ClInclude Include="..\src\audio\SDL_wave.h" />
     <ClInclude Include="..\src\audio\wasapi\SDL_wasapi.h" />
     <ClInclude Include="..\src\core\windows\SDL_directx.h" />
@@ -193,6 +195,8 @@
     <ClCompile Include="..\src\audio\SDL_audiocvt.c" />
     <ClCompile Include="..\src\audio\SDL_audiodev.c" />
     <ClCompile Include="..\src\audio\SDL_audiotypecvt.c" />
+    <ClCompile Include="..\src\audio\SDL_audioqueue.c" />
+    <ClCompile Include="..\src\audio\SDL_audioresample.c" />
     <ClCompile Include="..\src\audio\SDL_mixer.c" />
     <ClCompile Include="..\src\audio\SDL_wave.c" />
     <ClCompile Include="..\src\audio\wasapi\SDL_wasapi.c" />
diff --git a/VisualC-WinRT/SDL-UWP.vcxproj.filters b/VisualC-WinRT/SDL-UWP.vcxproj.filters
index 9e44ed1a2b52..2ba518c9dd12 100644
--- a/VisualC-WinRT/SDL-UWP.vcxproj.filters
+++ b/VisualC-WinRT/SDL-UWP.vcxproj.filters
@@ -183,6 +183,12 @@
     <ClInclude Include="..\src\audio\SDL_sysaudio.h">
       <Filter>Source Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\src\audio\SDL_audioqueue.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\src\audio\SDL_audioresample.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
     <ClInclude Include="..\src\audio\SDL_wave.h">
       <Filter>Source Files</Filter>
     </ClInclude>
@@ -471,6 +477,12 @@
     <ClCompile Include="..\src\audio\SDL_audiotypecvt.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\audio\SDL_audioqueue.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\src\audio\SDL_audioresample.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>        
     <ClCompile Include="..\src\audio\SDL_mixer.c">
       <Filter>Source Files</Filter>
     </ClCompile>
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index 2eae831c5643..e35540408280 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -319,6 +319,8 @@
     <ClInclude Include="..\..\src\audio\SDL_audio_c.h" />
     <ClInclude Include="..\..\src\audio\SDL_audiodev_c.h" />
     <ClInclude Include="..\..\src\audio\SDL_sysaudio.h" />
+    <ClInclude Include="..\..\src\audio\SDL_audioqueue.h" />
+    <ClInclude Include="..\..\src\audio\SDL_audioresample.h" />
     <ClInclude Include="..\..\src\audio\SDL_wave.h" />
     <ClInclude Include="..\..\src\audio\wasapi\SDL_wasapi.h" />
     <ClInclude Include="..\..\src\core\windows\SDL_directx.h" />
@@ -475,6 +477,8 @@
     <ClCompile Include="..\..\src\audio\SDL_audiocvt.c" />
     <ClCompile Include="..\..\src\audio\SDL_audiodev.c" />
     <ClCompile Include="..\..\src\audio\SDL_audiotypecvt.c" />
+    <ClCompile Include="..\..\src\audio\SDL_audioqueue.c" />
+    <ClCompile Include="..\..\src\audio\SDL_audioresample.c" />
     <ClCompile Include="..\..\src\audio\SDL_mixer.c" />
     <ClCompile Include="..\..\src\audio\SDL_wave.c" />
     <ClCompile Include="..\..\src\audio\wasapi\SDL_wasapi.c" />
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index 0be6f82e2f6c..dbf4e543eef0 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -410,6 +410,12 @@
     <ClInclude Include="..\..\src\audio\SDL_sysaudio.h">
       <Filter>audio</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\audio\SDL_audioqueue.h">
+      <Filter>audio</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\audio\SDL_audioresample.h">
+      <Filter>audio</Filter>
+    </ClInclude>    
     <ClInclude Include="..\..\src\core\windows\SDL_hid.h">
       <Filter>core\windows</Filter>
     </ClInclude>
@@ -833,6 +839,12 @@
     <ClCompile Include="..\..\src\audio\SDL_audiotypecvt.c">
       <Filter>audio</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\audio\SDL_audioqueue.c">
+      <Filter>audio</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\audio\SDL_audioresample.c">
+      <Filter>audio</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\src\audio\SDL_wave.c">
       <Filter>audio</Filter>
     </ClCompile>
diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c
index dc521f134971..5cad0d0ac081 100644
--- a/src/audio/SDL_audiocvt.c
+++ b/src/audio/SDL_audiocvt.c
@@ -20,707 +20,16 @@
 */
 #include "SDL_internal.h"
 
-// Functions for audio drivers to perform runtime conversion of audio format
-
 #include "SDL_audio_c.h"
 
-/* SDL's resampler uses a "bandlimited interpolation" algorithm:
-     https://ccrma.stanford.edu/~jos/resample/ */
-
-#include "SDL_audio_resampler_filter.h"
-
-typedef struct SDL_AudioChunk SDL_AudioChunk;
-typedef struct SDL_AudioTrack SDL_AudioTrack;
-typedef struct SDL_AudioQueue SDL_AudioQueue;
-
-struct SDL_AudioChunk
-{
-    SDL_AudioChunk *next;
-    size_t head;
-    size_t tail;
-    Uint8 data[SDL_VARIABLE_LENGTH_ARRAY];
-};
-
-struct SDL_AudioTrack
-{
-    SDL_AudioSpec spec;
-    SDL_AudioTrack *next;
-
-    SDL_AudioChunk *head;
-    SDL_AudioChunk *tail;
-
-    size_t queued_bytes;
-    SDL_bool flushed;
-};
-
-struct SDL_AudioQueue
-{
-    SDL_AudioTrack *head;
-    SDL_AudioTrack *tail;
-    size_t chunk_size;
-
-    SDL_AudioChunk *free_chunks;
-    size_t num_free_chunks;
-};
-
-static void DestroyAudioChunk(SDL_AudioChunk *chunk)
-{
-    SDL_free(chunk);
-}
-
-static void DestroyAudioChunks(SDL_AudioChunk *chunk)
-{
-    while (chunk) {
-        SDL_AudioChunk *next = chunk->next;
-        DestroyAudioChunk(chunk);
-        chunk = next;
-    }
-}
-
-static void DestroyAudioTrack(SDL_AudioTrack *track)
-{
-    DestroyAudioChunks(track->head);
-
-    SDL_free(track);
-}
-
-static SDL_AudioQueue *CreateAudioQueue(size_t chunk_size)
-{
-    SDL_AudioQueue *queue = (SDL_AudioQueue *)SDL_calloc(1, sizeof(*queue));
-
-    if (queue == NULL) {
-        SDL_OutOfMemory();
-        return NULL;
-    }
-
-    queue->chunk_size = chunk_size;
-    return queue;
-}
-
-static void ClearAudioQueue(SDL_AudioQueue *queue)
-{
-    SDL_AudioTrack *track = queue->head;
-
-    while (track) {
-        SDL_AudioTrack *next = track->next;
-        DestroyAudioTrack(track);
-        track = next;
-    }
-
-    queue->head = NULL;
-    queue->tail = NULL;
-
-    DestroyAudioChunks(queue->free_chunks);
-    queue->free_chunks = NULL;
-    queue->num_free_chunks = 0;
-}
-
-static void DestroyAudioQueue(SDL_AudioQueue *queue)
-{
-    ClearAudioQueue(queue);
-
-    SDL_free(queue);
-}
-
-static void ResetAudioChunk(SDL_AudioChunk* chunk)
-{
-    chunk->next = NULL;
-    chunk->head = 0;
-    chunk->tail = 0;
-}
-
-static SDL_AudioChunk *CreateAudioChunk(size_t chunk_size)
-{
-    SDL_AudioChunk *chunk = (SDL_AudioChunk *)SDL_malloc(sizeof(*chunk) + chunk_size);
-
-    if (chunk == NULL) {
-        return NULL;
-    }
-
-    ResetAudioChunk(chunk);
-
-    return chunk;
-}
-
-static SDL_AudioTrack *CreateAudioTrack(SDL_AudioQueue *queue, const SDL_AudioSpec *spec)
-{
-    SDL_AudioTrack *track = (SDL_AudioTrack *)SDL_calloc(1, sizeof(*track));
-
-    if (track == NULL) {
-        return NULL;
-    }
-
-    SDL_copyp(&track->spec, spec);
-
-    return track;
-}
-
-static SDL_AudioTrack *GetAudioQueueTrackForWriting(SDL_AudioQueue *queue, const SDL_AudioSpec *spec)
-{
-    SDL_AudioTrack *track = queue->tail;
-
-    if ((track == NULL) || track->flushed) {
-        SDL_AudioTrack *new_track = CreateAudioTrack(queue, spec);
-
-        if (new_track == NULL) {
-            SDL_OutOfMemory();
-            return NULL;
-        }
-
-        if (track) {
-            track->next = new_track;
-        } else {
-            queue->head = new_track;
-        }
-
-        queue->tail = new_track;
-
-        track = new_track;
-    } else {
-        SDL_assert((track->spec.format == spec->format) &&
-                   (track->spec.channels == spec->channels) &&
-                   (track->spec.freq == spec->freq));
-    }
-
-    return track;
-}
-
-static SDL_AudioChunk* CreateAudioChunkFromQueue(SDL_AudioQueue *queue)
-{
-    if (queue->num_free_chunks > 0) {
-        SDL_AudioChunk* chunk = queue->free_chunks;
-
-        queue->free_chunks = chunk->next;
-        --queue->num_free_chunks;
-
-        ResetAudioChunk(chunk);
-
-        return chunk;
-    }
-
-    return CreateAudioChunk(queue->chunk_size);
-}
-
-static int WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const Uint8 *data, size_t len)
-{
-    if (len == 0) {
-        return 0;
-    }
-
-    SDL_AudioTrack *track = GetAudioQueueTrackForWriting(queue, spec);
-
-    if (track == NULL) {
-        return -1;
-    }
-
-    SDL_AudioChunk *chunk = track->tail;
-    const size_t chunk_size = queue->chunk_size;
-
-    // Allocate the first chunk here to simplify the logic later on
-    if (chunk == NULL) {
-        chunk = CreateAudioChunkFromQueue(queue);
-
-        if (chunk == NULL) {
-            return SDL_OutOfMemory();
-        }
-
-        SDL_assert((track->head == NULL) && (track->queued_bytes == 0));
-        track->head = chunk;
-        track->tail = chunk;
-    }
-
-    size_t total = 0;
-
-    while (total < len) {
-        if (chunk->tail >= chunk_size) {
-            SDL_AudioChunk *next = CreateAudioChunkFromQueue(queue);
-
-            if (next == NULL) {
-                break;
-            }
-
-            chunk->next = next;
-            chunk = next;
-        }
-
-        size_t to_write = chunk_size - chunk->tail;
-        to_write = SDL_min(to_write, len - total);
-
-        SDL_memcpy(&chunk->data[chunk->tail], &data[total], to_write);
-        chunk->tail += to_write;
-
-        total += to_write;
-    }
-
-    // Roll back the changes if we couldn't write all the data
-    if (total < len) {
-        chunk = track->tail;
-
-        SDL_AudioChunk *next = chunk->next;
-        chunk->next = NULL;
-
-        while (next) {
-            chunk = next;
-            next = chunk->next;
-
-            SDL_assert(chunk->head == 0);
-            SDL_assert(total >= chunk->tail);
-            total -= chunk->tail;
-
-            DestroyAudioChunk(chunk);
-        }
-
-        chunk = track->tail;
-
-        SDL_assert(chunk->tail >= total);
-        chunk->tail -= total;
-        SDL_assert(chunk->head <= chunk->tail);
-
-        return SDL_OutOfMemory();
-    }
-
-    track->tail = chunk;
-    track->queued_bytes += total;
-
-    return 0;
-}
-
-static void ReadFromAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len)
-{
-    if (len == 0) {
-        return;
-    }
-
-    SDL_AudioTrack *track = queue->head;
-    SDL_AudioChunk *chunk = track->head;
-    size_t total = 0;
-
-    SDL_assert(len <= track->queued_bytes);
-
-    for (;;) {
-        SDL_assert(chunk != NULL);
-
-        size_t to_read = chunk->tail - chunk->head;
-        to_read = SDL_min(to_read, len - total);
-        SDL_memcpy(&data[total], &chunk->data[chunk->head], to_read);
-        total += to_read;
-
-        if (total == len) {
-            chunk->head += to_read;
-            break;
-        }
-
-        SDL_AudioChunk *next = chunk->next;
-
-        const size_t max_free_chunks = 4;
-
-        if (queue->num_free_chunks < max_free_chunks) {
-            chunk->next = queue->free_chunks;
-            queue->free_chunks = chunk;
-            ++queue->num_free_chunks;
-        } else {
-            DestroyAudioChunk(chunk);
-        }
-
-        chunk = next;
-    }
-
-    SDL_assert(total == len);
-    SDL_assert(chunk != NULL);
-    track->head = chunk;
-
-    SDL_assert(track->queued_bytes >= total);
-    track->queued_bytes -= total;
-}
-
-static size_t PeekIntoAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len)
-{
-    SDL_AudioTrack *track = queue->head;
-    SDL_AudioChunk *chunk = track->head;
-    size_t total = 0;
-
-    for (; chunk; chunk = chunk->next) {
-        size_t to_read = chunk->tail - chunk->head;
-        to_read = SDL_min(to_read, len - total);
-        SDL_memcpy(&data[total], &chunk->data[chunk->head], to_read);
-        total += to_read;
-
-        if (total == len) {
-            break;
-        }
-    }
-
-    return total;
-}
-
-static void FlushAudioQueue(SDL_AudioQueue *queue)
-{
-    SDL_AudioTrack *track = queue->tail;
-
-    if (track) {
-        track->flushed = SDL_TRUE;
-    }
-}
-
-static SDL_AudioTrack* GetCurrentAudioTrack(SDL_AudioQueue *queue)
-{
-    return queue->head;
-}
-
-static void PopCurrentAudioTrack(SDL_AudioQueue *queue)
-{
-    SDL_AudioTrack *track = queue->head;
-
-    SDL_assert(track->flushed);
-
-    SDL_AudioTrack *next = track->next;
-    DestroyAudioTrack(track);
-
-    queue->head = next;
-
-    if (next == NULL)
-        queue->tail = NULL;
-}
-
-static SDL_AudioChunk *CreateAudioChunks(size_t chunk_size, const Uint8 *data, size_t len)
-{
-    SDL_assert(len != 0);
-
-    SDL_AudioChunk *head = NULL;
-    SDL_AudioChunk *tail = NULL;
-
-    while (len > 0) {
-        SDL_AudioChunk *chunk = CreateAudioChunk(chunk_size);
-
-        if (chunk == NULL) {
-            break;
-        }
-
-        size_t to_write = SDL_min(len, chunk_size);
-
-        SDL_memcpy(chunk->data, data, to_write);
-        chunk->tail = to_write;
-
-        data += to_write;
-        len -= to_write;
-
-        if (tail) {
-            tail->next = chunk;
-        } else {
-            head = chunk;
-        }
-
-        tail = chunk;
-    }
-
-    if (len > 0) {
-        DestroyAudioChunks(head);
-        SDL_OutOfMemory();
-        return NULL;
-    }
-
-    tail->next = head;
-
-    return tail;
-}
-
-static int WriteChunksToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, SDL_AudioChunk *chunks, size_t len)
-{
-    SDL_AudioChunk *tail = chunks;
-    SDL_AudioChunk *head = tail->next;
-    tail->next = NULL;
-
-    SDL_AudioTrack *track = GetAudioQueueTrackForWriting(queue, spec);
-
-    if (track == NULL) {
-        DestroyAudioChunks(head);
-        return -1;
-    }
-
-    if (track->tail) {
-        track->tail->next = head;
-    } else {
-        track->head = head;
-    }
-
-    track->tail = tail;
-    track->queued_bytes += len;
-
-    return 0;
-}
-
-/* For a given srcpos, `srcpos + frame` are sampled, where `-RESAMPLER_ZERO_CROSSINGS < frame <= RESAMPLER_ZERO_CROSSINGS`.
- * Note, when upsampling, it is also possible to start sampling from `srcpos = -1`. */
-#define RESAMPLER_MAX_PADDING_FRAMES (RESAMPLER_ZERO_CROSSINGS + 1)
-
-/* The source position is tracked using 32:32 fixed-point arithmetic.
- * This gives high precision and avoids lots of divides in ResampleAudio. */
-static Sint64 GetResampleRate(const int src_rate, const int dst_rate)
-{
-    SDL_assert(src_rate > 0);
-    SDL_assert(dst_rate > 0);
-
-    if (src_rate == dst_rate) {
-        return 0;
-    }
-
-    Sint64 sample_rate = ((Sint64)src_rate << 32) / (Sint64)dst_rate;
-    SDL_assert(sample_rate > 0);
-
-    return sample_rate;
-}
+#include "SDL_audioqueue.h"
+#include "SDL_audioresample.h"
 
-// !!! FIXME: This will blow up on weird processors.
 #ifndef SDL_INT_MAX
-#define SDL_INT_MAX 0x7FFFFFFF
-#endif
-
-static int GetResamplerAvailableOutputFrames(const size_t input_frames, const Sint64 resample_rate, const Sint64 resample_offset)
-{
-    const Sint64 output_frames = (((Sint64)input_frames << 32) - resample_offset + resample_rate - 1) / resample_rate;
-
-    return (int) SDL_clamp(output_frames, 0, SDL_INT_MAX);
-}
-
-static int GetResamplerNeededInputFrames(const int output_frames, const Sint64 resample_rate, const Sint64 resample_offset)
-{
-    const Sint32 input_frames = (Sint32)((((output_frames - 1) * resample_rate) + resample_offset) >> 32) + 1;
-
-    return (int) SDL_clamp(input_frames, 0, SDL_INT_MAX);
-}
-
-static int GetResamplerPaddingFrames(const Sint64 resample_rate)
-{
-    // This must always be <= GetHistoryBufferSampleFrames()
-
-    return resample_rate ? RESAMPLER_MAX_PADDING_FRAMES : 0;
-}
-
-static int GetHistoryBufferSampleFrames()
-{
-    // Even if we aren't currently resampling, make sure to keep enough history in case we need to later.
-    return RESAMPLER_MAX_PADDING_FRAMES;
-}
-
-#define RESAMPLER_FILTER_INTERP_BITS (32 - RESAMPLER_BITS_PER_ZERO_CROSSING)
-#define RESAMPLER_FILTER_INTERP_RANGE (1 << RESAMPLER_FILTER_INTERP_BITS)
-
-#define RESAMPLER_SAMPLES_PER_FRAME (RESAMPLER_ZERO_CROSSINGS * 2)
-
-#define RESAMPLER_FULL_FILTER_SIZE (RESAMPLER_SAMPLES_PER_FRAME * (RESAMPLER_SAMPLES_PER_ZERO_CROSSING + 1))
-
-static void ResampleFrame_Scalar(const float* src, float* dst, const float* raw_filter, const float interp, const int chans)
-{
-    int i, chan;
-
-    float filter[RESAMPLER_SAMPLES_PER_FRAME];
-
-    // Interpolate between the nearest two filters
-    for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
-        filter[i] = (raw_filter[i] * (1.0f - interp)) + (raw_filter[i + RESAMPLER_SAMPLES_PER_FRAME] * interp);
-    }
-
-    if (chans == 2) {
-        float v0 = 0.0f;
-        float v1 = 0.0f;
-
-        for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
-            const float scale = filter[i];
-            v0 += src[i * 2 + 0] * scale;
-            v1 += src[i * 2 + 1] * scale;
-        }
-
-        dst[0] = v0;
-        dst[1] = v1;
-        return;
-    }
-
-    if (chans == 1) {
-        float v0 = 0.0f;
-
-        for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
-            v0 += src[i] * filter[i];
-        }
-
-        dst[0] = v0;
-        return;
-    }
-
-    for (chan = 0; chan < chans; chan++) {
-        float f = 0.0f;
-
-        for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
-            f += src[i * chans + chan] * filter[i];
-        }
-
-        dst[chan] = f;
-    }
-}
-
-#ifdef SDL_SSE_INTRINSICS
-static void SDL_TARGETING("sse") ResampleFrame_SSE(const float* src, float* dst, const float* raw_filter, const float interp, const int chans)
-{
-#if RESAMPLER_SAMPLES_PER_FRAME != 10
-#error Invalid samples per frame
-#endif
-
-    // Load the filter
-    __m128 f0 = _mm_loadu_ps(raw_filter + 0);
-    __m128 f1 = _mm_loadu_ps(raw_filter + 4);
-    __m128 f2 = _mm_loadl_pi(_mm_setzero_ps(), (const __m64*)(raw_filter + 8));
-
-    __m128 g0 = _mm_loadu_ps(raw_filter + 10);
-    __m128 g1 = _mm_loadu_ps(raw_filter + 14);
-    __m128 g2 = _mm_loadl_pi(_mm_setzero_ps(), (const __m64*)(raw_filter + 18));
-
-    __m128 interp1 = _mm_set1_ps(interp);
-    __m128 interp2 = _mm_sub_ps(_mm_set1_ps(1.0f), _mm_set1_ps(interp));
-
-    // Linear interpolate the filter
-    f0 = _mm_add_ps(_mm_mul_ps(f0, interp2), _mm_mul_ps(g0, interp1));
-    f1 = _mm_add_ps(_mm_mul_ps(f1, interp2), _mm_mul_ps(g1, interp1));
-    f2 = _mm_add_ps(_mm_mul_ps(f2, interp2), _mm_mul_ps(g2, interp1));
-
-    if (chans == 2) {
-        // Duplicate each of the filter elements
-        g0 = _mm_unpackhi_ps(f0, f0);
-        f0 = _mm_unpacklo_ps(f0, f0);
-        g1 = _mm_unpackhi_ps(f1, f1);
-        f1 = _mm_unpacklo_ps(f1, f1);
-        f2 = _mm_unpacklo_ps(f2, f2);
-
-        // Multiply the filter by the input
-        f0 = _mm_mul_ps(f0, _mm_loadu_ps(src + 0));
-        g0 = _mm_mul_ps(g0, _mm_loadu_ps(src + 4));
-        f1 = _mm_mul_ps(f1, _mm_loadu_ps(src + 8));
-        g1 = _mm_mul_ps(g1, _mm_loadu_ps(src + 12));
-        f2 = _mm_mul_ps(f2, _mm_loadu_ps(src + 16));
-
-        // Calculate the sum
-        f0 = _mm_add_ps(_mm_add_ps(_mm_add_ps(f0, g0), _mm_add_ps(f1, g1)), f2);
-        f0 = _mm_add_ps(f0, _mm_movehl_ps(f0, f0));
-
-        // Store the result
-        _mm_storel_pi((__m64*) dst, f0);
-        return;
-    }
-
-    if (chans == 1) {
-        // Multiply the filter by the input
-        f0 = _mm_mul_ps(f0, _mm_loadu_ps(src + 0));
-        f1 = _mm_mul_ps(f1, _mm_loadu_ps(src + 4));
-        f2 = _mm_mul_ps(f2, _mm_loadl_pi(_mm_setzero_ps(), (const __m64*)(src + 8)));
-
-        // Calculate the sum
-        f0 = _mm_add_ps(f0, f1);
-        f0 = _mm_add_ps(_mm_add_ps(f0, f2), _mm_movehl_ps(f0, f0));
-        f0 = _mm_add_ss(f0, _mm_shuffle_ps(f0, f0, _MM_SHUFFLE(1, 1, 1, 1)));
-
-        // Store the result
-        _mm_store_ss(dst, f0);
-        return;
-    }
-
-    float filter[RESAMPLER_SAMPLES_PER_FRAME];
-    _mm_storeu_ps(filter + 0, f0);
-    _mm_storeu_ps(filter + 4, f1);
-    _mm_storel_pi((__m64*)(filter + 8), f2);
-
-    int i, chan = 0;
-
-    for (; chan + 4 <= chans; chan += 4) {
-        f0 = _mm_setzero_ps();
-
-        for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
-            f0 = _mm_add_ps(f0, _mm_mul_ps(_mm_loadu_ps(&src[i * chans + chan]), _mm_load1_ps(&filter[i])));
-        }
-
-        _mm_storeu_ps(&dst[chan], f0);
-    }
-
-    for (; chan < chans; chan++) {
-        f0 = _mm_setzero_ps();
-
-        for (i = 0; i < RESAMPLER_SAMPLES_PER_FRAME; i++) {
-            f0 = _mm_add_ss(f0, _mm_mul_ss(_mm_load_ss(&src[i * chans + chan]), _mm_load_ss(&filter[i])));
-        }
-
-        _mm_store_ss(&dst[chan], f0);
-    }
-}
-#endif
-
-static void (*ResampleFrame)(const float* src, float* dst, const float* raw_filter, const float interp, const int chans);
-
-static float FullResamplerFilter[RESAMPLER_FULL_FILTER_SIZE];
-
-void SDL_SetupAudioResampler()
-{
-    static SDL_bool setup = SDL_FALSE;
-    if (setup) {
-        return;
-    }
-
-    // Build a table combining the left and right wings, for faster access
-    int i, j;
-
-    for (i = 0; i < RESAMPLER_SAMPLES_PER_ZERO_CROSSING; ++i) {
-        for (j = 0; j < RESAMPLER_ZERO_CROSSINGS; j++) {
-            int lwing = (i * RESAMPLER_SAMPLES_PER_FRAME) + (RESAMPLER_ZERO_CROSSINGS - 1) - j;
-            int rwing = (RESAMPLER_FULL_FILTER_SIZE - 1) - lwing;
-
-            float value = ResamplerFilter[(i * RESAMPLER_ZERO_CROSSINGS) + j];
-            FullResamplerFilter[lwing] = value;
-            FullResamplerFilter[rwing] = value;
-        }
-    }
-
-    for (i = 0; i < RESAMPLER_ZERO_CROSSINGS; ++i) {
-        int rwing = i + RESAMPLER_ZERO_CROSSINGS;
-        int lwing = (RESAMPLER_FULL_FILTER_SIZE - 1) - rwing;
-
-        FullResamplerFilter[lwing] = 0.0f;
-        FullResamplerFilter[rwing] = 0.0f;
-    }
-
-    ResampleFrame = ResampleFrame_Scalar;
-
-#ifdef SDL_SSE_INTRINSICS
-    if (SDL_HasSSE()) {
-        ResampleFrame = ResampleFrame_SSE;
-    }
+#define SDL_INT_MAX ((int)(~0u>>1))
 #endif
 
-    setup = SDL_TRUE;
-}
-
-static void ResampleAudio(const int chans, const float *inbuf, const int inframes, float *outbuf, const int outframes,
-                          const Sint64 resample_rate, Sint64* resample_offset)
-{
-    SDL_assert(resample_rate > 0);
-    float *dst = outbuf;
-    int i;
-
-    Sint64 srcpos = *resample_offset;
-
-    for (i = 0; i < outframes; i++) {
-        int srcindex = (int)(Sint32)(srcpos >> 32);
-        Uint32 srcfraction = (Uint32)(srcpos & 0xFFFFFFFF);
-        srcpos += resample_rate;
-
-        SDL_assert(srcindex >= -1 && srcindex < inframes);
-
-        const float* filter = &FullResamplerFilter[(srcfraction >> RESAMPLER_FILTER_INTERP_BITS) * RESAMPLER_SAMPLES_PER_FRAME];
-        const float interp = (float)(srcfraction & (RESAMPLER_FILTER_INTERP_RANGE - 1)) * (1.0f / RESAMPLER_FILTER_INTERP_RANGE);
-
-        const float* src = &inbuf[(srcindex - (RESAMPLER_ZERO_CROSSINGS - 1)) * chans];
-        ResampleFrame(src, dst, filter, interp, chans);
-
-        dst += chans;
-    }
-
-    *resample_offset = srcpos - ((Sint64)inframes << 32);
-}
+#define AUDIO_SPECS_EQUAL(x, y) (((x).format == (y).format) && ((x).channels == (y).channels) && ((x).freq == (y).freq))
 
 /*
  * CHANNEL LAYOUTS AS SDL EXPECTS THEM:
@@ -1056,16 +365,27 @@ static int CalculateMaxFrameSize(SDL_AudioFormat src_format, int src_channels, S
     return max_format_size * max_channels;
 }
 
-static Sint64 GetStreamResampleRate(SDL_AudioStream* stream, int src_freq)
+static Sint64 GetAudioStreamResampleRate(SDL_AudioStream* stream, int src_freq, Sint64 resample_offset)
 {
     src_freq = (int)((float)src_freq * stream->freq_ratio);
 
-    return GetResampleRate(src_freq, stream->dst_spec.freq);
+    Sint64 resample_rate = SDL_GetResampleRate(src_freq, stream->dst_spec.freq);
+
+    // If src_freq == dst_freq, and we aren't between frames, don't resample
+    if ((resample_rate == 0x100000000) && (resample_offset == 0)) {
+        resample_rate = 0;
+    }
+
+    return resample_rate;
 }
 
-static int ResetHistoryBuffer(SDL_AudioStream *stream, const SDL_AudioSpec *spec)
+static int UpdateAudioStreamInputSpec(SDL_AudioStream *stream, const SDL_AudioSpec *spec)
 {
-    const size_t history_buffer_allocation = GetHistoryBufferSampleFrames() * SDL_AUDIO_FRAMESIZE(*spec);
+    if (AUDIO_SPECS_EQUAL(stream->input_spec, *spec)) {
+        return 0;
+    }
+
+    const size_t history_buffer_allocation = SDL_GetResamplerHistoryFrames() * SDL_AUDIO_FRAMESIZE(*spec);
     Uint8 *history_buffer = stream->history_buffer;
 
     if (stream->history_buffer_allocation < history_buffer_allocation) {
@@ -1079,6 +399,7 @@ static int ResetHistoryBuffer(SDL_AudioStream *stream, const SDL_AudioSpec *spec
     }
 
     SDL_memset(history_buffer, SDL_GetSilenceValueForFormat(spec->format), history_buffer_allocation);
+    SDL_copyp(&stream->input_spec, spec);
 
     return 0;
 }
@@ -1097,8 +418,7 @@ SDL_AudioStream *SDL_CreateAudioStream(const SDL_AudioSpec *src_spec, const SDL_
     }
 
     retval->freq_ratio = 1.0f;
-    retval->queue = CreateAudioQueue(4096);
-    retval->track_changed = SDL_TRUE;
+    retval->queue = SDL_CreateAudioQueue(4096);
 
     if (retval->queue == NULL) {
         SDL_free(retval);
@@ -1219,13 +539,7 @@ int SDL_SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *src_s
     SDL_LockMutex(stream->lock);
 
     if (src_spec) {
-        // If the format hasn't changed, don't try and flush the stream.
-        if ((stream->src_spec.format != src_spec->format) ||
-            (stream->src_spec.channels != src_spec->channels) ||
-            (stream->src_spec.freq != src_spec->freq)) {
-            SDL_FlushAudioStream(stream);
-            SDL_copyp(&stream->src_spec, src_spec);
-        }
+        SDL_copyp(&stream->src_spec, src_spec);
     }
 
     if (dst_spec) {
@@ -1313,20 +627,22 @@ int SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len)
         return SDL_SetError("Can't add partial sample frames");
     }
 
-    SDL_AudioChunk* chunks = NULL;
+    SDL_AudioTrack* track = NULL;
 
     // When copying in large amounts of data, try and do as much work as possible
     // outside of the stream lock, otherwise the output device is likely to be starved.
-    const int large_input_thresh = 64 * 1024;
+    const int large_input_thresh = 1024 * 1024;
 
     if (len >= large_input_thresh) {
-        size_t chunk_size = stream->queue->chunk_size;
+        SDL_AudioSpec src_spec;
+        SDL_copyp(&src_spec, &stream->src_spec);
 
         SDL_UnlockMutex(stream->lock);
 
-        chunks = CreateAudioChunks(chunk_size, (const Uint8*) buf, len);
+        size_t chunk_size = SDL_GetAudioQueueChunkSize(stream->queue);
+        track = SDL_CreateChunkedAudioTrack(&src_spec, buf, len, chunk_size);
 
-        if (chunks == NULL) {
+        if (track == NULL) {
             return -1;
         }
 
@@ -1335,10 +651,13 @@ int SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len)
 
     const int prev_available = stream->put_callback ? SDL_GetAudioStreamAvailable(stream) : 0;
 
-    // just queue the data, we convert/resample when dequeueing.
-    const int retval = chunks
-        ? WriteChunksToAudioQueue(stream->queue, &stream->src_spec, chunks, len)
-        : WriteToAudioQueue(stream->queue, &stream->src_spec, (const Uint8*) buf, len);
+    int retval = 0;
+
+    if (track != NULL) {
+        SDL_AddTrackToAudioQ

(Patch may be truncated, please check the link at the top of this post.)