From 47e2f4e9507abe52b54d6b32a3d72750b779fbe9 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 24 Jan 2017 20:30:48 -0500 Subject: [PATCH] audio: libsamplerate can't resample in-place; make space for a copy if needed. --- src/audio/SDL_audiocvt.c | 64 +++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c index 73fb0c712f5fe..02b0559358413 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) {