Browse Source

Refactored GetAudioStreamDataInternal buffer handling
The final conversion step should now always go straight into the output buffer.

Brick 1 year ago
parent
commit
106abce69f
2 changed files with 174 additions and 129 deletions
  1. 174 128
      src/audio/SDL_audiocvt.c
  2. 0 1
      src/audio/SDL_sysaudio.h

+ 174 - 128
src/audio/SDL_audiocvt.c

@@ -439,7 +439,6 @@ static void AudioConvertToFloat(float *dst, const void *src, int num_samples, SD
         case SDL_AUDIO_U8: SDL_Convert_U8_to_F32(dst, (const Uint8 *) src, num_samples); break;
         case SDL_AUDIO_S16: SDL_Convert_S16_to_F32(dst, (const Sint16 *) src, num_samples); break;
         case SDL_AUDIO_S32: SDL_Convert_S32_to_F32(dst, (const Sint32 *) src, num_samples); break;
-        case SDL_AUDIO_F32: if (dst != src) { SDL_memcpy(dst, src, num_samples * sizeof (float)); } break;  // oh well, just pass it through.
         default: SDL_assert(!"Unexpected audio format!"); break;
     }
 }
@@ -452,7 +451,6 @@ static void AudioConvertFromFloat(void *dst, const float *src, int num_samples,
         case SDL_AUDIO_U8: SDL_Convert_F32_to_U8((Uint8 *) dst, src, num_samples); break;
         case SDL_AUDIO_S16: SDL_Convert_F32_to_S16((Sint16 *) dst, src, num_samples); break;
         case SDL_AUDIO_S32: SDL_Convert_F32_to_S32((Sint32 *) dst, src, num_samples); break;
-        case SDL_AUDIO_F32: if (dst != src) { SDL_memcpy(dst, src, num_samples * sizeof (float)); } break;  // oh well, just pass it through.
         default: SDL_assert(!"Unexpected audio format!"); break;
     }
 }
@@ -483,15 +481,15 @@ static SDL_bool SDL_IsSupportedChannelCount(const int channels)
 }
 
 
-/* this does type and channel conversions _but not resampling_ (resampling
-   happens in SDL_AudioStream). This expects data to be aligned/padded for
-   SIMD access. This does not check parameter validity,
-   (beyond asserts), it expects you did that already! */
-/* all of this has to function as if src==dst (conversion in-place), but as a convenience
-   if you're just going to copy the final output elsewhere, you can specify a different
-   output pointer. */
+// This does type and channel conversions _but not resampling_ (resampling happens in SDL_AudioStream).
+// This does not check parameter validity, (beyond asserts), it expects you did that already!
+// All of this has to function as if src==dst==scratch (conversion in-place), but as a convenience
+// if you're just going to copy the final output elsewhere, you can specify a different output pointer.
+//
+// The scratch buffer must be able to store `num_frames * CalculateMaxSampleFrameSize(src_format, src_channels, dst_format, dst_channels)` bytes.
+// If the scratch buffer is NULL, this restriction applies to the output buffer instead.
 static 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 *dst, SDL_AudioFormat dst_format, int dst_channels, void* scratch)
 {
     SDL_assert(src != NULL);
     SDL_assert(dst != NULL);
@@ -508,8 +506,10 @@ static void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_fo
     SDL_Log("SDL_AUDIO_CONVERT: Convert format %04x->%04x, channels %u->%u", src_format, dst_format, src_channels, dst_channels);
 #endif
 
-    const int dst_bitsize = (int) SDL_AUDIO_BITSIZE(dst_format);
     const int src_bitsize = (int) SDL_AUDIO_BITSIZE(src_format);
+    const int dst_bitsize = (int) SDL_AUDIO_BITSIZE(dst_format);
+
+    const int dst_sample_frame_size = (dst_bitsize / 8) * dst_channels;
 
     /* Type conversion goes like this now:
         - byteswap to CPU native format first if necessary.
@@ -530,7 +530,7 @@ static void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_fo
         if (src_format == dst_format) {
             // nothing to do, we're already in the right format, just copy it over if necessary.
             if (src != dst) {
-                SDL_memcpy(dst, src, num_frames * src_channels * (dst_bitsize / 8));
+                SDL_memcpy(dst, src, num_frames * dst_sample_frame_size);
             }
             return;
         }
@@ -539,7 +539,7 @@ static void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_fo
         if ((src_format & ~SDL_AUDIO_MASK_ENDIAN) == (dst_format & ~SDL_AUDIO_MASK_ENDIAN)) {
             if (src_bitsize == 8) {
                 if (src != dst) {
-                    SDL_memcpy(dst, src, num_frames * src_channels * (dst_bitsize / 8));
+                    SDL_memcpy(dst, src, num_frames * dst_sample_frame_size);
                 }
                 return;  // nothing to do, it's a 1-byte format.
             }
@@ -548,21 +548,33 @@ static void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_fo
         }
     }
 
+    if (scratch == NULL) {
+        scratch = dst;
+    }
+
+    const SDL_bool srcbyteswap = (SDL_AUDIO_ISBIGENDIAN(src_format) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN) && (src_bitsize > 8);
+    const SDL_bool srcconvert = !SDL_AUDIO_ISFLOAT(src_format);
+    const SDL_bool channelconvert = src_channels != dst_channels;
+    const SDL_bool dstconvert = !SDL_AUDIO_ISFLOAT(dst_format);
+    const SDL_bool dstbyteswap = (SDL_AUDIO_ISBIGENDIAN(dst_format) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN) && (dst_bitsize > 8);
+
     // make sure we're in native byte order.
-    if ((SDL_AUDIO_ISBIGENDIAN(src_format) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN) && (src_bitsize > 8)) {
-        AudioConvertByteswap(dst, src, num_frames * src_channels, src_bitsize);
-        src = dst;  // we've written to dst, future work will convert in-place.
+    if (srcbyteswap) {
+        // No point writing straight to dst. If we only need a byteswap, we wouldn't be bere.
+        AudioConvertByteswap(scratch, src, num_frames * src_channels, src_bitsize);
+        src = scratch;
     }
 
     // get us to float format.
-    if (!SDL_AUDIO_ISFLOAT(src_format)) {
-        AudioConvertToFloat((float *) dst, src, num_frames * src_channels, src_format);
-        src = dst;  // we've written to dst, future work will convert in-place.
+    if (srcconvert) {
+        void* buf = (channelconvert || dstconvert || dstbyteswap) ? scratch : dst;
+        AudioConvertToFloat((float *) buf, src, num_frames * src_channels, src_format);
+        src = buf;
     }
 
     // Channel conversion
 
-    if (src_channels != dst_channels) {
+    if (channelconvert) {
         SDL_AudioChannelConverter channel_converter;
         SDL_AudioChannelConverter override = NULL;
 
@@ -587,20 +599,22 @@ static void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_fo
         if (override) {
             channel_converter = override;
         }
-        channel_converter((float *) dst, (float *) src, num_frames);
-        src = dst;  // we've written to dst, future work will convert in-place.
+
+        void* buf = (dstconvert || dstbyteswap) ? scratch : dst;
+        channel_converter((float *) buf, (const float *) src, num_frames);
+        src = buf;
     }
 
     // Resampling is not done in here. SDL_AudioStream handles that.
 
     // Move to final data type.
-    if (!SDL_AUDIO_ISFLOAT(dst_format)) {
-        AudioConvertFromFloat(dst, (float *) src, num_frames * dst_channels, dst_format);
-        src = dst;  // we've written to dst, future work will convert in-place.
+    if (dstconvert) {
+        AudioConvertFromFloat(dst, (const float *) src, num_frames * dst_channels, dst_format);
+        src = dst;
     }
 
     // make sure we're in final byte order.
-    if ((SDL_AUDIO_ISBIGENDIAN(dst_format) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN) && (dst_bitsize > 8)) {
+    if (dstbyteswap) {
         AudioConvertByteswap(dst, src, num_frames * dst_channels, dst_bitsize);
         src = dst;  // we've written to dst, future work will convert in-place.
     }
@@ -608,13 +622,13 @@ static void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_fo
     SDL_assert(src == dst);  // if we got here, we _had_ to have done _something_. Otherwise, we should have memcpy'd!
 }
 
-// figure out the largest thing we might need for ConvertAudio, which might grow data in-place.
+// Calculate the largest frame size needed to convert between the two formats.
 static int CalculateMaxSampleFrameSize(SDL_AudioFormat src_format, int src_channels, SDL_AudioFormat dst_format, int dst_channels)
 {
     const int src_format_size = SDL_AUDIO_BITSIZE(src_format) / 8;
     const int dst_format_size = SDL_AUDIO_BITSIZE(dst_format) / 8;
     const int max_app_format_size = SDL_max(src_format_size, dst_format_size);
-    const int max_format_size = SDL_max(max_app_format_size, sizeof (float));  // ConvertAudio converts to float internally.
+    const int max_format_size = SDL_max(max_app_format_size, sizeof (float));  // ConvertAudio and ResampleAudio use floats.
     const int max_channels = SDL_max(src_channels, dst_channels);
     return max_format_size * max_channels;
 }
@@ -622,27 +636,23 @@ static int CalculateMaxSampleFrameSize(SDL_AudioFormat src_format, int src_chann
 // this assumes you're holding the stream's lock (or are still creating the stream).
 static int SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *src_spec, const SDL_AudioSpec *dst_spec)
 {
-    /* If increasing channels, do it after resampling, since we'd just
-       do more work to resample duplicate channels. If we're decreasing, do
-       it first so we resample the interpolated data instead of interpolating
-       the resampled data (!!! FIXME: decide if that works in practice, though!).
-       This is decided in pre_resample_channels. */
-
     const SDL_AudioFormat src_format = src_spec->format;
     const int src_channels = src_spec->channels;
     const int src_rate = src_spec->freq;
+
     const SDL_AudioFormat dst_format = dst_spec->format;
     const int dst_channels = dst_spec->channels;
     const int dst_rate = dst_spec->freq;
+
     const int src_sample_frame_size = (SDL_AUDIO_BITSIZE(src_format) / 8) * src_channels;
     const int dst_sample_frame_size = (SDL_AUDIO_BITSIZE(dst_format) / 8) * dst_channels;
-    const int max_sample_frame_size = CalculateMaxSampleFrameSize(src_format, src_channels, dst_format, dst_channels);
+
     const int prev_history_buffer_frames = stream->history_buffer_frames;
-    const int pre_resample_channels = SDL_min(src_channels, dst_channels);
     const Sint64 resample_rate = GetResampleRate(src_rate, dst_rate);
     const int resampler_padding_frames = GetResamplerPaddingFrames(resample_rate);
     const int history_buffer_frames = GetHistoryBufferSampleFrames(resampler_padding_frames);
-    const size_t history_buffer_allocation = history_buffer_frames * max_sample_frame_size;
+    const int history_buffer_frame_size = CalculateMaxSampleFrameSize(stream->src_spec.format, stream->src_spec.channels, src_format, src_channels);
+    const size_t history_buffer_allocation = history_buffer_frames * history_buffer_frame_size;
     Uint8 *history_buffer = stream->history_buffer;
 
     // do all the things that can fail upfront, so we can just return an error without changing the stream if anything goes wrong.
@@ -661,9 +671,15 @@ static int SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *sr
 
     if (stream->history_buffer) {
         if (history_buffer_frames <= prev_history_buffer_frames) {
-            ConvertAudio(history_buffer_frames, stream->history_buffer, stream->src_spec.format, stream->src_spec.channels, history_buffer, src_format, src_channels);
+            ConvertAudio(history_buffer_frames, stream->history_buffer,
+                stream->src_spec.format, stream->src_spec.channels,
+                history_buffer,
+                src_format, src_channels, NULL);
         } else {
-            ConvertAudio(prev_history_buffer_frames, stream->history_buffer, stream->src_spec.format, stream->src_spec.channels, history_buffer + ((history_buffer_frames - prev_history_buffer_frames) * src_sample_frame_size), src_format, src_channels);
+            ConvertAudio(prev_history_buffer_frames, stream->history_buffer,
+                stream->src_spec.format, stream->src_spec.channels,
+                history_buffer + ((history_buffer_frames - prev_history_buffer_frames) * src_sample_frame_size),
+                src_format, src_channels, NULL);
             SDL_memset(history_buffer, SDL_GetSilenceValueForFormat(src_format), (history_buffer_frames - prev_history_buffer_frames) * src_sample_frame_size);  // silence oldest history samples.
         }
     } else if (history_buffer != NULL) {
@@ -678,10 +694,9 @@ static int SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *sr
 
     stream->resampler_padding_frames = resampler_padding_frames;
     stream->history_buffer_frames = history_buffer_frames;
-    stream->max_sample_frame_size = max_sample_frame_size;
     stream->src_sample_frame_size = src_sample_frame_size;
     stream->dst_sample_frame_size = dst_sample_frame_size;
-    stream->pre_resample_channels = pre_resample_channels;
+    stream->max_sample_frame_size = CalculateMaxSampleFrameSize(src_format, src_channels, dst_format, dst_channels);
     stream->resample_rate = resample_rate;
 
     if (src_spec != &stream->src_spec) {
@@ -922,18 +937,26 @@ static Uint8 *EnsureStreamWorkBufferSize(SDL_AudioStream *stream, size_t newlen)
     return ptr;
 }
 
-static int CalculateAudioStreamWorkBufSize(const SDL_AudioStream *stream, int input_frames, int output_frames)
+static void UpdateStreamHistoryBuffer(SDL_AudioStream* stream, Uint8* input_buffer, int input_bytes, Uint8* left_padding, int padding_bytes)
 {
-    int workbuf_frames = input_frames + (stream->resampler_padding_frames * 2);
-    int workbuflen = workbuf_frames * stream->max_sample_frame_size;
+    // Even if we aren't currently resampling, we always need to update the history buffer
+    Uint8 *history_buffer = stream->history_buffer;
+    int history_bytes = stream->history_buffer_frames * stream->src_sample_frame_size;
 
-    if (stream->resample_rate) {
-        // Calculate space needed after resample (which lives in a second copy in the same buffer).
-        int resample_frame_size = stream->pre_resample_channels * sizeof(float);
-        workbuflen += output_frames * resample_frame_size;
+    if (left_padding != NULL) {
+        // Fill in the left padding using the history buffer
+        SDL_assert(padding_bytes <= history_bytes);
+        SDL_memcpy(left_padding, history_buffer + history_bytes - padding_bytes, padding_bytes);
+    }
+    
+    // Update the history buffer using the new input data
+    if (input_bytes >= history_bytes) {
+        SDL_memcpy(history_buffer, input_buffer + (input_bytes - history_bytes), history_bytes);
+    } else {
+        int preserve_bytes = history_bytes - input_bytes;
+        SDL_memmove(history_buffer, history_buffer + input_bytes, preserve_bytes);
+        SDL_memcpy(history_buffer + preserve_bytes, input_buffer, input_bytes);
     }
-
-    return workbuflen;
 }
 
 // You must hold stream->lock and validate your parameters before calling this!
@@ -948,7 +971,6 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
     const int dst_sample_frame_size = stream->dst_sample_frame_size;
 
     const int max_sample_frame_size = stream->max_sample_frame_size;
-    const int pre_resample_channels = stream->pre_resample_channels;
 
     const int resampler_padding_frames = stream->resampler_padding_frames;
     const Sint64 resample_rate = stream->resample_rate;
@@ -974,23 +996,98 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
     }
 
     int input_frames = output_frames;
+    const int output_bytes = output_frames * dst_sample_frame_size;
+
+    // Not resampling? It's an easy conversion (and maybe not even that!)
+    if (resample_rate == 0) {
+        SDL_assert(input_frames == output_frames);
+
+        Uint8* input_buffer = NULL;
+
+        // If no conversion is happening, read straight into the output buffer.
+        // Note, this is just to avoid extra copies.
+        // Some other formats may fit directly into the output buffer, but i'd rather process data in a SIMD-aligned buffer.
+        if ((src_format == dst_format) && (src_channels == dst_channels)) {
+            input_buffer = buf;
+        } else {
+            input_buffer = EnsureStreamWorkBufferSize(stream, input_frames * max_sample_frame_size);
+
+            if (!input_buffer) {
+                return -1;
+            }
+        }
+
+        const int input_bytes = input_frames * src_sample_frame_size;
+        const int bytes_read = (int) SDL_ReadFromDataQueue(stream->queue, input_buffer, input_bytes);
+        SDL_assert(bytes_read == input_bytes);
+
+        // Even if we aren't currently resampling, we always need to update the history buffer
+        UpdateStreamHistoryBuffer(stream, input_buffer, input_bytes, NULL, 0);
+
+        // Convert the data, if necessary
+        if (buf != input_buffer) {
+            ConvertAudio(input_frames, input_buffer, src_format, src_channels, buf, dst_format, dst_channels, input_buffer);
+        }
 
-    if (resample_rate) {
-        // Calculate the number of input frames necessary for this request.
-        // Because resampling happens "between" frames, The same number of output_frames
-        // can require a different number of input_frames, depending on the resample_offset.
-        input_frames = GetResamplerNeededInputFrames(output_frames, resample_rate, stream->resample_offset);
+        return output_bytes;
+    }
+
+    // Time to do some resampling!
+    // Calculate the number of input frames necessary for this request.
+    // Because resampling happens "between" frames, The same number of output_frames
+    // can require a different number of input_frames, depending on the resample_offset.
+    // Infact, input_frames can sometimes even be zero when upsampling.
+    input_frames = GetResamplerNeededInputFrames(output_frames, resample_rate, stream->resample_offset);
+    const int input_bytes = input_frames * src_sample_frame_size;
+
+    // If increasing channels, do it after resampling, since we'd just
+    // do more work to resample duplicate channels. If we're decreasing, do
+    // it first so we resample the interpolated data instead of interpolating
+    // the resampled data.
+    const int resample_channels = SDL_min(src_channels, dst_channels);
+
+    // The size of the frame used when resampling
+    const int resample_frame_size = resample_channels * sizeof(float);
+
+    // The main portion of the work_buffer can be used to store 3 things:
+    // src_sample_frame_size * (left_padding+input_buffer+right_padding)
+    //   resample_frame_size * (left_padding+input_buffer+right_padding)
+    // dst_sample_frame_size * output_frames
+    //
+    // ResampleAudio also requires an additional buffer if it can't write straight to the output:
+    //   resample_frame_size * output_frames
+    // 
+    // Note, ConvertAudio requires (num_frames * max_sample_frame_size) of scratch space
+    const int work_buffer_frames = input_frames + (resampler_padding_frames * 2);
+    int work_buffer_capacity = work_buffer_frames * max_sample_frame_size;
+    int resample_buffer_offset = -1;
+
+    // Check if we can resample directly into the output buffer.
+    // Note, this is just to avoid extra copies.
+    // Some other formats may fit directly into the output buffer, but i'd rather process data in a SIMD-aligned buffer.
+    if ((dst_format != SDL_AUDIO_F32SYS) || (dst_channels != resample_channels)) {
+        // Allocate space for converting the resampled output to the destination format
+        int resample_convert_bytes = output_frames * max_sample_frame_size;
+        work_buffer_capacity = SDL_max(work_buffer_capacity, resample_convert_bytes);
+
+        // SIMD-align the buffer
+        int simd_alignment = (int) SDL_SIMDGetAlignment();
+        work_buffer_capacity += simd_alignment - 1;
+        work_buffer_capacity -= work_buffer_capacity % simd_alignment;
+
+        // Allocate space for the resampled output
+        int resample_bytes = output_frames * resample_frame_size;
+        resample_buffer_offset = work_buffer_capacity;
+        work_buffer_capacity += resample_bytes;
     }
 
-    int work_buffer_capacity = CalculateAudioStreamWorkBufSize(stream, input_frames, output_frames);
     Uint8* work_buffer = EnsureStreamWorkBufferSize(stream, work_buffer_capacity);
 
     if (!work_buffer) {
         return -1;
     }
 
-    int input_bytes = input_frames * src_sample_frame_size;
-    int padding_bytes = resampler_padding_frames * src_sample_frame_size;
+    const int padding_bytes = resampler_padding_frames * src_sample_frame_size;
 
     Uint8* work_buffer_tail = work_buffer;
 
@@ -1012,91 +1109,40 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le
         SDL_assert(bytes_read == input_bytes);
     }
 
-    Uint8 *history_buffer = stream->history_buffer;
-    const int history_buffer_frames = stream->history_buffer_frames;
-    int history_bytes = history_buffer_frames * src_sample_frame_size;
+    // Update the history buffer and fill in the left padding
+    UpdateStreamHistoryBuffer(stream, input_buffer, input_bytes, left_padding, padding_bytes);
 
-    // Do we need to fill in the left/right padding?
-    if (resampler_padding_frames > 0) {
-        // Fill in the left padding using the history buffer
-        SDL_assert(padding_bytes <= history_bytes);
-        SDL_memcpy(left_padding, history_buffer + history_bytes - padding_bytes, padding_bytes);
+    // Fill in the right padding by peeking into the input queue
+    const int right_padding_bytes = (int) SDL_PeekIntoDataQueue(stream->queue, right_padding, padding_bytes);
 
-        // Fill in the right padding by peeking into the input queue
-        int right_padding_bytes = (int) SDL_PeekIntoDataQueue(stream->queue, right_padding, padding_bytes);
-
-        if (right_padding_bytes < padding_bytes) {
-            // If we have run out of data, fill the rest with silence.
-            // This should only happen if the stream has been flushed.
-            SDL_assert(stream->flushed);
-            SDL_memset(right_padding + right_padding_bytes, SDL_GetSilenceValueForFormat(src_format), padding_bytes - right_padding_bytes);
-        }
+    if (right_padding_bytes < padding_bytes) {
+        // If we have run out of data, fill the rest with silence.
+        // This should only happen if the stream has been flushed.
+        SDL_assert(stream->flushed);
+        SDL_memset(right_padding + right_padding_bytes, SDL_GetSilenceValueForFormat(src_format), padding_bytes - right_padding_bytes);
     }
     
-    // Update the history buffer using the new input data
-    if (input_bytes >= history_bytes) {
-        SDL_memcpy(history_buffer, input_buffer + (input_bytes - history_bytes), history_bytes);
-    } else {
-        int preserve_bytes = history_bytes - input_bytes;
-        SDL_memmove(history_buffer, history_buffer + input_bytes, preserve_bytes);
-        SDL_memcpy(history_buffer + preserve_bytes, input_buffer, input_bytes);
-    }
-
-    int output_bytes = output_frames * dst_sample_frame_size;
-
-    // Not resampling? It's an easy conversion (and maybe not even that!)
-    if (resample_rate == 0) {
-        SDL_assert(input_frames == output_frames);
-        SDL_assert(input_buffer == work_buffer);
-
-        // see if we can do the conversion in-place (will fit in `buf` while in-progress), or if we need to do it in the workbuf and copy it over
-        if (max_sample_frame_size <= dst_sample_frame_size) {
-            ConvertAudio(input_frames, input_buffer, src_format, src_channels, buf, dst_format, dst_channels);
-        } else {
-            ConvertAudio(input_frames, input_buffer, src_format, src_channels, input_buffer, dst_format, dst_channels);
-            SDL_memcpy(buf, input_buffer, output_bytes);
-        }
-
-        return output_bytes;
-    }
-
-    int work_buffer_frames = (int)(work_buffer_tail - work_buffer) / src_sample_frame_size;
     SDL_assert(work_buffer_frames == input_frames + (resampler_padding_frames * 2));
 
     // Resampling! get the work buffer to float32 format, etc, in-place.
-    ConvertAudio(work_buffer_frames, work_buffer, src_format, src_channels, work_buffer, SDL_AUDIO_F32SYS, pre_resample_channels);
-
-    const int resample_frame_size = pre_resample_channels * sizeof(float);
-    const int resample_bytes = output_frames * resample_frame_size;
+    ConvertAudio(work_buffer_frames, work_buffer, src_format, src_channels, work_buffer, SDL_AUDIO_F32SYS, resample_channels, NULL);
 
     // Update the work_buffer pointers based on the new frame size
     input_buffer = work_buffer + ((input_buffer - work_buffer) / src_sample_frame_size * resample_frame_size);
     work_buffer_tail = work_buffer + ((work_buffer_tail - work_buffer) / src_sample_frame_size * resample_frame_size);
-
-    float* resample_buffer;
-
-    // Check if we can resample directly into the output buffer
-    if ((dst_format == SDL_AUDIO_F32SYS) && (dst_channels == pre_resample_channels)) {
-        resample_buffer = (float *) buf;
-    } else {
-        resample_buffer = (float *) work_buffer_tail; // do at the end of the buffer so we have room for final convert at front.
-        work_buffer_tail += resample_bytes;
-    }
-
     SDL_assert((work_buffer_tail - work_buffer) <= work_buffer_capacity);
 
-    ResampleAudio(pre_resample_channels,
+    // Decide where the resampled output goes
+    void* resample_buffer = (resample_buffer_offset != -1) ? (work_buffer + resample_buffer_offset) : buf;
+
+    ResampleAudio(resample_channels,
                   (const float *) input_buffer, input_frames,
-                  resample_buffer, output_frames,
+                  (float*) resample_buffer, output_frames,
                   resample_rate, &stream->resample_offset);
 
-    // Get us to the final format!
-    // see if we can do the conversion in-place (will fit in `buf` while in-progress), or if we need to do it in the workbuf and copy it over
-    if (max_sample_frame_size <= dst_sample_frame_size) {
-        ConvertAudio(output_frames, resample_buffer, SDL_AUDIO_F32SYS, pre_resample_channels, buf, dst_format, dst_channels);
-    } else {
-        ConvertAudio(output_frames, resample_buffer, SDL_AUDIO_F32SYS, pre_resample_channels, work_buffer, dst_format, dst_channels);
-        SDL_memcpy(buf, work_buffer, output_bytes);
+    // Convert to the final format, if necessary
+    if (buf != resample_buffer) {
+        ConvertAudio(output_frames, resample_buffer, SDL_AUDIO_F32SYS, resample_channels, buf, dst_format, dst_channels, work_buffer);
     }
 
     return output_bytes;

+ 0 - 1
src/audio/SDL_sysaudio.h

@@ -185,7 +185,6 @@ struct SDL_AudioStream
     int dst_sample_frame_size;
     int max_sample_frame_size;
 
-    int pre_resample_channels;
     int packetlen;
 
     SDL_LogicalAudioDevice *bound_device;