When Speex is compiled with FIXED_POINT defined, it scales float input to +/-32768 instead of +/-1. In order to make floating point resampler work with speex compiled with FIXED_POINT, we need to rescale the input to speex. Unfortunately, there is no easy way to tell how speex has been built, so we probe at runtime. A patch to fix speex was rejected saying that speex API is stable. Further discussion is here http://lists.openembedded.org/pipermail/openembedded-core/2014-January/087886.html Signed-off-by: Peter Meerwald <pmeerw at pmeerw.net> Reported-by: Fahad Arslan <fahad_arslan at mentor.com> Cc: Damir Jeli? <poljarinho at gmail.com> --- src/pulsecore/resampler.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index 38389f3..4a9c1f5 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -39,6 +39,7 @@ #include <pulsecore/macro.h> #include <pulsecore/strbuf.h> #include <pulsecore/remap.h> +#include <pulsecore/once.h> #include <pulsecore/core-util.h> #include "ffmpeg/avcodec.h" @@ -1490,6 +1491,82 @@ static unsigned speex_resample_float(pa_resampler *r, const pa_memchunk *input, return 0; } +/* float resampler when speexdsp is built with FIXED_POINT */ +static unsigned speex_resample_float_fixed(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { + pa_memblock *b; + float *in, *out, *t; + uint32_t inf = in_n_frames, outf = *out_n_frames, i; + unsigned nsamples = r->work_channels * in_n_frames; + SpeexResamplerState *state; + + pa_assert(r); + pa_assert(input); + pa_assert(output); + pa_assert(out_n_frames); + + state = r->impl.data; + + b = pa_memblock_new(r->mempool, nsamples * sizeof(float)); + t = pa_memblock_acquire(b); + in = pa_memblock_acquire_chunk(input); + + /* speexdsp float API sample range is [-32768,32768] instead of [-1,1] + * when compiled with FIXED_POINT, rescale input before passing to speex. */ + for (i = 0; i < nsamples; i++) + t[i] = 32768.0f * in[i]; + + pa_memblock_release(input->memblock); + + out = pa_memblock_acquire_chunk(output); + + pa_assert_se(speex_resampler_process_interleaved_float(state, t, &inf, out, &outf) == 0); + + pa_memblock_release(b); + pa_memblock_unref(b); + + pa_memblock_release(output->memblock); + + pa_assert(inf == in_n_frames); + *out_n_frames = outf; + + return 0; +} + +/* speexdsp has a nasty issue: when built with FIXED_POINT, the float resampler + * expects samples in [-32768,32768] instead of [-1,1]; there is no easy way + * to figure out how speexdsp has been compiled. */ + +static unsigned (*speex_resampler_float)(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) = speex_resample_float; + +static void speex_probe_fixed_point() { + PA_ONCE_BEGIN { + float f_out = -1.0f, f_in = 1.0f; + spx_uint32_t in_len = 1, out_len = 1; + + SpeexResamplerState *s = speex_resampler_init(1, 1, 1, + SPEEX_RESAMPLER_QUALITY_MIN, NULL); + if (!s) + goto done; + + /* feed one sample */ + if (speex_resampler_process_float(s, 0, &f_in, &in_len, + &f_out, &out_len) != RESAMPLER_ERR_SUCCESS) + goto done; + + /* expecting sample has been processed, one sample output */ + if (in_len != 1 && out_len != 1) + goto done; + + /* FIXED_POINT implementation will output 0.0 */ + if (fabsf(f_out) < 0.00001f) + speex_resampler_float = speex_resample_float_fixed; + +done: + if (s) + speex_resampler_destroy(s); + } PA_ONCE_END; +} + static unsigned speex_resample_int(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { int16_t *in, *out; uint32_t inf = in_n_frames, outf = *out_n_frames; @@ -1563,8 +1640,10 @@ static int speex_init(pa_resampler *r) { } else { pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX); + speex_probe_fixed_point(); + q = r->method - PA_RESAMPLER_SPEEX_FLOAT_BASE; - r->impl.resample = speex_resample_float; + r->impl.resample = speex_resampler_float; } pa_log_info("Choosing speex quality setting %i.", q); -- 1.9.1