Commit 47e2f4e9507abe52b54d6b32a3d72750b779fbe9

Ryan C. Gordon 2017-01-24T20:30:48

audio: libsamplerate can't resample in-place; make space for a copy if needed.

diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c
index 73fb0c7..02b0559 100644
--- a/src/audio/SDL_audiocvt.c
+++ b/src/audio/SDL_audiocvt.c
@@ -867,7 +867,6 @@ struct SDL_AudioStream
     SDL_AudioCVT cvt_before_resampling;
     SDL_AudioCVT cvt_after_resampling;
     SDL_DataQueue *queue;
-    Uint8 *work_buffer;  /* always aligned to 16 bytes. */
     Uint8 *work_buffer_base;  /* maybe unaligned pointer from SDL_realloc(). */
     int work_buffer_len;
     int src_sample_frame_size;
@@ -887,6 +886,29 @@ struct SDL_AudioStream
     SDL_CleanupAudioStreamResamplerFunc cleanup_resampler_func;
 };
 
+static Uint8 *
+EnsureStreamBufferSize(SDL_AudioStream *stream, const int newlen)
+{
+    Uint8 *ptr;
+    size_t offset;
+
+    if (stream->work_buffer_len >= newlen) {
+        ptr = stream->work_buffer_base;
+    } else {
+        ptr = (Uint8 *) SDL_realloc(stream->work_buffer_base, newlen + 32);
+        if (!ptr) {
+            SDL_OutOfMemory();
+            return NULL;
+        }
+        /* Make sure we're aligned to 16 bytes for SIMD code. */
+        stream->work_buffer_base = ptr;
+        stream->work_buffer_len = newlen;
+    }
+
+    offset = ((size_t) ptr) & 15;
+    return offset ? ptr + (16 - offset) : ptr;
+}
+
 #ifdef HAVE_LIBSAMPLERATE_H
 static int
 SDL_ResampleAudioStream_SRC(SDL_AudioStream *stream, const void *_inbuf, const int inbuflen, void *_outbuf, const int outbuflen)
@@ -898,6 +920,17 @@ SDL_ResampleAudioStream_SRC(SDL_AudioStream *stream, const void *_inbuf, const i
     SRC_DATA data;
     int result;
 
+    if (inbuf == ((const float *) outbuf)) {  /* libsamplerate can't work in-place. */
+        Uint8 *ptr = EnsureStreamBufferSize(stream, inbuflen + outbuflen);
+        if (ptr == NULL) {
+            SDL_OutOfMemory();
+            return 0;
+        }
+        SDL_memcpy(ptr + outbuflen, ptr, inbuflen);
+        inbuf = (const float *) (ptr + outbuflen);
+        outbuf = (float *) ptr;
+    }
+
     data.data_in = (float *)inbuf; /* Older versions of libsamplerate had a non-const pointer, but didn't write to it */
     data.input_frames = inbuflen / framelen;
     data.input_frames_used = 0;
@@ -1125,24 +1158,6 @@ SDL_NewAudioStream(const SDL_AudioFormat src_format,
     return retval;
 }
 
-static Uint8 *
-EnsureStreamBufferSize(SDL_AudioStream *stream, const int newlen)
-{
-    if (stream->work_buffer_len < newlen) {
-        Uint8 *ptr = (Uint8 *) SDL_realloc(stream->work_buffer_base, newlen + 32);
-        const size_t offset = ((size_t) ptr) & 15;
-        if (!ptr) {
-            SDL_OutOfMemory();
-            return NULL;
-        }
-        /* Make sure we're aligned to 16 bytes for SIMD code. */
-        stream->work_buffer = offset ? ptr + (16 - offset) : ptr;
-        stream->work_buffer_base = ptr;
-        stream->work_buffer_len = newlen;
-    }
-    return stream->work_buffer;
-}
-
 int
 SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, const Uint32 _buflen)
 {
@@ -1190,11 +1205,14 @@ SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, const Uint32 _bufle
         if (workbuf == NULL) {
             return -1;  /* probably out of memory. */
         }
-        if (buf == origbuf) {  /* copy if we haven't before. */
-            SDL_memcpy(workbuf, buf, buflen);
+        /* don't SDL_memcpy(workbuf, buf, buflen) here; our resampler can work inplace or not,
+           libsamplerate needs buffers to be separate; either way, avoid a copy here if possible. */
+        if (buf != origbuf) {
+            buf = workbuf;  /* in case we realloc()'d the pointer. */
         }
-        buflen = stream->resampler_func(stream, workbuf, buflen, workbuf, workbuflen);
-        buf = workbuf;
+        buflen = stream->resampler_func(stream, buf, buflen, workbuf, workbuflen);
+        buf = EnsureStreamBufferSize(stream, workbuflen);
+        SDL_assert(buf != NULL);  /* shouldn't be growing, just aligning. */
     }
 
     if (stream->cvt_after_resampling.needed) {