[pulseaudio-discuss] [SUMMARY PATCH] lfe-filter: The first three patches (import, adjust, cleanup)

David Henningsson david.henningsson at canonical.com
Tue Feb 17 04:29:25 PST 2015


This is the combined diff of the first three patches for easier review.

diff --git a/LICENSE b/LICENSE
index 226c4ce..6932317 100644
--- a/LICENSE
+++ b/LICENSE
@@ -29,6 +29,9 @@ considered too small and stable to be considered as an external library) use the
 more permissive MIT license. This include the device reservation DBus protocol
 and realtime kit implementations.
 
+A more permissive BSD-style license is used for LFE filters, see
+src/pulsecore/filter/LICENSE.WEBKIT for details.
+
 Additionally, a more permissive Sun license is used for code that performs
 u-law, A-law and linear PCM conversions.
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 67f8627..302c532 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -911,6 +911,9 @@ lib_LTLIBRARIES += libpulsecore- at PA_MAJORMINOR@.la
 
 # Pure core stuff
 libpulsecore_ at PA_MAJORMINOR@_la_SOURCES = \
+		pulsecore/filter/lfe-filter.c pulsecore/filter/lfe-filter.h \
+		pulsecore/filter/biquad.c pulsecore/filter/biquad.h \
+		pulsecore/filter/crossover.c pulsecore/filter/crossover.h \
 		pulsecore/asyncmsgq.c pulsecore/asyncmsgq.h \
 		pulsecore/asyncq.c pulsecore/asyncq.h \
 		pulsecore/auth-cookie.c pulsecore/auth-cookie.h \
diff --git a/src/pulsecore/filter/LICENSE.WEBKIT b/src/pulsecore/filter/LICENSE.WEBKIT
new file mode 100644
index 0000000..2f69d9f
--- /dev/null
+++ b/src/pulsecore/filter/LICENSE.WEBKIT
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/src/pulsecore/filter/biquad.c b/src/pulsecore/filter/biquad.c
new file mode 100644
index 0000000..a6121f4
--- /dev/null
+++ b/src/pulsecore/filter/biquad.c
@@ -0,0 +1,126 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Copyright (C) 2010 Google Inc. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE.WEBKIT file.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+
+#include <math.h>
+#include "biquad.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+static void set_coefficient(struct biquad *bq, double b0, double b1, double b2,
+			    double a0, double a1, double a2)
+{
+	double a0_inv = 1 / a0;
+	bq->b0 = b0 * a0_inv;
+	bq->b1 = b1 * a0_inv;
+	bq->b2 = b2 * a0_inv;
+	bq->a1 = a1 * a0_inv;
+	bq->a2 = a2 * a0_inv;
+}
+
+static void biquad_lowpass(struct biquad *bq, double cutoff, double resonance)
+{
+	/* Limit cutoff to 0 to 1. */
+	cutoff = PA_MIN(cutoff, 1.0);
+	cutoff = PA_MAX(0.0, cutoff);
+
+	if (cutoff >= 1.0) {
+		/* When cutoff is 1, the z-transform is 1. */
+		set_coefficient(bq, 1, 0, 0, 1, 0, 0);
+	} else if (cutoff > 0) {
+		/* Compute biquad coefficients for lowpass filter */
+		double r = PA_MAX(0.0, resonance); /* can't go negative */
+		double g = pow(10.0, 0.05 * r);
+		double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2);
+
+		double theta = M_PI * cutoff;
+		double sn = 0.5 * d * sin(theta);
+		double beta = 0.5 * (1 - sn) / (1 + sn);
+		double gamma = (0.5 + beta) * cos(theta);
+		double alpha = 0.25 * (0.5 + beta - gamma);
+
+		double b0 = 2 * alpha;
+		double b1 = 2 * 2 * alpha;
+		double b2 = 2 * alpha;
+		double a1 = 2 * -gamma;
+		double a2 = 2 * beta;
+
+		set_coefficient(bq, b0, b1, b2, 1, a1, a2);
+	} else {
+		/* When cutoff is zero, nothing gets through the filter, so set
+		 * coefficients up correctly.
+		 */
+		set_coefficient(bq, 0, 0, 0, 1, 0, 0);
+	}
+}
+
+static void biquad_highpass(struct biquad *bq, double cutoff, double resonance)
+{
+	/* Limit cutoff to 0 to 1. */
+	cutoff = PA_MIN(cutoff, 1.0);
+	cutoff = PA_MAX(0.0, cutoff);
+
+	if (cutoff >= 1.0) {
+		/* The z-transform is 0. */
+		set_coefficient(bq, 0, 0, 0, 1, 0, 0);
+	} else if (cutoff > 0) {
+		/* Compute biquad coefficients for highpass filter */
+		double r = PA_MAX(0.0, resonance); /* can't go negative */
+		double g = pow(10.0, 0.05 * r);
+		double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2);
+
+		double theta = M_PI * cutoff;
+		double sn = 0.5 * d * sin(theta);
+		double beta = 0.5 * (1 - sn) / (1 + sn);
+		double gamma = (0.5 + beta) * cos(theta);
+		double alpha = 0.25 * (0.5 + beta + gamma);
+
+		double b0 = 2 * alpha;
+		double b1 = 2 * -2 * alpha;
+		double b2 = 2 * alpha;
+		double a1 = 2 * -gamma;
+		double a2 = 2 * beta;
+
+		set_coefficient(bq, b0, b1, b2, 1, a1, a2);
+	} else {
+		/* When cutoff is zero, we need to be careful because the above
+		 * gives a quadratic divided by the same quadratic, with poles
+		 * and zeros on the unit circle in the same place. When cutoff
+		 * is zero, the z-transform is 1.
+		 */
+		set_coefficient(bq, 1, 0, 0, 1, 0, 0);
+	}
+}
+
+void biquad_set(struct biquad *bq, enum biquad_type type, double freq, double Q,
+		double gain)
+{
+
+	switch (type) {
+	case BQ_LOWPASS:
+		biquad_lowpass(bq, freq, Q);
+		break;
+	case BQ_HIGHPASS:
+		biquad_highpass(bq, freq, Q);
+		break;
+	case BQ_NONE:
+		/* Identity filter. */
+		set_coefficient(bq, 1, 0, 0, 1, 0, 0);
+		break;
+	}
+}
diff --git a/src/pulsecore/filter/biquad.h b/src/pulsecore/filter/biquad.h
new file mode 100644
index 0000000..4a84b61
--- /dev/null
+++ b/src/pulsecore/filter/biquad.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef BIQUAD_H_
+#define BIQUAD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The biquad filter parameters. The transfer function H(z) is (b0 + b1 * z^(-1)
+ * + b2 * z^(-2)) / (1 + a1 * z^(-1) + a2 * z^(-2)).  The previous two inputs
+ * are stored in x1 and x2, and the previous two outputs are stored in y1 and
+ * y2.
+ *
+ * We use double during the coefficients calculation for better accurary, but
+ * float is used during the actual filtering for faster computation.
+ */
+struct biquad {
+	float b0, b1, b2;
+	float a1, a2;
+};
+
+/* The type of the biquad filters */
+enum biquad_type {
+	BQ_NONE,
+	BQ_LOWPASS,
+	BQ_HIGHPASS,
+};
+
+/* Initialize a biquad filter parameters from its type and parameters.
+ * Args:
+ *    bq - The biquad filter we want to set.
+ *    type - The type of the biquad filter.
+ *    frequency - The value should be in the range [0, 1]. It is relative to
+ *        half of the sampling rate.
+ *    Q - Quality factor. See Web Audio API for details.
+ *    gain - The value is in dB. See Web Audio API for details.
+ */
+void biquad_set(struct biquad *bq, enum biquad_type type, double freq, double Q,
+		double gain);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* BIQUAD_H_ */
diff --git a/src/pulsecore/filter/crossover.c b/src/pulsecore/filter/crossover.c
new file mode 100644
index 0000000..c2a584b
--- /dev/null
+++ b/src/pulsecore/filter/crossover.c
@@ -0,0 +1,97 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/macro.h>
+
+#include "crossover.h"
+
+void lr4_set(struct lr4 *lr4, enum biquad_type type, float freq)
+{
+	biquad_set(&lr4->bq, type, freq, 0, 0);
+	lr4->x1 = 0;
+	lr4->x2 = 0;
+	lr4->y1 = 0;
+	lr4->y2 = 0;
+	lr4->z1 = 0;
+	lr4->z2 = 0;
+}
+
+void lr4_process_float32(struct lr4 *lr4, int samples, int channels, float *src, float *dest)
+{
+	float lx1 = lr4->x1;
+	float lx2 = lr4->x2;
+	float ly1 = lr4->y1;
+	float ly2 = lr4->y2;
+	float lz1 = lr4->z1;
+	float lz2 = lr4->z2;
+	float lb0 = lr4->bq.b0;
+	float lb1 = lr4->bq.b1;
+	float lb2 = lr4->bq.b2;
+	float la1 = lr4->bq.a1;
+	float la2 = lr4->bq.a2;
+
+	int i;
+	for (i = 0; i < samples; i += channels) {
+		float x, y, z;
+		x = src[i];
+		y = lb0*x + lb1*lx1 + lb2*lx2 - la1*ly1 - la2*ly2;
+		z = lb0*y + lb1*ly1 + lb2*ly2 - la1*lz1 - la2*lz2;
+		lx2 = lx1;
+		lx1 = x;
+		ly2 = ly1;
+		ly1 = y;
+		lz2 = lz1;
+		lz1 = z;
+		dest[i] = PA_CLAMP_UNLIKELY((int) z, -0x8000, 0x7fff);
+	}
+
+	lr4->x1 = lx1;
+	lr4->x2 = lx2;
+	lr4->y1 = ly1;
+	lr4->y2 = ly2;
+	lr4->z1 = lz1;
+	lr4->z2 = lz2;
+}
+
+void lr4_process_s16(struct lr4 *lr4, int samples, int channels, short *src, short *dest)
+{
+	float lx1 = lr4->x1;
+	float lx2 = lr4->x2;
+	float ly1 = lr4->y1;
+	float ly2 = lr4->y2;
+	float lz1 = lr4->z1;
+	float lz2 = lr4->z2;
+	float lb0 = lr4->bq.b0;
+	float lb1 = lr4->bq.b1;
+	float lb2 = lr4->bq.b2;
+	float la1 = lr4->bq.a1;
+	float la2 = lr4->bq.a2;
+
+	int i;
+	for (i = 0; i < samples; i += channels) {
+		float x, y, z;
+		x = src[i];
+		y = lb0*x + lb1*lx1 + lb2*lx2 - la1*ly1 - la2*ly2;
+		z = lb0*y + lb1*ly1 + lb2*ly2 - la1*lz1 - la2*lz2;
+		lx2 = lx1;
+		lx1 = x;
+		ly2 = ly1;
+		ly1 = y;
+		lz2 = lz1;
+		lz1 = z;
+		dest[i] = z;
+	}
+
+	lr4->x1 = lx1;
+	lr4->x2 = lx2;
+	lr4->y1 = ly1;
+	lr4->y2 = ly2;
+	lr4->z1 = lz1;
+	lr4->z2 = lz2;
+}
diff --git a/src/pulsecore/filter/crossover.h b/src/pulsecore/filter/crossover.h
new file mode 100644
index 0000000..c5c9765
--- /dev/null
+++ b/src/pulsecore/filter/crossover.h
@@ -0,0 +1,29 @@
+/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef CROSSOVER_H_
+#define CROSSOVER_H_
+
+#include "biquad.h"
+/* An LR4 filter is two biquads with the same parameters connected in series:
+ *
+ * x -- [BIQUAD] -- y -- [BIQUAD] -- z
+ *
+ * Both biquad filter has the same parameter b[012] and a[12],
+ * The variable [xyz][12] keep the history values.
+ */
+struct lr4 {
+	struct biquad bq;
+	float x1, x2;
+	float y1, y2;
+	float z1, z2;
+};
+
+void lr4_set(struct lr4 *lr4, enum biquad_type type, float freq);
+
+void lr4_process_float32(struct lr4 *lr4, int samples, int channels, float *src, float *dest);
+void lr4_process_s16(struct lr4 *lr4, int samples, int channels, short *src, short *dest);
+
+#endif /* CROSSOVER_H_ */
diff --git a/src/pulsecore/filter/lfe-filter.c b/src/pulsecore/filter/lfe-filter.c
new file mode 100644
index 0000000..9b238fe
--- /dev/null
+++ b/src/pulsecore/filter/lfe-filter.c
@@ -0,0 +1,103 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2014 David Henningsson, Canonical Ltd.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "lfe-filter.h"
+#include <pulse/xmalloc.h>
+#include <pulsecore/filter/biquad.h>
+#include <pulsecore/filter/crossover.h>
+
+/* An LR4 filter, implemented as a chain of two Butterworth filters.
+
+   Currently the channel map is fixed so that a highpass filter is applied to all
+   channels except for the LFE channel, where a lowpass filter is applied.
+   This works well for e g stereo to 2.1/5.1/7.1 scenarios, where the remap engine
+   has calculated the LFE channel to be the average of all source channels.
+*/
+
+struct pa_lfe_filter {
+    float crossover;
+    pa_channel_map cm;
+    pa_sample_spec ss;
+    bool active;
+    struct lr4 lr4[PA_CHANNELS_MAX];
+};
+
+pa_lfe_filter_t * pa_lfe_filter_new(const pa_sample_spec* ss, const pa_channel_map* cm, float crossover_freq) {
+
+    pa_lfe_filter_t *f = pa_xnew0(struct pa_lfe_filter, 1);
+    f->crossover = crossover_freq;
+    f->cm = *cm;
+    f->ss = *ss;
+    pa_lfe_filter_update_rate(f, ss->rate);
+    return f;
+}
+
+void pa_lfe_filter_free(pa_lfe_filter_t *f) {
+    pa_xfree(f);
+}
+
+void pa_lfe_filter_reset(pa_lfe_filter_t *f) {
+    pa_lfe_filter_update_rate(f, f->ss.rate);
+}
+
+pa_memchunk * pa_lfe_filter_process(pa_lfe_filter_t *f, pa_memchunk *buf) {
+    int samples = buf->length / pa_sample_size_of_format(f->ss.format);
+
+    if (!f->active)
+        return buf;
+    if (f->ss.format == PA_SAMPLE_FLOAT32NE) {
+        int i;
+        float *data = pa_memblock_acquire_chunk(buf);
+        for (i = 0; i < f->cm.channels; i++)
+            lr4_process_float32(&f->lr4[i], samples, f->cm.channels, &data[i], &data[i]);
+        pa_memblock_release(buf->memblock);
+    }
+    else if (f->ss.format == PA_SAMPLE_S16NE) {
+        int i;
+        short *data = pa_memblock_acquire_chunk(buf);
+        for (i = 0; i < f->cm.channels; i++)
+            lr4_process_s16(&f->lr4[i], samples, f->cm.channels, &data[i], &data[i]);
+        pa_memblock_release(buf->memblock);
+    }
+    else pa_assert_not_reached();
+    return buf;
+}
+
+void pa_lfe_filter_update_rate(pa_lfe_filter_t *f, uint32_t new_rate) {
+    int i;
+    float biquad_freq = f->crossover / (new_rate / 2);
+
+    f->ss.rate = new_rate;
+    if (biquad_freq <= 0 || biquad_freq >= 1) {
+        pa_log_warn("Crossover frequency (%f) outside range for sample rate %d", f->crossover, new_rate);
+        f->active = false;
+        return;
+    }
+
+    for (i = 0; i < f->cm.channels; i++)
+        lr4_set(&f->lr4[i], f->cm.map[i] == PA_CHANNEL_POSITION_LFE ? BQ_LOWPASS : BQ_HIGHPASS, biquad_freq);
+
+    f->active = true;
+}
diff --git a/src/pulsecore/filter/lfe-filter.h b/src/pulsecore/filter/lfe-filter.h
new file mode 100644
index 0000000..25db8a0
--- /dev/null
+++ b/src/pulsecore/filter/lfe-filter.h
@@ -0,0 +1,38 @@
+#ifndef foolfefilterhfoo
+#define foolfefilterhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2014 David Henningsson, Canonical Ltd.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
+#include <pulsecore/memchunk.h>
+
+
+typedef struct pa_lfe_filter pa_lfe_filter_t;
+
+pa_lfe_filter_t * pa_lfe_filter_new(const pa_sample_spec* ss, const pa_channel_map* cm, float crossover_freq);
+void pa_lfe_filter_free(pa_lfe_filter_t *);
+void pa_lfe_filter_reset(pa_lfe_filter_t *);
+pa_memchunk * pa_lfe_filter_process(pa_lfe_filter_t *filter, pa_memchunk *buf);
+void pa_lfe_filter_update_rate(pa_lfe_filter_t *, uint32_t new_rate);
+
+#endif
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index 0d7cc20..157671d 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -40,7 +40,7 @@ struct ffmpeg_data { /* data specific to ffmpeg */
 
 static int copy_init(pa_resampler *r);
 
-static void setup_remap(const pa_resampler *r, pa_remap_t *m);
+static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_filter_required);
 static void free_remap(pa_remap_t *m);
 
 static int (* const init_table[])(pa_resampler *r) = {
@@ -324,6 +324,7 @@ pa_resampler* pa_resampler_new(
         pa_resample_flags_t flags) {
 
     pa_resampler *r = NULL;
+    bool lfe_filter_required = false;
 
     pa_assert(pool);
     pa_assert(a);
@@ -412,7 +413,15 @@ pa_resampler* pa_resampler_new(
 
     /* set up the remap structure */
     if (r->map_required)
-        setup_remap(r, &r->remap);
+        setup_remap(r, &r->remap, &lfe_filter_required);
+
+    if (lfe_filter_required) {
+        pa_sample_spec wss = r->o_ss;
+        wss.format = r->work_format;
+        /* TODO: Temporary code that sets crossover freq to 120 Hz. This should be a parameter */
+        r->lfe_filter = pa_lfe_filter_new(&wss, &r->o_cm, 120.0f);
+        pa_log_debug("  lfe filter activated (LR4 type)");
+    }
 
     /* initialize implementation */
     if (init_table[method](r) < 0)
@@ -434,6 +443,9 @@ void pa_resampler_free(pa_resampler *r) {
     else
         pa_xfree(r->impl.data);
 
+    if (r->lfe_filter)
+        pa_lfe_filter_free(r->lfe_filter);
+
     if (r->to_work_format_buf.memblock)
         pa_memblock_unref(r->to_work_format_buf.memblock);
     if (r->remap_buf.memblock)
@@ -472,6 +484,9 @@ void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate) {
     r->o_ss.rate = rate;
 
     r->impl.update_rates(r);
+
+    if (r->lfe_filter)
+        pa_lfe_filter_update_rate(r->lfe_filter, rate);
 }
 
 size_t pa_resampler_request(pa_resampler *r, size_t out_length) {
@@ -556,6 +571,9 @@ void pa_resampler_reset(pa_resampler *r) {
     if (r->impl.reset)
         r->impl.reset(r);
 
+    if (r->lfe_filter)
+        pa_lfe_filter_reset(r->lfe_filter);
+
     *r->have_leftover = false;
 }
 
@@ -756,7 +774,7 @@ static int front_rear_side(pa_channel_position_t p) {
     return ON_OTHER;
 }
 
-static void setup_remap(const pa_resampler *r, pa_remap_t *m) {
+static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_filter_required) {
     unsigned oc, ic;
     unsigned n_oc, n_ic;
     bool ic_connected[PA_CHANNELS_MAX];
@@ -765,6 +783,7 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m) {
 
     pa_assert(r);
     pa_assert(m);
+    pa_assert(lfe_filter_required);
 
     n_oc = r->o_ss.channels;
     n_ic = r->i_ss.channels;
@@ -777,6 +796,7 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m) {
     memset(m->map_table_i, 0, sizeof(m->map_table_i));
 
     memset(ic_connected, 0, sizeof(ic_connected));
+    *lfe_filter_required = false;
 
     if (r->flags & PA_RESAMPLER_NO_REMAP) {
         for (oc = 0; oc < PA_MIN(n_ic, n_oc); oc++)
@@ -888,6 +908,9 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m) {
 
                     oc_connected = true;
                     ic_connected[ic] = true;
+
+                    if (a == PA_CHANNEL_POSITION_MONO && on_lfe(b) && !(r->flags & PA_RESAMPLER_NO_LFE))
+                        *lfe_filter_required = true;
                 }
                 else if (b == PA_CHANNEL_POSITION_MONO) {
                     m->map_table_f[oc][ic] = 1.0f / (float) n_ic;
@@ -970,6 +993,8 @@ static void setup_remap(const pa_resampler *r, pa_remap_t *m) {
 
                     /* Please note that a channel connected to LFE doesn't
                      * really count as connected. */
+
+                    *lfe_filter_required = true;
                 }
             }
         }
@@ -1340,6 +1365,9 @@ void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out)
         buf = remap_channels(r, buf);
     }
 
+    if (r->lfe_filter)
+        buf = pa_lfe_filter_process(r->lfe_filter, buf);
+
     if (buf->length) {
         buf = convert_from_work_format(r, buf);
         *out = *buf;
diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h
index 4840a5e..45ad75a 100644
--- a/src/pulsecore/resampler.h
+++ b/src/pulsecore/resampler.h
@@ -26,6 +26,7 @@
 #include <pulsecore/memchunk.h>
 #include <pulsecore/sconv.h>
 #include <pulsecore/remap.h>
+#include <pulsecore/filter/lfe-filter.h>
 
 typedef struct pa_resampler pa_resampler;
 typedef struct pa_resampler_impl pa_resampler_impl;
@@ -106,6 +107,8 @@ struct pa_resampler {
     pa_remap_t remap;
     bool map_required;
 
+    pa_lfe_filter_t *lfe_filter;
+
     pa_resampler_impl impl;
 };
 


More information about the pulseaudio-discuss mailing list