]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/resampler.c
resampler: Remove prefix from static functions
[pulseaudio] / src / pulsecore / resampler.c
index 78ad553078af3537f056f3fd7a01fc175a07cfc5..2842e653b47547fb45bf7386effb569b6d106830 100644 (file)
@@ -5,7 +5,7 @@
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
 
   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 of the License,
+  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
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
 #include <samplerate.h>
 #endif
 
 #include <samplerate.h>
 #endif
 
+#ifdef HAVE_SPEEX
 #include <speex/speex_resampler.h>
 #include <speex/speex_resampler.h>
-
-#include <liboil/liboilfuncs.h>
-#include <liboil/liboil.h>
+#endif
 
 #include <pulse/xmalloc.h>
 #include <pulsecore/sconv.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/strbuf.h>
 
 #include <pulse/xmalloc.h>
 #include <pulsecore/sconv.h>
 #include <pulsecore/log.h>
 #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"
 
 #include "resampler.h"
 #include "ffmpeg/avcodec.h"
 
 #include "resampler.h"
@@ -53,65 +54,69 @@ struct pa_resampler {
 
     pa_sample_spec i_ss, o_ss;
     pa_channel_map i_cm, o_cm;
 
     pa_sample_spec i_ss, o_ss;
     pa_channel_map i_cm, o_cm;
-    size_t i_fz, o_fz, w_sz;
+    size_t i_fz, o_fz, w_fz, w_sz;
     pa_mempool *mempool;
 
     pa_mempool *mempool;
 
-    pa_memchunk buf1, buf2, buf3, buf4;
-    unsigned buf1_samples, buf2_samples, buf3_samples, buf4_samples;
+    pa_memchunk to_work_format_buf;
+    pa_memchunk remap_buf;
+    pa_memchunk resample_buf;
+    pa_memchunk from_work_format_buf;
+    size_t to_work_format_buf_size;
+    size_t remap_buf_size;
+    size_t resample_buf_size;
+    size_t from_work_format_buf_size;
+
+    /* points to buffer before resampling stage, remap or to_work */
+    pa_memchunk *leftover_buf;
+    size_t *leftover_buf_size;
+
+    /* have_leftover points to leftover_in_remap or leftover_in_to_work */
+    bool *have_leftover;
+    bool leftover_in_remap;
+    bool leftover_in_to_work;
 
     pa_sample_format_t work_format;
 
     pa_sample_format_t work_format;
+    uint8_t work_channels;
 
     pa_convert_func_t to_work_format_func;
     pa_convert_func_t from_work_format_func;
 
 
     pa_convert_func_t to_work_format_func;
     pa_convert_func_t from_work_format_func;
 
-    float map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
-    pa_bool_t map_required;
-
-    void (*impl_free)(pa_resampler *r);
-    void (*impl_update_rates)(pa_resampler *r);
-    void (*impl_resample)(pa_resampler *r, const pa_memchunk *in, unsigned in_samples, pa_memchunk *out, unsigned *out_samples);
-    void (*impl_reset)(pa_resampler *r);
+    pa_remap_t remap;
+    bool map_required;
 
 
-    struct { /* data specific to the trivial resampler */
-        unsigned o_counter;
-        unsigned i_counter;
-    } trivial;
-
-    struct { /* data specific to the peak finder pseudo resampler */
-        unsigned o_counter;
-        unsigned i_counter;
-
-        float max_f[PA_CHANNELS_MAX];
-        int16_t max_i[PA_CHANNELS_MAX];
+    pa_resampler_impl impl;
+};
 
 
-    } peaks;
+struct trivial_data { /* data specific to the trivial resampler */
+    unsigned o_counter;
+    unsigned i_counter;
+};
 
 
-#ifdef HAVE_LIBSAMPLERATE
-    struct { /* data specific to libsamplerate */
-        SRC_STATE *state;
-    } src;
-#endif
+struct peaks_data { /* data specific to the peak finder pseudo resampler */
+    unsigned o_counter;
+    unsigned i_counter;
 
 
-    struct { /* data specific to speex */
-        SpeexResamplerState* state;
-    } speex;
+    float max_f[PA_CHANNELS_MAX];
+    int16_t max_i[PA_CHANNELS_MAX];
+};
 
 
-    struct { /* data specific to ffmpeg */
-        struct AVResampleContext *state;
-        pa_memchunk buf[PA_CHANNELS_MAX];
-    } ffmpeg;
+struct ffmpeg_data { /* data specific to ffmpeg */
+    struct AVResampleContext *state;
 };
 
 static int copy_init(pa_resampler *r);
 static int trivial_init(pa_resampler*r);
 };
 
 static int copy_init(pa_resampler *r);
 static int trivial_init(pa_resampler*r);
+#ifdef HAVE_SPEEX
 static int speex_init(pa_resampler*r);
 static int speex_init(pa_resampler*r);
+#endif
 static int ffmpeg_init(pa_resampler*r);
 static int peaks_init(pa_resampler*r);
 #ifdef HAVE_LIBSAMPLERATE
 static int libsamplerate_init(pa_resampler*r);
 #endif
 
 static int ffmpeg_init(pa_resampler*r);
 static int peaks_init(pa_resampler*r);
 #ifdef HAVE_LIBSAMPLERATE
 static int libsamplerate_init(pa_resampler*r);
 #endif
 
-static void calc_map_table(pa_resampler *r);
+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
 
 static int (* const init_table[])(pa_resampler*r) = {
 #ifdef HAVE_LIBSAMPLERATE
@@ -128,6 +133,7 @@ static int (* const init_table[])(pa_resampler*r) = {
     [PA_RESAMPLER_SRC_LINEAR]              = NULL,
 #endif
     [PA_RESAMPLER_TRIVIAL]                 = trivial_init,
     [PA_RESAMPLER_SRC_LINEAR]              = NULL,
 #endif
     [PA_RESAMPLER_TRIVIAL]                 = trivial_init,
+#ifdef HAVE_SPEEX
     [PA_RESAMPLER_SPEEX_FLOAT_BASE+0]      = speex_init,
     [PA_RESAMPLER_SPEEX_FLOAT_BASE+1]      = speex_init,
     [PA_RESAMPLER_SPEEX_FLOAT_BASE+2]      = speex_init,
     [PA_RESAMPLER_SPEEX_FLOAT_BASE+0]      = speex_init,
     [PA_RESAMPLER_SPEEX_FLOAT_BASE+1]      = speex_init,
     [PA_RESAMPLER_SPEEX_FLOAT_BASE+2]      = speex_init,
@@ -150,12 +156,216 @@ static int (* const init_table[])(pa_resampler*r) = {
     [PA_RESAMPLER_SPEEX_FIXED_BASE+8]      = speex_init,
     [PA_RESAMPLER_SPEEX_FIXED_BASE+9]      = speex_init,
     [PA_RESAMPLER_SPEEX_FIXED_BASE+10]     = speex_init,
     [PA_RESAMPLER_SPEEX_FIXED_BASE+8]      = speex_init,
     [PA_RESAMPLER_SPEEX_FIXED_BASE+9]      = speex_init,
     [PA_RESAMPLER_SPEEX_FIXED_BASE+10]     = speex_init,
+#else
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+0]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+1]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+2]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+3]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+4]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+5]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+6]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+7]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+8]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+9]      = NULL,
+    [PA_RESAMPLER_SPEEX_FLOAT_BASE+10]     = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+0]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+1]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+2]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+3]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+4]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+5]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+6]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+7]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+8]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+9]      = NULL,
+    [PA_RESAMPLER_SPEEX_FIXED_BASE+10]     = NULL,
+#endif
     [PA_RESAMPLER_FFMPEG]                  = ffmpeg_init,
     [PA_RESAMPLER_AUTO]                    = NULL,
     [PA_RESAMPLER_COPY]                    = copy_init,
     [PA_RESAMPLER_PEAKS]                   = peaks_init,
 };
 
     [PA_RESAMPLER_FFMPEG]                  = ffmpeg_init,
     [PA_RESAMPLER_AUTO]                    = NULL,
     [PA_RESAMPLER_COPY]                    = copy_init,
     [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;
+
+    if (pa_resample_method_supported(PA_RESAMPLER_SPEEX_FLOAT_BASE + 1))
+        method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 1;
+    else if (flags & PA_RESAMPLER_VARIABLE_RATE)
+        method = PA_RESAMPLER_TRIVIAL;
+    else
+        method = PA_RESAMPLER_FFMPEG;
+
+    return method;
+}
+
+static pa_resample_method_t fix_method(
+                pa_resample_flags_t flags,
+                pa_resample_method_t method,
+                const uint32_t rate_a,
+                const uint32_t rate_b) {
+
+    pa_assert(pa_sample_rate_valid(rate_a));
+    pa_assert(pa_sample_rate_valid(rate_b));
+    pa_assert(method >= 0);
+    pa_assert(method < PA_RESAMPLER_MAX);
+
+    if (!(flags & PA_RESAMPLER_VARIABLE_RATE) && rate_a == rate_b) {
+        pa_log_info("Forcing resampler 'copy', because of fixed, identical sample rates.");
+        method = PA_RESAMPLER_COPY;
+    }
+
+    if (!pa_resample_method_supported(method)) {
+        pa_log_warn("Support for resampler '%s' not compiled in, reverting to 'auto'.", pa_resample_method_to_string(method));
+        method = PA_RESAMPLER_AUTO;
+    }
+
+    switch (method) {
+        case PA_RESAMPLER_COPY:
+            if (rate_a != rate_b) {
+                pa_log_info("Resampler 'copy' cannot change sampling rate, reverting to resampler 'auto'.");
+                method = PA_RESAMPLER_AUTO;
+                break;
+            }
+                                     /* Else fall through */
+        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));
+                method = PA_RESAMPLER_AUTO;
+            }
+            break;
+
+        /* The Peaks resampler only supports downsampling.
+         * Revert to auto if we are upsampling */
+        case PA_RESAMPLER_PEAKS:
+            if (rate_a < rate_b) {
+                pa_log_warn("The 'peaks' resampler only supports downsampling, reverting to resampler 'auto'.");
+                method = PA_RESAMPLER_AUTO;
+            }
+            break;
+
+        default:
+            break;
+    }
+
+    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;
+}
+
+/* Return true if a is a more precise sample format than b, else return false */
+static bool sample_format_more_precise(pa_sample_format_t a, pa_sample_format_t b) {
+    pa_assert(pa_sample_format_valid(a));
+    pa_assert(pa_sample_format_valid(b));
+
+    switch (a) {
+        case PA_SAMPLE_U8:
+        case PA_SAMPLE_ALAW:
+        case PA_SAMPLE_ULAW:
+            return false;
+            break;
+
+        case PA_SAMPLE_S16LE:
+        case PA_SAMPLE_S16BE:
+            if (b == PA_SAMPLE_ULAW || b == PA_SAMPLE_ALAW || b == PA_SAMPLE_U8)
+                return true;
+            else
+                return false;
+            break;
+
+        case PA_SAMPLE_S24LE:
+        case PA_SAMPLE_S24BE:
+        case PA_SAMPLE_S24_32LE:
+        case PA_SAMPLE_S24_32BE:
+            if (b == PA_SAMPLE_ULAW || b == PA_SAMPLE_ALAW || b == PA_SAMPLE_U8 ||
+                b == PA_SAMPLE_S16LE || b == PA_SAMPLE_S16BE)
+                return true;
+            else
+                return false;
+            break;
+
+        case PA_SAMPLE_FLOAT32LE:
+        case PA_SAMPLE_FLOAT32BE:
+        case PA_SAMPLE_S32LE:
+        case PA_SAMPLE_S32BE:
+            if (b == PA_SAMPLE_FLOAT32LE || b == PA_SAMPLE_FLOAT32BE ||
+                b == PA_SAMPLE_S32LE || b == PA_SAMPLE_FLOAT32BE)
+                return false;
+            else
+                return true;
+            break;
+
+        default:
+            return false;
+    }
+}
+
+static pa_sample_format_t choose_work_format(
+                    pa_resample_method_t method,
+                    pa_sample_format_t a,
+                    pa_sample_format_t b,
+                    bool map_required) {
+    pa_sample_format_t work_format;
+
+    pa_assert(pa_sample_format_valid(a));
+    pa_assert(pa_sample_format_valid(b));
+    pa_assert(method >= 0);
+    pa_assert(method < PA_RESAMPLER_MAX);
+
+    if (method >= PA_RESAMPLER_SPEEX_FIXED_BASE && method <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+        method = PA_RESAMPLER_SPEEX_FIXED_BASE;
+
+    switch (method) {
+        /* This block is for resampling functions that only
+         * support the S16 sample format. */
+        case PA_RESAMPLER_SPEEX_FIXED_BASE:     /* fall through */
+        case PA_RESAMPLER_FFMPEG:
+            work_format = PA_SAMPLE_S16NE;
+            break;
+
+        /* This block is for resampling functions that support
+         * any sample format. */
+        case PA_RESAMPLER_COPY:                 /* fall through */
+        case PA_RESAMPLER_TRIVIAL:
+            if (!map_required && a == b) {
+                work_format = a;
+                break;
+            }
+                                                /* Else fall trough */
+        case PA_RESAMPLER_PEAKS:
+            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))
+                work_format = PA_SAMPLE_FLOAT32NE;
+            else
+                work_format = PA_SAMPLE_S16NE;
+            break;
+
+        default:
+            work_format = PA_SAMPLE_FLOAT32NE;
+    }
+
+    return work_format;
+}
+
 pa_resampler* pa_resampler_new(
         pa_mempool *pool,
         const pa_sample_spec *a,
 pa_resampler* pa_resampler_new(
         pa_mempool *pool,
         const pa_sample_spec *a,
@@ -175,41 +385,13 @@ pa_resampler* pa_resampler_new(
     pa_assert(method >= 0);
     pa_assert(method < PA_RESAMPLER_MAX);
 
     pa_assert(method >= 0);
     pa_assert(method < PA_RESAMPLER_MAX);
 
-    /* Fix method */
-
-    if (!(flags & PA_RESAMPLER_VARIABLE_RATE) && a->rate == b->rate) {
-        pa_log_info("Forcing resampler 'copy', because of fixed, identical sample rates.");
-        method = PA_RESAMPLER_COPY;
-    }
-
-    if (!pa_resample_method_supported(method)) {
-        pa_log_warn("Support for resampler '%s' not compiled in, reverting to 'auto'.", pa_resample_method_to_string(method));
-        method = PA_RESAMPLER_AUTO;
-    }
-
-    if (method == PA_RESAMPLER_FFMPEG && (flags & PA_RESAMPLER_VARIABLE_RATE)) {
-        pa_log_info("Resampler 'ffmpeg' cannot do variable rate, reverting to resampler 'auto'.");
-        method = PA_RESAMPLER_AUTO;
-    }
-
-    if (method == PA_RESAMPLER_COPY && ((flags & PA_RESAMPLER_VARIABLE_RATE) || a->rate != b->rate)) {
-        pa_log_info("Resampler 'copy' cannot change sampling rate, reverting to resampler 'auto'.");
-        method = PA_RESAMPLER_AUTO;
-    }
+    method = fix_method(flags, method, a->rate, b->rate);
 
 
-    if (method == PA_RESAMPLER_AUTO)
-        method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
-
-    r = pa_xnew(pa_resampler, 1);
+    r = pa_xnew0(pa_resampler, 1);
     r->mempool = pool;
     r->method = method;
     r->flags = flags;
 
     r->mempool = pool;
     r->method = method;
     r->flags = flags;
 
-    r->impl_free = NULL;
-    r->impl_update_rates = NULL;
-    r->impl_resample = NULL;
-    r->impl_reset = NULL;
-
     /* Fill sample specs */
     r->i_ss = *a;
     r->o_ss = *b;
     /* Fill sample specs */
     r->i_ss = *a;
     r->o_ss = *b;
@@ -227,67 +409,62 @@ pa_resampler* pa_resampler_new(
     r->i_fz = pa_frame_size(a);
     r->o_fz = pa_frame_size(b);
 
     r->i_fz = pa_frame_size(a);
     r->o_fz = pa_frame_size(b);
 
-    pa_memchunk_reset(&r->buf1);
-    pa_memchunk_reset(&r->buf2);
-    pa_memchunk_reset(&r->buf3);
-    pa_memchunk_reset(&r->buf4);
-
-    r->buf1_samples = r->buf2_samples = r->buf3_samples = r->buf4_samples = 0;
-
-    calc_map_table(r);
-
-    pa_log_info("Using resampler '%s'", pa_resample_method_to_string(method));
+    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)));
 
 
-    if ((method >= PA_RESAMPLER_SPEEX_FIXED_BASE && method <= PA_RESAMPLER_SPEEX_FIXED_MAX) ||
-        (method == PA_RESAMPLER_FFMPEG))
-        r->work_format = PA_SAMPLE_S16NE;
-    else if (method == PA_RESAMPLER_TRIVIAL || method == PA_RESAMPLER_COPY || method == PA_RESAMPLER_PEAKS) {
-
-        if (r->map_required || a->format != b->format || method == PA_RESAMPLER_PEAKS) {
-
-            if (a->format == PA_SAMPLE_S32NE || a->format == PA_SAMPLE_S32RE ||
-                a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE ||
-                a->format == PA_SAMPLE_S24NE || a->format == PA_SAMPLE_S24RE ||
-                a->format == PA_SAMPLE_S24_32NE || a->format == PA_SAMPLE_S24_32RE ||
-                b->format == PA_SAMPLE_S32NE || b->format == PA_SAMPLE_S32RE ||
-                b->format == PA_SAMPLE_FLOAT32NE || b->format == PA_SAMPLE_FLOAT32RE ||
-                b->format == PA_SAMPLE_S24NE || b->format == PA_SAMPLE_S24RE ||
-                b->format == PA_SAMPLE_S24_32NE || b->format == PA_SAMPLE_S24_32RE)
-                r->work_format = PA_SAMPLE_FLOAT32NE;
-            else
-                r->work_format = PA_SAMPLE_S16NE;
-
-        } else
-            r->work_format = a->format;
+    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);
 
 
-    } else
-        r->work_format = PA_SAMPLE_FLOAT32NE;
+    if (r->i_ss.format != r->work_format) {
+        if (r->work_format == PA_SAMPLE_FLOAT32NE) {
+            if (!(r->to_work_format_func = pa_get_convert_to_float32ne_function(r->i_ss.format)))
+                goto fail;
+        } else {
+            pa_assert(r->work_format == PA_SAMPLE_S16NE);
+            if (!(r->to_work_format_func = pa_get_convert_to_s16ne_function(r->i_ss.format)))
+                goto fail;
+        }
+    }
 
 
-    pa_log_info("Using %s as working format.", pa_sample_format_to_string(r->work_format));
+    if (r->o_ss.format != r->work_format) {
+        if (r->work_format == PA_SAMPLE_FLOAT32NE) {
+            if (!(r->from_work_format_func = pa_get_convert_from_float32ne_function(r->o_ss.format)))
+                goto fail;
+        } else {
+            pa_assert(r->work_format == PA_SAMPLE_S16NE);
+            if (!(r->from_work_format_func = pa_get_convert_from_s16ne_function(r->o_ss.format)))
+                goto fail;
+        }
+    }
 
 
-    r->w_sz = pa_sample_size_of_format(r->work_format);
+    if (r->o_ss.channels <= r->i_ss.channels) {
+        /* pipeline is: format conv. -> remap -> resample -> format conv. */
+        r->work_channels = r->o_ss.channels;
 
 
-    if (r->i_ss.format == r->work_format)
-        r->to_work_format_func = NULL;
-    else if (r->work_format == PA_SAMPLE_FLOAT32NE) {
-        if (!(r->to_work_format_func = pa_get_convert_to_float32ne_function(r->i_ss.format)))
-            goto fail;
+        /* leftover buffer is remap output buffer (before resampling) */
+        r->leftover_buf = &r->remap_buf;
+        r->leftover_buf_size = &r->remap_buf_size;
+        r->have_leftover = &r->leftover_in_remap;
     } else {
     } else {
-        pa_assert(r->work_format == PA_SAMPLE_S16NE);
-        if (!(r->to_work_format_func = pa_get_convert_to_s16ne_function(r->i_ss.format)))
-            goto fail;
-    }
+        /* pipeline is: format conv. -> resample -> remap -> format conv. */
+        r->work_channels = r->i_ss.channels;
 
 
-    if (r->o_ss.format == r->work_format)
-        r->from_work_format_func = NULL;
-    else if (r->work_format == PA_SAMPLE_FLOAT32NE) {
-        if (!(r->from_work_format_func = pa_get_convert_from_float32ne_function(r->o_ss.format)))
-            goto fail;
-    } else {
-        pa_assert(r->work_format == PA_SAMPLE_S16NE);
-        if (!(r->from_work_format_func = pa_get_convert_from_s16ne_function(r->o_ss.format)))
-            goto fail;
+        /* leftover buffer is to_work output buffer (before resampling) */
+        r->leftover_buf = &r->to_work_format_buf;
+        r->leftover_buf_size = &r->to_work_format_buf_size;
+        r->have_leftover = &r->leftover_in_to_work;
     }
     }
+    r->w_fz = pa_sample_size_of_format(r->work_format) * r->work_channels;
+
+    pa_log_debug("Resampler:");
+    pa_log_debug("  rate %d -> %d (method %s)", a->rate, b->rate, pa_resample_method_to_string(r->method));
+    pa_log_debug("  format %s -> %s (intermediate %s)", pa_sample_format_to_string(a->format),
+                 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)
 
     /* initialize implementation */
     if (init_table[method](r) < 0)
@@ -296,8 +473,7 @@ pa_resampler* pa_resampler_new(
     return r;
 
 fail:
     return r;
 
 fail:
-    if (r)
-        pa_xfree(r);
+    pa_xfree(r);
 
     return NULL;
 }
 
     return NULL;
 }
@@ -305,17 +481,21 @@ fail:
 void pa_resampler_free(pa_resampler *r) {
     pa_assert(r);
 
 void pa_resampler_free(pa_resampler *r) {
     pa_assert(r);
 
-    if (r->impl_free)
-        r->impl_free(r);
+    if (r->impl.free)
+        r->impl.free(r);
+    else
+        pa_xfree(r->impl.data);
+
+    if (r->to_work_format_buf.memblock)
+        pa_memblock_unref(r->to_work_format_buf.memblock);
+    if (r->remap_buf.memblock)
+        pa_memblock_unref(r->remap_buf.memblock);
+    if (r->resample_buf.memblock)
+        pa_memblock_unref(r->resample_buf.memblock);
+    if (r->from_work_format_buf.memblock)
+        pa_memblock_unref(r->from_work_format_buf.memblock);
 
 
-    if (r->buf1.memblock)
-        pa_memblock_unref(r->buf1.memblock);
-    if (r->buf2.memblock)
-        pa_memblock_unref(r->buf2.memblock);
-    if (r->buf3.memblock)
-        pa_memblock_unref(r->buf3.memblock);
-    if (r->buf4.memblock)
-        pa_memblock_unref(r->buf4.memblock);
+    free_remap(&r->remap);
 
     pa_xfree(r);
 }
 
     pa_xfree(r);
 }
@@ -323,43 +503,64 @@ void pa_resampler_free(pa_resampler *r) {
 void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate) {
     pa_assert(r);
     pa_assert(rate > 0);
 void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate) {
     pa_assert(r);
     pa_assert(rate > 0);
+    pa_assert(r->impl.update_rates);
 
     if (r->i_ss.rate == rate)
         return;
 
     r->i_ss.rate = rate;
 
 
     if (r->i_ss.rate == rate)
         return;
 
     r->i_ss.rate = rate;
 
-    r->impl_update_rates(r);
+    r->impl.update_rates(r);
 }
 
 void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate) {
     pa_assert(r);
     pa_assert(rate > 0);
 }
 
 void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate) {
     pa_assert(r);
     pa_assert(rate > 0);
+    pa_assert(r->impl.update_rates);
 
     if (r->o_ss.rate == rate)
         return;
 
     r->o_ss.rate = rate;
 
 
     if (r->o_ss.rate == rate)
         return;
 
     r->o_ss.rate = rate;
 
-    r->impl_update_rates(r);
+    r->impl.update_rates(r);
 }
 
 size_t pa_resampler_request(pa_resampler *r, size_t out_length) {
     pa_assert(r);
 
 }
 
 size_t pa_resampler_request(pa_resampler *r, size_t out_length) {
     pa_assert(r);
 
-    return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz;
+    /* Let's round up here to make it more likely that the caller will get at
+     * least out_length amount of data from pa_resampler_run().
+     *
+     * We don't take the leftover into account here. If we did, then it might
+     * be in theory possible that this function would return 0 and
+     * pa_resampler_run() would also return 0. That could lead to infinite
+     * loops. When the leftover is ignored here, such loops would eventually
+     * terminate, because the leftover would grow each round, finally
+     * surpassing the minimum input threshold of the resampler. */
+    return ((((uint64_t) ((out_length + r->o_fz-1) / r->o_fz) * r->i_ss.rate) + r->o_ss.rate-1) / r->o_ss.rate) * r->i_fz;
 }
 
 size_t pa_resampler_result(pa_resampler *r, size_t in_length) {
 }
 
 size_t pa_resampler_result(pa_resampler *r, size_t in_length) {
+    size_t frames;
+
     pa_assert(r);
 
     pa_assert(r);
 
-    return (((in_length / r->i_fz)*r->o_ss.rate)/r->i_ss.rate) * r->o_fz;
+    /* Let's round up here to ensure that the caller will always allocate big
+     * enough output buffer. */
+
+    frames = (in_length + r->i_fz - 1) / r->i_fz;
+    if (*r->have_leftover)
+        frames += r->leftover_buf->length / r->w_fz;
+
+    return (((uint64_t) frames * r->o_ss.rate + r->i_ss.rate - 1) / r->i_ss.rate) * r->o_fz;
 }
 
 size_t pa_resampler_max_block_size(pa_resampler *r) {
     size_t block_size_max;
 }
 
 size_t pa_resampler_max_block_size(pa_resampler *r) {
     size_t block_size_max;
-    pa_sample_spec ss;
-    size_t fs;
+    pa_sample_spec max_ss;
+    size_t max_fs;
+    size_t frames;
 
     pa_assert(r);
 
 
     pa_assert(r);
 
@@ -367,24 +568,47 @@ size_t pa_resampler_max_block_size(pa_resampler *r) {
 
     /* We deduce the "largest" sample spec we're using during the
      * conversion */
 
     /* We deduce the "largest" sample spec we're using during the
      * conversion */
-    ss.channels = (uint8_t) (PA_MAX(r->i_ss.channels, r->o_ss.channels));
+    max_ss.channels = (uint8_t) (PA_MAX(r->i_ss.channels, r->o_ss.channels));
 
     /* We silently assume that the format enum is ordered by size */
 
     /* We silently assume that the format enum is ordered by size */
-    ss.format = PA_MAX(r->i_ss.format, r->o_ss.format);
-    ss.format = PA_MAX(ss.format, r->work_format);
-
-    ss.rate = PA_MAX(r->i_ss.rate, r->o_ss.rate);
-
-    fs = pa_frame_size(&ss);
-
-    return (((block_size_max/fs - EXTRA_FRAMES)*r->i_ss.rate)/ss.rate)*r->i_fz;
+    max_ss.format = PA_MAX(r->i_ss.format, r->o_ss.format);
+    max_ss.format = PA_MAX(max_ss.format, r->work_format);
+
+    max_ss.rate = PA_MAX(r->i_ss.rate, r->o_ss.rate);
+
+    max_fs = pa_frame_size(&max_ss);
+    frames = block_size_max / max_fs - EXTRA_FRAMES;
+
+    pa_assert(frames >= (r->leftover_buf->length / r->w_fz));
+    if (*r->have_leftover)
+        frames -= r->leftover_buf->length / r->w_fz;
+
+    block_size_max = ((uint64_t) frames * r->i_ss.rate / max_ss.rate) * r->i_fz;
+
+    if (block_size_max > 0)
+        return block_size_max;
+    else
+        /* A single input frame may result in so much output that it doesn't
+         * fit in one standard memblock (e.g. converting 1 Hz to 44100 Hz). In
+         * this case the max block size will be set to one frame, and some
+         * memory will be probably be allocated with malloc() instead of using
+         * the memory pool.
+         *
+         * XXX: Should we support this case at all? We could also refuse to
+         * create resamplers whose max block size would exceed the memory pool
+         * block size. In this case also updating the resampler rate should
+         * fail if the new rate would cause an excessive max block size (in
+         * which case the stream would probably have to be killed). */
+        return r->i_fz;
 }
 
 void pa_resampler_reset(pa_resampler *r) {
     pa_assert(r);
 
 }
 
 void pa_resampler_reset(pa_resampler *r) {
     pa_assert(r);
 
-    if (r->impl_reset)
-        r->impl_reset(r);
+    if (r->impl.reset)
+        r->impl.reset(r);
+
+    *r->have_leftover = false;
 }
 
 pa_resample_method_t pa_resampler_get_method(pa_resampler *r) {
 }
 
 pa_resample_method_t pa_resampler_get_method(pa_resampler *r) {
@@ -470,6 +694,13 @@ int pa_resample_method_supported(pa_resample_method_t m) {
         return 0;
 #endif
 
         return 0;
 #endif
 
+#ifndef HAVE_SPEEX
+    if (m >= PA_RESAMPLER_SPEEX_FLOAT_BASE && m <= PA_RESAMPLER_SPEEX_FLOAT_MAX)
+        return 0;
+    if (m >= PA_RESAMPLER_SPEEX_FIXED_BASE && m <= PA_RESAMPLER_SPEEX_FIXED_MAX)
+        return 0;
+#endif
+
     return 1;
 }
 
     return 1;
 }
 
@@ -479,19 +710,19 @@ pa_resample_method_t pa_parse_resample_method(const char *string) {
     pa_assert(string);
 
     for (m = 0; m < PA_RESAMPLER_MAX; m++)
     pa_assert(string);
 
     for (m = 0; m < PA_RESAMPLER_MAX; m++)
-        if (!strcmp(string, resample_methods[m]))
+        if (pa_streq(string, resample_methods[m]))
             return m;
 
             return m;
 
-    if (!strcmp(string, "speex-fixed"))
-        return PA_RESAMPLER_SPEEX_FIXED_BASE + 3;
+    if (pa_streq(string, "speex-fixed"))
+        return PA_RESAMPLER_SPEEX_FIXED_BASE + 1;
 
 
-    if (!strcmp(string, "speex-float"))
-        return PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
+    if (pa_streq(string, "speex-float"))
+        return PA_RESAMPLER_SPEEX_FLOAT_BASE + 1;
 
     return PA_RESAMPLER_INVALID;
 }
 
 
     return PA_RESAMPLER_INVALID;
 }
 
-static pa_bool_t on_left(pa_channel_position_t p) {
+static bool on_left(pa_channel_position_t p) {
 
     return
         p == PA_CHANNEL_POSITION_FRONT_LEFT ||
 
     return
         p == PA_CHANNEL_POSITION_FRONT_LEFT ||
@@ -502,7 +733,7 @@ static pa_bool_t on_left(pa_channel_position_t p) {
         p == PA_CHANNEL_POSITION_TOP_REAR_LEFT;
 }
 
         p == PA_CHANNEL_POSITION_TOP_REAR_LEFT;
 }
 
-static pa_bool_t on_right(pa_channel_position_t p) {
+static bool on_right(pa_channel_position_t p) {
 
     return
         p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
 
     return
         p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
@@ -513,7 +744,7 @@ static pa_bool_t on_right(pa_channel_position_t p) {
         p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
 }
 
         p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
 }
 
-static pa_bool_t on_center(pa_channel_position_t p) {
+static bool on_center(pa_channel_position_t p) {
 
     return
         p == PA_CHANNEL_POSITION_FRONT_CENTER ||
 
     return
         p == PA_CHANNEL_POSITION_FRONT_CENTER ||
@@ -523,12 +754,12 @@ static pa_bool_t on_center(pa_channel_position_t p) {
         p == PA_CHANNEL_POSITION_TOP_REAR_CENTER;
 }
 
         p == PA_CHANNEL_POSITION_TOP_REAR_CENTER;
 }
 
-static pa_bool_t on_lfe(pa_channel_position_t p) {
+static bool on_lfe(pa_channel_position_t p) {
     return
         p == PA_CHANNEL_POSITION_LFE;
 }
 
     return
         p == PA_CHANNEL_POSITION_LFE;
 }
 
-static pa_bool_t on_front(pa_channel_position_t p) {
+static bool on_front(pa_channel_position_t p) {
     return
         p == PA_CHANNEL_POSITION_FRONT_LEFT ||
         p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
     return
         p == PA_CHANNEL_POSITION_FRONT_LEFT ||
         p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
@@ -540,7 +771,7 @@ static pa_bool_t on_front(pa_channel_position_t p) {
         p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
 }
 
         p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
 }
 
-static pa_bool_t on_rear(pa_channel_position_t p) {
+static bool on_rear(pa_channel_position_t p) {
     return
         p == PA_CHANNEL_POSITION_REAR_LEFT ||
         p == PA_CHANNEL_POSITION_REAR_RIGHT ||
     return
         p == PA_CHANNEL_POSITION_REAR_LEFT ||
         p == PA_CHANNEL_POSITION_REAR_RIGHT ||
@@ -550,7 +781,7 @@ static pa_bool_t on_rear(pa_channel_position_t p) {
         p == PA_CHANNEL_POSITION_TOP_REAR_CENTER;
 }
 
         p == PA_CHANNEL_POSITION_TOP_REAR_CENTER;
 }
 
-static pa_bool_t on_side(pa_channel_position_t p) {
+static bool on_side(pa_channel_position_t p) {
     return
         p == PA_CHANNEL_POSITION_SIDE_LEFT ||
         p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
     return
         p == PA_CHANNEL_POSITION_SIDE_LEFT ||
         p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
@@ -574,237 +805,232 @@ static int front_rear_side(pa_channel_position_t p) {
     return ON_OTHER;
 }
 
     return ON_OTHER;
 }
 
-static void calc_map_table(pa_resampler *r) {
+static void setup_remap(const pa_resampler *r, pa_remap_t *m) {
     unsigned oc, ic;
     unsigned oc, ic;
-    pa_bool_t ic_connected[PA_CHANNELS_MAX];
-    pa_bool_t remix;
+    unsigned n_oc, n_ic;
+    bool ic_connected[PA_CHANNELS_MAX];
+    bool remix;
     pa_strbuf *s;
     char *t;
 
     pa_assert(r);
     pa_strbuf *s;
     char *t;
 
     pa_assert(r);
+    pa_assert(m);
 
 
-    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)))))
-        return;
+    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));
 
 
-    memset(r->map_table, 0, sizeof(r->map_table));
     memset(ic_connected, 0, sizeof(ic_connected));
     memset(ic_connected, 0, sizeof(ic_connected));
-    remix = (r->flags & (PA_RESAMPLER_NO_REMAP|PA_RESAMPLER_NO_REMIX)) == 0;
+    remix = (r->flags & (PA_RESAMPLER_NO_REMAP | PA_RESAMPLER_NO_REMIX)) == 0;
 
 
-    for (oc = 0; oc < r->o_ss.channels; oc++) {
-        pa_bool_t oc_connected = FALSE;
-        pa_channel_position_t b = r->o_cm.map[oc];
+    if (r->flags & PA_RESAMPLER_NO_REMAP) {
+        pa_assert(!remix);
 
 
-        for (ic = 0; ic < r->i_ss.channels; ic++) {
-            pa_channel_position_t a = r->i_cm.map[ic];
+        for (oc = 0; oc < PA_MIN(n_ic, n_oc); oc++)
+            m->map_table_f[oc][oc] = 1.0f;
 
 
-            if (r->flags & PA_RESAMPLER_NO_REMAP) {
-                /* We shall not do any remapping. Hence, just check by index */
+    } else if (r->flags & PA_RESAMPLER_NO_REMIX) {
+        pa_assert(!remix);
+        for (oc = 0; oc < n_oc; oc++) {
+            pa_channel_position_t b = r->o_cm.map[oc];
 
 
-                if (ic == oc)
-                    r->map_table[oc][ic] = 1.0;
+            for (ic = 0; ic < n_ic; ic++) {
+                pa_channel_position_t a = r->i_cm.map[ic];
 
 
-                continue;
+                /* We shall not do any remixing. Hence, just check by name */
+                if (a == b)
+                    m->map_table_f[oc][ic] = 1.0f;
             }
             }
+        }
+    } else {
 
 
-            if (r->flags & PA_RESAMPLER_NO_REMIX) {
-                /* We shall not do any remixing. Hence, just check by name */
+        /* OK, we shall do the full monty: upmixing and downmixing. Our
+         * algorithm is relatively simple, does not do spacialization, delay
+         * elements or apply lowpass filters for LFE. Patches are always
+         * welcome, though. Oh, and it doesn't do any matrix decoding. (Which
+         * probably wouldn't make any sense anyway.)
+         *
+         * This code is not idempotent: downmixing an upmixed stereo stream is
+         * not identical to the original. The volume will not match, and the
+         * two channels will be a linear combination of both.
+         *
+         * This is loosely based on random suggestions found on the Internet,
+         * such as this:
+         * http://www.halfgaar.net/surround-sound-in-linux and the alsa upmix
+         * plugin.
+         *
+         * The algorithm works basically like this:
+         *
+         * 1) Connect all channels with matching names.
+         *
+         * 2) Mono Handling:
+         *    S:Mono: Copy into all D:channels
+         *    D:Mono: Avg all S:channels
+         *
+         * 3) Mix D:Left, D:Right:
+         *    D:Left: If not connected, avg all S:Left
+         *    D:Right: If not connected, avg all S:Right
+         *
+         * 4) Mix D:Center
+         *    If not connected, avg all S:Center
+         *    If still not connected, avg all S:Left, S:Right
+         *
+         * 5) Mix D:LFE
+         *    If not connected, avg all S:*
+         *
+         * 6) Make sure S:Left/S:Right is used: S:Left/S:Right: If not
+         *    connected, mix into all D:left and all D:right channels. Gain is
+         *    1/9.
+         *
+         * 7) Make sure S:Center, S:LFE is used:
+         *
+         *    S:Center, S:LFE: If not connected, mix into all D:left, all
+         *    D:right, all D:center channels. Gain is 0.5 for center and 0.375
+         *    for LFE. C-front is only mixed into L-front/R-front if available,
+         *    otherwise into all L/R channels. Similarly for C-rear.
+         *
+         * 8) Normalize each row in the matrix such that the sum for each row is
+         *    not larger than 1.0 in order to avoid clipping.
+         *
+         * S: and D: shall relate to the source resp. destination channels.
+         *
+         * Rationale: 1, 2 are probably obvious. For 3: this copies front to
+         * rear if needed. For 4: we try to find some suitable C source for C,
+         * if we don't find any, we avg L and R. For 5: LFE is mixed from all
+         * channels. For 6: the rear channels should not be dropped entirely,
+         * however have only minimal impact. For 7: movies usually encode
+         * speech on the center channel. Thus we have to make sure this channel
+         * is distributed to L and R if not available in the output. Also, LFE
+         * is used to achieve a greater dynamic range, and thus we should try
+         * to do our best to pass it to L+R.
+         */
 
 
-                if (a == b)
-                    r->map_table[oc][ic] = 1.0;
+        unsigned
+            ic_left = 0,
+            ic_right = 0,
+            ic_center = 0,
+            ic_unconnected_left = 0,
+            ic_unconnected_right = 0,
+            ic_unconnected_center = 0,
+            ic_unconnected_lfe = 0;
+        bool ic_unconnected_center_mixed_in = 0;
 
 
-                continue;
-            }
+        pa_assert(remix);
 
 
-            pa_assert(remix);
-
-            /* OK, we shall do the full monty: upmixing and
-             * downmixing. Our algorithm is relatively simple, does
-             * not do spacialization, delay elements or apply lowpass
-             * filters for LFE. Patches are always welcome,
-             * though. Oh, and it doesn't do any matrix
-             * decoding. (Which probably wouldn't make any sense
-             * anyway.)
-             *
-             * This code is not idempotent: downmixing an upmixed
-             * stereo stream is not identical to the original. The
-             * volume will not match, and the two channels will be a
-             * linear combination of both.
-             *
-             * This is losely based on random suggestions found on the
-             * Internet, such as this:
-             * http://www.halfgaar.net/surround-sound-in-linux and the
-             * alsa upmix plugin.
-             *
-             * The algorithm works basically like this:
-             *
-             * 1) Connect all channels with matching names.
-             *
-             * 2) Mono Handling:
-             *    S:Mono: Copy into all D:channels
-             *    D:Mono: Copy in all S:channels
-             *
-             * 3) Mix D:Left, D:Right:
-             *    D:Left: If not connected, avg all S:Left
-             *    D:Right: If not connected, avg all S:Right
-             *
-             * 4) Mix D:Center
-             *       If not connected, avg all S:Center
-             *       If still not connected, avg all S:Left, S:Right
-             *
-             * 5) Mix D:LFE
-             *       If not connected, avg all S:*
-             *
-             * 6) Make sure S:Left/S:Right is used: S:Left/S:Right: If
-             *    not connected, mix into all D:left and all D:right
-             *    channels. Gain is 0.1, the current left and right
-             *    should be multiplied by 0.9.
-             *
-             * 7) Make sure S:Center, S:LFE is used:
-             *
-             *    S:Center, S:LFE: If not connected, mix into all
-             *    D:left, all D:right, all D:center channels, gain is
-             *    0.375. The current (as result of 1..6) factors
-             *    should be multiplied by 0.75. (Alt. suggestion: 0.25
-             *    vs. 0.5) If C-front is only mixed into
-             *    L-front/R-front if available, otherwise into all L/R
-             *    channels. Similarly for C-rear.
-             *
-             * S: and D: shall relate to the source resp. destination channels.
-             *
-             * Rationale: 1, 2 are probably obvious. For 3: this
-             * copies front to rear if needed. For 4: we try to find
-             * some suitable C source for C, if we don't find any, we
-             * avg L and R. For 5: LFE is mixed from all channels. For
-             * 6: the rear channels should not be dropped entirely,
-             * however have only minimal impact. For 7: movies usually
-             * encode speech on the center channel. Thus we have to
-             * make sure this channel is distributed to L and R if not
-             * available in the output. Also, LFE is used to achieve a
-             * greater dynamic range, and thus we should try to do our
-             * best to pass it to L+R.
-             */
-
-            if (a == b || a == PA_CHANNEL_POSITION_MONO || b == PA_CHANNEL_POSITION_MONO) {
-                r->map_table[oc][ic] = 1.0;
-
-                oc_connected = TRUE;
-                ic_connected[ic] = TRUE;
-            }
+        for (ic = 0; ic < n_ic; ic++) {
+            if (on_left(r->i_cm.map[ic]))
+                ic_left++;
+            if (on_right(r->i_cm.map[ic]))
+                ic_right++;
+            if (on_center(r->i_cm.map[ic]))
+                ic_center++;
         }
 
         }
 
-        if (!oc_connected && remix) {
-            /* OK, we shall remix */
+        for (oc = 0; oc < n_oc; oc++) {
+            bool oc_connected = false;
+            pa_channel_position_t b = r->o_cm.map[oc];
 
 
-            /* Try to find matching input ports for this output port */
+            for (ic = 0; ic < n_ic; ic++) {
+                pa_channel_position_t a = r->i_cm.map[ic];
 
 
-            if (on_left(b)) {
-                unsigned n = 0;
+                if (a == b || a == PA_CHANNEL_POSITION_MONO) {
+                    m->map_table_f[oc][ic] = 1.0f;
 
 
-                /* We are not connected and on the left side, let's
-                 * average all left side input channels. */
+                    oc_connected = true;
+                    ic_connected[ic] = true;
+                }
+                else if (b == PA_CHANNEL_POSITION_MONO) {
+                    m->map_table_f[oc][ic] = 1.0f / (float) n_ic;
 
 
-                for (ic = 0; ic < r->i_ss.channels; ic++)
-                    if (on_left(r->i_cm.map[ic]))
-                        n++;
+                    oc_connected = true;
+                    ic_connected[ic] = true;
+                }
+            }
 
 
-                if (n > 0)
-                    for (ic = 0; ic < r->i_ss.channels; ic++)
-                        if (on_left(r->i_cm.map[ic])) {
-                            r->map_table[oc][ic] = 1.0f / (float) n;
-                            ic_connected[ic] = TRUE;
-                        }
+            if (!oc_connected) {
+                /* Try to find matching input ports for this output port */
 
 
-                /* We ignore the case where there is no left input
-                 * channel. Something is really wrong in this case
-                 * anyway. */
+                if (on_left(b)) {
 
 
-            } else if (on_right(b)) {
-                unsigned n = 0;
+                    /* We are not connected and on the left side, let's
+                     * average all left side input channels. */
 
 
-                /* We are not connected and on the right side, let's
-                 * average all right side input channels. */
+                    if (ic_left > 0)
+                        for (ic = 0; ic < n_ic; ic++)
+                            if (on_left(r->i_cm.map[ic])) {
+                                m->map_table_f[oc][ic] = 1.0f / (float) ic_left;
+                                ic_connected[ic] = true;
+                            }
 
 
-                for (ic = 0; ic < r->i_ss.channels; ic++)
-                    if (on_right(r->i_cm.map[ic]))
-                        n++;
+                    /* We ignore the case where there is no left input channel.
+                     * Something is really wrong in this case anyway. */
 
 
-                if (n > 0)
-                    for (ic = 0; ic < r->i_ss.channels; ic++)
-                        if (on_right(r->i_cm.map[ic])) {
-                            r->map_table[oc][ic] = 1.0f / (float) n;
-                            ic_connected[ic] = TRUE;
-                        }
+                } else if (on_right(b)) {
 
 
-                /* We ignore the case where there is no right input
-                 * channel. Something is really wrong in this case
-                 * anyway. */
+                    /* We are not connected and on the right side, let's
+                     * average all right side input channels. */
 
 
-            } else if (on_center(b)) {
-                unsigned n = 0;
+                    if (ic_right > 0)
+                        for (ic = 0; ic < n_ic; ic++)
+                            if (on_right(r->i_cm.map[ic])) {
+                                m->map_table_f[oc][ic] = 1.0f / (float) ic_right;
+                                ic_connected[ic] = true;
+                            }
+
+                    /* We ignore the case where there is no right input
+                     * channel. Something is really wrong in this case anyway.
+                     * */
 
 
-                /* We are not connected and at the center. Let's
-                 * average all center input channels. */
+                } else if (on_center(b)) {
 
 
-                for (ic = 0; ic < r->i_ss.channels; ic++)
-                    if (on_center(r->i_cm.map[ic]))
-                        n++;
+                    if (ic_center > 0) {
 
 
-                if (n > 0) {
-                    for (ic = 0; ic < r->i_ss.channels; ic++)
-                        if (on_center(r->i_cm.map[ic])) {
-                            r->map_table[oc][ic] = 1.0f / (float) n;
-                            ic_connected[ic] = TRUE;
-                        }
-                } else {
+                        /* We are not connected and at the center. Let's average
+                         * all center input channels. */
 
 
-                    /* Hmm, no center channel around, let's synthesize
-                     * it by mixing L and R.*/
+                        for (ic = 0; ic < n_ic; ic++)
+                            if (on_center(r->i_cm.map[ic])) {
+                                m->map_table_f[oc][ic] = 1.0f / (float) ic_center;
+                                ic_connected[ic] = true;
+                            }
 
 
-                    n = 0;
+                    } else if (ic_left + ic_right > 0) {
 
 
-                    for (ic = 0; ic < r->i_ss.channels; ic++)
-                        if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic]))
-                            n++;
+                        /* Hmm, no center channel around, let's synthesize it
+                         * by mixing L and R.*/
 
 
-                    if (n > 0)
-                        for (ic = 0; ic < r->i_ss.channels; ic++)
+                        for (ic = 0; ic < n_ic; ic++)
                             if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic])) {
                             if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic])) {
-                                r->map_table[oc][ic] = 1.0f / (float) n;
-                                ic_connected[ic] = TRUE;
+                                m->map_table_f[oc][ic] = 1.0f / (float) (ic_left + ic_right);
+                                ic_connected[ic] = true;
                             }
                             }
+                    }
 
 
-                    /* We ignore the case where there is not even a
-                     * left or right input channel. Something is
-                     * really wrong in this case anyway. */
-                }
-
-            } else if (on_lfe(b)) {
+                    /* We ignore the case where there is not even a left or
+                     * right input channel. Something is really wrong in this
+                     * case anyway. */
 
 
-                /* We are not connected and an LFE. Let's average all
-                 * channels for LFE. */
+                } else if (on_lfe(b) && !(r->flags & PA_RESAMPLER_NO_LFE)) {
 
 
-                for (ic = 0; ic < r->i_ss.channels; ic++) {
+                    /* We are not connected and an LFE. Let's average all
+                     * channels for LFE. */
 
 
-                    if (!(r->flags & PA_RESAMPLER_NO_LFE))
-                        r->map_table[oc][ic] = 1.0f / (float) r->i_ss.channels;
-                    else
-                        r->map_table[oc][ic] = 0;
+                    for (ic = 0; ic < n_ic; ic++)
+                        m->map_table_f[oc][ic] = 1.0f / (float) n_ic;
 
 
-                    /* Please note that a channel connected to LFE
-                     * doesn't really count as connected. */
+                    /* Please note that a channel connected to LFE doesn't
+                     * really count as connected. */
                 }
             }
         }
                 }
             }
         }
-    }
-
-    if (remix) {
-        unsigned
-            ic_unconnected_left = 0,
-            ic_unconnected_right = 0,
-            ic_unconnected_center = 0,
-            ic_unconnected_lfe = 0;
 
 
-        for (ic = 0; ic < r->i_ss.channels; ic++) {
+        for (ic = 0; ic < n_ic; ic++) {
             pa_channel_position_t a = r->i_cm.map[ic];
 
             if (ic_connected[ic])
             pa_channel_position_t a = r->i_cm.map[ic];
 
             if (ic_connected[ic])
@@ -820,396 +1046,305 @@ static void calc_map_table(pa_resampler *r) {
                 ic_unconnected_lfe++;
         }
 
                 ic_unconnected_lfe++;
         }
 
-        if (ic_unconnected_left > 0) {
+        for (ic = 0; ic < n_ic; ic++) {
+            pa_channel_position_t a = r->i_cm.map[ic];
 
 
-            /* OK, so there are unconnected input channels on the
-             * left. Let's multiply all already connected channels on
-             * the left side by .9 and add in our averaged unconnected
-             * channels multplied by .1 */
+            if (ic_connected[ic])
+                continue;
 
 
-            for (oc = 0; oc < r->o_ss.channels; oc++) {
+            for (oc = 0; oc < n_oc; oc++) {
+                pa_channel_position_t b = r->o_cm.map[oc];
 
 
-                if (!on_left(r->o_cm.map[oc]))
-                    continue;
+                if (on_left(a) && on_left(b))
+                    m->map_table_f[oc][ic] = (1.f/9.f) / (float) ic_unconnected_left;
 
 
-                for (ic = 0; ic < r->i_ss.channels; ic++) {
+                else if (on_right(a) && on_right(b))
+                    m->map_table_f[oc][ic] = (1.f/9.f) / (float) ic_unconnected_right;
 
 
-                    if (ic_connected[ic]) {
-                        r->map_table[oc][ic] *= .9f;
-                        continue;
-                    }
+                else if (on_center(a) && on_center(b)) {
+                    m->map_table_f[oc][ic] = (1.f/9.f) / (float) ic_unconnected_center;
+                    ic_unconnected_center_mixed_in = true;
 
 
-                    if (on_left(r->i_cm.map[ic]))
-                        r->map_table[oc][ic] = .1f / (float) ic_unconnected_left;
-                }
+                } else if (on_lfe(a) && !(r->flags & PA_RESAMPLER_NO_LFE))
+                    m->map_table_f[oc][ic] = .375f / (float) ic_unconnected_lfe;
             }
         }
 
             }
         }
 
-        if (ic_unconnected_right > 0) {
-
-            /* OK, so there are unconnected input channels on the
-             * right. Let's multiply all already connected channels on
-             * the right side by .9 and add in our averaged unconnected
-             * channels multplied by .1 */
+        if (ic_unconnected_center > 0 && !ic_unconnected_center_mixed_in) {
+            unsigned ncenter[PA_CHANNELS_MAX];
+            bool found_frs[PA_CHANNELS_MAX];
 
 
-            for (oc = 0; oc < r->o_ss.channels; oc++) {
+            memset(ncenter, 0, sizeof(ncenter));
+            memset(found_frs, 0, sizeof(found_frs));
 
 
-                if (!on_right(r->o_cm.map[oc]))
-                    continue;
-
-                for (ic = 0; ic < r->i_ss.channels; ic++) {
+            /* Hmm, as it appears there was no center channel we
+               could mix our center channel in. In this case, mix it into
+               left and right. Using .5 as the factor. */
 
 
-                    if (ic_connected[ic]) {
-                        r->map_table[oc][ic] *= .9f;
-                        continue;
-                    }
+            for (ic = 0; ic < n_ic; ic++) {
 
 
-                    if (on_right(r->i_cm.map[ic]))
-                        r->map_table[oc][ic] = .1f / (float) ic_unconnected_right;
-                }
-            }
-        }
-
-        if (ic_unconnected_center > 0) {
-            pa_bool_t mixed_in = FALSE;
-
-            /* OK, so there are unconnected input channels on the
-             * center. Let's multiply all already connected channels on
-             * the center side by .9 and add in our averaged unconnected
-             * channels multplied by .1 */
-
-            for (oc = 0; oc < r->o_ss.channels; oc++) {
+                if (ic_connected[ic])
+                    continue;
 
 
-                if (!on_center(r->o_cm.map[oc]))
+                if (!on_center(r->i_cm.map[ic]))
                     continue;
 
                     continue;
 
-                for (ic = 0; ic < r->i_ss.channels; ic++)  {
+                for (oc = 0; oc < n_oc; oc++) {
 
 
-                    if (ic_connected[ic]) {
-                        r->map_table[oc][ic] *= .9f;
+                    if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
                         continue;
                         continue;
-                    }
 
 
-                    if (on_center(r->i_cm.map[ic])) {
-                        r->map_table[oc][ic] = .1f / (float) ic_unconnected_center;
-                        mixed_in = TRUE;
+                    if (front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc])) {
+                        found_frs[ic] = true;
+                        break;
                     }
                 }
                     }
                 }
-            }
-
-            if (!mixed_in) {
-                unsigned ncenter[PA_CHANNELS_MAX];
-                pa_bool_t found_frs[PA_CHANNELS_MAX];
-
-                memset(ncenter, 0, sizeof(ncenter));
-                memset(found_frs, 0, sizeof(found_frs));
-
-                /* Hmm, as it appears there was no center channel we
-                   could mix our center channel in. In this case, mix
-                   it into left and right. Using .375 and 0.75 as
-                   factors. */
 
 
-                for (ic = 0; ic < r->i_ss.channels; ic++) {
+                for (oc = 0; oc < n_oc; oc++) {
 
 
-                    if (ic_connected[ic])
-                        continue;
-
-                    if (!on_center(r->i_cm.map[ic]))
+                    if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
                         continue;
 
                         continue;
 
-                    for (oc = 0; oc < r->o_ss.channels; oc++) {
-
-                        if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
-                            continue;
-
-                        if (front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc])) {
-                            found_frs[ic] = TRUE;
-                            break;
-                        }
-                    }
-
-                    for (oc = 0; oc < r->o_ss.channels; oc++) {
-
-                        if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
-                            continue;
-
-                        if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc]))
-                            ncenter[oc]++;
-                    }
+                    if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc]))
+                        ncenter[oc]++;
                 }
                 }
+            }
 
 
-                for (oc = 0; oc < r->o_ss.channels; oc++) {
-
-                    if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
-                        continue;
+            for (oc = 0; oc < n_oc; oc++) {
 
 
-                    if (ncenter[oc] <= 0)
-                        continue;
+                if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
+                    continue;
 
 
-                    for (ic = 0; ic < r->i_ss.channels; ic++)  {
+                if (ncenter[oc] <= 0)
+                    continue;
 
 
-                        if (ic_connected[ic]) {
-                            r->map_table[oc][ic] *= .75f;
-                            continue;
-                        }
+                for (ic = 0; ic < n_ic; ic++) {
 
 
-                        if (!on_center(r->i_cm.map[ic]))
-                            continue;
+                    if (!on_center(r->i_cm.map[ic]))
+                        continue;
 
 
-                        if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc]))
-                            r->map_table[oc][ic] = .375f / (float) ncenter[oc];
-                    }
+                    if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc]))
+                        m->map_table_f[oc][ic] = .5f / (float) ncenter[oc];
                 }
             }
         }
                 }
             }
         }
+    }
 
 
-        if (ic_unconnected_lfe > 0 && !(r->flags & PA_RESAMPLER_NO_LFE)) {
-
-            /* OK, so there is an unconnected LFE channel. Let's mix
-             * it into all channels, with factor 0.375 */
-
-            for (ic = 0; ic < r->i_ss.channels; ic++)  {
-
-                if (!on_lfe(r->i_cm.map[ic]))
-                    continue;
+    for (oc = 0; oc < n_oc; oc++) {
+        float sum = 0.0f;
+        for (ic = 0; ic < n_ic; ic++)
+            sum += m->map_table_f[oc][ic];
 
 
-                for (oc = 0; oc < r->o_ss.channels; oc++)
-                    r->map_table[oc][ic] = 0.375f / (float) ic_unconnected_lfe;
-            }
-        }
+        if (sum > 1.0f)
+            for (ic = 0; ic < n_ic; ic++)
+                m->map_table_f[oc][ic] /= sum;
     }
 
     }
 
+    /* make an 16:16 int version of the matrix */
+    for (oc = 0; oc < n_oc; oc++)
+        for (ic = 0; ic < n_ic; ic++)
+            m->map_table_i[oc][ic] = (int32_t) (m->map_table_f[oc][ic] * 0x10000);
 
     s = pa_strbuf_new();
 
     pa_strbuf_printf(s, "     ");
 
     s = pa_strbuf_new();
 
     pa_strbuf_printf(s, "     ");
-    for (ic = 0; ic < r->i_ss.channels; ic++)
+    for (ic = 0; ic < n_ic; ic++)
         pa_strbuf_printf(s, "  I%02u ", ic);
     pa_strbuf_puts(s, "\n    +");
 
         pa_strbuf_printf(s, "  I%02u ", ic);
     pa_strbuf_puts(s, "\n    +");
 
-    for (ic = 0; ic < r->i_ss.channels; ic++)
+    for (ic = 0; ic < n_ic; ic++)
         pa_strbuf_printf(s, "------");
     pa_strbuf_puts(s, "\n");
 
         pa_strbuf_printf(s, "------");
     pa_strbuf_puts(s, "\n");
 
-    for (oc = 0; oc < r->o_ss.channels; oc++) {
+    for (oc = 0; oc < n_oc; oc++) {
         pa_strbuf_printf(s, "O%02u |", oc);
 
         pa_strbuf_printf(s, "O%02u |", oc);
 
-        for (ic = 0; ic < r->i_ss.channels; ic++)
-            pa_strbuf_printf(s, " %1.3f", r->map_table[oc][ic]);
+        for (ic = 0; ic < n_ic; ic++)
+            pa_strbuf_printf(s, " %1.3f", m->map_table_f[oc][ic]);
 
         pa_strbuf_puts(s, "\n");
     }
 
     pa_log_debug("Channel matrix:\n%s", t = pa_strbuf_tostring_free(s));
     pa_xfree(t);
 
         pa_strbuf_puts(s, "\n");
     }
 
     pa_log_debug("Channel matrix:\n%s", t = pa_strbuf_tostring_free(s));
     pa_xfree(t);
-}
-
-static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) {
-    unsigned n_samples;
-    void *src, *dst;
-
-    pa_assert(r);
-    pa_assert(input);
-    pa_assert(input->memblock);
 
 
-    /* Convert the incoming sample into the work sample format and place them in buf1 */
+    /* initialize the remapping function */
+    pa_init_remap_func(m);
+}
 
 
-    if (!r->to_work_format_func || !input->length)
-        return input;
+static void free_remap(pa_remap_t *m) {
+    pa_assert(m);
 
 
-    n_samples = (unsigned) ((input->length / r->i_fz) * r->i_ss.channels);
+    pa_xfree(m->state);
+}
 
 
-    r->buf1.index = 0;
-    r->buf1.length = r->w_sz * n_samples;
+/* 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) {
+    pa_assert(size);
+
+    if (!buf->memblock || len > *size) {
+        pa_memblock *new_block = pa_memblock_new(r->mempool, len);
+
+        if (buf->memblock) {
+            if (copy > 0) {
+                void *src = pa_memblock_acquire(buf->memblock);
+                void *dst = pa_memblock_acquire(new_block);
+                pa_assert(copy <= len);
+                memcpy(dst, src, copy);
+                pa_memblock_release(new_block);
+                pa_memblock_release(buf->memblock);
+            }
 
 
-    if (!r->buf1.memblock || r->buf1_samples < n_samples) {
-        if (r->buf1.memblock)
-            pa_memblock_unref(r->buf1.memblock);
+            pa_memblock_unref(buf->memblock);
+        }
 
 
-        r->buf1_samples = n_samples;
-        r->buf1.memblock = pa_memblock_new(r->mempool, r->buf1.length);
+        buf->memblock = new_block;
+        *size = len;
     }
 
     }
 
-    src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index;
-    dst = (uint8_t*) pa_memblock_acquire(r->buf1.memblock);
+    buf->length = len;
+}
 
 
-    r->to_work_format_func(n_samples, src, dst);
+static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) {
+    unsigned in_n_samples, out_n_samples;
+    void *src, *dst;
+    bool have_leftover;
+    size_t leftover_length = 0;
 
 
-    pa_memblock_release(input->memblock);
-    pa_memblock_release(r->buf1.memblock);
+    pa_assert(r);
+    pa_assert(input);
+    pa_assert(input->memblock);
 
 
-    return &r->buf1;
-}
+    /* Convert the incoming sample into the work sample format and place them
+     * in to_work_format_buf. The leftover data is already converted, so it's
+     * part of the output buffer. */
 
 
-static void vectoradd_s16_with_fraction(
-        int16_t *d, int dstr,
-        const int16_t *s1, int sstr1,
-        const int16_t *s2, int sstr2,
-        int n,
-        float s3, float s4) {
+    have_leftover = r->leftover_in_to_work;
+    r->leftover_in_to_work = false;
 
 
-    int32_t i3, i4;
+    if (!have_leftover && (!r->to_work_format_func || !input->length))
+        return input;
+    else if (input->length <= 0)
+        return &r->to_work_format_buf;
 
 
-    i3 = (int32_t) (s3 * 0x10000);
-    i4 = (int32_t) (s4 * 0x10000);
+    in_n_samples = out_n_samples = (unsigned) ((input->length / r->i_fz) * r->i_ss.channels);
 
 
-    for (; n > 0; n--) {
-        int32_t a, b;
+    if (have_leftover) {
+        leftover_length = r->to_work_format_buf.length;
+        out_n_samples += (unsigned) (leftover_length / r->w_sz);
+    }
 
 
-        a = *s1;
-        b = *s2;
+    fit_buf(r, &r->to_work_format_buf, r->w_sz * out_n_samples, &r->to_work_format_buf_size, leftover_length);
 
 
-        a = (a * i3) / 0x10000;
-        b = (b * i4) / 0x10000;
+    src = pa_memblock_acquire_chunk(input);
+    dst = (uint8_t *) pa_memblock_acquire(r->to_work_format_buf.memblock) + leftover_length;
 
 
-        *d = (int16_t) (a + b);
+    if (r->to_work_format_func)
+        r->to_work_format_func(in_n_samples, src, dst);
+    else
+        memcpy(dst, src, input->length);
 
 
-        s1 = (const int16_t*) ((const uint8_t*) s1 + sstr1);
-        s2 = (const int16_t*) ((const uint8_t*) s2 + sstr2);
-        d = (int16_t*) ((uint8_t*) d + dstr);
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(r->to_work_format_buf.memblock);
 
 
-    }
+    return &r->to_work_format_buf;
 }
 
 static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
 }
 
 static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) {
-    unsigned in_n_samples, out_n_samples, n_frames;
-    int i_skip, o_skip;
-    unsigned oc;
+    unsigned in_n_samples, out_n_samples, in_n_frames, out_n_frames;
     void *src, *dst;
     void *src, *dst;
+    size_t leftover_length = 0;
+    bool have_leftover;
 
     pa_assert(r);
     pa_assert(input);
     pa_assert(input->memblock);
 
 
     pa_assert(r);
     pa_assert(input);
     pa_assert(input->memblock);
 
-    /* Remap channels and place the result int buf2 */
+    /* Remap channels and place the result in remap_buf. There may be leftover
+     * data in the beginning of remap_buf. The leftover data is already
+     * remapped, so it's not part of the input, it's part of the output. */
+
+    have_leftover = r->leftover_in_remap;
+    r->leftover_in_remap = false;
 
 
-    if (!r->map_required || !input->length)
+    if (!have_leftover && (!r->map_required || input->length <= 0))
         return input;
         return input;
+    else if (input->length <= 0)
+        return &r->remap_buf;
 
     in_n_samples = (unsigned) (input->length / r->w_sz);
 
     in_n_samples = (unsigned) (input->length / r->w_sz);
-    n_frames = in_n_samples / r->i_ss.channels;
-    out_n_samples = n_frames * r->o_ss.channels;
-
-    r->buf2.index = 0;
-    r->buf2.length = r->w_sz * out_n_samples;
+    in_n_frames = out_n_frames = in_n_samples / r->i_ss.channels;
 
 
-    if (!r->buf2.memblock || r->buf2_samples < out_n_samples) {
-        if (r->buf2.memblock)
-            pa_memblock_unref(r->buf2.memblock);
-
-        r->buf2_samples = out_n_samples;
-        r->buf2.memblock = pa_memblock_new(r->mempool, r->buf2.length);
+    if (have_leftover) {
+        leftover_length = r->remap_buf.length;
+        out_n_frames += leftover_length / r->w_fz;
     }
 
     }
 
-    src = ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
-    dst = pa_memblock_acquire(r->buf2.memblock);
-
-    memset(dst, 0, r->buf2.length);
-
-    o_skip = (int) (r->w_sz * r->o_ss.channels);
-    i_skip = (int) (r->w_sz * r->i_ss.channels);
-
-    switch (r->work_format) {
-        case PA_SAMPLE_FLOAT32NE:
-
-            for (oc = 0; oc < r->o_ss.channels; oc++) {
-                unsigned ic;
-                static const float one = 1.0;
-
-                for (ic = 0; ic < r->i_ss.channels; ic++) {
-
-                    if (r->map_table[oc][ic] <= 0.0)
-                        continue;
-
-                    oil_vectoradd_f32(
-                            (float*) dst + oc, o_skip,
-                            (float*) dst + oc, o_skip,
-                            (float*) src + ic, i_skip,
-                            (int) n_frames,
-                            &one, &r->map_table[oc][ic]);
-                }
-            }
-
-            break;
-
-        case PA_SAMPLE_S16NE:
-
-            for (oc = 0; oc < r->o_ss.channels; oc++) {
-                unsigned ic;
+    out_n_samples = out_n_frames * r->o_ss.channels;
+    fit_buf(r, &r->remap_buf, out_n_samples * r->w_sz, &r->remap_buf_size, leftover_length);
 
 
-                for (ic = 0; ic < r->i_ss.channels; ic++) {
+    src = pa_memblock_acquire_chunk(input);
+    dst = (uint8_t *) pa_memblock_acquire(r->remap_buf.memblock) + leftover_length;
 
 
-                    if (r->map_table[oc][ic] <= 0.0)
-                        continue;
+    if (r->map_required) {
+        pa_remap_t *remap = &r->remap;
 
 
-                    if (r->map_table[oc][ic] >= 1.0) {
-                        static const int16_t one = 1;
+        pa_assert(remap->do_remap);
+        remap->do_remap(remap, dst, src, in_n_frames);
 
 
-                        oil_vectoradd_s16(
-                                (int16_t*) dst + oc, o_skip,
-                                (int16_t*) dst + oc, o_skip,
-                                (int16_t*) src + ic, i_skip,
-                                (int) n_frames,
-                                &one, &one);
+    } else
+        memcpy(dst, src, input->length);
 
 
-                    } else
+    pa_memblock_release(input->memblock);
+    pa_memblock_release(r->remap_buf.memblock);
 
 
-                        vectoradd_s16_with_fraction(
-                                (int16_t*) dst + oc, o_skip,
-                                (int16_t*) dst + oc, o_skip,
-                                (int16_t*) src + ic, i_skip,
-                                (int) n_frames,
-                                1.0f, r->map_table[oc][ic]);
-                }
-            }
+    return &r->remap_buf;
+}
 
 
-            break;
+static void save_leftover(pa_resampler *r, void *buf, size_t len) {
+    void *dst;
 
 
-        default:
-            pa_assert_not_reached();
-    }
-
-    pa_memblock_release(input->memblock);
-    pa_memblock_release(r->buf2.memblock);
+    pa_assert(r);
+    pa_assert(buf);
+    pa_assert(len > 0);
 
 
-    r->buf2.length = out_n_samples * r->w_sz;
+    /* Store the leftover data. */
+    fit_buf(r, r->leftover_buf, len, r->leftover_buf_size, 0);
+    *r->have_leftover = true;
 
 
-    return &r->buf2;
+    dst = pa_memblock_acquire(r->leftover_buf->memblock);
+    memmove(dst, buf, len);
+    pa_memblock_release(r->leftover_buf->memblock);
 }
 
 static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) {
 }
 
 static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) {
-    unsigned in_n_frames, in_n_samples;
-    unsigned out_n_frames, out_n_samples;
+    unsigned in_n_frames, out_n_frames, leftover_n_frames;
 
     pa_assert(r);
     pa_assert(input);
 
 
     pa_assert(r);
     pa_assert(input);
 
-    /* Resample the data and place the result in buf3 */
+    /* Resample the data and place the result in resample_buf. */
 
 
-    if (!r->impl_resample || !input->length)
+    if (!r->impl.resample || !input->length)
         return input;
 
         return input;
 
-    in_n_samples = (unsigned) (input->length / r->w_sz);
-    in_n_frames = (unsigned) (in_n_samples / r->o_ss.channels);
+    in_n_frames = (unsigned) (input->length / r->w_fz);
 
     out_n_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_FRAMES;
 
     out_n_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_FRAMES;
-    out_n_samples = out_n_frames * r->o_ss.channels;
+    fit_buf(r, &r->resample_buf, r->w_fz * out_n_frames, &r->resample_buf_size, 0);
 
 
-    r->buf3.index = 0;
-    r->buf3.length = r->w_sz * out_n_samples;
+    leftover_n_frames = r->impl.resample(r, input, in_n_frames, &r->resample_buf, &out_n_frames);
 
 
-    if (!r->buf3.memblock || r->buf3_samples < out_n_samples) {
-        if (r->buf3.memblock)
-            pa_memblock_unref(r->buf3.memblock);
-
-        r->buf3_samples = out_n_samples;
-        r->buf3.memblock = pa_memblock_new(r->mempool, r->buf3.length);
+    if (leftover_n_frames > 0) {
+        void *leftover_data = (uint8_t *) pa_memblock_acquire_chunk(input) + (in_n_frames - leftover_n_frames) * r->w_fz;
+        save_leftover(r, leftover_data, leftover_n_frames * r->w_fz);
+        pa_memblock_release(input->memblock);
     }
 
     }
 
-    r->impl_resample(r, input, in_n_frames, &r->buf3, &out_n_frames);
-    r->buf3.length = out_n_frames * r->w_sz * r->o_ss.channels;
+    r->resample_buf.length = out_n_frames * r->w_fz;
 
 
-    return &r->buf3;
+    return &r->resample_buf;
 }
 
 static pa_memchunk *convert_from_work_format(pa_resampler *r, pa_memchunk *input) {
 }
 
 static pa_memchunk *convert_from_work_format(pa_resampler *r, pa_memchunk *input) {
@@ -1219,34 +1354,23 @@ static pa_memchunk *convert_from_work_format(pa_resampler *r, pa_memchunk *input
     pa_assert(r);
     pa_assert(input);
 
     pa_assert(r);
     pa_assert(input);
 
-    /* Convert the data into the correct sample type and place the result in buf4 */
+    /* Convert the data into the correct sample type and place the result in
+     * from_work_format_buf. */
 
     if (!r->from_work_format_func || !input->length)
         return input;
 
     n_samples = (unsigned) (input->length / r->w_sz);
     n_frames = n_samples / r->o_ss.channels;
 
     if (!r->from_work_format_func || !input->length)
         return input;
 
     n_samples = (unsigned) (input->length / r->w_sz);
     n_frames = n_samples / r->o_ss.channels;
+    fit_buf(r, &r->from_work_format_buf, r->o_fz * n_frames, &r->from_work_format_buf_size, 0);
 
 
-    r->buf4.index = 0;
-    r->buf4.length = r->o_fz * n_frames;
-
-    if (!r->buf4.memblock || r->buf4_samples < n_samples) {
-        if (r->buf4.memblock)
-            pa_memblock_unref(r->buf4.memblock);
-
-        r->buf4_samples = n_samples;
-        r->buf4.memblock = pa_memblock_new(r->mempool, r->buf4.length);
-    }
-
-    src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index;
-    dst = pa_memblock_acquire(r->buf4.memblock);
+    src = pa_memblock_acquire_chunk(input);
+    dst = pa_memblock_acquire(r->from_work_format_buf.memblock);
     r->from_work_format_func(n_samples, src, dst);
     pa_memblock_release(input->memblock);
     r->from_work_format_func(n_samples, src, dst);
     pa_memblock_release(input->memblock);
-    pa_memblock_release(r->buf4.memblock);
-
-    r->buf4.length = r->o_fz * n_frames;
+    pa_memblock_release(r->from_work_format_buf.memblock);
 
 
-    return &r->buf4;
+    return &r->from_work_format_buf;
 }
 
 void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
 }
 
 void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) {
@@ -1261,8 +1385,16 @@ void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out)
 
     buf = (pa_memchunk*) in;
     buf = convert_to_work_format(r, buf);
 
     buf = (pa_memchunk*) in;
     buf = convert_to_work_format(r, buf);
-    buf = remap_channels(r, buf);
-    buf = resample(r, buf);
+
+    /* Try to save resampling effort: if we have more output channels than
+     * input channels, do resampling first, then remapping. */
+    if (r->o_ss.channels <= r->i_ss.channels) {
+        buf = remap_channels(r, buf);
+        buf = resample(r, buf);
+    } else {
+        buf = resample(r, buf);
+        buf = remap_channels(r, buf);
+    }
 
     if (buf->length) {
         buf = convert_from_work_format(r, buf);
 
     if (buf->length) {
         buf = convert_from_work_format(r, buf);
@@ -1279,65 +1411,76 @@ void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out)
 /*** libsamplerate based implementation ***/
 
 #ifdef HAVE_LIBSAMPLERATE
 /*** libsamplerate based implementation ***/
 
 #ifdef HAVE_LIBSAMPLERATE
-static void libsamplerate_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+static unsigned libsamplerate_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
     SRC_DATA data;
     SRC_DATA data;
+    SRC_STATE *state;
 
     pa_assert(r);
     pa_assert(input);
     pa_assert(output);
     pa_assert(out_n_frames);
 
 
     pa_assert(r);
     pa_assert(input);
     pa_assert(output);
     pa_assert(out_n_frames);
 
+    state = r->impl.data;
     memset(&data, 0, sizeof(data));
 
     memset(&data, 0, sizeof(data));
 
-    data.data_in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
+    data.data_in = pa_memblock_acquire_chunk(input);
     data.input_frames = (long int) in_n_frames;
 
     data.input_frames = (long int) in_n_frames;
 
-    data.data_out = (float*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index);
+    data.data_out = pa_memblock_acquire_chunk(output);
     data.output_frames = (long int) *out_n_frames;
 
     data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate;
     data.end_of_input = 0;
 
     data.output_frames = (long int) *out_n_frames;
 
     data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate;
     data.end_of_input = 0;
 
-    pa_assert_se(src_process(r->src.state, &data) == 0);
-    pa_assert((unsigned) data.input_frames_used == in_n_frames);
+    pa_assert_se(src_process(state, &data) == 0);
 
     pa_memblock_release(input->memblock);
     pa_memblock_release(output->memblock);
 
     *out_n_frames = (unsigned) data.output_frames_gen;
 
     pa_memblock_release(input->memblock);
     pa_memblock_release(output->memblock);
 
     *out_n_frames = (unsigned) data.output_frames_gen;
+
+    return in_n_frames - data.input_frames_used;
 }
 
 static void libsamplerate_update_rates(pa_resampler *r) {
 }
 
 static void libsamplerate_update_rates(pa_resampler *r) {
+    SRC_STATE *state;
     pa_assert(r);
 
     pa_assert(r);
 
-    pa_assert_se(src_set_ratio(r->src.state, (double) r->o_ss.rate / r->i_ss.rate) == 0);
+    state = r->impl.data;
+    pa_assert_se(src_set_ratio(state, (double) r->o_ss.rate / r->i_ss.rate) == 0);
 }
 
 static void libsamplerate_reset(pa_resampler *r) {
 }
 
 static void libsamplerate_reset(pa_resampler *r) {
+    SRC_STATE *state;
     pa_assert(r);
 
     pa_assert(r);
 
-    pa_assert_se(src_reset(r->src.state) == 0);
+    state = r->impl.data;
+    pa_assert_se(src_reset(state) == 0);
 }
 
 static void libsamplerate_free(pa_resampler *r) {
 }
 
 static void libsamplerate_free(pa_resampler *r) {
+    SRC_STATE *state;
     pa_assert(r);
 
     pa_assert(r);
 
-    if (r->src.state)
-        src_delete(r->src.state);
+    state = r->impl.data;
+    if (state)
+        src_delete(state);
 }
 
 static int libsamplerate_init(pa_resampler *r) {
     int err;
 }
 
 static int libsamplerate_init(pa_resampler *r) {
     int err;
+    SRC_STATE *state;
 
     pa_assert(r);
 
 
     pa_assert(r);
 
-    if (!(r->src.state = src_new(r->method, r->o_ss.channels, &err)))
+    if (!(state = src_new(r->method, r->work_channels, &err)))
         return -1;
 
         return -1;
 
-    r->impl_free = libsamplerate_free;
-    r->impl_update_rates = libsamplerate_update_rates;
-    r->impl_resample = libsamplerate_resample;
-    r->impl_reset = libsamplerate_reset;
+    r->impl.free = libsamplerate_free;
+    r->impl.update_rates = libsamplerate_update_rates;
+    r->impl.resample = libsamplerate_resample;
+    r->impl.reset = libsamplerate_reset;
+    r->impl.data = state;
 
     return 0;
 }
 
     return 0;
 }
@@ -1345,128 +1488,186 @@ static int libsamplerate_init(pa_resampler *r) {
 
 /*** speex based implementation ***/
 
 
 /*** speex based implementation ***/
 
-static void speex_resample_float(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+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;
     float *in, *out;
     uint32_t inf = in_n_frames, outf = *out_n_frames;
+    SpeexResamplerState *state;
 
     pa_assert(r);
     pa_assert(input);
     pa_assert(output);
     pa_assert(out_n_frames);
 
 
     pa_assert(r);
     pa_assert(input);
     pa_assert(output);
     pa_assert(out_n_frames);
 
-    in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
-    out = (float*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index);
+    state = r->impl.data;
+
+    in = pa_memblock_acquire_chunk(input);
+    out = pa_memblock_acquire_chunk(output);
 
 
-    pa_assert_se(speex_resampler_process_interleaved_float(r->speex.state, in, &inf, out, &outf) == 0);
+    /* 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);
     pa_memblock_release(output->memblock);
 
     pa_assert(inf == in_n_frames);
     *out_n_frames = outf;
 
     pa_memblock_release(input->memblock);
     pa_memblock_release(output->memblock);
 
     pa_assert(inf == in_n_frames);
     *out_n_frames = outf;
+
+    return 0;
 }
 
 }
 
-static void speex_resample_int(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+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;
     int16_t *in, *out;
     uint32_t inf = in_n_frames, outf = *out_n_frames;
+    SpeexResamplerState *state;
 
     pa_assert(r);
     pa_assert(input);
     pa_assert(output);
     pa_assert(out_n_frames);
 
 
     pa_assert(r);
     pa_assert(input);
     pa_assert(output);
     pa_assert(out_n_frames);
 
-    in = (int16_t*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index);
-    out = (int16_t*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index);
+    state = r->impl.data;
+
+    in = pa_memblock_acquire_chunk(input);
+    out = pa_memblock_acquire_chunk(output);
 
 
-    pa_assert_se(speex_resampler_process_interleaved_int(r->speex.state, in, &inf, out, &outf) == 0);
+    pa_assert_se(speex_resampler_process_interleaved_int(state, in, &inf, out, &outf) == 0);
 
     pa_memblock_release(input->memblock);
     pa_memblock_release(output->memblock);
 
     pa_assert(inf == in_n_frames);
     *out_n_frames = outf;
 
     pa_memblock_release(input->memblock);
     pa_memblock_release(output->memblock);
 
     pa_assert(inf == in_n_frames);
     *out_n_frames = outf;
+
+    return 0;
 }
 
 static void speex_update_rates(pa_resampler *r) {
 }
 
 static void speex_update_rates(pa_resampler *r) {
+    SpeexResamplerState *state;
     pa_assert(r);
 
     pa_assert(r);
 
-    pa_assert_se(speex_resampler_set_rate(r->speex.state, r->i_ss.rate, r->o_ss.rate) == 0);
+    state = r->impl.data;
+
+    pa_assert_se(speex_resampler_set_rate(state, r->i_ss.rate, r->o_ss.rate) == 0);
 }
 
 static void speex_reset(pa_resampler *r) {
 }
 
 static void speex_reset(pa_resampler *r) {
+    SpeexResamplerState *state;
     pa_assert(r);
 
     pa_assert(r);
 
-    pa_assert_se(speex_resampler_reset_mem(r->speex.state) == 0);
+    state = r->impl.data;
+
+    pa_assert_se(speex_resampler_reset_mem(state) == 0);
 }
 
 static void speex_free(pa_resampler *r) {
 }
 
 static void speex_free(pa_resampler *r) {
+    SpeexResamplerState *state;
     pa_assert(r);
 
     pa_assert(r);
 
-    if (!r->speex.state)
+    state = r->impl.data;
+    if (!state)
         return;
 
         return;
 
-    speex_resampler_destroy(r->speex.state);
+    speex_resampler_destroy(state);
 }
 
 static int speex_init(pa_resampler *r) {
     int q, err;
 }
 
 static int speex_init(pa_resampler *r) {
     int q, err;
+    SpeexResamplerState *state;
 
     pa_assert(r);
 
 
     pa_assert(r);
 
-    r->impl_free = speex_free;
-    r->impl_update_rates = speex_update_rates;
-    r->impl_reset = speex_reset;
+    r->impl.free = speex_free;
+    r->impl.update_rates = speex_update_rates;
+    r->impl.reset = speex_reset;
 
     if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX) {
 
         q = r->method - PA_RESAMPLER_SPEEX_FIXED_BASE;
 
     if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX) {
 
         q = r->method - PA_RESAMPLER_SPEEX_FIXED_BASE;
-        r->impl_resample = speex_resample_int;
+        r->impl.resample = speex_resample_int;
 
     } else {
         pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
 
         q = r->method - PA_RESAMPLER_SPEEX_FLOAT_BASE;
 
     } else {
         pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
 
         q = r->method - PA_RESAMPLER_SPEEX_FLOAT_BASE;
-        r->impl_resample = speex_resample_float;
+        r->impl.resample = speex_resample_float;
     }
 
     pa_log_info("Choosing speex quality setting %i.", q);
 
     }
 
     pa_log_info("Choosing speex quality setting %i.", q);
 
-    if (!(r->speex.state = speex_resampler_init(r->o_ss.channels, r->i_ss.rate, r->o_ss.rate, q, &err)))
+    if (!(state = speex_resampler_init(r->work_channels, r->i_ss.rate, r->o_ss.rate, q, &err)))
         return -1;
 
         return -1;
 
+    r->impl.data = state;
+
     return 0;
 }
     return 0;
 }
+#endif
 
 /* Trivial implementation */
 
 
 /* Trivial implementation */
 
-static void trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
-    size_t fz;
-    unsigned o_index;
+static unsigned trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+    unsigned i_index, o_index;
     void *src, *dst;
     void *src, *dst;
+    struct trivial_data *trivial_data;
 
     pa_assert(r);
     pa_assert(input);
     pa_assert(output);
     pa_assert(out_n_frames);
 
 
     pa_assert(r);
     pa_assert(input);
     pa_assert(output);
     pa_assert(out_n_frames);
 
-    fz = r->w_sz * r->o_ss.channels;
+    trivial_data = r->impl.data;
 
 
-    src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index;
-    dst = (uint8_t*) pa_memblock_acquire(output->memblock) + output->index;
+    src = pa_memblock_acquire_chunk(input);
+    dst = pa_memblock_acquire_chunk(output);
 
 
-    for (o_index = 0;; o_index++, r->trivial.o_counter++) {
-        unsigned j;
+    for (o_index = 0;; o_index++, trivial_data->o_counter++) {
+        i_index = ((uint64_t) trivial_data->o_counter * r->i_ss.rate) / r->o_ss.rate;
+        i_index = i_index > trivial_data->i_counter ? i_index - trivial_data->i_counter : 0;
 
 
-        j = ((r->trivial.o_counter * r->i_ss.rate) / r->o_ss.rate);
-        j = j > r->trivial.i_counter ? j - r->trivial.i_counter : 0;
-
-        if (j >= in_n_frames)
+        if (i_index >= in_n_frames)
             break;
 
             break;
 
-        pa_assert(o_index * fz < pa_memblock_get_length(output->memblock));
+        pa_assert_fp(o_index * r->w_fz < pa_memblock_get_length(output->memblock));
 
 
-        oil_memcpy((uint8_t*) dst + fz * o_index,
-                   (uint8_t*) src + fz * j, (int) fz);
+        memcpy((uint8_t*) dst + r->w_fz * o_index, (uint8_t*) src + r->w_fz * i_index, (int) r->w_fz);
     }
 
     pa_memblock_release(input->memblock);
     }
 
     pa_memblock_release(input->memblock);
@@ -1474,115 +1675,125 @@ static void trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned
 
     *out_n_frames = o_index;
 
 
     *out_n_frames = o_index;
 
-    r->trivial.i_counter += in_n_frames;
+    trivial_data->i_counter += in_n_frames;
 
     /* Normalize counters */
 
     /* Normalize counters */
-    while (r->trivial.i_counter >= r->i_ss.rate) {
-        pa_assert(r->trivial.o_counter >= r->o_ss.rate);
+    while (trivial_data->i_counter >= r->i_ss.rate) {
+        pa_assert(trivial_data->o_counter >= r->o_ss.rate);
 
 
-        r->trivial.i_counter -= r->i_ss.rate;
-        r->trivial.o_counter -= r->o_ss.rate;
+        trivial_data->i_counter -= r->i_ss.rate;
+        trivial_data->o_counter -= r->o_ss.rate;
     }
     }
+
+    return 0;
 }
 
 static void trivial_update_rates_or_reset(pa_resampler *r) {
 }
 
 static void trivial_update_rates_or_reset(pa_resampler *r) {
+    struct trivial_data *trivial_data;
     pa_assert(r);
 
     pa_assert(r);
 
-    r->trivial.i_counter = 0;
-    r->trivial.o_counter = 0;
+    trivial_data = r->impl.data;
+
+    trivial_data->i_counter = 0;
+    trivial_data->o_counter = 0;
 }
 
 static int trivial_init(pa_resampler*r) {
 }
 
 static int trivial_init(pa_resampler*r) {
+    struct trivial_data *trivial_data;
     pa_assert(r);
 
     pa_assert(r);
 
-    r->trivial.o_counter = r->trivial.i_counter = 0;
+    trivial_data = pa_xnew0(struct trivial_data, 1);
 
 
-    r->impl_resample = trivial_resample;
-    r->impl_update_rates = trivial_update_rates_or_reset;
-    r->impl_reset = trivial_update_rates_or_reset;
+    r->impl.resample = trivial_resample;
+    r->impl.update_rates = trivial_update_rates_or_reset;
+    r->impl.reset = trivial_update_rates_or_reset;
+    r->impl.data = trivial_data;
 
     return 0;
 }
 
 /* Peak finder implementation */
 
 
     return 0;
 }
 
 /* Peak finder implementation */
 
-static void peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
-    size_t fz;
-    unsigned o_index;
+static unsigned peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+    unsigned c, o_index = 0;
+    unsigned i, i_end = 0;
     void *src, *dst;
     void *src, *dst;
-    unsigned start = 0;
+    struct peaks_data *peaks_data;
 
     pa_assert(r);
     pa_assert(input);
     pa_assert(output);
     pa_assert(out_n_frames);
 
 
     pa_assert(r);
     pa_assert(input);
     pa_assert(output);
     pa_assert(out_n_frames);
 
-    fz = r->w_sz * r->o_ss.channels;
+    peaks_data = r->impl.data;
+    src = pa_memblock_acquire_chunk(input);
+    dst = pa_memblock_acquire_chunk(output);
 
 
-    src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index;
-    dst = (uint8_t*) pa_memblock_acquire(output->memblock) + output->index;
+    i = ((uint64_t) peaks_data->o_counter * r->i_ss.rate) / r->o_ss.rate;
+    i = i > peaks_data->i_counter ? i - peaks_data->i_counter : 0;
 
 
-    for (o_index = 0;; o_index++, r->peaks.o_counter++) {
-        unsigned j;
+    while (i_end < in_n_frames) {
+        i_end = ((uint64_t) (peaks_data->o_counter + 1) * r->i_ss.rate) / r->o_ss.rate;
+        i_end = i_end > peaks_data->i_counter ? i_end - peaks_data->i_counter : 0;
 
 
-        j = ((r->peaks.o_counter * r->i_ss.rate) / r->o_ss.rate);
+        pa_assert_fp(o_index * r->w_fz < pa_memblock_get_length(output->memblock));
 
 
-        if (j > r->peaks.i_counter)
-            j -= r->peaks.i_counter;
-        else
-            j = 0;
+        /* 1ch float is treated separately, because that is the common case */
+        if (r->work_channels == 1 && r->work_format == PA_SAMPLE_FLOAT32NE) {
+            float *s = (float*) src + i;
+            float *d = (float*) dst + o_index;
 
 
-        pa_assert(o_index * fz < pa_memblock_get_length(output->memblock));
+            for (; i < i_end && i < in_n_frames; i++) {
+                float n = fabsf(*s++);
 
 
-        if (r->work_format == PA_SAMPLE_S16NE) {
-            unsigned i, c;
-            int16_t *s = (int16_t*) ((uint8_t*) src + fz * start);
-            int16_t *d = (int16_t*) ((uint8_t*) dst + fz * o_index);
-
-            for (i = start; i <= j && i < in_n_frames; i++)
+                if (n > peaks_data->max_f[0])
+                    peaks_data->max_f[0] = n;
+            }
 
 
-                for (c = 0; c < r->o_ss.channels; c++, s++) {
-                    int16_t n;
+            if (i == i_end) {
+                *d = peaks_data->max_f[0];
+                peaks_data->max_f[0] = 0;
+                o_index++, peaks_data->o_counter++;
+            }
+        } else if (r->work_format == PA_SAMPLE_S16NE) {
+            int16_t *s = (int16_t*) src + r->work_channels * i;
+            int16_t *d = (int16_t*) dst + r->work_channels * o_index;
 
 
-                    n = (int16_t) (*s < 0 ? -*s : *s);
+            for (; i < i_end && i < in_n_frames; i++)
+                for (c = 0; c < r->work_channels; c++) {
+                    int16_t n = abs(*s++);
 
 
-                    if (PA_UNLIKELY(n > r->peaks.max_i[c]))
-                        r->peaks.max_i[c] = n;
+                    if (n > peaks_data->max_i[c])
+                        peaks_data->max_i[c] = n;
                 }
 
                 }
 
-            if (i >= in_n_frames)
-                break;
-
-            for (c = 0; c < r->o_ss.channels; c++, d++) {
-                *d = r->peaks.max_i[c];
-                r->peaks.max_i[c] = 0;
+            if (i == i_end) {
+                for (c = 0; c < r->work_channels; c++, d++) {
+                    *d = peaks_data->max_i[c];
+                    peaks_data->max_i[c] = 0;
+                }
+                o_index++, peaks_data->o_counter++;
             }
             }
-
         } else {
         } else {
-            unsigned i, c;
-            float *s = (float*) ((uint8_t*) src + fz * start);
-            float *d = (float*) ((uint8_t*) dst + fz * o_index);
-
-            pa_assert(r->work_format == PA_SAMPLE_FLOAT32NE);
+            float *s = (float*) src + r->work_channels * i;
+            float *d = (float*) dst + r->work_channels * o_index;
 
 
-            for (i = start; i <= j && i < in_n_frames; i++)
-                for (c = 0; c < r->o_ss.channels; c++, s++) {
-                    float n = fabsf(*s);
+            for (; i < i_end && i < in_n_frames; i++)
+                for (c = 0; c < r->work_channels; c++) {
+                    float n = fabsf(*s++);
 
 
-                    if (n > r->peaks.max_f[c])
-                        r->peaks.max_f[c] = n;
+                    if (n > peaks_data->max_f[c])
+                        peaks_data->max_f[c] = n;
                 }
 
                 }
 
-            if (i >= in_n_frames)
-                break;
-
-            for (c = 0; c < r->o_ss.channels; c++, d++) {
-                *d = r->peaks.max_f[c];
-                r->peaks.max_f[c] = 0;
+            if (i == i_end) {
+                for (c = 0; c < r->work_channels; c++, d++) {
+                    *d = peaks_data->max_f[c];
+                    peaks_data->max_f[c] = 0;
+                }
+                o_index++, peaks_data->o_counter++;
             }
         }
             }
         }
-
-        start = j;
     }
 
     pa_memblock_release(input->memblock);
     }
 
     pa_memblock_release(input->memblock);
@@ -1590,110 +1801,103 @@ static void peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned i
 
     *out_n_frames = o_index;
 
 
     *out_n_frames = o_index;
 
-    r->peaks.i_counter += in_n_frames;
+    peaks_data->i_counter += in_n_frames;
 
     /* Normalize counters */
 
     /* Normalize counters */
-    while (r->peaks.i_counter >= r->i_ss.rate) {
-        pa_assert(r->peaks.o_counter >= r->o_ss.rate);
+    while (peaks_data->i_counter >= r->i_ss.rate) {
+        pa_assert(peaks_data->o_counter >= r->o_ss.rate);
 
 
-        r->peaks.i_counter -= r->i_ss.rate;
-        r->peaks.o_counter -= r->o_ss.rate;
+        peaks_data->i_counter -= r->i_ss.rate;
+        peaks_data->o_counter -= r->o_ss.rate;
     }
     }
+
+    return 0;
 }
 
 static void peaks_update_rates_or_reset(pa_resampler *r) {
 }
 
 static void peaks_update_rates_or_reset(pa_resampler *r) {
+    struct peaks_data *peaks_data;
     pa_assert(r);
 
     pa_assert(r);
 
-    r->peaks.i_counter = 0;
-    r->peaks.o_counter = 0;
+    peaks_data = r->impl.data;
+
+    peaks_data->i_counter = 0;
+    peaks_data->o_counter = 0;
 }
 
 static int peaks_init(pa_resampler*r) {
 }
 
 static int peaks_init(pa_resampler*r) {
+    struct peaks_data *peaks_data;
     pa_assert(r);
     pa_assert(r);
+    pa_assert(r->i_ss.rate >= r->o_ss.rate);
+    pa_assert(r->work_format == PA_SAMPLE_S16NE || r->work_format == PA_SAMPLE_FLOAT32NE);
 
 
-    r->peaks.o_counter = r->peaks.i_counter = 0;
-    memset(r->peaks.max_i, 0, sizeof(r->peaks.max_i));
-    memset(r->peaks.max_f, 0, sizeof(r->peaks.max_f));
+    peaks_data = pa_xnew0(struct peaks_data, 1);
 
 
-    r->impl_resample = peaks_resample;
-    r->impl_update_rates = peaks_update_rates_or_reset;
-    r->impl_reset = peaks_update_rates_or_reset;
+    r->impl.resample = peaks_resample;
+    r->impl.update_rates = peaks_update_rates_or_reset;
+    r->impl.reset = peaks_update_rates_or_reset;
+    r->impl.data = peaks_data;
 
     return 0;
 }
 
 /*** ffmpeg based implementation ***/
 
 
     return 0;
 }
 
 /*** ffmpeg based implementation ***/
 
-static void ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+static unsigned ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
     unsigned used_frames = 0, c;
     unsigned used_frames = 0, c;
+    int previous_consumed_frames = -1;
+    struct ffmpeg_data *ffmpeg_data;
 
     pa_assert(r);
     pa_assert(input);
     pa_assert(output);
     pa_assert(out_n_frames);
 
 
     pa_assert(r);
     pa_assert(input);
     pa_assert(output);
     pa_assert(out_n_frames);
 
-    for (c = 0; c < r->o_ss.channels; c++) {
+    ffmpeg_data = r->impl.data;
+
+    for (c = 0; c < r->work_channels; c++) {
         unsigned u;
         pa_memblock *b, *w;
         int16_t *p, *t, *k, *q, *s;
         int consumed_frames;
         unsigned u;
         pa_memblock *b, *w;
         int16_t *p, *t, *k, *q, *s;
         int consumed_frames;
-        unsigned in, l;
 
         /* Allocate a new block */
 
         /* Allocate a new block */
-        b = pa_memblock_new(r->mempool, r->ffmpeg.buf[c].length + in_n_frames * sizeof(int16_t));
+        b = pa_memblock_new(r->mempool, in_n_frames * sizeof(int16_t));
         p = pa_memblock_acquire(b);
 
         p = pa_memblock_acquire(b);
 
-        /* Copy the remaining data into it */
-        l = (unsigned) r->ffmpeg.buf[c].length;
-        if (r->ffmpeg.buf[c].memblock) {
-            t = (int16_t*) ((uint8_t*) pa_memblock_acquire(r->ffmpeg.buf[c].memblock) + r->ffmpeg.buf[c].index);
-            memcpy(p, t, l);
-            pa_memblock_release(r->ffmpeg.buf[c].memblock);
-            pa_memblock_unref(r->ffmpeg.buf[c].memblock);
-            pa_memchunk_reset(&r->ffmpeg.buf[c]);
-        }
-
-        /* Now append the new data, splitting up channels */
-        t = ((int16_t*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index)) + c;
-        k = (int16_t*) ((uint8_t*) p + l);
+        /* Now copy the input data, splitting up channels */
+        t = (int16_t*) pa_memblock_acquire_chunk(input) + c;
+        k = p;
         for (u = 0; u < in_n_frames; u++) {
             *k = *t;
         for (u = 0; u < in_n_frames; u++) {
             *k = *t;
-            t += r->o_ss.channels;
+            t += r->work_channels;
             k ++;
         }
         pa_memblock_release(input->memblock);
 
             k ++;
         }
         pa_memblock_release(input->memblock);
 
-        /* Calculate the resulting number of frames */
-        in = (unsigned) in_n_frames + l / (unsigned) sizeof(int16_t);
-
         /* Allocate buffer for the result */
         w = pa_memblock_new(r->mempool, *out_n_frames * sizeof(int16_t));
         q = pa_memblock_acquire(w);
 
         /* Now, resample */
         /* Allocate buffer for the result */
         w = pa_memblock_new(r->mempool, *out_n_frames * sizeof(int16_t));
         q = pa_memblock_acquire(w);
 
         /* Now, resample */
-        used_frames = (unsigned) av_resample(r->ffmpeg.state,
+        used_frames = (unsigned) av_resample(ffmpeg_data->state,
                                              q, p,
                                              &consumed_frames,
                                              q, p,
                                              &consumed_frames,
-                                             (int) in, (int) *out_n_frames,
-                                             c >= (unsigned) (r->o_ss.channels-1));
+                                             (int) in_n_frames, (int) *out_n_frames,
+                                             c >= (unsigned) (r->work_channels-1));
 
         pa_memblock_release(b);
 
         pa_memblock_release(b);
+        pa_memblock_unref(b);
 
 
-        /* Now store the remaining samples away */
-        pa_assert(consumed_frames <= (int) in);
-        if (consumed_frames < (int) in) {
-            r->ffmpeg.buf[c].memblock = b;
-            r->ffmpeg.buf[c].index = (size_t) consumed_frames * sizeof(int16_t);
-            r->ffmpeg.buf[c].length = (size_t) (in - (unsigned) consumed_frames) * sizeof(int16_t);
-        } else
-            pa_memblock_unref(b);
+        pa_assert(consumed_frames <= (int) in_n_frames);
+        pa_assert(previous_consumed_frames == -1 || consumed_frames == previous_consumed_frames);
+        previous_consumed_frames = consumed_frames;
 
         /* And place the results in the output buffer */
 
         /* And place the results in the output buffer */
-        s = (short*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index) + c;
+        s = (int16_t *) pa_memblock_acquire_chunk(output) + c;
         for (u = 0; u < used_frames; u++) {
             *s = *q;
             q++;
         for (u = 0; u < used_frames; u++) {
             *s = *q;
             q++;
-            s += r->o_ss.channels;
+            s += r->work_channels;
         }
         pa_memblock_release(output->memblock);
         pa_memblock_release(w);
         }
         pa_memblock_release(output->memblock);
         pa_memblock_release(w);
@@ -1701,39 +1905,38 @@ static void ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned
     }
 
     *out_n_frames = used_frames;
     }
 
     *out_n_frames = used_frames;
+
+    return in_n_frames - previous_consumed_frames;
 }
 
 static void ffmpeg_free(pa_resampler *r) {
 }
 
 static void ffmpeg_free(pa_resampler *r) {
-    unsigned c;
+    struct ffmpeg_data *ffmpeg_data;
 
     pa_assert(r);
 
 
     pa_assert(r);
 
-    if (r->ffmpeg.state)
-        av_resample_close(r->ffmpeg.state);
-
-    for (c = 0; c < PA_ELEMENTSOF(r->ffmpeg.buf); c++)
-        if (r->ffmpeg.buf[c].memblock)
-            pa_memblock_unref(r->ffmpeg.buf[c].memblock);
+    ffmpeg_data = r->impl.data;
+    if (ffmpeg_data->state)
+        av_resample_close(ffmpeg_data->state);
 }
 
 static int ffmpeg_init(pa_resampler *r) {
 }
 
 static int ffmpeg_init(pa_resampler *r) {
-    unsigned c;
+    struct ffmpeg_data *ffmpeg_data;
 
     pa_assert(r);
 
 
     pa_assert(r);
 
+    ffmpeg_data = pa_xnew(struct ffmpeg_data, 1);
+
     /* We could probably implement different quality levels by
      * adjusting the filter parameters here. However, ffmpeg
      * internally only uses these hardcoded values, so let's use them
      * here for now as well until ffmpeg makes this configurable. */
 
     /* We could probably implement different quality levels by
      * adjusting the filter parameters here. However, ffmpeg
      * internally only uses these hardcoded values, so let's use them
      * here for now as well until ffmpeg makes this configurable. */
 
-    if (!(r->ffmpeg.state = av_resample_init((int) r->o_ss.rate, (int) r->i_ss.rate, 16, 10, 0, 0.8)))
+    if (!(ffmpeg_data->state = av_resample_init((int) r->o_ss.rate, (int) r->i_ss.rate, 16, 10, 0, 0.8)))
         return -1;
 
         return -1;
 
-    r->impl_free = ffmpeg_free;
-    r->impl_resample = ffmpeg_resample;
-
-    for (c = 0; c < PA_ELEMENTSOF(r->ffmpeg.buf); c++)
-        pa_memchunk_reset(&r->ffmpeg.buf[c]);
+    r->impl.free = ffmpeg_free;
+    r->impl.resample = ffmpeg_resample;
+    r->impl.data = (void *) ffmpeg_data;
 
     return 0;
 }
 
     return 0;
 }