From f12ab8f2b3d94ca91b261b8043c1e01cac16f85e Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 5 Jan 2017 19:12:20 -0500 Subject: [PATCH] audio: More effort to improve and simplify audio resamplers. --- src/audio/SDL_audio_c.h | 5 +- src/audio/SDL_audiocvt.c | 190 +++++++++++++++++------------------ src/audio/SDL_audiotypecvt.c | 95 +++++++----------- 3 files changed, 128 insertions(+), 162 deletions(-) diff --git a/src/audio/SDL_audio_c.h b/src/audio/SDL_audio_c.h index f6a2e9705..f1be201f2 100644 --- a/src/audio/SDL_audio_c.h +++ b/src/audio/SDL_audio_c.h @@ -50,9 +50,8 @@ void SDLCALL SDL_Convert_F32_to_S16(SDL_AudioCVT *cvt, SDL_AudioFormat format); void SDLCALL SDL_Convert_F32_to_U16(SDL_AudioCVT *cvt, SDL_AudioFormat format); void SDLCALL SDL_Convert_F32_to_S32(SDL_AudioCVT *cvt, SDL_AudioFormat format); void SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels); +void SDL_Upsample_Multiple(SDL_AudioCVT *cvt, const int channels); void SDL_Downsample_Arbitrary(SDL_AudioCVT *cvt, const int channels); -void SDL_Upsample_x2(SDL_AudioCVT *cvt, const int channels); -void SDL_Upsample_x4(SDL_AudioCVT *cvt, const int channels); -void SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int multiple, const int channels); +void SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int channels); /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c index a299c7d9f..e48deb1ec 100644 --- a/src/audio/SDL_audiocvt.c +++ b/src/audio/SDL_audiocvt.c @@ -331,14 +331,43 @@ SDL_BuildAudioTypeCVTFromFloat(SDL_AudioCVT *cvt, const SDL_AudioFormat dst_fmt) return retval; } + +/* !!! FIXME: We only have this macro salsa because SDL_AudioCVT doesn't store + !!! FIXME: channel info or integer sample rates, so we have to have + !!! FIXME: function entry points for each supported channel count and + !!! FIXME: multiple vs arbitrary. When we rev the ABI, remove this. */ +#define RESAMPLER_FUNCS(chans) \ + static void SDLCALL \ + SDL_Upsample_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ + SDL_assert(format == AUDIO_F32SYS); \ + SDL_Upsample_Multiple(cvt, chans); \ + } \ + static void SDLCALL \ + SDL_Upsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ + SDL_assert(format == AUDIO_F32SYS); \ + SDL_Upsample_Arbitrary(cvt, chans); \ + }\ + static void SDLCALL \ + SDL_Downsample_Multiple_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ + SDL_assert(format == AUDIO_F32SYS); \ + SDL_Downsample_Multiple(cvt, chans); \ + } \ + static void SDLCALL \ + SDL_Downsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ + SDL_assert(format == AUDIO_F32SYS); \ + SDL_Downsample_Arbitrary(cvt, chans); \ + } +RESAMPLER_FUNCS(1) +RESAMPLER_FUNCS(2) +RESAMPLER_FUNCS(4) +RESAMPLER_FUNCS(6) +RESAMPLER_FUNCS(8) +#undef RESAMPLER_FUNCS + static int SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate) { - int retval = 0; - - /* If we only built with the arbitrary resamplers, ignore multiples. */ int lo, hi; - int div; SDL_assert(src_rate != 0); SDL_assert(dst_rate != 0); @@ -352,110 +381,73 @@ SDL_FindFrequencyMultiple(const int src_rate, const int dst_rate) hi = src_rate; } - /* zero means "not a supported multiple" ... we only do 2x and 4x. */ if ((hi % lo) != 0) return 0; /* not a multiple. */ - div = hi / lo; - retval = ((div == 2) || (div == 4)) ? div : 0; - - return retval; + return hi / lo; } -#define RESAMPLER_FUNCS(chans) \ - static void SDLCALL \ - SDL_Upsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ - SDL_assert(format == AUDIO_F32SYS); \ - SDL_Upsample_Arbitrary(cvt, chans); \ - }\ - static void SDLCALL \ - SDL_Downsample_Arbitrary_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ - SDL_assert(format == AUDIO_F32SYS); \ - SDL_Downsample_Arbitrary(cvt, chans); \ - } \ - static void SDLCALL \ - SDL_Upsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ - SDL_assert(format == AUDIO_F32SYS); \ - SDL_Upsample_x2(cvt, chans); \ - } \ - static void SDLCALL \ - SDL_Downsample_x2_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ - SDL_assert(format == AUDIO_F32SYS); \ - SDL_Downsample_Multiple(cvt, 2, chans); \ - } \ - static void SDLCALL \ - SDL_Upsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ - SDL_assert(format == AUDIO_F32SYS); \ - SDL_Upsample_x4(cvt, chans); \ - } \ - static void SDLCALL \ - SDL_Downsample_x4_c##chans(SDL_AudioCVT *cvt, SDL_AudioFormat format) { \ - SDL_assert(format == AUDIO_F32SYS); \ - SDL_Downsample_Multiple(cvt, 4, chans); \ +static SDL_AudioFilter +ChooseResampler(const int dst_channels, const int src_rate, const int dst_rate) +{ + const int upsample = (src_rate < dst_rate) ? 1 : 0; + const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate); + SDL_AudioFilter filter = NULL; + + #define PICK_CHANNEL_FILTER(upordown, resampler) switch (dst_channels) { \ + case 1: filter = SDL_##upordown##_##resampler##_c1; break; \ + case 2: filter = SDL_##upordown##_##resampler##_c2; break; \ + case 4: filter = SDL_##upordown##_##resampler##_c4; break; \ + case 6: filter = SDL_##upordown##_##resampler##_c6; break; \ + case 8: filter = SDL_##upordown##_##resampler##_c8; break; \ + default: break; \ } -RESAMPLER_FUNCS(1) -RESAMPLER_FUNCS(2) -RESAMPLER_FUNCS(4) -RESAMPLER_FUNCS(6) -RESAMPLER_FUNCS(8) -#undef RESAMPLER_FUNCS + + if (upsample) { + if (multiple) { + PICK_CHANNEL_FILTER(Upsample, Multiple); + } else { + PICK_CHANNEL_FILTER(Upsample, Arbitrary); + } + } else { + if (multiple) { + PICK_CHANNEL_FILTER(Downsample, Multiple); + } else { + PICK_CHANNEL_FILTER(Downsample, Arbitrary); + } + } + + #undef PICK_CHANNEL_FILTER + + return filter; +} static int -SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, int dst_channels, - int src_rate, int dst_rate) +SDL_BuildAudioResampleCVT(SDL_AudioCVT * cvt, const int dst_channels, + const int src_rate, const int dst_rate) { - if (src_rate != dst_rate) { - const int upsample = (src_rate < dst_rate) ? 1 : 0; - const int multiple = SDL_FindFrequencyMultiple(src_rate, dst_rate); - SDL_AudioFilter filter = NULL; + SDL_AudioFilter filter; - #define PICK_CHANNEL_FILTER(upordown, resampler) switch (dst_channels) { \ - case 1: filter = SDL_##upordown##_##resampler##_c1; break; \ - case 2: filter = SDL_##upordown##_##resampler##_c2; break; \ - case 4: filter = SDL_##upordown##_##resampler##_c4; break; \ - case 6: filter = SDL_##upordown##_##resampler##_c6; break; \ - case 8: filter = SDL_##upordown##_##resampler##_c8; break; \ - default: break; \ - } - - if (upsample) { - if (multiple == 0) { - PICK_CHANNEL_FILTER(Upsample, Arbitrary); - } else if (multiple == 2) { - PICK_CHANNEL_FILTER(Upsample, x2); - } else if (multiple == 4) { - PICK_CHANNEL_FILTER(Upsample, x4); - } - } else { - if (multiple == 0) { - PICK_CHANNEL_FILTER(Downsample, Arbitrary); - } else if (multiple == 2) { - PICK_CHANNEL_FILTER(Downsample, x2); - } else if (multiple == 4) { - PICK_CHANNEL_FILTER(Downsample, x4); - } - } - - #undef PICK_CHANNEL_FILTER - - if (filter == NULL) { - return SDL_SetError("No conversion available for these rates"); - } - - /* Update (cvt) with filter details... */ - cvt->filters[cvt->filter_index++] = filter; - if (src_rate < dst_rate) { - const double mult = ((double) dst_rate) / ((double) src_rate); - cvt->len_mult *= (int) SDL_ceil(mult); - cvt->len_ratio *= mult; - } else { - cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate); - } - - return 1; /* added a converter. */ + if (src_rate == dst_rate) { + return 0; /* no conversion necessary. */ } - return 0; /* no conversion necessary. */ + filter = ChooseResampler(dst_channels, src_rate, dst_rate); + if (filter == NULL) { + return SDL_SetError("No conversion available for these rates"); + } + + /* Update (cvt) with filter details... */ + cvt->filters[cvt->filter_index++] = filter; + if (src_rate < dst_rate) { + const double mult = ((double) dst_rate) / ((double) src_rate); + cvt->len_mult *= (int) SDL_ceil(mult); + cvt->len_ratio *= mult; + } else { + cvt->len_ratio /= ((double) src_rate) / ((double) dst_rate); + } + + return 1; /* added a converter. */ } @@ -514,7 +506,7 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt, The expectation is we can process data faster in float32 (possibly with SIMD), and making several passes over the same - buffer in is likely to be CPU cache-friendly, avoiding the + buffer is likely to be CPU cache-friendly, avoiding the biggest performance hit in modern times. Previously we had (script-generated) custom converters for every data type and it was a bloat on SDL compile times and final library size. */ @@ -585,11 +577,11 @@ SDL_BuildAudioCVT(SDL_AudioCVT * cvt, } /* Do rate conversion, if necessary. Updates (cvt). */ - if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) == - -1) { + if (SDL_BuildAudioResampleCVT(cvt, dst_channels, src_rate, dst_rate) == -1) { return -1; /* shouldn't happen, but just in case... */ } + /* Move to final data type. */ if (SDL_BuildAudioTypeCVTFromFloat(cvt, dst_fmt) == -1) { return -1; /* shouldn't happen, but just in case... */ } diff --git a/src/audio/SDL_audiotypecvt.c b/src/audio/SDL_audiotypecvt.c index dcc8dd3d9..0364e4a78 100644 --- a/src/audio/SDL_audiotypecvt.c +++ b/src/audio/SDL_audiotypecvt.c @@ -220,14 +220,14 @@ void SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels) { const int srcsize = cvt->len_cvt - (64 * channels); - const int dstsize = (int) (((double)(cvt->len_cvt/(channels*4))) * cvt->rate_incr) * (channels*4); + const int dstsize = (int) ((((double)(cvt->len_cvt/(channels*4))) * cvt->rate_incr)) * (channels*4); register int eps = 0; float *dst = ((float *) (cvt->buf + dstsize)) - channels; const float *src = ((float *) (cvt->buf + cvt->len_cvt)) - channels; const float *target = ((const float *) cvt->buf); const size_t cpy = sizeof (float) * channels; - float last_sample[8]; float sample[8]; + float last_sample[8]; int i; #if DEBUG_CONVERT @@ -236,7 +236,9 @@ SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels) SDL_assert(channels <= 8); - SDL_memcpy(sample, src, cpy); + for (i = 0; i < channels; i++) { + sample[i] = (float) ((((double) src[i]) + ((double) src[i - channels])) * 0.5); + } SDL_memcpy(last_sample, src, cpy); while (dst > target) { @@ -244,11 +246,15 @@ SDL_Upsample_Arbitrary(SDL_AudioCVT *cvt, const int channels) dst -= channels; eps += srcsize; if ((eps << 1) >= dstsize) { - src -= channels; - for (i = 0; i < channels; i++) { - sample[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.5); + if (src > target) { + src -= channels; + for (i = 0; i < channels; i++) { + sample[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.5); + } + } else { + } - SDL_memcpy(last_sample, sample, cpy); + SDL_memcpy(last_sample, src, cpy); eps -= dstsize; } } @@ -291,7 +297,7 @@ SDL_Downsample_Arbitrary(SDL_AudioCVT *cvt, const int channels) for (i = 0; i < channels; i++) { sample[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.5); } - SDL_memcpy(last_sample, sample, cpy); + SDL_memcpy(last_sample, src, cpy); eps -= srcsize; } } @@ -303,32 +309,43 @@ SDL_Downsample_Arbitrary(SDL_AudioCVT *cvt, const int channels) } void -SDL_Upsample_x2(SDL_AudioCVT *cvt, const int channels) +SDL_Upsample_Multiple(SDL_AudioCVT *cvt, const int channels) { - const int dstsize = cvt->len_cvt * 2; - float *dst = ((float *) (cvt->buf + dstsize)) - (channels * 2); + const int multiple = (int) cvt->rate_incr; + const int dstsize = cvt->len_cvt * multiple; + float *buf = (float *) cvt->buf; + float *dst = ((float *) (cvt->buf + dstsize)) - channels; const float *src = ((float *) (cvt->buf + cvt->len_cvt)) - channels; - const float *target = ((const float *) cvt->buf); + const float *target = buf + channels; const size_t cpy = sizeof (float) * channels; float last_sample[8]; int i; #if DEBUG_CONVERT - fprintf(stderr, "Upsample (x2), %d channels.\n", channels); + fprintf(stderr, "Upsample (x%d), %d channels.\n", multiple, channels); #endif SDL_assert(channels <= 8); + SDL_memcpy(last_sample, src, cpy); while (dst > target) { + SDL_assert(src >= buf); + for (i = 0; i < channels; i++) { dst[i] = (float) ((((double)src[i]) + ((double)last_sample[i])) * 0.5); } dst -= channels; - SDL_memcpy(dst, src, cpy); - SDL_memcpy(last_sample, src, cpy); + + for (i = 1; i < multiple; i++) { + SDL_memcpy(dst, dst + channels, cpy); + dst -= channels; + } + src -= channels; - dst -= channels; + if (src > buf) { + SDL_memcpy(last_sample, src - channels, cpy); + } } cvt->len_cvt = dstsize; @@ -338,51 +355,9 @@ SDL_Upsample_x2(SDL_AudioCVT *cvt, const int channels) } void -SDL_Upsample_x4(SDL_AudioCVT *cvt, const int channels) -{ - const int dstsize = cvt->len_cvt * 4; - float *dst = ((float *) (cvt->buf + dstsize)) - (channels * 4); - const float *src = ((float *) (cvt->buf + cvt->len_cvt)) - channels; - const float *target = ((const float *) cvt->buf); - const size_t cpy = sizeof (float) * channels; - float last_sample[8]; - int i; - -#if DEBUG_CONVERT - fprintf(stderr, "Upsample (x4), %d channels.\n", channels); -#endif - - SDL_assert(channels <= 8); - SDL_memcpy(last_sample, src, cpy); - - while (dst > target) { - for (i = 0; i < channels; i++) { - dst[i] = (float) ((((double) src[i]) + (3.0 * ((double) last_sample[i]))) * 0.25); - } - dst -= channels; - for (i = 0; i < channels; i++) { - dst[i] = (float) ((((double) src[i]) + ((double) last_sample[i])) * 0.25); - } - dst -= channels; - for (i = 0; i < channels; i++) { - dst[i] = (float) (((3.0 * ((double) src[i])) + ((double) last_sample[i])) * 0.25); - } - dst -= channels; - SDL_memcpy(dst, src, cpy); - dst -= channels; - SDL_memcpy(last_sample, src, cpy); - src -= channels; - } - - cvt->len_cvt = dstsize; - if (cvt->filters[++cvt->filter_index]) { - cvt->filters[cvt->filter_index](cvt, AUDIO_F32SYS); - } -} - -void -SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int multiple, const int channels) +SDL_Downsample_Multiple(SDL_AudioCVT *cvt, const int channels) { + const int multiple = (int) (1.0 / cvt->rate_incr); const int dstsize = cvt->len_cvt / multiple; float *dst = (float *) cvt->buf; const float *src = (float *) cvt->buf;