see http://sourceforge.net/p/soxr/wiki/Home/ Signed-off-by: Peter Meerwald <pmeerw at pmeerw.net> Signed-off-by: poljar (Damir Jeli?) <poljarinho at gmail.com> -- rebased and leftover handling is external to _resample() now; this fixes a bug in the leftover_length computation since soxr supports s16 and float32, hence sizeof(float) is wrong --- configure.ac | 17 ++++++ src/Makefile.am | 6 ++ src/pulsecore/resampler.c | 14 ++++- src/pulsecore/resampler.h | 2 + src/pulsecore/resampler/soxr.c | 124 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 src/pulsecore/resampler/soxr.c diff --git a/configure.ac b/configure.ac index 837e81e..cf63eca 100644 --- a/configure.ac +++ b/configure.ac @@ -1109,6 +1109,21 @@ AS_IF([test "x$with_speex" = "xyes" && test "x$HAVE_SPEEX" = "x0"], AM_CONDITIONAL([HAVE_SPEEX], [test "x$HAVE_SPEEX" = "x1"]) AS_IF([test "x$HAVE_SPEEX" = "x1"], AC_DEFINE([HAVE_SPEEX], 1, [Have speex])) +#### sox resampler support (optional) #### + +AC_ARG_ENABLE([soxr], + AS_HELP_STRING([--disable-soxr],[Disable optional soxr support])) + +AS_IF([test "x$enable_soxr" != "xno"], + [PKG_CHECK_MODULES(SOXR, soxr, HAVE_SOXR=1, HAVE_SOXR=0)], + HAVE_SOXR=0) + +AS_IF([test "x$enable_soxr" = "xyes" && test "x$HAVE_SOXR" = "x0"], + [AC_MSG_ERROR([*** soxr not found])]) + +AM_CONDITIONAL([HAVE_SOXR], [test "x$HAVE_SOXR" = x1]) +AS_IF([test "x$HAVE_SOXR" = "x1"], AC_DEFINE([HAVE_SOXR], 1, [Have soxr])) + #### Xen support (optional) #### AC_ARG_ENABLE([xen], @@ -1445,6 +1460,7 @@ AS_IF([test "x$HAVE_FFTW" = "x1"], ENABLE_FFTW=yes, ENABLE_FFTW=no) AS_IF([test "x$HAVE_ORC" = "xyes"], ENABLE_ORC=yes, ENABLE_ORC=no) AS_IF([test "x$HAVE_ADRIAN_EC" = "x1"], ENABLE_ADRIAN_EC=yes, ENABLE_ADRIAN_EC=no) AS_IF([test "x$HAVE_SPEEX" = "x1"], ENABLE_SPEEX=yes, ENABLE_SPEEX=no) +AS_IF([test "x$HAVE_SOXR" = "x1"], ENABLE_SOXR=yes, ENABLE_SOXR=no) AS_IF([test "x$HAVE_WEBRTC" = "x1"], ENABLE_WEBRTC=yes, ENABLE_WEBRTC=no) AS_IF([test "x$HAVE_TDB" = "x1"], ENABLE_TDB=yes, ENABLE_TDB=no) AS_IF([test "x$HAVE_GDBM" = "x1"], ENABLE_GDBM=yes, ENABLE_GDBM=no) @@ -1501,6 +1517,7 @@ echo " Enable orc: ${ENABLE_ORC} Enable Adrian echo canceller: ${ENABLE_ADRIAN_EC} Enable speex (resampler, AEC): ${ENABLE_SPEEX} + Enable soxr: ${ENABLE_SOXR} Enable WebRTC echo canceller: ${ENABLE_WEBRTC} Enable gcov coverage: ${ENABLE_GCOV} Enable unit tests: ${ENABLE_TESTS} diff --git a/src/Makefile.am b/src/Makefile.am index 21eb365..9df52df 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -992,6 +992,12 @@ libpulsecore_ at PA_MAJORMINOR@_la_CFLAGS += $(LIBSAMPLERATE_CFLAGS) libpulsecore_ at PA_MAJORMINOR@_la_LIBADD += $(LIBSAMPLERATE_LIBS) endif +if HAVE_SOXR +libpulsecore_ at PA_MAJORMINOR@_la_SOURCES += pulsecore/resampler/soxr.c +libpulsecore_ at PA_MAJORMINOR@_la_CFLAGS += $(SOXR_CFLAGS) +libpulsecore_ at PA_MAJORMINOR@_la_LIBADD += $(SOXR_LIBS) +endif + # We split the foreign code off to not be annoyed by warnings we don't care about noinst_LTLIBRARIES += libpulsecore-foreign.la diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index de58f3f..e366be6 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -117,6 +117,11 @@ static int (* const init_table[])(pa_resampler *r) = { [PA_RESAMPLER_AUTO] = NULL, [PA_RESAMPLER_COPY] = copy_init, [PA_RESAMPLER_PEAKS] = pa_resampler_peaks_init, +#ifdef HAVE_SOXR + [PA_RESAMPLER_SOXR] = pa_resampler_soxr_init, +#else + [PA_RESAMPLER_SOXR] = NULL, +#endif }; static bool speex_is_fixed_point(void); @@ -163,6 +168,7 @@ static pa_resample_method_t fix_method( break; } /* Else fall through */ + case PA_RESAMPLER_SOXR: case PA_RESAMPLER_FFMPEG: if (flags & PA_RESAMPLER_VARIABLE_RATE) { pa_log_info("Resampler '%s' cannot do variable rate, reverting to resampler 'auto'.", pa_resample_method_to_string(method)); @@ -606,7 +612,8 @@ static const char * const resample_methods[] = { "ffmpeg", "auto", "copy", - "peaks" + "peaks", + "soxr" }; const char *pa_resample_method_to_string(pa_resample_method_t m) { @@ -634,6 +641,11 @@ int pa_resample_method_supported(pa_resample_method_t m) { return 0; #endif +#ifndef HAVE_SOXR + if (m == PA_RESAMPLER_SOXR) + return 0; +#endif + return 1; } diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h index 13d8c35..1d248e2 100644 --- a/src/pulsecore/resampler.h +++ b/src/pulsecore/resampler.h @@ -61,6 +61,7 @@ typedef enum pa_resample_method { PA_RESAMPLER_AUTO, /* automatic select based on sample format */ PA_RESAMPLER_COPY, PA_RESAMPLER_PEAKS, + PA_RESAMPLER_SOXR, PA_RESAMPLER_MAX } pa_resample_method_t; @@ -164,6 +165,7 @@ int pa_resampler_ffmpeg_init(pa_resampler *r); int pa_resampler_libsamplerate_init(pa_resampler *r); int pa_resampler_peaks_init(pa_resampler *r); int pa_resampler_speex_init(pa_resampler *r); +int pa_resampler_soxr_init(pa_resampler *r); int pa_resampler_trivial_init(pa_resampler*r); #endif diff --git a/src/pulsecore/resampler/soxr.c b/src/pulsecore/resampler/soxr.c new file mode 100644 index 0000000..4614a4f --- /dev/null +++ b/src/pulsecore/resampler/soxr.c @@ -0,0 +1,124 @@ +/*** + This file is part of PulseAudio. + + Copyright 2013 Damir Jeli? + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <soxr.h> + +#include "pulsecore/resampler.h" + +static void soxr_free(pa_resampler *r); + +static unsigned soxr_resample(pa_resampler *r, const pa_memchunk *input, + unsigned in_n_frames, pa_memchunk *output, + unsigned *out_n_frames) { + soxr_t state; + soxr_error_t error; + uint8_t *out; + uint8_t *in; + size_t odone; + size_t consumed; + + pa_assert(r); + pa_assert(input); + pa_assert(output); + pa_assert(out_n_frames); + + state = r->impl.data; + + in = pa_memblock_acquire_chunk(input); + out = pa_memblock_acquire_chunk(output); + + error = soxr_process(state, in, in_n_frames, &consumed, out, *out_n_frames, &odone); + pa_assert(error == 0); + + pa_memblock_release(input->memblock); + pa_memblock_release(output->memblock); + + *out_n_frames = odone; + + return in_n_frames - consumed; +} + +static void soxr_udpate_rates(pa_resampler *r) { + pa_assert(r); + + soxr_free(r); + pa_resampler_soxr_init(r); +} + +static void soxr_reset(pa_resampler *r) { + pa_assert(r); + + /* TODO use soxr_clear() since it is the documented way to reset the resamlper; + * for some reason soxr_process crashes after we use soxr_clear() here */ + soxr_free(r); + pa_resampler_soxr_init(r); +} + +static void soxr_free(pa_resampler *r) { + soxr_t state; + pa_assert(r); + + state = r->impl.data; + soxr_delete(state); +} + +int pa_resampler_soxr_init(pa_resampler *r) { + soxr_t state; + soxr_error_t error; + soxr_io_spec_t io_spec; + soxr_runtime_spec_t runtime_spec; + soxr_quality_spec_t quality_spec; + unsigned int format; + + pa_assert(r); + + switch (r->work_format) { + case PA_SAMPLE_S16NE: + format = SOXR_INT16; + break; + case PA_SAMPLE_FLOAT32NE: + format = SOXR_FLOAT32; + break; + default: + pa_assert_not_reached(); + } + + io_spec = soxr_io_spec(format, format); + runtime_spec = soxr_runtime_spec(0); + quality_spec = soxr_quality_spec(SOXR_QQ, 0); + + state = soxr_create(r->i_ss.rate, r->o_ss.rate, r->work_channels, &error, &io_spec, &quality_spec, &runtime_spec); + + if (error) + return -1; + + r->impl.resample = soxr_resample; + r->impl.update_rates = soxr_udpate_rates; + r->impl.reset = soxr_reset; + r->impl.free = soxr_free; + r->impl.data = state; + + return 0; +} -- 1.9.1