Refactored GetAudioStreamDataInternal buffer handling

The final conversion step should now always go straight into the output buffer.
This commit is contained in:
Brick
2023-08-25 12:28:21 +01:00
committed by Ryan C. Gordon
parent e44f54ec54
commit 106abce69f
2 changed files with 174 additions and 129 deletions

View File

@@ -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_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_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_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; 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_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_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_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; 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 // This does type and channel conversions _but not resampling_ (resampling happens in SDL_AudioStream).
happens in SDL_AudioStream). This expects data to be aligned/padded for // This does not check parameter validity, (beyond asserts), it expects you did that already!
SIMD access. This does not check parameter validity, // All of this has to function as if src==dst==scratch (conversion in-place), but as a convenience
(beyond asserts), it expects you did that already! */ // if you're just going to copy the final output elsewhere, you can specify a different output pointer.
/* 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 // The scratch buffer must be able to store `num_frames * CalculateMaxSampleFrameSize(src_format, src_channels, dst_format, dst_channels)` bytes.
output pointer. */ // 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, 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(src != NULL);
SDL_assert(dst != 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); SDL_Log("SDL_AUDIO_CONVERT: Convert format %04x->%04x, channels %u->%u", src_format, dst_format, src_channels, dst_channels);
#endif #endif
const int dst_bitsize = (int) SDL_AUDIO_BITSIZE(dst_format);
const int src_bitsize = (int) SDL_AUDIO_BITSIZE(src_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: /* Type conversion goes like this now:
- byteswap to CPU native format first if necessary. - 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) { if (src_format == dst_format) {
// nothing to do, we're already in the right format, just copy it over if necessary. // nothing to do, we're already in the right format, just copy it over if necessary.
if (src != dst) { 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; 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_format & ~SDL_AUDIO_MASK_ENDIAN) == (dst_format & ~SDL_AUDIO_MASK_ENDIAN)) {
if (src_bitsize == 8) { if (src_bitsize == 8) {
if (src != dst) { 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. 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. // make sure we're in native byte order.
if ((SDL_AUDIO_ISBIGENDIAN(src_format) != 0) == (SDL_BYTEORDER == SDL_LIL_ENDIAN) && (src_bitsize > 8)) { if (srcbyteswap) {
AudioConvertByteswap(dst, src, num_frames * src_channels, src_bitsize); // No point writing straight to dst. If we only need a byteswap, we wouldn't be bere.
src = dst; // we've written to dst, future work will convert in-place. AudioConvertByteswap(scratch, src, num_frames * src_channels, src_bitsize);
src = scratch;
} }
// get us to float format. // get us to float format.
if (!SDL_AUDIO_ISFLOAT(src_format)) { if (srcconvert) {
AudioConvertToFloat((float *) dst, src, num_frames * src_channels, src_format); void* buf = (channelconvert || dstconvert || dstbyteswap) ? scratch : dst;
src = dst; // we've written to dst, future work will convert in-place. AudioConvertToFloat((float *) buf, src, num_frames * src_channels, src_format);
src = buf;
} }
// Channel conversion // Channel conversion
if (src_channels != dst_channels) { if (channelconvert) {
SDL_AudioChannelConverter channel_converter; SDL_AudioChannelConverter channel_converter;
SDL_AudioChannelConverter override = NULL; SDL_AudioChannelConverter override = NULL;
@@ -587,20 +599,22 @@ static void ConvertAudio(int num_frames, const void *src, SDL_AudioFormat src_fo
if (override) { if (override) {
channel_converter = 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. // Resampling is not done in here. SDL_AudioStream handles that.
// Move to final data type. // Move to final data type.
if (!SDL_AUDIO_ISFLOAT(dst_format)) { if (dstconvert) {
AudioConvertFromFloat(dst, (float *) src, num_frames * dst_channels, dst_format); AudioConvertFromFloat(dst, (const float *) src, num_frames * dst_channels, dst_format);
src = dst; // we've written to dst, future work will convert in-place. src = dst;
} }
// make sure we're in final byte order. // 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); AudioConvertByteswap(dst, src, num_frames * dst_channels, dst_bitsize);
src = dst; // we've written to dst, future work will convert in-place. 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! 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) 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 src_format_size = SDL_AUDIO_BITSIZE(src_format) / 8;
const int dst_format_size = SDL_AUDIO_BITSIZE(dst_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_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); const int max_channels = SDL_max(src_channels, dst_channels);
return max_format_size * max_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). // 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) 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 SDL_AudioFormat src_format = src_spec->format;
const int src_channels = src_spec->channels; const int src_channels = src_spec->channels;
const int src_rate = src_spec->freq; const int src_rate = src_spec->freq;
const SDL_AudioFormat dst_format = dst_spec->format; const SDL_AudioFormat dst_format = dst_spec->format;
const int dst_channels = dst_spec->channels; const int dst_channels = dst_spec->channels;
const int dst_rate = dst_spec->freq; const int dst_rate = dst_spec->freq;
const int src_sample_frame_size = (SDL_AUDIO_BITSIZE(src_format) / 8) * src_channels; 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 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 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 Sint64 resample_rate = GetResampleRate(src_rate, dst_rate);
const int resampler_padding_frames = GetResamplerPaddingFrames(resample_rate); const int resampler_padding_frames = GetResamplerPaddingFrames(resample_rate);
const int history_buffer_frames = GetHistoryBufferSampleFrames(resampler_padding_frames); 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; 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. // 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 (stream->history_buffer) {
if (history_buffer_frames <= prev_history_buffer_frames) { 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 { } 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. 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) { } 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->resampler_padding_frames = resampler_padding_frames;
stream->history_buffer_frames = history_buffer_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->src_sample_frame_size = src_sample_frame_size;
stream->dst_sample_frame_size = dst_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; stream->resample_rate = resample_rate;
if (src_spec != &stream->src_spec) { if (src_spec != &stream->src_spec) {
@@ -922,18 +937,26 @@ static Uint8 *EnsureStreamWorkBufferSize(SDL_AudioStream *stream, size_t newlen)
return ptr; 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); // Even if we aren't currently resampling, we always need to update the history buffer
int workbuflen = workbuf_frames * stream->max_sample_frame_size; Uint8 *history_buffer = stream->history_buffer;
int history_bytes = stream->history_buffer_frames * stream->src_sample_frame_size;
if (stream->resample_rate) { if (left_padding != NULL) {
// Calculate space needed after resample (which lives in a second copy in the same buffer). // Fill in the left padding using the history buffer
int resample_frame_size = stream->pre_resample_channels * sizeof(float); SDL_assert(padding_bytes <= history_bytes);
workbuflen += output_frames * resample_frame_size; 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! // 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 dst_sample_frame_size = stream->dst_sample_frame_size;
const int max_sample_frame_size = stream->max_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 int resampler_padding_frames = stream->resampler_padding_frames;
const Sint64 resample_rate = stream->resample_rate; 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; int input_frames = output_frames;
const int output_bytes = output_frames * dst_sample_frame_size;
if (resample_rate) { // Not resampling? It's an easy conversion (and maybe not even that!)
// Calculate the number of input frames necessary for this request. if (resample_rate == 0) {
// Because resampling happens "between" frames, The same number of output_frames SDL_assert(input_frames == 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); 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);
}
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); Uint8* work_buffer = EnsureStreamWorkBufferSize(stream, work_buffer_capacity);
if (!work_buffer) { if (!work_buffer) {
return -1; return -1;
} }
int input_bytes = input_frames * src_sample_frame_size; const int padding_bytes = resampler_padding_frames * src_sample_frame_size;
int padding_bytes = resampler_padding_frames * src_sample_frame_size;
Uint8* work_buffer_tail = work_buffer; 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); SDL_assert(bytes_read == input_bytes);
} }
Uint8 *history_buffer = stream->history_buffer; // Update the history buffer and fill in the left padding
const int history_buffer_frames = stream->history_buffer_frames; UpdateStreamHistoryBuffer(stream, input_buffer, input_bytes, left_padding, padding_bytes);
int history_bytes = history_buffer_frames * src_sample_frame_size;
// Do we need to fill in the left/right padding? // Fill in the right padding by peeking into the input queue
if (resampler_padding_frames > 0) { const int right_padding_bytes = (int) SDL_PeekIntoDataQueue(stream->queue, right_padding, padding_bytes);
// 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 if (right_padding_bytes < padding_bytes) {
int right_padding_bytes = (int) SDL_PeekIntoDataQueue(stream->queue, right_padding, padding_bytes); // If we have run out of data, fill the rest with silence.
// This should only happen if the stream has been flushed.
if (right_padding_bytes < padding_bytes) { SDL_assert(stream->flushed);
// If we have run out of data, fill the rest with silence. SDL_memset(right_padding + right_padding_bytes, SDL_GetSilenceValueForFormat(src_format), padding_bytes - right_padding_bytes);
// 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)); SDL_assert(work_buffer_frames == input_frames + (resampler_padding_frames * 2));
// Resampling! get the work buffer to float32 format, etc, in-place. // 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); ConvertAudio(work_buffer_frames, work_buffer, src_format, src_channels, work_buffer, SDL_AUDIO_F32SYS, resample_channels, NULL);
const int resample_frame_size = pre_resample_channels * sizeof(float);
const int resample_bytes = output_frames * resample_frame_size;
// Update the work_buffer pointers based on the new frame size // 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); 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); 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); 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, (const float *) input_buffer, input_frames,
resample_buffer, output_frames, (float*) resample_buffer, output_frames,
resample_rate, &stream->resample_offset); resample_rate, &stream->resample_offset);
// Get us to the final format! // Convert to the final format, if necessary
// 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 (buf != resample_buffer) {
if (max_sample_frame_size <= dst_sample_frame_size) { ConvertAudio(output_frames, resample_buffer, SDL_AUDIO_F32SYS, resample_channels, buf, dst_format, dst_channels, work_buffer);
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);
} }
return output_bytes; return output_bytes;

View File

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