From ac984f59d36ef555bc5b0df9af1cd48193d0d14f Mon Sep 17 00:00:00 2001 From: "Alexander E. Patrakov" Date: Tue, 13 May 2014 21:50:52 +0600 Subject: [PATCH] resampler: Support speex resampler compiled with FIXED_POINT MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit speex_resample_float() does not work with speex compiled with --enable-fixed-point, because speex expects its float input to be normalized to ±32768 instead of the more usual ±1. It is possible to fix speex_resample_float(), as demonstrated at http://lists.freedesktop.org/archives/pulseaudio-discuss/2014-May/020617.html However, a better idea is to avoid using the speex-float resampler and the associated s16 <-> float conversions that speex will immediately undo internally if it is known that speex has been compiled with FIXED_POINT. So, transparently change speex-float-* to speex-fixed-* in that case. Signed-off-by: Alexander E. Patrakov Reported-by: Fahad Arslan Cc: Damir Jelić Cc: Peter Meerwald FIXED_POINT detection is based on code by Peter Meerwald. --- src/pulsecore/resampler.c | 57 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index 1153281c..7799cfa3 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include "ffmpeg/avcodec.h" @@ -185,6 +186,8 @@ static int (* const init_table[])(pa_resampler*r) = { [PA_RESAMPLER_PEAKS] = peaks_init, }; +static bool speex_is_fixed_point(void); + static pa_resample_method_t choose_auto_resampler(pa_resample_flags_t flags) { pa_resample_method_t method; @@ -250,6 +253,20 @@ static pa_resample_method_t pa_resampler_fix_method( if (method == PA_RESAMPLER_AUTO) method = choose_auto_resampler(flags); + /* At this point, method is supported in the sense that it + * has an init function and supports the required flags. However, + * speex-float implementation in PulseAudio relies on the + * assumption that is invalid if speex has been compiled with + * --enable-fixed-point. Besides, speex-fixed is more efficient + * in this configuration. So use it instead. + */ + if (method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && method <= PA_RESAMPLER_SPEEX_FLOAT_MAX) { + if (speex_is_fixed_point()) { + pa_log_info("Speex appears to be compiled with --enable-fixed-point. " + "Switching to a fixed-point resampler because it should be faster."); + method = method - PA_RESAMPLER_SPEEX_FLOAT_BASE + PA_RESAMPLER_SPEEX_FIXED_BASE; + } + } return method; } @@ -1469,9 +1486,37 @@ static int libsamplerate_init(pa_resampler *r) { } #endif -#ifdef HAVE_SPEEX /*** speex based implementation ***/ +static bool speex_is_fixed_point(void) { + static bool result = false; +#ifdef HAVE_SPEEX + PA_ONCE_BEGIN { + float f_out = -1.0f, f_in = 1.0f; + spx_uint32_t in_len = 1, out_len = 1; + SpeexResamplerState *s; + + pa_assert_se(s = speex_resampler_init(1, 1, 1, + SPEEX_RESAMPLER_QUALITY_MIN, NULL)); + + /* feed one sample that is too soft for fixed-point speex */ + pa_assert_se(speex_resampler_process_float(s, 0, &f_in, &in_len, + &f_out, &out_len) == RESAMPLER_ERR_SUCCESS); + + /* expecting sample has been processed, one sample output */ + pa_assert_se(in_len == 1 && out_len == 1); + + /* speex compiled with --enable-fixed-point will output 0.0 due to insufficient precision */ + if (fabsf(f_out) < 0.00001f) + result = true; + + speex_resampler_destroy(s); + } PA_ONCE_END; +#endif + return result; +} + +#ifdef HAVE_SPEEX static unsigned speex_resample_float(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { float *in, *out; uint32_t inf = in_n_frames, outf = *out_n_frames; @@ -1487,6 +1532,16 @@ static unsigned speex_resample_float(pa_resampler *r, const pa_memchunk *input, in = pa_memblock_acquire_chunk(input); out = pa_memblock_acquire_chunk(output); + /* Strictly speaking, speex resampler expects its input + * to be normalized to the [-32768.0 .. 32767.0] range. + * This matters if speex has been compiled with --enable-fixed-point, + * because such speex will round the samples to the nearest + * integer. speex with --enable-fixed-point is therefore incompatible + * with PulseAudio's floating-point sample range [-1 .. 1]. speex + * without --enable-fixed-point works fine with this range. + * Care has been taken to call speex_resample_float() only + * for speex compiled without --enable-fixed-point. + */ pa_assert_se(speex_resampler_process_interleaved_float(state, in, &inf, out, &outf) == 0); pa_memblock_release(input->memblock); -- 2.39.2