diff --git a/build-scripts/gen_audio_resampler_filter.c b/build-scripts/gen_audio_resampler_filter.c index 3aab606ddc..7d2ca04c07 100644 --- a/build-scripts/gen_audio_resampler_filter.c +++ b/build-scripts/gen_audio_resampler_filter.c @@ -47,7 +47,8 @@ gcc -o genfilter build-scripts/gen_audio_resampler_filter.c -lm && ./genfilter > #define RESAMPLER_ZERO_CROSSINGS 5 #define RESAMPLER_BITS_PER_SAMPLE 16 -#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)) +#define RESAMPLER_BITS_PER_ZERO_CROSSING ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1) +#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << RESAMPLER_BITS_PER_ZERO_CROSSING) #define RESAMPLER_FILTER_SIZE (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS) /* This is a "modified" bessel function, so you can't use POSIX j0() */ @@ -135,7 +136,8 @@ int main(void) "\n" "#define RESAMPLER_ZERO_CROSSINGS %d\n" "#define RESAMPLER_BITS_PER_SAMPLE %d\n" - "#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1))\n" + "#define RESAMPLER_BITS_PER_ZERO_CROSSING ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)\n" + "#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << RESAMPLER_BITS_PER_ZERO_CROSSING)\n" "#define RESAMPLER_FILTER_SIZE (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS)\n" "\n", RESAMPLER_ZERO_CROSSINGS, RESAMPLER_BITS_PER_SAMPLE ); diff --git a/src/audio/SDL_audio_resampler_filter.h b/src/audio/SDL_audio_resampler_filter.h index 94e98e3461..08b615547a 100644 --- a/src/audio/SDL_audio_resampler_filter.h +++ b/src/audio/SDL_audio_resampler_filter.h @@ -23,7 +23,8 @@ #define RESAMPLER_ZERO_CROSSINGS 5 #define RESAMPLER_BITS_PER_SAMPLE 16 -#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1)) +#define RESAMPLER_BITS_PER_ZERO_CROSSING ((RESAMPLER_BITS_PER_SAMPLE / 2) + 1) +#define RESAMPLER_SAMPLES_PER_ZERO_CROSSING (1 << RESAMPLER_BITS_PER_ZERO_CROSSING) #define RESAMPLER_FILTER_SIZE (RESAMPLER_SAMPLES_PER_ZERO_CROSSING * RESAMPLER_ZERO_CROSSINGS) static const float ResamplerFilter[RESAMPLER_FILTER_SIZE] = { diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c index a3c7dada30..68a0976adf 100644 --- a/src/audio/SDL_audiocvt.c +++ b/src/audio/SDL_audiocvt.c @@ -32,22 +32,21 @@ #include "SDL_audio_resampler_filter.h" -static int GetResamplerPaddingFrames(const int iinrate, const int ioutrate) +static Sint64 GetResampleRate(const int inrate, const int outrate) { - /* This function uses integer arithmetics to avoid precision loss caused - * by large floating point numbers. Sint32 is needed for the large number - * multiplication. The integers are assumed to be non-negative so that - * division rounds by truncation. */ - const Sint32 inrate = (Sint32) iinrate; - const Sint32 outrate = (Sint32) ioutrate; - SDL_assert(inrate >= 0); - SDL_assert(outrate >= 0); + return ((Sint64)inrate << 32) / (Sint64)outrate; +} + +static int GetResamplerPaddingFrames(const int inrate, const int outrate) +{ + SDL_assert(inrate > 0); + SDL_assert(outrate > 0); + if (inrate == outrate) { return 0; - } else if (inrate > outrate) { - return (int) (((RESAMPLER_SAMPLES_PER_ZERO_CROSSING * inrate) + (outrate - 1)) / outrate); } - return RESAMPLER_SAMPLES_PER_ZERO_CROSSING; + + return RESAMPLER_ZERO_CROSSINGS + 1; } static int GetHistoryBufferSampleFrames(const Sint32 required_resampler_frames) @@ -68,36 +67,28 @@ static int GetHistoryBufferSampleFrames(const Sint32 required_resampler_frames) static void ResampleAudio(const int chans, const int inrate, const int outrate, const float *lpadding, const float *rpadding, const float *inbuf, const int inframes, - float *outbuf, const int outframes, const int offset) + float *outbuf, const int outframes, const Sint64 offset) { - /* This function uses integer arithmetics to avoid precision loss caused - * by large floating point numbers. For some operations, Sint32 or Sint64 - * are needed for the large number multiplications. The input integers are - * assumed to be non-negative so that division rounds by truncation and - * modulo is always non-negative. Note that the operator order is important - * for these integer divisions. */ const int paddinglen = GetResamplerPaddingFrames(inrate, outrate); float *dst = outbuf; int i, j, chan; + const Sint64 srcstep = GetResampleRate(inrate, outrate); + Sint64 srcpos = offset; + for (i = 0; i < outframes; i++) { - /* Offset by outrate to avoid negative numbers (which don't round correctly) */ - Sint64 srcpos = ((Sint64)i * inrate) + offset + outrate; - int srcindex = (int)(srcpos / outrate) - 1; + int srcindex = (int)(Sint32)(srcpos >> 32); + Uint32 srcfraction = (Uint32)(srcpos & 0xFFFFFFFF); + srcpos += srcstep; + SDL_assert(srcindex >= -1); SDL_assert(srcindex < inframes); - /* Calculating the following way avoids subtraction or modulo of large - * floats which have low result precision. - * interpolation1 - * = (i / outrate * inrate) - floor(i / outrate * inrate) - * = mod(i / outrate * inrate, 1) - * = mod(i * inrate, outrate) / outrate */ - const int srcfraction = (int)(srcpos % outrate); - const float interpolation1 = ((float)srcfraction) / ((float)outrate); - const int filterindex1 = (((Sint32)srcfraction) * RESAMPLER_SAMPLES_PER_ZERO_CROSSING / outrate) * RESAMPLER_ZERO_CROSSINGS; + const float interpolation1 = (float)srcfraction * 0x1p-32f; + const int filterindex1 = (int)(srcfraction >> (32 - RESAMPLER_BITS_PER_ZERO_CROSSING)) * RESAMPLER_ZERO_CROSSINGS; + const float interpolation2 = 1.0f - interpolation1; - const int filterindex2 = RESAMPLER_FILTER_SIZE - filterindex1 - RESAMPLER_ZERO_CROSSINGS; + const int filterindex2 = RESAMPLER_FILTER_SIZE - RESAMPLER_ZERO_CROSSINGS - filterindex1; for (chan = 0; chan < chans; chan++) { float outsample = 0.0f; @@ -834,7 +825,7 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le int future_buffer_filled_frames = stream->future_buffer_filled_frames; Uint8 *future_buffer = stream->future_buffer; Uint8 *history_buffer = stream->history_buffer; - int resample_offset = stream->resample_offset; + Sint64 resample_offset = stream->resample_offset; float *resample_outbuf; int input_frames; int output_frames; @@ -861,13 +852,14 @@ static int GetAudioStreamDataInternal(SDL_AudioStream *stream, void *buf, int le input_frames = output_frames; // total sample frames caller wants if (dst_rate != src_rate) { - Sint64 last_offset = ((Sint64)(input_frames - 1) * src_rate) + resample_offset; - input_frames = (int)(last_offset / dst_rate) + 1; + // Make sure this matches the logic used in ResampleAudio + const Sint64 srcstep = GetResampleRate(src_rate, dst_rate); - Sint64 next_offset = last_offset + src_rate; - stream->resample_offset = (int)(next_offset - ((Sint64)input_frames * dst_rate)); + Sint64 nextpos = (output_frames * srcstep) + resample_offset; + Sint64 lastpos = nextpos - srcstep; - // SDL_Log("Fraction: %i, %i", resampler_fraction, stream->resampler_fraction); + input_frames = (int)(Sint32)(lastpos >> 32) + 1; + stream->resample_offset = nextpos - ((Sint64)input_frames << 32); if (input_frames == 0) { // uhoh, not enough input frames! // if they are upsampling and we end up needing less than a frame of input, we reject it because it would cause artifacts on future reads to eat a full input frame. diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h index 487514daa8..0ed53165d4 100644 --- a/src/audio/SDL_sysaudio.h +++ b/src/audio/SDL_sysaudio.h @@ -179,7 +179,7 @@ struct SDL_AudioStream int resampler_padding_frames; int history_buffer_frames; int future_buffer_filled_frames; - int resample_offset; + Sint64 resample_offset; SDL_AudioSpec src_spec; SDL_AudioSpec dst_spec;