]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/resampler.c
resampler: Remove prefix from static functions
[pulseaudio] / src / pulsecore / resampler.c
index cbe5e522c527ef4ad82b8924b9046acefb7a1a5c..2842e653b47547fb45bf7386effb569b6d106830 100644 (file)
@@ -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"
 
@@ -114,7 +115,8 @@ static int peaks_init(pa_resampler*r);
 static int libsamplerate_init(pa_resampler*r);
 #endif
 
-static void calc_map_table(const pa_resampler *r, pa_remap_t *m);
+static void setup_remap(const pa_resampler *r, pa_remap_t *m);
+static void free_remap(pa_remap_t *m);
 
 static int (* const init_table[])(pa_resampler*r) = {
 #ifdef HAVE_LIBSAMPLERATE
@@ -184,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;
 
@@ -197,7 +201,7 @@ static pa_resample_method_t choose_auto_resampler(pa_resample_flags_t flags) {
     return method;
 }
 
-static pa_resample_method_t pa_resampler_fix_method(
+static pa_resample_method_t fix_method(
                 pa_resample_flags_t flags,
                 pa_resample_method_t method,
                 const uint32_t rate_a,
@@ -249,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;
 }
 
@@ -299,7 +317,7 @@ static bool sample_format_more_precise(pa_sample_format_t a, pa_sample_format_t
     }
 }
 
-static pa_sample_format_t pa_resampler_choose_work_format(
+static pa_sample_format_t choose_work_format(
                     pa_resample_method_t method,
                     pa_sample_format_t a,
                     pa_sample_format_t b,
@@ -367,7 +385,7 @@ pa_resampler* pa_resampler_new(
     pa_assert(method >= 0);
     pa_assert(method < PA_RESAMPLER_MAX);
 
-    method = pa_resampler_fix_method(flags, method, a->rate, b->rate);
+    method = fix_method(flags, method, a->rate, b->rate);
 
     r = pa_xnew0(pa_resampler, 1);
     r->mempool = pool;
@@ -378,11 +396,6 @@ pa_resampler* pa_resampler_new(
     r->i_ss = *a;
     r->o_ss = *b;
 
-    /* set up the remap structure */
-    r->remap.i_ss = &r->i_ss;
-    r->remap.o_ss = &r->o_ss;
-    r->remap.format = &r->work_format;
-
     if (am)
         r->i_cm = *am;
     else if (!pa_channel_map_init_auto(&r->i_cm, r->i_ss.channels, PA_CHANNEL_MAP_DEFAULT))
@@ -396,12 +409,10 @@ pa_resampler* pa_resampler_new(
     r->i_fz = pa_frame_size(a);
     r->o_fz = pa_frame_size(b);
 
-    /* compute channel remap table if needed */
-    if ((r->map_required = (r->i_ss.channels != r->o_ss.channels || (!(r->flags & PA_RESAMPLER_NO_REMAP) &&
-        !pa_channel_map_equal(&r->i_cm, &r->o_cm)))))
-        calc_map_table(r, &r->remap);
+    r->map_required = (r->i_ss.channels != r->o_ss.channels || (!(r->flags & PA_RESAMPLER_NO_REMAP) &&
+        !pa_channel_map_equal(&r->i_cm, &r->o_cm)));
 
-    r->work_format = pa_resampler_choose_work_format(method, a->format, b->format, r->map_required);
+    r->work_format = choose_work_format(method, a->format, b->format, r->map_required);
     r->w_sz = pa_sample_size_of_format(r->work_format);
 
     if (r->i_ss.format != r->work_format) {
@@ -451,6 +462,10 @@ pa_resampler* pa_resampler_new(
                  pa_sample_format_to_string(b->format), pa_sample_format_to_string(r->work_format));
     pa_log_debug("  channels %d -> %d (resampling %d)", a->channels, b->channels, r->work_channels);
 
+    /* set up the remap structure */
+    if (r->map_required)
+        setup_remap(r, &r->remap);
+
     /* initialize implementation */
     if (init_table[method](r) < 0)
         goto fail;
@@ -480,6 +495,8 @@ void pa_resampler_free(pa_resampler *r) {
     if (r->from_work_format_buf.memblock)
         pa_memblock_unref(r->from_work_format_buf.memblock);
 
+    free_remap(&r->remap);
+
     pa_xfree(r);
 }
 
@@ -788,7 +805,7 @@ static int front_rear_side(pa_channel_position_t p) {
     return ON_OTHER;
 }
 
-static void calc_map_table(const pa_resampler *r, pa_remap_t *m) {
+static void setup_remap(const pa_resampler *r, pa_remap_t *m) {
     unsigned oc, ic;
     unsigned n_oc, n_ic;
     bool ic_connected[PA_CHANNELS_MAX];
@@ -802,6 +819,10 @@ static void calc_map_table(const pa_resampler *r, pa_remap_t *m) {
     n_oc = r->o_ss.channels;
     n_ic = r->i_ss.channels;
 
+    m->format = r->work_format;
+    m->i_ss = r->i_ss;
+    m->o_ss = r->o_ss;
+
     memset(m->map_table_f, 0, sizeof(m->map_table_f));
     memset(m->map_table_i, 0, sizeof(m->map_table_i));
 
@@ -1151,6 +1172,12 @@ static void calc_map_table(const pa_resampler *r, pa_remap_t *m) {
     pa_init_remap_func(m);
 }
 
+static void free_remap(pa_remap_t *m) {
+    pa_assert(m);
+
+    pa_xfree(m->state);
+}
+
 /* check if buf's memblock is large enough to hold 'len' bytes; create a
  * new memblock if necessary and optionally preserve 'copy' data bytes */
 static void fit_buf(pa_resampler *r, pa_memchunk *buf, size_t len, size_t *size, size_t copy) {
@@ -1459,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;
@@ -1477,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);