[pulseaudio-commits] Branch 'next' - 4 commits - configure.ac man/pulse-daemon.conf.5.xml.in src/Makefile.am src/pulsecore

Peter Meerwald pmeerw at kemper.freedesktop.org
Wed Jan 14 13:05:58 PST 2015


 configure.ac                   |   17 ++++
 man/pulse-daemon.conf.5.xml.in |   14 ++-
 src/Makefile.am                |    6 +
 src/pulsecore/resampler.c      |   33 +++++++-
 src/pulsecore/resampler.h      |    4 
 src/pulsecore/resampler/soxr.c |  167 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 234 insertions(+), 7 deletions(-)

New commits:
commit 6f50d2e58d319aa243590bd224d939eeae40d238
Author: Andrey Semashev <andrey.semashev at gmail.com>
Date:   Wed Jan 14 22:22:04 2015 +0300

    Added documentation for soxr resampling methods.

diff --git a/man/pulse-daemon.conf.5.xml.in b/man/pulse-daemon.conf.5.xml.in
index 754312e..d17131b 100644
--- a/man/pulse-daemon.conf.5.xml.in
+++ b/man/pulse-daemon.conf.5.xml.in
@@ -88,7 +88,8 @@ USA.
       <opt>src-sinc-medium-quality</opt>, <opt>src-sinc-fastest</opt>,
       <opt>src-zero-order-hold</opt>, <opt>src-linear</opt>,
       <opt>trivial</opt>, <opt>speex-float-N</opt>,
-      <opt>speex-fixed-N</opt>, <opt>ffmpeg</opt>. See the
+      <opt>speex-fixed-N</opt>, <opt>ffmpeg</opt>, <opt>soxr-mq</opt>,
+      <opt>soxr-hq</opt>, <opt>soxr-vhq</opt>. See the
       documentation of libsamplerate and speex for explanations of the
       different src- and speex- methods, respectively. The method
       <opt>trivial</opt> is the most basic algorithm implemented. If
@@ -98,8 +99,15 @@ USA.
       exist in two flavours: <opt>fixed</opt> and <opt>float</opt>. The former uses fixed point
       numbers, the latter relies on floating point numbers. On most
       desktop CPUs the float point resampler is a lot faster, and it
-      also offers slightly better quality. See the output of
-      <opt>dump-resample-methods</opt> for a complete list of all
+      also offers slightly better quality. The soxr-family methods
+      are based on libsoxr, a resampler library from the SoX sound processing utility.
+      The mq variant has the best performance of the three. The hq is more expensive
+      and, according to SoX developers, is considered the best choice for audio of up to 16 bits per sample.
+      The vhq variant has more precision than hq and is more suitable for larger samples. The Soxr resamplers
+      generally offer better quality at less CPU compared to other resamplers, such as speex.
+      The downside is that they can add a significant delay to the output
+      (usually up to around 20 ms, in rare cases more).
+      See the output of <opt>dump-resample-methods</opt> for a complete list of all
       available resamplers. Defaults to <opt>speex-float-1</opt>. The
       <opt>--resample-method</opt> command line option takes precedence.
       Note that some modules overwrite or allow overwriting of the

commit 0f029fc0acbc5cac4adb7f723350c627be8c75aa
Author: Andrey Semashev <andrey.semashev at gmail.com>
Date:   Wed Jan 14 22:22:03 2015 +0300

    Added libsoxr detection and optional build of soxr resampler backend.

diff --git a/configure.ac b/configure.ac
index 9165bab..1ba699a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1140,6 +1140,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]))
 
+#### soxr (optional) ####
+
+AC_ARG_WITH([soxr],
+    AS_HELP_STRING([--without-soxr],[Omit soxr (resampling)]))
+
+AS_IF([test "x$with_soxr" != "xno"],
+    [PKG_CHECK_MODULES(LIBSOXR, [ soxr >= 0.1.1 ], HAVE_SOXR=1, HAVE_SOXR=0)],
+    HAVE_SOXR=0)
+
+AS_IF([test "x$with_soxr" = "xyes" && test "x$HAVE_SOXR" = "x0"],
+    [AC_MSG_ERROR([*** soxr support 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],
@@ -1527,6 +1542,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)
@@ -1588,6 +1604,7 @@ echo "
     Enable orc:                    ${ENABLE_ORC}
     Enable Adrian echo canceller:  ${ENABLE_ADRIAN_EC}
     Enable speex (resampler, AEC): ${ENABLE_SPEEX}
+    Enable soxr (resampler):       ${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 88a824e..5a88531 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1021,6 +1021,12 @@ libpulsecore_ at PA_MAJORMINOR@_la_CFLAGS += $(LIBSPEEX_CFLAGS)
 libpulsecore_ at PA_MAJORMINOR@_la_LIBADD += $(LIBSPEEX_LIBS)
 endif
 
+if HAVE_SOXR
+libpulsecore_ at PA_MAJORMINOR@_la_SOURCES += pulsecore/resampler/soxr.c
+libpulsecore_ at PA_MAJORMINOR@_la_CFLAGS += $(LIBSOXR_CFLAGS)
+libpulsecore_ at PA_MAJORMINOR@_la_LIBADD += $(LIBSOXR_LIBS)
+endif
+
 if HAVE_LIBSAMPLERATE
 libpulsecore_ at PA_MAJORMINOR@_la_SOURCES += pulsecore/resampler/libsamplerate.c
 libpulsecore_ at PA_MAJORMINOR@_la_CFLAGS += $(LIBSAMPLERATE_CFLAGS)

commit 7f8f7e0606e7eea7eeab82e386842fe51254ed68
Author: Andrey Semashev <andrey.semashev at gmail.com>
Date:   Wed Jan 14 22:22:02 2015 +0300

    Enabled libsoxr resampler backend.
    
    Added ID and names for the resampler presets and also updated the working sample rate deduction to take the new resampler into account. The initial libsoxr backend version does not variable rate resampling, so it is disabled in this case.

diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index 183d05f..6bbbff3 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -111,6 +111,15 @@ 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_MQ]                 = pa_resampler_soxr_init,
+    [PA_RESAMPLER_SOXR_HQ]                 = pa_resampler_soxr_init,
+    [PA_RESAMPLER_SOXR_VHQ]                = pa_resampler_soxr_init,
+#else
+    [PA_RESAMPLER_SOXR_MQ]                 = NULL,
+    [PA_RESAMPLER_SOXR_HQ]                 = NULL,
+    [PA_RESAMPLER_SOXR_VHQ]                = NULL,
+#endif
 };
 
 static pa_resample_method_t choose_auto_resampler(pa_resample_flags_t flags) {
@@ -156,6 +165,9 @@ static pa_resample_method_t fix_method(
             }
                                      /* Else fall through */
         case PA_RESAMPLER_FFMPEG:
+        case PA_RESAMPLER_SOXR_MQ:
+        case PA_RESAMPLER_SOXR_HQ:
+        case PA_RESAMPLER_SOXR_VHQ:
             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));
                 method = PA_RESAMPLER_AUTO;
@@ -278,10 +290,20 @@ static pa_sample_format_t choose_work_format(
             }
                                                 /* Else fall trough */
         case PA_RESAMPLER_PEAKS:
-            if (a == PA_SAMPLE_S16NE || b == PA_SAMPLE_S16NE)
+            /* PEAKS, COPY and TRIVIAL do not benefit from increased
+             * working precision, so for better performance use s16ne
+             * if either input or output fits in it. */
+            if (a == PA_SAMPLE_S16NE || b == PA_SAMPLE_S16NE) {
                 work_format = PA_SAMPLE_S16NE;
-            else if (sample_format_more_precise(a, PA_SAMPLE_S16NE) ||
-                     sample_format_more_precise(b, PA_SAMPLE_S16NE))
+                break;
+            }
+                                                /* Else fall trough */
+        case PA_RESAMPLER_SOXR_MQ:
+        case PA_RESAMPLER_SOXR_HQ:
+        case PA_RESAMPLER_SOXR_VHQ:
+            /* Do processing with max precision of input and output. */
+            if (sample_format_more_precise(a, PA_SAMPLE_S16NE) ||
+                sample_format_more_precise(b, PA_SAMPLE_S16NE))
                 work_format = PA_SAMPLE_FLOAT32NE;
             else
                 work_format = PA_SAMPLE_S16NE;
@@ -601,7 +623,10 @@ static const char * const resample_methods[] = {
     "ffmpeg",
     "auto",
     "copy",
-    "peaks"
+    "peaks",
+    "soxr-mq",
+    "soxr-hq",
+    "soxr-vhq"
 };
 
 const char *pa_resample_method_to_string(pa_resample_method_t m) {
diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h
index 5a84cf0..a0306e7 100644
--- a/src/pulsecore/resampler.h
+++ b/src/pulsecore/resampler.h
@@ -59,6 +59,9 @@ typedef enum pa_resample_method {
     PA_RESAMPLER_AUTO, /* automatic select based on sample format */
     PA_RESAMPLER_COPY,
     PA_RESAMPLER_PEAKS,
+    PA_RESAMPLER_SOXR_MQ,
+    PA_RESAMPLER_SOXR_HQ,
+    PA_RESAMPLER_SOXR_VHQ,
     PA_RESAMPLER_MAX
 } pa_resample_method_t;
 
@@ -163,6 +166,7 @@ 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_trivial_init(pa_resampler*r);
+int pa_resampler_soxr_init(pa_resampler *r);
 
 /* Resampler-specific quirks */
 bool pa_speex_is_fixed_point(void);

commit 074e4eee62b347c99519e64da1ab4fb3b1ee0571
Author: Andrey Semashev <andrey.semashev at gmail.com>
Date:   Wed Jan 14 22:22:01 2015 +0300

    Added libsoxr resampler backend.
    
    The new backend supports 3 quality levels: mq, hq and vhq; 16-bit integer and 32-bit float samples. Discussion and quality assessment are here:
    
    http://comments.gmane.org/gmane.comp.audio.pulseaudio.general/22158

diff --git a/src/pulsecore/resampler/soxr.c b/src/pulsecore/resampler/soxr.c
new file mode 100644
index 0000000..b5f0007
--- /dev/null
+++ b/src/pulsecore/resampler/soxr.c
@@ -0,0 +1,167 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2014, 2015 Andrey Semashev
+
+  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, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stddef.h>
+#include <soxr.h>
+
+#include <pulsecore/resampler.h>
+
+static unsigned resampler_soxr_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames,
+                                        pa_memchunk *output, unsigned *out_n_frames) {
+    soxr_t state;
+    void *in, *out;
+    size_t consumed = 0, produced = 0;
+
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(output);
+    pa_assert(out_n_frames);
+
+    state = r->impl.data;
+    pa_assert(state);
+
+    in = pa_memblock_acquire_chunk(input);
+    out = pa_memblock_acquire_chunk(output);
+
+    pa_assert_se(soxr_process(state, in, in_n_frames, &consumed, out, *out_n_frames, &produced) == 0);
+
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(output->memblock);
+
+    *out_n_frames = produced;
+
+    return in_n_frames - consumed;
+}
+
+static void resampler_soxr_free(pa_resampler *r) {
+    pa_assert(r);
+
+    if (!r->impl.data)
+        return;
+
+    soxr_delete(r->impl.data);
+    r->impl.data = NULL;
+}
+
+static void resampler_soxr_reset(pa_resampler *r) {
+    soxr_t old_state;
+
+    pa_assert(r);
+
+    /*
+     * soxr_clear() makes soxr_process() crash afterwards,
+     * so don't use this function until libsoxr is fixed.
+     *
+     * soxr_clear(r->impl.data);
+     */
+
+    old_state = r->impl.data;
+    r->impl.data = NULL;
+
+    if (pa_resampler_soxr_init(r) == 0) {
+        if (old_state)
+            soxr_delete(old_state);
+    } else {
+        r->impl.data = old_state;
+        pa_log_error("Failed to reset libsoxr context");
+    }
+}
+
+static void resampler_soxr_update_rates(pa_resampler *r) {
+    soxr_t old_state;
+
+    pa_assert(r);
+
+    /* There is no update method in libsoxr,
+     * so just re-create the resampler context */
+
+    old_state = r->impl.data;
+    r->impl.data = NULL;
+
+    if (pa_resampler_soxr_init(r) == 0) {
+        if (old_state)
+            soxr_delete(old_state);
+    } else {
+        r->impl.data = old_state;
+        pa_log_error("Failed to update libsoxr sample rates");
+    }
+}
+
+int pa_resampler_soxr_init(pa_resampler *r) {
+    soxr_t state;
+    soxr_datatype_t io_format;
+    soxr_io_spec_t io_spec;
+    soxr_runtime_spec_t runtime_spec;
+    unsigned long quality_recipe;
+    soxr_quality_spec_t quality;
+    soxr_error_t err = NULL;
+
+    pa_assert(r);
+
+    switch (r->work_format) {
+        case PA_SAMPLE_S16NE:
+            io_format = SOXR_INT16_I;
+            break;
+        case PA_SAMPLE_FLOAT32NE:
+            io_format = SOXR_FLOAT32_I;
+            break;
+        default:
+            pa_assert_not_reached();
+    }
+
+    io_spec = soxr_io_spec(io_format, io_format);
+
+    /* Resample in one thread. Multithreading makes
+     * performance worse with small chunks of audio. */
+    runtime_spec = soxr_runtime_spec(1);
+
+    switch (r->method) {
+        case PA_RESAMPLER_SOXR_MQ:
+            quality_recipe = SOXR_MQ | SOXR_LINEAR_PHASE;
+            break;
+        case PA_RESAMPLER_SOXR_HQ:
+            quality_recipe = SOXR_HQ | SOXR_LINEAR_PHASE;
+            break;
+        case PA_RESAMPLER_SOXR_VHQ:
+            quality_recipe = SOXR_VHQ | SOXR_LINEAR_PHASE;
+            break;
+        default:
+            pa_assert_not_reached();
+    }
+
+    quality = soxr_quality_spec(quality_recipe, 0);
+
+    state = soxr_create(r->i_ss.rate, r->o_ss.rate, r->work_channels, &err, &io_spec, &quality, &runtime_spec);
+    if (!state) {
+        pa_log_error("Failed to create libsoxr resampler context: %s.", (err ? err : "[unknown error]"));
+        return -1;
+    }
+
+    r->impl.free = resampler_soxr_free;
+    r->impl.reset = resampler_soxr_reset;
+    r->impl.update_rates = resampler_soxr_update_rates;
+    r->impl.resample = resampler_soxr_resample;
+    r->impl.data = state;
+
+    return 0;
+}



More information about the pulseaudio-commits mailing list