This is an automated email from the git hooks/post-receive script. diwic-guest pushed a commit to branch ubuntu in repository pulseaudio.
commit 5365dc9961a64e08207774f0f0139d02dc267b37 Author: David Henningsson <[email protected]> Date: Wed May 13 14:06:09 2015 +0200 Add LFE filter patches (LP: #1286021) Signed-off-by: David Henningsson <[email protected]> --- debian/changelog | 18 + ...Import-code-from-the-Chrome-OS-audio-serv.patch | 782 +++++++++++++++++++++ ...filter-Enable-LFE-filter-in-the-resampler.patch | 458 ++++++++++++ .../0302-lfe-filter-Cleanup-and-refactor.patch | 707 +++++++++++++++++++ ...change-the-crossover-frequency-as-a-param.patch | 274 ++++++++ ...ange-pa_memblock_new_malloced-to-an-inlin.patch | 42 ++ .../0305-lfe-filter-Add-rewind-support.patch | 231 ++++++ ...r-Make-some-basic-functions-for-rewinding.patch | 96 +++ .../0307-tests-adding-lfe-filter-test.patch | 248 +++++++ ...n-conf-enable-the-lfe-remixing-by-default.patch | 54 ++ ...llow-disabling-the-LFE-filter-by-setting-.patch | 43 ++ ...Rename-lfe_filter_required-to-lfe_remixed.patch | 92 +++ debian/patches/series | 13 + 13 files changed, 3058 insertions(+) diff --git a/debian/changelog b/debian/changelog index 99eeda7..2bbb4fe 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,21 @@ +pulseaudio (1:6.0-0ubuntu7) wily; urgency=medium + + * debian/patches/0300-lfe-filter-Import-code-from-the-Chrome-OS-audio-serv.patch + * debian/patches/0301-lfe-filter-Enable-LFE-filter-in-the-resampler.patch + * debian/patches/0302-lfe-filter-Cleanup-and-refactor.patch + * debian/patches/0303-lfe-filter-change-the-crossover-frequency-as-a-param.patch + * debian/patches/0304-memblock-Change-pa_memblock_new_malloced-to-an-inlin.patch + * debian/patches/0305-lfe-filter-Add-rewind-support.patch + * debian/patches/0306-resampler-Make-some-basic-functions-for-rewinding.patch + * debian/patches/0307-tests-adding-lfe-filter-test.patch + * debian/patches/0308-daemon-conf-enable-the-lfe-remixing-by-default.patch + * debian/patches/0309-resampler-Allow-disabling-the-LFE-filter-by-setting-.patch + * debian/patches/0310-resampler-Rename-lfe_filter_required-to-lfe_remixed.patch + - Add lfe filter patches + (LP: #1286021) + + -- Hui Wang <[email protected]> Wed, 13 May 2015 15:06:28 +0800 + pulseaudio (1:6.0-0ubuntu6) vivid; urgency=medium * debian/patches/0099-pa-yes-no.patch: diff --git a/debian/patches/0300-lfe-filter-Import-code-from-the-Chrome-OS-audio-serv.patch b/debian/patches/0300-lfe-filter-Import-code-from-the-Chrome-OS-audio-serv.patch new file mode 100644 index 0000000..a733d7b --- /dev/null +++ b/debian/patches/0300-lfe-filter-Import-code-from-the-Chrome-OS-audio-serv.patch @@ -0,0 +1,782 @@ +From f3ebf6b667b155f5fe6526bd70881c79e07d7874 Mon Sep 17 00:00:00 2001 +From: David Henningsson <[email protected]> +Date: Tue, 24 Mar 2015 10:29:12 +0100 +Subject: [PATCH 300/311] lfe-filter: Import code from the Chrome OS audio + server + +The chrome OS audio server has some already existing code, which +has been made available under a BSD-style license, which should be +safe to import by us. + +Signed-off-by: David Henningsson <[email protected]> +--- + LICENSE | 3 + + src/pulsecore/filter/LICENSE.WEBKIT | 27 +++ + src/pulsecore/filter/biquad.c | 368 ++++++++++++++++++++++++++++++++++++ + src/pulsecore/filter/biquad.h | 57 ++++++ + src/pulsecore/filter/crossover.c | 188 ++++++++++++++++++ + src/pulsecore/filter/crossover.h | 70 +++++++ + 6 files changed, 713 insertions(+) + create mode 100644 src/pulsecore/filter/LICENSE.WEBKIT + create mode 100644 src/pulsecore/filter/biquad.c + create mode 100644 src/pulsecore/filter/biquad.h + create mode 100644 src/pulsecore/filter/crossover.c + create mode 100644 src/pulsecore/filter/crossover.h + +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/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..b28256d +--- /dev/null ++++ b/src/pulsecore/filter/biquad.c +@@ -0,0 +1,368 @@ ++/* 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. ++ */ ++ ++#include <math.h> ++#include "biquad.h" ++ ++#ifndef max ++#define max(a, b) ({ __typeof__(a) _a = (a); \ ++ __typeof__(b) _b = (b); \ ++ _a > _b ? _a : _b; }) ++#endif ++ ++#ifndef min ++#define min(a, b) ({ __typeof__(a) _a = (a); \ ++ __typeof__(b) _b = (b); \ ++ _a < _b ? _a : _b; }) ++#endif ++ ++#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 = max(0.0, min(cutoff, 1.0)); ++ ++ if (cutoff == 1) { ++ /* 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 */ ++ resonance = max(0.0, resonance); /* can't go negative */ ++ double g = pow(10.0, 0.05 * resonance); ++ 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 = max(0.0, min(cutoff, 1.0)); ++ ++ if (cutoff == 1) { ++ /* The z-transform is 0. */ ++ set_coefficient(bq, 0, 0, 0, 1, 0, 0); ++ } else if (cutoff > 0) { ++ /* Compute biquad coefficients for highpass filter */ ++ resonance = max(0.0, resonance); /* can't go negative */ ++ double g = pow(10.0, 0.05 * resonance); ++ 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); ++ } ++} ++ ++static void biquad_bandpass(struct biquad *bq, double frequency, double Q) ++{ ++ /* No negative frequencies allowed. */ ++ frequency = max(0.0, frequency); ++ ++ /* Don't let Q go negative, which causes an unstable filter. */ ++ Q = max(0.0, Q); ++ ++ if (frequency > 0 && frequency < 1) { ++ double w0 = M_PI * frequency; ++ if (Q > 0) { ++ double alpha = sin(w0) / (2 * Q); ++ double k = cos(w0); ++ ++ double b0 = alpha; ++ double b1 = 0; ++ double b2 = -alpha; ++ double a0 = 1 + alpha; ++ double a1 = -2 * k; ++ double a2 = 1 - alpha; ++ ++ set_coefficient(bq, b0, b1, b2, a0, a1, a2); ++ } else { ++ /* When Q = 0, the above formulas have problems. If we ++ * look at the z-transform, we can see that the limit ++ * as Q->0 is 1, so set the filter that way. ++ */ ++ set_coefficient(bq, 1, 0, 0, 1, 0, 0); ++ } ++ } else { ++ /* When the cutoff is zero, the z-transform approaches 0, if Q ++ * > 0. When both Q and cutoff are zero, the z-transform is ++ * pretty much undefined. What should we do in this case? ++ * For now, just make the filter 0. When the cutoff is 1, the ++ * z-transform also approaches 0. ++ */ ++ set_coefficient(bq, 0, 0, 0, 1, 0, 0); ++ } ++} ++ ++static void biquad_lowshelf(struct biquad *bq, double frequency, double db_gain) ++{ ++ /* Clip frequencies to between 0 and 1, inclusive. */ ++ frequency = max(0.0, min(frequency, 1.0)); ++ ++ double A = pow(10.0, db_gain / 40); ++ ++ if (frequency == 1) { ++ /* The z-transform is a constant gain. */ ++ set_coefficient(bq, A * A, 0, 0, 1, 0, 0); ++ } else if (frequency > 0) { ++ double w0 = M_PI * frequency; ++ double S = 1; /* filter slope (1 is max value) */ ++ double alpha = 0.5 * sin(w0) * ++ sqrt((A + 1 / A) * (1 / S - 1) + 2); ++ double k = cos(w0); ++ double k2 = 2 * sqrt(A) * alpha; ++ double a_plus_one = A + 1; ++ double a_minus_one = A - 1; ++ ++ double b0 = A * (a_plus_one - a_minus_one * k + k2); ++ double b1 = 2 * A * (a_minus_one - a_plus_one * k); ++ double b2 = A * (a_plus_one - a_minus_one * k - k2); ++ double a0 = a_plus_one + a_minus_one * k + k2; ++ double a1 = -2 * (a_minus_one + a_plus_one * k); ++ double a2 = a_plus_one + a_minus_one * k - k2; ++ ++ set_coefficient(bq, b0, b1, b2, a0, a1, a2); ++ } else { ++ /* When frequency is 0, the z-transform is 1. */ ++ set_coefficient(bq, 1, 0, 0, 1, 0, 0); ++ } ++} ++ ++static void biquad_highshelf(struct biquad *bq, double frequency, ++ double db_gain) ++{ ++ /* Clip frequencies to between 0 and 1, inclusive. */ ++ frequency = max(0.0, min(frequency, 1.0)); ++ ++ double A = pow(10.0, db_gain / 40); ++ ++ if (frequency == 1) { ++ /* The z-transform is 1. */ ++ set_coefficient(bq, 1, 0, 0, 1, 0, 0); ++ } else if (frequency > 0) { ++ double w0 = M_PI * frequency; ++ double S = 1; /* filter slope (1 is max value) */ ++ double alpha = 0.5 * sin(w0) * ++ sqrt((A + 1 / A) * (1 / S - 1) + 2); ++ double k = cos(w0); ++ double k2 = 2 * sqrt(A) * alpha; ++ double a_plus_one = A + 1; ++ double a_minus_one = A - 1; ++ ++ double b0 = A * (a_plus_one + a_minus_one * k + k2); ++ double b1 = -2 * A * (a_minus_one + a_plus_one * k); ++ double b2 = A * (a_plus_one + a_minus_one * k - k2); ++ double a0 = a_plus_one - a_minus_one * k + k2; ++ double a1 = 2 * (a_minus_one - a_plus_one * k); ++ double a2 = a_plus_one - a_minus_one * k - k2; ++ ++ set_coefficient(bq, b0, b1, b2, a0, a1, a2); ++ } else { ++ /* When frequency = 0, the filter is just a gain, A^2. */ ++ set_coefficient(bq, A * A, 0, 0, 1, 0, 0); ++ } ++} ++ ++static void biquad_peaking(struct biquad *bq, double frequency, double Q, ++ double db_gain) ++{ ++ /* Clip frequencies to between 0 and 1, inclusive. */ ++ frequency = max(0.0, min(frequency, 1.0)); ++ ++ /* Don't let Q go negative, which causes an unstable filter. */ ++ Q = max(0.0, Q); ++ ++ double A = pow(10.0, db_gain / 40); ++ ++ if (frequency > 0 && frequency < 1) { ++ if (Q > 0) { ++ double w0 = M_PI * frequency; ++ double alpha = sin(w0) / (2 * Q); ++ double k = cos(w0); ++ ++ double b0 = 1 + alpha * A; ++ double b1 = -2 * k; ++ double b2 = 1 - alpha * A; ++ double a0 = 1 + alpha / A; ++ double a1 = -2 * k; ++ double a2 = 1 - alpha / A; ++ ++ set_coefficient(bq, b0, b1, b2, a0, a1, a2); ++ } else { ++ /* When Q = 0, the above formulas have problems. If we ++ * look at the z-transform, we can see that the limit ++ * as Q->0 is A^2, so set the filter that way. ++ */ ++ set_coefficient(bq, A * A, 0, 0, 1, 0, 0); ++ } ++ } else { ++ /* When frequency is 0 or 1, the z-transform is 1. */ ++ set_coefficient(bq, 1, 0, 0, 1, 0, 0); ++ } ++} ++ ++static void biquad_notch(struct biquad *bq, double frequency, double Q) ++{ ++ /* Clip frequencies to between 0 and 1, inclusive. */ ++ frequency = max(0.0, min(frequency, 1.0)); ++ ++ /* Don't let Q go negative, which causes an unstable filter. */ ++ Q = max(0.0, Q); ++ ++ if (frequency > 0 && frequency < 1) { ++ if (Q > 0) { ++ double w0 = M_PI * frequency; ++ double alpha = sin(w0) / (2 * Q); ++ double k = cos(w0); ++ ++ double b0 = 1; ++ double b1 = -2 * k; ++ double b2 = 1; ++ double a0 = 1 + alpha; ++ double a1 = -2 * k; ++ double a2 = 1 - alpha; ++ ++ set_coefficient(bq, b0, b1, b2, a0, a1, a2); ++ } else { ++ /* When Q = 0, the above formulas have problems. If we ++ * look at the z-transform, we can see that the limit ++ * as Q->0 is 0, so set the filter that way. ++ */ ++ set_coefficient(bq, 0, 0, 0, 1, 0, 0); ++ } ++ } else { ++ /* When frequency is 0 or 1, the z-transform is 1. */ ++ set_coefficient(bq, 1, 0, 0, 1, 0, 0); ++ } ++} ++ ++static void biquad_allpass(struct biquad *bq, double frequency, double Q) ++{ ++ /* Clip frequencies to between 0 and 1, inclusive. */ ++ frequency = max(0.0, min(frequency, 1.0)); ++ ++ /* Don't let Q go negative, which causes an unstable filter. */ ++ Q = max(0.0, Q); ++ ++ if (frequency > 0 && frequency < 1) { ++ if (Q > 0) { ++ double w0 = M_PI * frequency; ++ double alpha = sin(w0) / (2 * Q); ++ double k = cos(w0); ++ ++ double b0 = 1 - alpha; ++ double b1 = -2 * k; ++ double b2 = 1 + alpha; ++ double a0 = 1 + alpha; ++ double a1 = -2 * k; ++ double a2 = 1 - alpha; ++ ++ set_coefficient(bq, b0, b1, b2, a0, a1, a2); ++ } else { ++ /* When Q = 0, the above formulas have problems. If we ++ * look at the z-transform, we can see that the limit ++ * as Q->0 is -1, so set the filter that way. ++ */ ++ set_coefficient(bq, -1, 0, 0, 1, 0, 0); ++ } ++ } else { ++ /* When frequency is 0 or 1, 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) ++{ ++ /* Default is an identity filter. Also clear history values. */ ++ set_coefficient(bq, 1, 0, 0, 1, 0, 0); ++ bq->x1 = 0; ++ bq->x2 = 0; ++ bq->y1 = 0; ++ bq->y2 = 0; ++ ++ switch (type) { ++ case BQ_LOWPASS: ++ biquad_lowpass(bq, freq, Q); ++ break; ++ case BQ_HIGHPASS: ++ biquad_highpass(bq, freq, Q); ++ break; ++ case BQ_BANDPASS: ++ biquad_bandpass(bq, freq, Q); ++ break; ++ case BQ_LOWSHELF: ++ biquad_lowshelf(bq, freq, gain); ++ break; ++ case BQ_HIGHSHELF: ++ biquad_highshelf(bq, freq, gain); ++ break; ++ case BQ_PEAKING: ++ biquad_peaking(bq, freq, Q, gain); ++ break; ++ case BQ_NOTCH: ++ biquad_notch(bq, freq, Q); ++ break; ++ case BQ_ALLPASS: ++ biquad_allpass(bq, freq, Q); ++ break; ++ case BQ_NONE: ++ break; ++ } ++} +diff --git a/src/pulsecore/filter/biquad.h b/src/pulsecore/filter/biquad.h +new file mode 100644 +index 0000000..c584aa9 +--- /dev/null ++++ b/src/pulsecore/filter/biquad.h +@@ -0,0 +1,57 @@ ++/* 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; ++ float x1, x2; ++ float y1, y2; ++}; ++ ++/* The type of the biquad filters */ ++enum biquad_type { ++ BQ_NONE, ++ BQ_LOWPASS, ++ BQ_HIGHPASS, ++ BQ_BANDPASS, ++ BQ_LOWSHELF, ++ BQ_HIGHSHELF, ++ BQ_PEAKING, ++ BQ_NOTCH, ++ BQ_ALLPASS ++}; ++ ++/* 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..11a8c6e +--- /dev/null ++++ b/src/pulsecore/filter/crossover.c +@@ -0,0 +1,188 @@ ++/* 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. ++ */ ++ ++#include "crossover.h" ++#include "biquad.h" ++ ++static void lr4_set(struct lr4 *lr4, enum biquad_type type, float freq) ++{ ++ struct biquad q; ++ biquad_set(&q, type, freq, 0, 0); ++ lr4->b0 = q.b0; ++ lr4->b1 = q.b1; ++ lr4->b2 = q.b2; ++ lr4->a1 = q.a1; ++ lr4->a2 = q.a2; ++ lr4->x1 = 0; ++ lr4->x2 = 0; ++ lr4->y1 = 0; ++ lr4->y2 = 0; ++ lr4->z1 = 0; ++ lr4->z2 = 0; ++} ++ ++/* Split input data using two LR4 filters, put the result into the input array ++ * and another array. ++ * ++ * data0 --+-- lp --> data0 ++ * | ++ * \-- hp --> data1 ++ */ ++static void lr4_split(struct lr4 *lp, struct lr4 *hp, int count, float *data0, ++ float *data1) ++{ ++ float lx1 = lp->x1; ++ float lx2 = lp->x2; ++ float ly1 = lp->y1; ++ float ly2 = lp->y2; ++ float lz1 = lp->z1; ++ float lz2 = lp->z2; ++ float lb0 = lp->b0; ++ float lb1 = lp->b1; ++ float lb2 = lp->b2; ++ float la1 = lp->a1; ++ float la2 = lp->a2; ++ ++ float hx1 = hp->x1; ++ float hx2 = hp->x2; ++ float hy1 = hp->y1; ++ float hy2 = hp->y2; ++ float hz1 = hp->z1; ++ float hz2 = hp->z2; ++ float hb0 = hp->b0; ++ float hb1 = hp->b1; ++ float hb2 = hp->b2; ++ float ha1 = hp->a1; ++ float ha2 = hp->a2; ++ ++ int i; ++ for (i = 0; i < count; i++) { ++ float x, y, z; ++ x = data0[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; ++ data0[i] = z; ++ ++ y = hb0*x + hb1*hx1 + hb2*hx2 - ha1*hy1 - ha2*hy2; ++ z = hb0*y + hb1*hy1 + hb2*hy2 - ha1*hz1 - ha2*hz2; ++ hx2 = hx1; ++ hx1 = x; ++ hy2 = hy1; ++ hy1 = y; ++ hz2 = hz1; ++ hz1 = z; ++ data1[i] = z; ++ } ++ ++ lp->x1 = lx1; ++ lp->x2 = lx2; ++ lp->y1 = ly1; ++ lp->y2 = ly2; ++ lp->z1 = lz1; ++ lp->z2 = lz2; ++ ++ hp->x1 = hx1; ++ hp->x2 = hx2; ++ hp->y1 = hy1; ++ hp->y2 = hy2; ++ hp->z1 = hz1; ++ hp->z2 = hz2; ++} ++ ++/* Split input data using two LR4 filters and sum them back to the original ++ * data array. ++ * ++ * data --+-- lp --+--> data ++ * | | ++ * \-- hp --/ ++ */ ++static void lr4_merge(struct lr4 *lp, struct lr4 *hp, int count, float *data) ++{ ++ float lx1 = lp->x1; ++ float lx2 = lp->x2; ++ float ly1 = lp->y1; ++ float ly2 = lp->y2; ++ float lz1 = lp->z1; ++ float lz2 = lp->z2; ++ float lb0 = lp->b0; ++ float lb1 = lp->b1; ++ float lb2 = lp->b2; ++ float la1 = lp->a1; ++ float la2 = lp->a2; ++ ++ float hx1 = hp->x1; ++ float hx2 = hp->x2; ++ float hy1 = hp->y1; ++ float hy2 = hp->y2; ++ float hz1 = hp->z1; ++ float hz2 = hp->z2; ++ float hb0 = hp->b0; ++ float hb1 = hp->b1; ++ float hb2 = hp->b2; ++ float ha1 = hp->a1; ++ float ha2 = hp->a2; ++ ++ int i; ++ for (i = 0; i < count; i++) { ++ float x, y, z; ++ x = data[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; ++ ++ y = hb0*x + hb1*hx1 + hb2*hx2 - ha1*hy1 - ha2*hy2; ++ z = hb0*y + hb1*hy1 + hb2*hy2 - ha1*hz1 - ha2*hz2; ++ hx2 = hx1; ++ hx1 = x; ++ hy2 = hy1; ++ hy1 = y; ++ hz2 = hz1; ++ hz1 = z; ++ data[i] = z + lz1; ++ } ++ ++ lp->x1 = lx1; ++ lp->x2 = lx2; ++ lp->y1 = ly1; ++ lp->y2 = ly2; ++ lp->z1 = lz1; ++ lp->z2 = lz2; ++ ++ hp->x1 = hx1; ++ hp->x2 = hx2; ++ hp->y1 = hy1; ++ hp->y2 = hy2; ++ hp->z1 = hz1; ++ hp->z2 = hz2; ++} ++ ++void crossover_init(struct crossover *xo, float freq1, float freq2) ++{ ++ int i; ++ for (i = 0; i < 3; i++) { ++ float f = (i == 0) ? freq1 : freq2; ++ lr4_set(&xo->lp[i], BQ_LOWPASS, f); ++ lr4_set(&xo->hp[i], BQ_HIGHPASS, f); ++ } ++} ++ ++void crossover_process(struct crossover *xo, int count, float *data0, ++ float *data1, float *data2) ++{ ++ lr4_split(&xo->lp[0], &xo->hp[0], count, data0, data1); ++ lr4_merge(&xo->lp[1], &xo->hp[1], count, data0); ++ lr4_split(&xo->lp[2], &xo->hp[2], count, data1, data2); ++} +diff --git a/src/pulsecore/filter/crossover.h b/src/pulsecore/filter/crossover.h +new file mode 100644 +index 0000000..99a601c +--- /dev/null ++++ b/src/pulsecore/filter/crossover.h +@@ -0,0 +1,70 @@ ++/* 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_ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* 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 { ++ float b0, b1, b2; ++ float a1, a2; ++ float x1, x2; ++ float y1, y2; ++ float z1, z2; ++}; ++ ++/* Three bands crossover filter: ++ * ++ * INPUT --+-- lp0 --+-- lp1 --+---> LOW (0) ++ * | | | ++ * | \-- hp1 --/ ++ * | ++ * \-- hp0 --+-- lp2 ------> MID (1) ++ * | ++ * \-- hp2 ------> HIGH (2) ++ * ++ * [f0] [f1] ++ * ++ * Each lp or hp is an LR4 filter, which consists of two second-order ++ * lowpass or highpass butterworth filters. ++ */ ++struct crossover { ++ struct lr4 lp[3], hp[3]; ++}; ++ ++/* Initializes a crossover filter ++ * Args: ++ * xo - The crossover filter we want to initialize. ++ * freq1 - The normalized frequency splits low and mid band. ++ * freq2 - The normalized frequency splits mid and high band. ++ */ ++void crossover_init(struct crossover *xo, float freq1, float freq2); ++ ++/* Splits input samples to three bands. ++ * Args: ++ * xo - The crossover filter to use. ++ * count - The number of input samples. ++ * data0 - The input samples, also the place to store low band output. ++ * data1 - The place to store mid band output. ++ * data2 - The place to store high band output. ++ */ ++void crossover_process(struct crossover *xo, int count, float *data0, ++ float *data1, float *data2); ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* CROSSOVER_H_ */ +-- +1.9.1 + diff --git a/debian/patches/0301-lfe-filter-Enable-LFE-filter-in-the-resampler.patch b/debian/patches/0301-lfe-filter-Enable-LFE-filter-in-the-resampler.patch new file mode 100644 index 0000000..4d6413b --- /dev/null +++ b/debian/patches/0301-lfe-filter-Enable-LFE-filter-in-the-resampler.patch @@ -0,0 +1,458 @@ +From 979f19a434733afba0480e2ba456cccc98362e05 Mon Sep 17 00:00:00 2001 +From: David Henningsson <[email protected]> +Date: Tue, 24 Mar 2015 10:29:13 +0100 +Subject: [PATCH 301/311] lfe-filter: Enable LFE filter in the resampler + +When enable-lfe-remixing is set, an LFE channel is present in the +resampler's destination channel map but not in the source channel map, +we insert a low-pass filter instead of just averaging the channels. +Other channels will get a high-pass filter. + +In this patch, the crossover frequency is hardcoded to 120Hz (to be fixed +in later patches). + +Note that in current state the LFE filter is + - not very optimised + - not rewind friendly (rewinding can cause audible artifacts) + +Signed-off-by: David Henningsson <[email protected]> +--- + src/Makefile.am | 3 ++ + src/pulsecore/filter/crossover.c | 85 +++++++++++++++++++++++++++++++- + src/pulsecore/filter/crossover.h | 6 +++ + src/pulsecore/filter/lfe-filter.c | 101 ++++++++++++++++++++++++++++++++++++++ + src/pulsecore/filter/lfe-filter.h | 38 ++++++++++++++ + src/pulsecore/resampler.c | 34 +++++++++++-- + src/pulsecore/resampler.h | 3 ++ + 7 files changed, 265 insertions(+), 5 deletions(-) + create mode 100644 src/pulsecore/filter/lfe-filter.c + create mode 100644 src/pulsecore/filter/lfe-filter.h + +Index: pulseaudio/src/Makefile.am +=================================================================== +--- pulseaudio.orig/src/Makefile.am 2015-05-13 14:59:27.522147266 +0800 ++++ pulseaudio/src/Makefile.am 2015-05-13 14:59:27.518147266 +0800 +@@ -911,6 +911,9 @@ + + # Pure core stuff + libpulsecore_@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 \ +Index: pulseaudio/src/pulsecore/filter/crossover.c +=================================================================== +--- pulseaudio.orig/src/pulsecore/filter/crossover.c 2015-05-13 14:59:27.522147266 +0800 ++++ pulseaudio/src/pulsecore/filter/crossover.c 2015-05-13 14:59:27.518147266 +0800 +@@ -3,10 +3,16 @@ + * found in the LICENSE file. + */ + +-#include "crossover.h" ++#ifdef HAVE_CONFIG_H ++#include <config.h> ++#endif ++ ++#include <pulsecore/macro.h> ++ + #include "biquad.h" ++#include "crossover.h" + +-static void lr4_set(struct lr4 *lr4, enum biquad_type type, float freq) ++void lr4_set(struct lr4 *lr4, enum biquad_type type, float freq) + { + struct biquad q; + biquad_set(&q, type, freq, 0, 0); +@@ -23,6 +29,81 @@ + 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->b0; ++ float lb1 = lr4->b1; ++ float lb2 = lr4->b2; ++ float la1 = lr4->a1; ++ float la2 = lr4->a2; ++ ++ int i; ++ for (i = 0; i < samples * channels; 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; ++} ++ ++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->b0; ++ float lb1 = lr4->b1; ++ float lb2 = lr4->b2; ++ float la1 = lr4->a1; ++ float la2 = lr4->a2; ++ ++ int i; ++ for (i = 0; i < samples * channels; 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; ++} ++ ++ + /* Split input data using two LR4 filters, put the result into the input array + * and another array. + * +Index: pulseaudio/src/pulsecore/filter/crossover.h +=================================================================== +--- pulseaudio.orig/src/pulsecore/filter/crossover.h 2015-05-13 14:59:27.522147266 +0800 ++++ pulseaudio/src/pulsecore/filter/crossover.h 2015-05-13 14:59:27.518147266 +0800 +@@ -25,6 +25,12 @@ + 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); ++ ++ + /* Three bands crossover filter: + * + * INPUT --+-- lp0 --+-- lp1 --+---> LOW (0) +Index: pulseaudio/src/pulsecore/filter/lfe-filter.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ pulseaudio/src/pulsecore/filter/lfe-filter.c 2015-05-13 14:59:27.518147266 +0800 +@@ -0,0 +1,101 @@ ++/*** ++ 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, see <http://www.gnu.org/licenses/>. ++***/ ++ ++#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_frame_size(&f->ss); ++ ++ 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; ++} +Index: pulseaudio/src/pulsecore/filter/lfe-filter.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ pulseaudio/src/pulsecore/filter/lfe-filter.h 2015-05-13 14:59:27.518147266 +0800 +@@ -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 +Index: pulseaudio/src/pulsecore/resampler.c +=================================================================== +--- pulseaudio.orig/src/pulsecore/resampler.c 2015-05-13 14:59:27.522147266 +0800 ++++ pulseaudio/src/pulsecore/resampler.c 2015-05-13 14:59:27.518147266 +0800 +@@ -40,7 +40,7 @@ + + 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) = { +@@ -302,6 +302,7 @@ + pa_resample_flags_t flags) { + + pa_resampler *r = NULL; ++ bool lfe_filter_required = false; + + pa_assert(pool); + pa_assert(a); +@@ -390,7 +391,15 @@ + + /* 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) +@@ -412,6 +421,9 @@ + 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) +@@ -450,6 +462,9 @@ + 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) { +@@ -534,6 +549,9 @@ + if (r->impl.reset) + r->impl.reset(r); + ++ if (r->lfe_filter) ++ pa_lfe_filter_reset(r->lfe_filter); ++ + *r->have_leftover = false; + } + +@@ -731,7 +749,7 @@ + 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]; +@@ -740,6 +758,7 @@ + + pa_assert(r); + pa_assert(m); ++ pa_assert(lfe_filter_required); + + n_oc = r->o_ss.channels; + n_ic = r->i_ss.channels; +@@ -752,6 +771,7 @@ + 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++) +@@ -863,6 +883,9 @@ + + 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; +@@ -945,6 +968,8 @@ + + /* Please note that a channel connected to LFE doesn't + * really count as connected. */ ++ ++ *lfe_filter_required = true; + } + } + } +@@ -1315,6 +1340,9 @@ + 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; +Index: pulseaudio/src/pulsecore/resampler.h +=================================================================== +--- pulseaudio.orig/src/pulsecore/resampler.h 2015-05-13 14:59:27.522147266 +0800 ++++ pulseaudio/src/pulsecore/resampler.h 2015-05-13 14:59:27.518147266 +0800 +@@ -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; +@@ -103,6 +104,8 @@ + pa_remap_t remap; + bool map_required; + ++ pa_lfe_filter_t *lfe_filter; ++ + pa_resampler_impl impl; + }; + diff --git a/debian/patches/0302-lfe-filter-Cleanup-and-refactor.patch b/debian/patches/0302-lfe-filter-Cleanup-and-refactor.patch new file mode 100644 index 0000000..9c552b9 --- /dev/null +++ b/debian/patches/0302-lfe-filter-Cleanup-and-refactor.patch @@ -0,0 +1,707 @@ +From 3538e6636edc1ba0b75e7409db618dbb7fe79d3e Mon Sep 17 00:00:00 2001 +From: David Henningsson <[email protected]> +Date: Tue, 24 Mar 2015 10:29:14 +0100 +Subject: [PATCH 302/311] lfe-filter: Cleanup and refactor + + - Remove imported dead code + - Fix compiler warnings + - Fix non-GCC compiler compilation (use more portable macros) + - Change lr4 struct to include a biquad struct + +Thanks to Alexander Patrakov for suggesting many of these changes. + +Signed-off-by: David Henningsson <[email protected]> +--- + src/pulsecore/filter/biquad.c | 289 +++------------------------------------ + src/pulsecore/filter/biquad.h | 14 +- + src/pulsecore/filter/crossover.c | 194 ++------------------------ + src/pulsecore/filter/crossover.h | 51 +------ + 4 files changed, 33 insertions(+), 515 deletions(-) + +diff --git a/src/pulsecore/filter/biquad.c b/src/pulsecore/filter/biquad.c +index b28256d..7c21a29 100644 +--- a/src/pulsecore/filter/biquad.c ++++ b/src/pulsecore/filter/biquad.c +@@ -8,20 +8,15 @@ + * found in the LICENSE.WEBKIT file. + */ + +-#include <math.h> +-#include "biquad.h" + +-#ifndef max +-#define max(a, b) ({ __typeof__(a) _a = (a); \ +- __typeof__(b) _b = (b); \ +- _a > _b ? _a : _b; }) ++#ifdef HAVE_CONFIG_H ++#include <config.h> + #endif + +-#ifndef min +-#define min(a, b) ({ __typeof__(a) _a = (a); \ +- __typeof__(b) _b = (b); \ +- _a < _b ? _a : _b; }) +-#endif ++#include <pulsecore/macro.h> ++ ++#include <math.h> ++#include "biquad.h" + + #ifndef M_PI + #define M_PI 3.14159265358979323846 +@@ -38,19 +33,18 @@ static void set_coefficient(struct biquad *bq, double b0, double b1, double b2, + bq->a2 = a2 * a0_inv; + } + +-static void biquad_lowpass(struct biquad *bq, double cutoff, double resonance) ++static void biquad_lowpass(struct biquad *bq, double cutoff) + { + /* Limit cutoff to 0 to 1. */ +- cutoff = max(0.0, min(cutoff, 1.0)); ++ cutoff = PA_MIN(cutoff, 1.0); ++ cutoff = PA_MAX(0.0, cutoff); + +- if (cutoff == 1) { ++ 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 */ +- resonance = max(0.0, resonance); /* can't go negative */ +- double g = pow(10.0, 0.05 * resonance); +- double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2); ++ double d = sqrt(2); + + double theta = M_PI * cutoff; + double sn = 0.5 * d * sin(theta); +@@ -73,19 +67,18 @@ static void biquad_lowpass(struct biquad *bq, double cutoff, double resonance) + } + } + +-static void biquad_highpass(struct biquad *bq, double cutoff, double resonance) ++static void biquad_highpass(struct biquad *bq, double cutoff) + { + /* Limit cutoff to 0 to 1. */ +- cutoff = max(0.0, min(cutoff, 1.0)); ++ cutoff = PA_MIN(cutoff, 1.0); ++ cutoff = PA_MAX(0.0, cutoff); + +- if (cutoff == 1) { ++ 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 */ +- resonance = max(0.0, resonance); /* can't go negative */ +- double g = pow(10.0, 0.05 * resonance); +- double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2); ++ double d = sqrt(2); + + double theta = M_PI * cutoff; + double sn = 0.5 * d * sin(theta); +@@ -110,259 +103,15 @@ static void biquad_highpass(struct biquad *bq, double cutoff, double resonance) + } + } + +-static void biquad_bandpass(struct biquad *bq, double frequency, double Q) +-{ +- /* No negative frequencies allowed. */ +- frequency = max(0.0, frequency); +- +- /* Don't let Q go negative, which causes an unstable filter. */ +- Q = max(0.0, Q); +- +- if (frequency > 0 && frequency < 1) { +- double w0 = M_PI * frequency; +- if (Q > 0) { +- double alpha = sin(w0) / (2 * Q); +- double k = cos(w0); +- +- double b0 = alpha; +- double b1 = 0; +- double b2 = -alpha; +- double a0 = 1 + alpha; +- double a1 = -2 * k; +- double a2 = 1 - alpha; +- +- set_coefficient(bq, b0, b1, b2, a0, a1, a2); +- } else { +- /* When Q = 0, the above formulas have problems. If we +- * look at the z-transform, we can see that the limit +- * as Q->0 is 1, so set the filter that way. +- */ +- set_coefficient(bq, 1, 0, 0, 1, 0, 0); +- } +- } else { +- /* When the cutoff is zero, the z-transform approaches 0, if Q +- * > 0. When both Q and cutoff are zero, the z-transform is +- * pretty much undefined. What should we do in this case? +- * For now, just make the filter 0. When the cutoff is 1, the +- * z-transform also approaches 0. +- */ +- set_coefficient(bq, 0, 0, 0, 1, 0, 0); +- } +-} +- +-static void biquad_lowshelf(struct biquad *bq, double frequency, double db_gain) +-{ +- /* Clip frequencies to between 0 and 1, inclusive. */ +- frequency = max(0.0, min(frequency, 1.0)); +- +- double A = pow(10.0, db_gain / 40); +- +- if (frequency == 1) { +- /* The z-transform is a constant gain. */ +- set_coefficient(bq, A * A, 0, 0, 1, 0, 0); +- } else if (frequency > 0) { +- double w0 = M_PI * frequency; +- double S = 1; /* filter slope (1 is max value) */ +- double alpha = 0.5 * sin(w0) * +- sqrt((A + 1 / A) * (1 / S - 1) + 2); +- double k = cos(w0); +- double k2 = 2 * sqrt(A) * alpha; +- double a_plus_one = A + 1; +- double a_minus_one = A - 1; +- +- double b0 = A * (a_plus_one - a_minus_one * k + k2); +- double b1 = 2 * A * (a_minus_one - a_plus_one * k); +- double b2 = A * (a_plus_one - a_minus_one * k - k2); +- double a0 = a_plus_one + a_minus_one * k + k2; +- double a1 = -2 * (a_minus_one + a_plus_one * k); +- double a2 = a_plus_one + a_minus_one * k - k2; +- +- set_coefficient(bq, b0, b1, b2, a0, a1, a2); +- } else { +- /* When frequency is 0, the z-transform is 1. */ +- set_coefficient(bq, 1, 0, 0, 1, 0, 0); +- } +-} +- +-static void biquad_highshelf(struct biquad *bq, double frequency, +- double db_gain) ++void biquad_set(struct biquad *bq, enum biquad_type type, double freq) + { +- /* Clip frequencies to between 0 and 1, inclusive. */ +- frequency = max(0.0, min(frequency, 1.0)); +- +- double A = pow(10.0, db_gain / 40); +- +- if (frequency == 1) { +- /* The z-transform is 1. */ +- set_coefficient(bq, 1, 0, 0, 1, 0, 0); +- } else if (frequency > 0) { +- double w0 = M_PI * frequency; +- double S = 1; /* filter slope (1 is max value) */ +- double alpha = 0.5 * sin(w0) * +- sqrt((A + 1 / A) * (1 / S - 1) + 2); +- double k = cos(w0); +- double k2 = 2 * sqrt(A) * alpha; +- double a_plus_one = A + 1; +- double a_minus_one = A - 1; +- +- double b0 = A * (a_plus_one + a_minus_one * k + k2); +- double b1 = -2 * A * (a_minus_one + a_plus_one * k); +- double b2 = A * (a_plus_one + a_minus_one * k - k2); +- double a0 = a_plus_one - a_minus_one * k + k2; +- double a1 = 2 * (a_minus_one - a_plus_one * k); +- double a2 = a_plus_one - a_minus_one * k - k2; +- +- set_coefficient(bq, b0, b1, b2, a0, a1, a2); +- } else { +- /* When frequency = 0, the filter is just a gain, A^2. */ +- set_coefficient(bq, A * A, 0, 0, 1, 0, 0); +- } +-} +- +-static void biquad_peaking(struct biquad *bq, double frequency, double Q, +- double db_gain) +-{ +- /* Clip frequencies to between 0 and 1, inclusive. */ +- frequency = max(0.0, min(frequency, 1.0)); +- +- /* Don't let Q go negative, which causes an unstable filter. */ +- Q = max(0.0, Q); +- +- double A = pow(10.0, db_gain / 40); +- +- if (frequency > 0 && frequency < 1) { +- if (Q > 0) { +- double w0 = M_PI * frequency; +- double alpha = sin(w0) / (2 * Q); +- double k = cos(w0); +- +- double b0 = 1 + alpha * A; +- double b1 = -2 * k; +- double b2 = 1 - alpha * A; +- double a0 = 1 + alpha / A; +- double a1 = -2 * k; +- double a2 = 1 - alpha / A; +- +- set_coefficient(bq, b0, b1, b2, a0, a1, a2); +- } else { +- /* When Q = 0, the above formulas have problems. If we +- * look at the z-transform, we can see that the limit +- * as Q->0 is A^2, so set the filter that way. +- */ +- set_coefficient(bq, A * A, 0, 0, 1, 0, 0); +- } +- } else { +- /* When frequency is 0 or 1, the z-transform is 1. */ +- set_coefficient(bq, 1, 0, 0, 1, 0, 0); +- } +-} +- +-static void biquad_notch(struct biquad *bq, double frequency, double Q) +-{ +- /* Clip frequencies to between 0 and 1, inclusive. */ +- frequency = max(0.0, min(frequency, 1.0)); +- +- /* Don't let Q go negative, which causes an unstable filter. */ +- Q = max(0.0, Q); +- +- if (frequency > 0 && frequency < 1) { +- if (Q > 0) { +- double w0 = M_PI * frequency; +- double alpha = sin(w0) / (2 * Q); +- double k = cos(w0); +- +- double b0 = 1; +- double b1 = -2 * k; +- double b2 = 1; +- double a0 = 1 + alpha; +- double a1 = -2 * k; +- double a2 = 1 - alpha; +- +- set_coefficient(bq, b0, b1, b2, a0, a1, a2); +- } else { +- /* When Q = 0, the above formulas have problems. If we +- * look at the z-transform, we can see that the limit +- * as Q->0 is 0, so set the filter that way. +- */ +- set_coefficient(bq, 0, 0, 0, 1, 0, 0); +- } +- } else { +- /* When frequency is 0 or 1, the z-transform is 1. */ +- set_coefficient(bq, 1, 0, 0, 1, 0, 0); +- } +-} +- +-static void biquad_allpass(struct biquad *bq, double frequency, double Q) +-{ +- /* Clip frequencies to between 0 and 1, inclusive. */ +- frequency = max(0.0, min(frequency, 1.0)); +- +- /* Don't let Q go negative, which causes an unstable filter. */ +- Q = max(0.0, Q); +- +- if (frequency > 0 && frequency < 1) { +- if (Q > 0) { +- double w0 = M_PI * frequency; +- double alpha = sin(w0) / (2 * Q); +- double k = cos(w0); +- +- double b0 = 1 - alpha; +- double b1 = -2 * k; +- double b2 = 1 + alpha; +- double a0 = 1 + alpha; +- double a1 = -2 * k; +- double a2 = 1 - alpha; +- +- set_coefficient(bq, b0, b1, b2, a0, a1, a2); +- } else { +- /* When Q = 0, the above formulas have problems. If we +- * look at the z-transform, we can see that the limit +- * as Q->0 is -1, so set the filter that way. +- */ +- set_coefficient(bq, -1, 0, 0, 1, 0, 0); +- } +- } else { +- /* When frequency is 0 or 1, 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) +-{ +- /* Default is an identity filter. Also clear history values. */ +- set_coefficient(bq, 1, 0, 0, 1, 0, 0); +- bq->x1 = 0; +- bq->x2 = 0; +- bq->y1 = 0; +- bq->y2 = 0; + + switch (type) { + case BQ_LOWPASS: +- biquad_lowpass(bq, freq, Q); ++ biquad_lowpass(bq, freq); + break; + case BQ_HIGHPASS: +- biquad_highpass(bq, freq, Q); +- break; +- case BQ_BANDPASS: +- biquad_bandpass(bq, freq, Q); +- break; +- case BQ_LOWSHELF: +- biquad_lowshelf(bq, freq, gain); +- break; +- case BQ_HIGHSHELF: +- biquad_highshelf(bq, freq, gain); +- break; +- case BQ_PEAKING: +- biquad_peaking(bq, freq, Q, gain); +- break; +- case BQ_NOTCH: +- biquad_notch(bq, freq, Q); +- break; +- case BQ_ALLPASS: +- biquad_allpass(bq, freq, Q); +- break; +- case BQ_NONE: ++ biquad_highpass(bq, freq); + break; + } + } +diff --git a/src/pulsecore/filter/biquad.h b/src/pulsecore/filter/biquad.h +index c584aa9..bb8f2fb 100644 +--- a/src/pulsecore/filter/biquad.h ++++ b/src/pulsecore/filter/biquad.h +@@ -21,21 +21,12 @@ extern "C" { + struct biquad { + float b0, b1, b2; + float a1, a2; +- float x1, x2; +- float y1, y2; + }; + + /* The type of the biquad filters */ + enum biquad_type { +- BQ_NONE, + BQ_LOWPASS, + BQ_HIGHPASS, +- BQ_BANDPASS, +- BQ_LOWSHELF, +- BQ_HIGHSHELF, +- BQ_PEAKING, +- BQ_NOTCH, +- BQ_ALLPASS + }; + + /* Initialize a biquad filter parameters from its type and parameters. +@@ -44,11 +35,8 @@ enum biquad_type { + * 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); ++void biquad_set(struct biquad *bq, enum biquad_type type, double freq); + + #ifdef __cplusplus + } /* extern "C" */ +diff --git a/src/pulsecore/filter/crossover.c b/src/pulsecore/filter/crossover.c +index 0a571c3..dab34af 100644 +--- a/src/pulsecore/filter/crossover.c ++++ b/src/pulsecore/filter/crossover.c +@@ -9,18 +9,11 @@ + + #include <pulsecore/macro.h> + +-#include "biquad.h" + #include "crossover.h" + + void lr4_set(struct lr4 *lr4, enum biquad_type type, float freq) + { +- struct biquad q; +- biquad_set(&q, type, freq, 0, 0); +- lr4->b0 = q.b0; +- lr4->b1 = q.b1; +- lr4->b2 = q.b2; +- lr4->a1 = q.a1; +- lr4->a2 = q.a2; ++ biquad_set(&lr4->bq, type, freq); + lr4->x1 = 0; + lr4->x2 = 0; + lr4->y1 = 0; +@@ -37,11 +30,11 @@ void lr4_process_float32(struct lr4 *lr4, int samples, int channels, float *src, + float ly2 = lr4->y2; + float lz1 = lr4->z1; + float lz2 = lr4->z2; +- float lb0 = lr4->b0; +- float lb1 = lr4->b1; +- float lb2 = lr4->b2; +- float la1 = lr4->a1; +- float la2 = lr4->a2; ++ 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 * channels; i += channels) { +@@ -74,11 +67,11 @@ void lr4_process_s16(struct lr4 *lr4, int samples, int channels, short *src, sho + float ly2 = lr4->y2; + float lz1 = lr4->z1; + float lz2 = lr4->z2; +- float lb0 = lr4->b0; +- float lb1 = lr4->b1; +- float lb2 = lr4->b2; +- float la1 = lr4->a1; +- float la2 = lr4->a2; ++ 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 * channels; i += channels) { +@@ -102,168 +95,3 @@ void lr4_process_s16(struct lr4 *lr4, int samples, int channels, short *src, sho + lr4->z1 = lz1; + lr4->z2 = lz2; + } +- +- +-/* Split input data using two LR4 filters, put the result into the input array +- * and another array. +- * +- * data0 --+-- lp --> data0 +- * | +- * \-- hp --> data1 +- */ +-static void lr4_split(struct lr4 *lp, struct lr4 *hp, int count, float *data0, +- float *data1) +-{ +- float lx1 = lp->x1; +- float lx2 = lp->x2; +- float ly1 = lp->y1; +- float ly2 = lp->y2; +- float lz1 = lp->z1; +- float lz2 = lp->z2; +- float lb0 = lp->b0; +- float lb1 = lp->b1; +- float lb2 = lp->b2; +- float la1 = lp->a1; +- float la2 = lp->a2; +- +- float hx1 = hp->x1; +- float hx2 = hp->x2; +- float hy1 = hp->y1; +- float hy2 = hp->y2; +- float hz1 = hp->z1; +- float hz2 = hp->z2; +- float hb0 = hp->b0; +- float hb1 = hp->b1; +- float hb2 = hp->b2; +- float ha1 = hp->a1; +- float ha2 = hp->a2; +- +- int i; +- for (i = 0; i < count; i++) { +- float x, y, z; +- x = data0[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; +- data0[i] = z; +- +- y = hb0*x + hb1*hx1 + hb2*hx2 - ha1*hy1 - ha2*hy2; +- z = hb0*y + hb1*hy1 + hb2*hy2 - ha1*hz1 - ha2*hz2; +- hx2 = hx1; +- hx1 = x; +- hy2 = hy1; +- hy1 = y; +- hz2 = hz1; +- hz1 = z; +- data1[i] = z; +- } +- +- lp->x1 = lx1; +- lp->x2 = lx2; +- lp->y1 = ly1; +- lp->y2 = ly2; +- lp->z1 = lz1; +- lp->z2 = lz2; +- +- hp->x1 = hx1; +- hp->x2 = hx2; +- hp->y1 = hy1; +- hp->y2 = hy2; +- hp->z1 = hz1; +- hp->z2 = hz2; +-} +- +-/* Split input data using two LR4 filters and sum them back to the original +- * data array. +- * +- * data --+-- lp --+--> data +- * | | +- * \-- hp --/ +- */ +-static void lr4_merge(struct lr4 *lp, struct lr4 *hp, int count, float *data) +-{ +- float lx1 = lp->x1; +- float lx2 = lp->x2; +- float ly1 = lp->y1; +- float ly2 = lp->y2; +- float lz1 = lp->z1; +- float lz2 = lp->z2; +- float lb0 = lp->b0; +- float lb1 = lp->b1; +- float lb2 = lp->b2; +- float la1 = lp->a1; +- float la2 = lp->a2; +- +- float hx1 = hp->x1; +- float hx2 = hp->x2; +- float hy1 = hp->y1; +- float hy2 = hp->y2; +- float hz1 = hp->z1; +- float hz2 = hp->z2; +- float hb0 = hp->b0; +- float hb1 = hp->b1; +- float hb2 = hp->b2; +- float ha1 = hp->a1; +- float ha2 = hp->a2; +- +- int i; +- for (i = 0; i < count; i++) { +- float x, y, z; +- x = data[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; +- +- y = hb0*x + hb1*hx1 + hb2*hx2 - ha1*hy1 - ha2*hy2; +- z = hb0*y + hb1*hy1 + hb2*hy2 - ha1*hz1 - ha2*hz2; +- hx2 = hx1; +- hx1 = x; +- hy2 = hy1; +- hy1 = y; +- hz2 = hz1; +- hz1 = z; +- data[i] = z + lz1; +- } +- +- lp->x1 = lx1; +- lp->x2 = lx2; +- lp->y1 = ly1; +- lp->y2 = ly2; +- lp->z1 = lz1; +- lp->z2 = lz2; +- +- hp->x1 = hx1; +- hp->x2 = hx2; +- hp->y1 = hy1; +- hp->y2 = hy2; +- hp->z1 = hz1; +- hp->z2 = hz2; +-} +- +-void crossover_init(struct crossover *xo, float freq1, float freq2) +-{ +- int i; +- for (i = 0; i < 3; i++) { +- float f = (i == 0) ? freq1 : freq2; +- lr4_set(&xo->lp[i], BQ_LOWPASS, f); +- lr4_set(&xo->hp[i], BQ_HIGHPASS, f); +- } +-} +- +-void crossover_process(struct crossover *xo, int count, float *data0, +- float *data1, float *data2) +-{ +- lr4_split(&xo->lp[0], &xo->hp[0], count, data0, data1); +- lr4_merge(&xo->lp[1], &xo->hp[1], count, data0); +- lr4_split(&xo->lp[2], &xo->hp[2], count, data1, data2); +-} +diff --git a/src/pulsecore/filter/crossover.h b/src/pulsecore/filter/crossover.h +index a88f5b6..c5c9765 100644 +--- a/src/pulsecore/filter/crossover.h ++++ b/src/pulsecore/filter/crossover.h +@@ -6,10 +6,7 @@ + #ifndef CROSSOVER_H_ + #define CROSSOVER_H_ + +-#ifdef __cplusplus +-extern "C" { +-#endif +- ++#include "biquad.h" + /* An LR4 filter is two biquads with the same parameters connected in series: + * + * x -- [BIQUAD] -- y -- [BIQUAD] -- z +@@ -18,8 +15,7 @@ extern "C" { + * The variable [xyz][12] keep the history values. + */ + struct lr4 { +- float b0, b1, b2; +- float a1, a2; ++ struct biquad bq; + float x1, x2; + float y1, y2; + float z1, z2; +@@ -30,47 +26,4 @@ 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); + +- +-/* Three bands crossover filter: +- * +- * INPUT --+-- lp0 --+-- lp1 --+---> LOW (0) +- * | | | +- * | \-- hp1 --/ +- * | +- * \-- hp0 --+-- lp2 ------> MID (1) +- * | +- * \-- hp2 ------> HIGH (2) +- * +- * [f0] [f1] +- * +- * Each lp or hp is an LR4 filter, which consists of two second-order +- * lowpass or highpass butterworth filters. +- */ +-struct crossover { +- struct lr4 lp[3], hp[3]; +-}; +- +-/* Initializes a crossover filter +- * Args: +- * xo - The crossover filter we want to initialize. +- * freq1 - The normalized frequency splits low and mid band. +- * freq2 - The normalized frequency splits mid and high band. +- */ +-void crossover_init(struct crossover *xo, float freq1, float freq2); +- +-/* Splits input samples to three bands. +- * Args: +- * xo - The crossover filter to use. +- * count - The number of input samples. +- * data0 - The input samples, also the place to store low band output. +- * data1 - The place to store mid band output. +- * data2 - The place to store high band output. +- */ +-void crossover_process(struct crossover *xo, int count, float *data0, +- float *data1, float *data2); +- +-#ifdef __cplusplus +-} /* extern "C" */ +-#endif +- + #endif /* CROSSOVER_H_ */ +-- +1.9.1 + diff --git a/debian/patches/0303-lfe-filter-change-the-crossover-frequency-as-a-param.patch b/debian/patches/0303-lfe-filter-change-the-crossover-frequency-as-a-param.patch new file mode 100644 index 0000000..20c6213 --- /dev/null +++ b/debian/patches/0303-lfe-filter-change-the-crossover-frequency-as-a-param.patch @@ -0,0 +1,274 @@ +From c36e191ce5d38173424c3db9ba06638fd6b8408e Mon Sep 17 00:00:00 2001 +From: Hui Wang <[email protected]> +Date: Tue, 24 Mar 2015 10:29:16 +0100 +Subject: [PATCH 303/311] lfe-filter: change the crossover frequency as a + parameter + +Add a user defined parameter lfe-crossover-freq for the lfe-filter, +to pass this parameter to the lfe-filter, we need to change the +pa_resampler_new() API as well. + +Signed-off-by: Hui Wang <[email protected]> +--- + man/pulse-daemon.conf.5.xml.in | 5 +++++ + src/daemon/daemon-conf.c | 3 +++ + src/daemon/daemon-conf.h | 1 + + src/daemon/daemon.conf.in | 1 + + src/daemon/main.c | 1 + + src/modules/module-virtual-surround-sink.c | 2 +- + src/pulsecore/core.c | 1 + + src/pulsecore/core.h | 1 + + src/pulsecore/resampler.c | 6 +++--- + src/pulsecore/resampler.h | 1 + + src/pulsecore/sink-input.c | 2 ++ + src/pulsecore/source-output.c | 2 ++ + src/tests/remix-test.c | 3 ++- + src/tests/resampler-test.c | 7 ++++--- + 14 files changed, 28 insertions(+), 8 deletions(-) + +Index: pulseaudio/man/pulse-daemon.conf.5.xml.in +=================================================================== +--- pulseaudio.orig/man/pulse-daemon.conf.5.xml.in 2015-05-13 14:59:47.094147583 +0800 ++++ pulseaudio/man/pulse-daemon.conf.5.xml.in 2015-05-13 14:59:47.086147583 +0800 +@@ -121,6 +121,11 @@ + </option> + + <option> ++ <p><opt>lfe-crossover-freq=</opt> The crossover frequency (in Hz) for the ++ LFE filter. Defaults to 120 Hz.</p> ++ </option> ++ ++ <option> + <p><opt>use-pid-file=</opt> Create a PID file in the runtime directory + (<file>$XDG_RUNTIME_DIR/pulse/pid</file>). If this is enabled you may + use commands like <opt>--kill</opt> or <opt>--check</opt>. If +Index: pulseaudio/src/daemon/daemon-conf.c +=================================================================== +--- pulseaudio.orig/src/daemon/daemon-conf.c 2015-05-13 14:59:47.094147583 +0800 ++++ pulseaudio/src/daemon/daemon-conf.c 2015-05-13 14:59:47.086147583 +0800 +@@ -83,6 +83,7 @@ + .resample_method = PA_RESAMPLER_AUTO, + .disable_remixing = false, + .disable_lfe_remixing = true, ++ .lfe_crossover_freq = 120, + .config_file = NULL, + .use_pid_file = true, + .system_instance = false, +@@ -553,6 +554,7 @@ + { "enable-remixing", pa_config_parse_not_bool, &c->disable_remixing, NULL }, + { "disable-lfe-remixing", pa_config_parse_bool, &c->disable_lfe_remixing, NULL }, + { "enable-lfe-remixing", pa_config_parse_not_bool, &c->disable_lfe_remixing, NULL }, ++ { "lfe-crossover-freq", pa_config_parse_unsigned, &c->lfe_crossover_freq, NULL }, + { "load-default-script-file", pa_config_parse_bool, &c->load_default_script_file, NULL }, + { "shm-size-bytes", pa_config_parse_size, &c->shm_size, NULL }, + { "log-meta", pa_config_parse_bool, &c->log_meta, NULL }, +@@ -745,6 +747,7 @@ + pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method)); + pa_strbuf_printf(s, "enable-remixing = %s\n", pa_yes_no(!c->disable_remixing)); + pa_strbuf_printf(s, "enable-lfe-remixing = %s\n", pa_yes_no(!c->disable_lfe_remixing)); ++ pa_strbuf_printf(s, "lfe-crossover-freq = %u\n", c->lfe_crossover_freq); + pa_strbuf_printf(s, "default-sample-format = %s\n", pa_sample_format_to_string(c->default_sample_spec.format)); + pa_strbuf_printf(s, "default-sample-rate = %u\n", c->default_sample_spec.rate); + pa_strbuf_printf(s, "alternate-sample-rate = %u\n", c->alternate_sample_rate); +Index: pulseaudio/src/daemon/daemon-conf.h +=================================================================== +--- pulseaudio.orig/src/daemon/daemon-conf.h 2015-05-13 14:59:47.094147583 +0800 ++++ pulseaudio/src/daemon/daemon-conf.h 2015-05-13 14:59:47.086147583 +0800 +@@ -127,6 +127,7 @@ + unsigned default_n_fragments, default_fragment_size_msec; + unsigned deferred_volume_safety_margin_usec; + int deferred_volume_extra_delay_usec; ++ unsigned lfe_crossover_freq; + pa_sample_spec default_sample_spec; + uint32_t alternate_sample_rate; + pa_channel_map default_channel_map; +Index: pulseaudio/src/daemon/daemon.conf.in +=================================================================== +--- pulseaudio.orig/src/daemon/daemon.conf.in 2015-05-13 14:59:47.094147583 +0800 ++++ pulseaudio/src/daemon/daemon.conf.in 2015-05-13 14:59:47.086147583 +0800 +@@ -55,6 +55,7 @@ + ; resample-method = speex-float-1 + ; enable-remixing = yes + ; enable-lfe-remixing = no ++; lfe-crossover-freq = 120 + + flat-volumes = no + +Index: pulseaudio/src/daemon/main.c +=================================================================== +--- pulseaudio.orig/src/daemon/main.c 2015-05-13 14:59:47.094147583 +0800 ++++ pulseaudio/src/daemon/main.c 2015-05-13 14:59:47.090147583 +0800 +@@ -1042,6 +1042,7 @@ + c->default_fragment_size_msec = conf->default_fragment_size_msec; + c->deferred_volume_safety_margin_usec = conf->deferred_volume_safety_margin_usec; + c->deferred_volume_extra_delay_usec = conf->deferred_volume_extra_delay_usec; ++ c->lfe_crossover_freq = conf->lfe_crossover_freq; + c->exit_idle_time = conf->exit_idle_time; + c->scache_idle_time = conf->scache_idle_time; + c->resample_method = conf->resample_method; +Index: pulseaudio/src/modules/module-virtual-surround-sink.c +=================================================================== +--- pulseaudio.orig/src/modules/module-virtual-surround-sink.c 2015-05-13 14:59:47.094147583 +0800 ++++ pulseaudio/src/modules/module-virtual-surround-sink.c 2015-05-13 14:59:47.090147583 +0800 +@@ -743,7 +743,7 @@ + pa_memblock_unref(silence.memblock); + + /* resample hrir */ +- resampler = pa_resampler_new(u->sink->core->mempool, &hrir_temp_ss, &hrir_map, &hrir_ss, &hrir_map, ++ resampler = pa_resampler_new(u->sink->core->mempool, &hrir_temp_ss, &hrir_map, &hrir_ss, &hrir_map, u->sink->core->lfe_crossover_freq, + PA_RESAMPLER_SRC_SINC_BEST_QUALITY, PA_RESAMPLER_NO_REMAP); + + u->hrir_samples = hrir_temp_chunk.length / pa_frame_size(&hrir_temp_ss) * hrir_ss.rate / hrir_temp_ss.rate; +Index: pulseaudio/src/pulsecore/core.c +=================================================================== +--- pulseaudio.orig/src/pulsecore/core.c 2015-05-13 14:59:47.094147583 +0800 ++++ pulseaudio/src/pulsecore/core.c 2015-05-13 14:59:47.090147583 +0800 +@@ -144,6 +144,7 @@ + c->realtime_priority = 5; + c->disable_remixing = false; + c->disable_lfe_remixing = false; ++ c->lfe_crossover_freq = 120; + c->deferred_volume = true; + c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 1; + +Index: pulseaudio/src/pulsecore/core.h +=================================================================== +--- pulseaudio.orig/src/pulsecore/core.h 2015-05-13 14:59:47.094147583 +0800 ++++ pulseaudio/src/pulsecore/core.h 2015-05-13 14:59:47.090147583 +0800 +@@ -161,6 +161,7 @@ + unsigned default_n_fragments, default_fragment_size_msec; + unsigned deferred_volume_safety_margin_usec; + int deferred_volume_extra_delay_usec; ++ unsigned lfe_crossover_freq; + + pa_defer_event *module_defer_unload_event; + pa_hashmap *modules_pending_unload; /* pa_module -> pa_module (hashmap-as-a-set) */ +Index: pulseaudio/src/pulsecore/resampler.c +=================================================================== +--- pulseaudio.orig/src/pulsecore/resampler.c 2015-05-13 14:59:47.094147583 +0800 ++++ pulseaudio/src/pulsecore/resampler.c 2015-05-13 14:59:47.090147583 +0800 +@@ -298,6 +298,7 @@ + const pa_channel_map *am, + const pa_sample_spec *b, + const pa_channel_map *bm, ++ unsigned crossover_freq, + pa_resample_method_t method, + pa_resample_flags_t flags) { + +@@ -396,9 +397,8 @@ + 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)"); ++ r->lfe_filter = pa_lfe_filter_new(&wss, &r->o_cm, (float)crossover_freq); ++ pa_log_debug(" lfe filter activated (LR4 type), the crossover_freq = %uHz", crossover_freq); + } + + /* initialize implementation */ +Index: pulseaudio/src/pulsecore/resampler.h +=================================================================== +--- pulseaudio.orig/src/pulsecore/resampler.h 2015-05-13 14:59:47.094147583 +0800 ++++ pulseaudio/src/pulsecore/resampler.h 2015-05-13 14:59:47.090147583 +0800 +@@ -115,6 +115,7 @@ + const pa_channel_map *am, + const pa_sample_spec *b, + const pa_channel_map *bm, ++ unsigned crossover_freq, + pa_resample_method_t resample_method, + pa_resample_flags_t flags); + +Index: pulseaudio/src/pulsecore/sink-input.c +=================================================================== +--- pulseaudio.orig/src/pulsecore/sink-input.c 2015-05-13 14:59:47.094147583 +0800 ++++ pulseaudio/src/pulsecore/sink-input.c 2015-05-13 14:59:47.090147583 +0800 +@@ -451,6 +451,7 @@ + core->mempool, + &data->sample_spec, &data->channel_map, + &data->sink->sample_spec, &data->sink->channel_map, ++ core->lfe_crossover_freq, + data->resample_method, + ((data->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | + ((data->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | +@@ -2168,6 +2169,7 @@ + new_resampler = pa_resampler_new(i->core->mempool, + &i->sample_spec, &i->channel_map, + &i->sink->sample_spec, &i->sink->channel_map, ++ i->core->lfe_crossover_freq, + i->requested_resample_method, + ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | + ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | +Index: pulseaudio/src/pulsecore/source-output.c +=================================================================== +--- pulseaudio.orig/src/pulsecore/source-output.c 2015-05-13 14:59:47.094147583 +0800 ++++ pulseaudio/src/pulsecore/source-output.c 2015-05-13 14:59:47.090147583 +0800 +@@ -396,6 +396,7 @@ + core->mempool, + &data->source->sample_spec, &data->source->channel_map, + &data->sample_spec, &data->channel_map, ++ core->lfe_crossover_freq, + data->resample_method, + ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | + ((data->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | +@@ -1625,6 +1626,7 @@ + new_resampler = pa_resampler_new(o->core->mempool, + &o->source->sample_spec, &o->source->channel_map, + &o->sample_spec, &o->channel_map, ++ o->core->lfe_crossover_freq, + o->requested_resample_method, + ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | + ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | +Index: pulseaudio/src/tests/remix-test.c +=================================================================== +--- pulseaudio.orig/src/tests/remix-test.c 2015-05-13 14:59:47.094147583 +0800 ++++ pulseaudio/src/tests/remix-test.c 2015-05-13 14:59:47.090147583 +0800 +@@ -47,6 +47,7 @@ + + unsigned i, j; + pa_mempool *pool; ++ unsigned crossover_freq = 120; + + pa_log_set_level(PA_LOG_DEBUG); + +@@ -66,7 +67,7 @@ + ss1.rate = ss2.rate = 44100; + ss1.format = ss2.format = PA_SAMPLE_S16NE; + +- r = pa_resampler_new(pool, &ss1, &maps[i], &ss2, &maps[j], PA_RESAMPLER_AUTO, 0); ++ r = pa_resampler_new(pool, &ss1, &maps[i], &ss2, &maps[j], crossover_freq, PA_RESAMPLER_AUTO, 0); + + /* We don't really care for the resampler. We just want to + * see the remixing debug output. */ +Index: pulseaudio/src/tests/resampler-test.c +=================================================================== +--- pulseaudio.orig/src/tests/resampler-test.c 2015-05-13 14:59:47.094147583 +0800 ++++ pulseaudio/src/tests/resampler-test.c 2015-05-13 14:59:47.090147583 +0800 +@@ -303,6 +303,7 @@ + bool all_formats = true; + pa_resample_method_t method; + int seconds; ++ unsigned crossover_freq = 120; + + static const struct option long_options[] = { + {"help", 0, NULL, 'h'}, +@@ -419,7 +420,7 @@ + b.rate, b.channels, pa_sample_format_to_string(b.format)); + + ts = pa_rtclock_now(); +- pa_assert_se(resampler = pa_resampler_new(pool, &a, NULL, &b, NULL, method, 0)); ++ pa_assert_se(resampler = pa_resampler_new(pool, &a, NULL, &b, NULL, crossover_freq, method, 0)); + pa_log_info("init: %llu", (long long unsigned)(pa_rtclock_now() - ts)); + + i.memblock = pa_memblock_new(pool, pa_usec_to_bytes(1*PA_USEC_PER_SEC, &a)); +@@ -450,8 +451,8 @@ + pa_sample_format_to_string(b.format), + pa_sample_format_to_string(a.format)); + +- pa_assert_se(forth = pa_resampler_new(pool, &a, NULL, &b, NULL, method, 0)); +- pa_assert_se(back = pa_resampler_new(pool, &b, NULL, &a, NULL, method, 0)); ++ pa_assert_se(forth = pa_resampler_new(pool, &a, NULL, &b, NULL, crossover_freq, method, 0)); ++ pa_assert_se(back = pa_resampler_new(pool, &b, NULL, &a, NULL, crossover_freq, method, 0)); + + i.memblock = generate_block(pool, &a); + i.length = pa_memblock_get_length(i.memblock); diff --git a/debian/patches/0304-memblock-Change-pa_memblock_new_malloced-to-an-inlin.patch b/debian/patches/0304-memblock-Change-pa_memblock_new_malloced-to-an-inlin.patch new file mode 100644 index 0000000..680f59d --- /dev/null +++ b/debian/patches/0304-memblock-Change-pa_memblock_new_malloced-to-an-inlin.patch @@ -0,0 +1,42 @@ +From d0e8b0fe077b2d59e111c57ed5ed75b7a7d3e92d Mon Sep 17 00:00:00 2001 +From: David Henningsson <[email protected]> +Date: Wed, 25 Mar 2015 10:13:13 +0100 +Subject: [PATCH 304/311] memblock: Change pa_memblock_new_malloced to an + inline function + +To avoid the macro trap: I call pa_memblock_new_malloced with +"pa_xmemdup" as data parameter, and that would expand to *two* +calls to pa_xmemdup in case that remains a macro, which is clearly +not intended. + +Signed-off-by: David Henningsson <[email protected]> +--- + src/pulsecore/memblock.h | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h +index dbea213..4faef75 100644 +--- a/src/pulsecore/memblock.h ++++ b/src/pulsecore/memblock.h +@@ -27,6 +27,7 @@ typedef struct pa_memblock pa_memblock; + #include <inttypes.h> + + #include <pulse/def.h> ++#include <pulse/xmalloc.h> + #include <pulsecore/atomic.h> + #include <pulsecore/memchunk.h> + +@@ -86,7 +87,9 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *, size_t length); + pa_memblock *pa_memblock_new_user(pa_mempool *, void *data, size_t length, pa_free_cb_t free_cb, void *free_cb_data, bool read_only); + + /* A special case of pa_memblock_new_user: take a memory buffer previously allocated with pa_xmalloc() */ +-#define pa_memblock_new_malloced(p,data,length) pa_memblock_new_user(p, data, length, pa_xfree, data, 0) ++static inline pa_memblock *pa_memblock_new_malloced(pa_mempool *p, void *data, size_t length) { ++ return pa_memblock_new_user(p, data, length, pa_xfree, data, 0); ++} + + /* Allocate a new memory block of type PA_MEMBLOCK_FIXED */ + pa_memblock *pa_memblock_new_fixed(pa_mempool *, void *data, size_t length, bool read_only); +-- +1.9.1 + diff --git a/debian/patches/0305-lfe-filter-Add-rewind-support.patch b/debian/patches/0305-lfe-filter-Add-rewind-support.patch new file mode 100644 index 0000000..15ad0f3 --- /dev/null +++ b/debian/patches/0305-lfe-filter-Add-rewind-support.patch @@ -0,0 +1,231 @@ +From defc2b702bd7358634e70635a7614172836d632e Mon Sep 17 00:00:00 2001 +From: David Henningsson <[email protected]> +Date: Tue, 24 Mar 2015 10:29:17 +0100 +Subject: [PATCH 305/311] lfe-filter: Add rewind support + +Store current filter state at every normal block process. +When a rewind happens, rewind back to the nearest saved state, +then calculate forward to the actual sample position. + +Signed-off-by: David Henningsson <[email protected]> +--- + src/pulsecore/filter/lfe-filter.c | 109 +++++++++++++++++++++++++++++++++++--- + src/pulsecore/filter/lfe-filter.h | 5 +- + src/pulsecore/resampler.c | 3 +- + 3 files changed, 108 insertions(+), 9 deletions(-) + +Index: pulseaudio/src/pulsecore/filter/lfe-filter.c +=================================================================== +--- pulseaudio.orig/src/pulsecore/filter/lfe-filter.c 2015-05-13 14:59:58.310147765 +0800 ++++ pulseaudio/src/pulsecore/filter/lfe-filter.c 2015-05-13 14:59:58.306147765 +0800 +@@ -23,9 +23,20 @@ + + #include "lfe-filter.h" + #include <pulse/xmalloc.h> ++#include <pulsecore/flist.h> ++#include <pulsecore/llist.h> + #include <pulsecore/filter/biquad.h> + #include <pulsecore/filter/crossover.h> + ++struct saved_state { ++ PA_LLIST_FIELDS(struct saved_state); ++ pa_memchunk chunk; ++ int64_t index; ++ struct lr4 lr4[PA_CHANNELS_MAX]; ++}; ++ ++PA_STATIC_FLIST_DECLARE(lfe_state, 0, pa_xfree); ++ + /* 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 +@@ -35,24 +46,37 @@ + */ + + struct pa_lfe_filter { ++ int64_t index; ++ PA_LLIST_HEAD(struct saved_state, saved); + float crossover; + pa_channel_map cm; + pa_sample_spec ss; ++ size_t maxrewind; + 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) { ++static void remove_state(pa_lfe_filter_t *f, struct saved_state *s) { ++ PA_LLIST_REMOVE(struct saved_state, f->saved, s); ++ pa_memblock_unref(s->chunk.memblock); ++ pa_xfree(s); ++} ++ ++pa_lfe_filter_t * pa_lfe_filter_new(const pa_sample_spec* ss, const pa_channel_map* cm, float crossover_freq, size_t maxrewind) { + + pa_lfe_filter_t *f = pa_xnew0(struct pa_lfe_filter, 1); + f->crossover = crossover_freq; + f->cm = *cm; + f->ss = *ss; ++ f->maxrewind = maxrewind; + pa_lfe_filter_update_rate(f, ss->rate); + return f; + } + + void pa_lfe_filter_free(pa_lfe_filter_t *f) { ++ while (f->saved) ++ remove_state(f, f->saved); ++ + pa_xfree(f); + } + +@@ -60,26 +84,61 @@ + pa_lfe_filter_update_rate(f, f->ss.rate); + } + +-pa_memchunk * pa_lfe_filter_process(pa_lfe_filter_t *f, pa_memchunk *buf) { ++static void process_block(pa_lfe_filter_t *f, pa_memchunk *buf, bool store_result) { + int samples = buf->length / pa_frame_size(&f->ss); + +- if (!f->active) +- return buf; ++ void *garbage = store_result ? NULL : pa_xmalloc(buf->length); ++ + 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]); ++ lr4_process_float32(&f->lr4[i], samples, f->cm.channels, &data[i], garbage ? garbage : &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]); ++ lr4_process_s16(&f->lr4[i], samples, f->cm.channels, &data[i], garbage ? garbage : &data[i]); + pa_memblock_release(buf->memblock); + } + else pa_assert_not_reached(); ++ ++ pa_xfree(garbage); ++ f->index += samples; ++} ++ ++pa_memchunk * pa_lfe_filter_process(pa_lfe_filter_t *f, pa_memchunk *buf) { ++ struct saved_state *s, *s2; ++ void *data; ++ ++ if (!f->active) ++ return buf; ++ ++ /* Remove old states (FIXME: we could do better than searching the entire array here?) */ ++ PA_LLIST_FOREACH_SAFE(s, s2, f->saved) ++ if (s->index + (int64_t) (s->chunk.length / pa_frame_size(&f->ss) + f->maxrewind) < f->index) ++ remove_state(f, s); ++ ++ /* Insert our existing state into the flist */ ++ if ((s = pa_flist_pop(PA_STATIC_FLIST_GET(lfe_state))) == NULL) ++ s = pa_xnew(struct saved_state, 1); ++ PA_LLIST_INIT(struct saved_state, s); ++ ++ /* TODO: This actually memcpys the entire chunk into a new allocation, because we need to retain the original ++ in case of rewinding. Investigate whether this can be avoided. */ ++ data = pa_memblock_acquire_chunk(buf); ++ s->chunk.memblock = pa_memblock_new_malloced(pa_memblock_get_pool(buf->memblock), pa_xmemdup(data, buf->length), buf->length); ++ s->chunk.length = buf->length; ++ s->chunk.index = 0; ++ pa_memblock_release(buf->memblock); ++ ++ s->index = f->index; ++ memcpy(s->lr4, f->lr4, sizeof(struct lr4) * f->cm.channels); ++ PA_LLIST_PREPEND(struct saved_state, f->saved, s); ++ ++ process_block(f, buf, true); + return buf; + } + +@@ -87,6 +146,10 @@ + int i; + float biquad_freq = f->crossover / (new_rate / 2); + ++ while (f->saved) ++ remove_state(f, f->saved); ++ ++ f->index = 0; + 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); +@@ -99,3 +162,37 @@ + + f->active = true; + } ++ ++void pa_lfe_filter_rewind(pa_lfe_filter_t *f, size_t amount) { ++ struct saved_state *i, *s = NULL; ++ size_t samples = amount / pa_frame_size(&f->ss); ++ f->index -= samples; ++ ++ /* Find the closest saved position */ ++ PA_LLIST_FOREACH(i, f->saved) { ++ if (i->index > f->index) ++ continue; ++ if (s == NULL || i->index > s->index) ++ s = i; ++ } ++ if (s == NULL) { ++ pa_log_debug("Rewinding LFE filter %lu samples to position %lli. No saved state found", samples, (long long) f->index); ++ pa_lfe_filter_update_rate(f, f->ss.rate); ++ return; ++ } ++ pa_log_debug("Rewinding LFE filter %lu samples to position %lli. Found saved state at position %lli", ++ samples, (long long) f->index, (long long) s->index); ++ memcpy(f->lr4, s->lr4, sizeof(struct lr4) * f->cm.channels); ++ ++ /* now fast forward to the actual position */ ++ if (f->index > s->index) { ++ pa_memchunk x = s->chunk; ++ x.length = (f->index - s->index) * pa_frame_size(&f->ss); ++ if (x.length > s->chunk.length) { ++ pa_log_error("Hole in stream, cannot fast forward LFE filter"); ++ return; ++ } ++ f->index = s->index; ++ process_block(f, &x, false); ++ } ++} +Index: pulseaudio/src/pulsecore/filter/lfe-filter.h +=================================================================== +--- pulseaudio.orig/src/pulsecore/filter/lfe-filter.h 2015-05-13 14:59:58.310147765 +0800 ++++ pulseaudio/src/pulsecore/filter/lfe-filter.h 2015-05-13 14:59:58.306147765 +0800 +@@ -25,13 +25,14 @@ + #include <pulse/sample.h> + #include <pulse/channelmap.h> + #include <pulsecore/memchunk.h> +- ++#include <pulsecore/memblockq.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); ++pa_lfe_filter_t * pa_lfe_filter_new(const pa_sample_spec* ss, const pa_channel_map* cm, float crossover_freq, size_t maxrewind); + void pa_lfe_filter_free(pa_lfe_filter_t *); + void pa_lfe_filter_reset(pa_lfe_filter_t *); ++void pa_lfe_filter_rewind(pa_lfe_filter_t *, size_t amount); + 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); + +Index: pulseaudio/src/pulsecore/resampler.c +=================================================================== +--- pulseaudio.orig/src/pulsecore/resampler.c 2015-05-13 14:59:58.310147765 +0800 ++++ pulseaudio/src/pulsecore/resampler.c 2015-05-13 14:59:58.306147765 +0800 +@@ -397,7 +397,8 @@ + if (lfe_filter_required) { + pa_sample_spec wss = r->o_ss; + wss.format = r->work_format; +- r->lfe_filter = pa_lfe_filter_new(&wss, &r->o_cm, (float)crossover_freq); ++ /* FIXME: For now just hardcode maxrewind to 3 seconds */ ++ r->lfe_filter = pa_lfe_filter_new(&wss, &r->o_cm, (float)crossover_freq, b->rate * 3); + pa_log_debug(" lfe filter activated (LR4 type), the crossover_freq = %uHz", crossover_freq); + } + diff --git a/debian/patches/0306-resampler-Make-some-basic-functions-for-rewinding.patch b/debian/patches/0306-resampler-Make-some-basic-functions-for-rewinding.patch new file mode 100644 index 0000000..0f5e6d5 --- /dev/null +++ b/debian/patches/0306-resampler-Make-some-basic-functions-for-rewinding.patch @@ -0,0 +1,96 @@ +From 7fb531d9369bb8a8edcdf84633e5e455b0fa7e40 Mon Sep 17 00:00:00 2001 +From: David Henningsson <[email protected]> +Date: Tue, 24 Mar 2015 10:29:18 +0100 +Subject: [PATCH 306/311] resampler: Make some basic functions for rewinding + +The resampler framework just forwards the request to the lfe filter. +There are no resampler impl that can rewind yet, so just reset the +resampler impl instead of properly rewinding yet. + +Signed-off-by: David Henningsson <[email protected]> +--- + src/pulsecore/resampler.c | 18 ++++++++++++++++-- + src/pulsecore/resampler.h | 3 +++ + src/pulsecore/sink-input.c | 4 ++-- + src/pulsecore/source-output.c | 2 +- + 4 files changed, 22 insertions(+), 5 deletions(-) + +Index: pulseaudio/src/pulsecore/resampler.c +=================================================================== +--- pulseaudio.orig/src/pulsecore/resampler.c 2015-05-13 15:00:12.066147988 +0800 ++++ pulseaudio/src/pulsecore/resampler.c 2015-05-13 15:00:12.062147988 +0800 +@@ -556,6 +556,20 @@ + *r->have_leftover = false; + } + ++void pa_resampler_rewind(pa_resampler *r, size_t out_frames) { ++ pa_assert(r); ++ ++ /* For now, we don't have any rewindable resamplers, so we just ++ reset the resampler instead (and hope that nobody hears the difference). */ ++ if (r->impl.reset) ++ r->impl.reset(r); ++ ++ if (r->lfe_filter) ++ pa_lfe_filter_rewind(r->lfe_filter, out_frames); ++ ++ *r->have_leftover = false; ++} ++ + pa_resample_method_t pa_resampler_get_method(pa_resampler *r) { + pa_assert(r); + +@@ -793,8 +807,8 @@ + } else { + + /* 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 ++ * algorithm is relatively simple, does not do spacialization, or delay ++ * elements. LFE filters are done after the remap step. Patches are always + * welcome, though. Oh, and it doesn't do any matrix decoding. (Which + * probably wouldn't make any sense anyway.) + * +Index: pulseaudio/src/pulsecore/resampler.h +=================================================================== +--- pulseaudio.orig/src/pulsecore/resampler.h 2015-05-13 15:00:12.066147988 +0800 ++++ pulseaudio/src/pulsecore/resampler.h 2015-05-13 15:00:12.062147988 +0800 +@@ -142,6 +142,9 @@ + /* Reinitialize state of the resampler, possibly due to seeking or other discontinuities */ + void pa_resampler_reset(pa_resampler *r); + ++/* Rewind resampler */ ++void pa_resampler_rewind(pa_resampler *r, size_t out_frames); ++ + /* Return the resampling method of the resampler object */ + pa_resample_method_t pa_resampler_get_method(pa_resampler *r); + +Index: pulseaudio/src/pulsecore/sink-input.c +=================================================================== +--- pulseaudio.orig/src/pulsecore/sink-input.c 2015-05-13 15:00:12.066147988 +0800 ++++ pulseaudio/src/pulsecore/sink-input.c 2015-05-13 15:00:12.062147988 +0800 +@@ -1106,9 +1106,9 @@ + if (i->thread_info.rewrite_flush) + pa_memblockq_silence(i->thread_info.render_memblockq); + +- /* And reset the resampler */ ++ /* And rewind the resampler */ + if (i->thread_info.resampler) +- pa_resampler_reset(i->thread_info.resampler); ++ pa_resampler_rewind(i->thread_info.resampler, amount); + } + } + +Index: pulseaudio/src/pulsecore/source-output.c +=================================================================== +--- pulseaudio.orig/src/pulsecore/source-output.c 2015-05-13 15:00:12.066147988 +0800 ++++ pulseaudio/src/pulsecore/source-output.c 2015-05-13 15:00:12.062147988 +0800 +@@ -851,7 +851,7 @@ + o->process_rewind(o, nbytes); + + if (o->thread_info.resampler) +- pa_resampler_reset(o->thread_info.resampler); ++ pa_resampler_rewind(o->thread_info.resampler, nbytes); + + } else + pa_memblockq_rewind(o->thread_info.delay_memblockq, nbytes); diff --git a/debian/patches/0307-tests-adding-lfe-filter-test.patch b/debian/patches/0307-tests-adding-lfe-filter-test.patch new file mode 100644 index 0000000..dfb0d49 --- /dev/null +++ b/debian/patches/0307-tests-adding-lfe-filter-test.patch @@ -0,0 +1,248 @@ +From 98e01c8a9c0e62c9b0a733fc84409d1299392d5b Mon Sep 17 00:00:00 2001 +From: Hui Wang <[email protected]> +Date: Tue, 24 Mar 2015 10:29:19 +0100 +Subject: [PATCH 307/311] tests: adding lfe-filter-test + +so far, this test only includes rewind test, it works as below: +let lfe-filter process 2 blocks mono lfe channel audio samples, the +sample format is PA_SAMPLE_S16LE, save the processed data to the temp +buffer, then rewind the lfe-filter back 1 block and 1.5 blocks +respectively, reprocess the audio samples from the rewind position, +then comparing the output data with previously saved data. + +Signed-off-by: Hui Wang <[email protected]> +--- + src/Makefile.am | 8 +- + src/tests/lfe-filter-test.c | 194 ++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 201 insertions(+), 1 deletion(-) + create mode 100644 src/tests/lfe-filter-test.c + +diff --git a/src/Makefile.am b/src/Makefile.am +index 302c532..d582e57 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -262,7 +262,8 @@ TESTS_default = \ + cpu-sconv-test \ + cpu-volume-test \ + lock-autospawn-test \ +- mult-s16-test ++ mult-s16-test \ ++ lfe-filter-test + + TESTS_norun = \ + ipacl-test \ +@@ -554,6 +555,11 @@ mult_s16_test_LDADD = $(AM_LDADD) libpulsecore-@[email protected] libpulse.la li + mult_s16_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS) + mult_s16_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS) + ++lfe_filter_test_SOURCES = tests/lfe-filter-test.c ++lfe_filter_test_LDADD = $(AM_LDADD) libpulsecore-@[email protected] libpulse.la libpulsecommon-@[email protected] ++lfe_filter_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS) ++lfe_filter_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS) ++ + rtstutter_SOURCES = tests/rtstutter.c + rtstutter_LDADD = $(AM_LDADD) libpulsecore-@[email protected] libpulse.la libpulsecommon-@[email protected] + rtstutter_CFLAGS = $(AM_CFLAGS) +diff --git a/src/tests/lfe-filter-test.c b/src/tests/lfe-filter-test.c +new file mode 100644 +index 0000000..2c6d597 +--- /dev/null ++++ b/src/tests/lfe-filter-test.c +@@ -0,0 +1,194 @@ ++/*** ++ This file is part of PulseAudio. ++ ++ PulseAudio is free software; you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as published ++ by the Free Software Foundation; either version 2.1 of the License, ++ or (at your option) any later version. ++ ++ PulseAudio is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. ++***/ ++ ++#ifdef HAVE_CONFIG_H ++#include <config.h> ++#endif ++ ++#include <check.h> ++ ++#include <pulse/pulseaudio.h> ++#include <pulse/sample.h> ++#include <pulsecore/memblock.h> ++ ++#include <pulsecore/filter/lfe-filter.h> ++ ++struct lfe_filter_test { ++ pa_lfe_filter_t *lf; ++ pa_mempool *pool; ++ pa_sample_spec *ss; ++}; ++ ++static uint8_t *ori_sample_ptr; ++ ++#define ONE_BLOCK_SAMPLES 4096 ++#define TOTAL_SAMPLES 8192 ++ ++static void save_data_block(struct lfe_filter_test *lft, void *d, pa_memblock *blk) { ++ uint8_t *dst = d, *src; ++ size_t blk_size = pa_frame_size(lft->ss) * ONE_BLOCK_SAMPLES; ++ ++ src = pa_memblock_acquire(blk); ++ memcpy(dst, src, blk_size); ++ pa_memblock_release(blk); ++} ++ ++static pa_memblock* generate_data_block(struct lfe_filter_test *lft, int start) { ++ pa_memblock *r; ++ uint8_t *d, *s = ori_sample_ptr; ++ size_t blk_size = pa_frame_size(lft->ss) * ONE_BLOCK_SAMPLES; ++ ++ pa_assert_se(r = pa_memblock_new(lft->pool, blk_size)); ++ d = pa_memblock_acquire(r); ++ memcpy(d, s + start, blk_size); ++ pa_memblock_release(r); ++ ++ return r; ++} ++ ++static int compare_data_block(struct lfe_filter_test *lft, void *a, void *b) { ++ int ret = 0; ++ uint32_t i; ++ uint32_t fz = pa_frame_size(lft->ss); ++ uint8_t *r = a, *u = b; ++ ++ for (i = 0; i < ONE_BLOCK_SAMPLES * fz; i++) { ++ if (*r++ != *u++) { ++ pa_log_error("lfe-filter-test: test failed, the output data in the position 0x%x of a block does not equal!\n", i); ++ ret = -1; ++ break; ++ } ++ } ++ return ret; ++} ++ ++/* in this test case, we pass two blocks of sample data to lfe-filter, each ++ block contains 4096 samples, and don't let rewind_samples exceed TOTAL_SAMPLES */ ++static int lfe_filter_rewind_test(struct lfe_filter_test *lft, int rewind_samples) ++{ ++ int ret = -1, pos, i; ++ pa_memchunk mc; ++ uint8_t *outptr; ++ uint32_t fz = pa_frame_size(lft->ss); ++ ++ if (rewind_samples > TOTAL_SAMPLES || rewind_samples < TOTAL_SAMPLES - ONE_BLOCK_SAMPLES) { ++ pa_log_error("lfe-filter-test: Please keep %d samples < rewind_samples < %d samples\n", TOTAL_SAMPLES - ONE_BLOCK_SAMPLES, TOTAL_SAMPLES); ++ return ret; ++ } ++ ++ outptr = pa_xmalloc(fz * TOTAL_SAMPLES); ++ ++ /* let lfe-filter process all samples first, and save the processed data to the temp buffer, ++ then rewind back to some position, reprocess some samples and compare the output data with ++ the processed data saved before. */ ++ for (i = 0; i < TOTAL_SAMPLES / ONE_BLOCK_SAMPLES; i++) { ++ mc.memblock = generate_data_block(lft, i * ONE_BLOCK_SAMPLES * fz); ++ mc.length = pa_memblock_get_length(mc.memblock); ++ mc.index = 0; ++ pa_lfe_filter_process(lft->lf, &mc); ++ save_data_block(lft, outptr + i * ONE_BLOCK_SAMPLES * fz, mc.memblock); ++ pa_memblock_unref(mc.memblock); ++ } ++ ++ pa_lfe_filter_rewind(lft->lf, rewind_samples * fz); ++ pos = (TOTAL_SAMPLES - rewind_samples) * fz; ++ mc.memblock = generate_data_block(lft, pos); ++ mc.length = pa_memblock_get_length(mc.memblock); ++ mc.index = 0; ++ pa_lfe_filter_process(lft->lf, &mc); ++ ret = compare_data_block(lft, outptr + pos, pa_memblock_acquire(mc.memblock)); ++ pa_memblock_release(mc.memblock); ++ pa_memblock_unref(mc.memblock); ++ ++ pa_xfree(outptr); ++ ++ return ret; ++} ++ ++START_TEST (lfe_filter_test) { ++ pa_sample_spec a; ++ int ret = -1; ++ unsigned i, crossover_freq = 120; ++ pa_channel_map chmapmono = {1, {PA_CHANNEL_POSITION_LFE}}; ++ struct lfe_filter_test lft; ++ short *tmp_ptr; ++ ++ pa_log_set_level(PA_LOG_DEBUG); ++ ++ a.channels = 1; ++ a.rate = 44100; ++ a.format = PA_SAMPLE_S16LE; ++ ++ lft.ss = &a; ++ pa_assert_se(lft.pool = pa_mempool_new(false, 0)); ++ ++ /* We prepare pseudo-random input audio samples for lfe-filter rewind testing*/ ++ ori_sample_ptr = pa_xmalloc(pa_frame_size(lft.ss) * TOTAL_SAMPLES); ++ tmp_ptr = (short *) ori_sample_ptr; ++ for (i = 0; i < pa_frame_size(lft.ss) * TOTAL_SAMPLES / sizeof(short); i++) ++ *tmp_ptr++ = random(); ++ ++ /* we create a lfe-filter with cutoff frequency 120Hz and max rewind time 10 seconds */ ++ pa_assert_se(lft.lf = pa_lfe_filter_new(&a, &chmapmono, crossover_freq, a.rate * 10)); ++ /* rewind to a block boundary */ ++ ret = lfe_filter_rewind_test(&lft, ONE_BLOCK_SAMPLES); ++ if (ret) ++ pa_log_error("lfe-filer-test: rewind to block boundary test failed!!!"); ++ pa_lfe_filter_free(lft.lf); ++ ++ /* we create a lfe-filter with cutoff frequency 120Hz and max rewind time 10 seconds */ ++ pa_assert_se(lft.lf = pa_lfe_filter_new(&a, &chmapmono, crossover_freq, a.rate * 10)); ++ /* rewind to the middle position of a block */ ++ ret = lfe_filter_rewind_test(&lft, ONE_BLOCK_SAMPLES + ONE_BLOCK_SAMPLES / 2); ++ if (ret) ++ pa_log_error("lfe-filer-test: rewind to middle of block test failed!!!"); ++ ++ pa_xfree(ori_sample_ptr); ++ ++ pa_lfe_filter_free(lft.lf); ++ ++ pa_mempool_free(lft.pool); ++ ++ if (!ret) ++ pa_log_debug("lfe-filter-test: tests for both rewind to block boundary and rewind to middle position of a block passed!"); ++ ++ fail_unless(ret == 0); ++} ++END_TEST ++ ++int main(int argc, char *argv[]) { ++ int failed = 0; ++ Suite *s; ++ TCase *tc; ++ SRunner *sr; ++ ++ if (!getenv("MAKE_CHECK")) ++ pa_log_set_level(PA_LOG_DEBUG); ++ ++ s = suite_create("lfe-filter"); ++ tc = tcase_create("lfe-filter"); ++ tcase_add_test(tc, lfe_filter_test); ++ tcase_set_timeout(tc, 10); ++ suite_add_tcase(s, tc); ++ ++ sr = srunner_create(s); ++ srunner_run_all(sr, CK_NORMAL); ++ failed = srunner_ntests_failed(sr); ++ srunner_free(sr); ++ ++ return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; ++} +-- +1.9.1 + diff --git a/debian/patches/0308-daemon-conf-enable-the-lfe-remixing-by-default.patch b/debian/patches/0308-daemon-conf-enable-the-lfe-remixing-by-default.patch new file mode 100644 index 0000000..5eaf280 --- /dev/null +++ b/debian/patches/0308-daemon-conf-enable-the-lfe-remixing-by-default.patch @@ -0,0 +1,54 @@ +From a9059be749b6043d6cbc5b79652e8a4adda8994e Mon Sep 17 00:00:00 2001 +From: Hui Wang <[email protected]> +Date: Tue, 24 Mar 2015 10:29:15 +0100 +Subject: [PATCH 308/311] daemon-conf: enable the lfe remixing by default + +Since we have a workable lfe filter, it is time to enable the lfe +remixing by default. + +Signed-off-by: Hui Wang <[email protected]> +--- + man/pulse-daemon.conf.5.xml.in | 2 +- + src/daemon/daemon-conf.c | 2 +- + src/daemon/daemon.conf.in | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +Index: pulseaudio/man/pulse-daemon.conf.5.xml.in +=================================================================== +--- pulseaudio.orig/man/pulse-daemon.conf.5.xml.in 2015-05-13 15:00:23.030148165 +0800 ++++ pulseaudio/man/pulse-daemon.conf.5.xml.in 2015-05-13 15:00:23.026148165 +0800 +@@ -117,7 +117,7 @@ + channel is available as well. If no input LFE channel is + available the output LFE channel will always be 0. If no output + LFE channel is available the signal on the input LFE channel +- will be ignored. Defaults to <opt>no</opt>.</p> ++ will be ignored. Defaults to <opt>yes</opt>.</p> + </option> + + <option> +Index: pulseaudio/src/daemon/daemon-conf.c +=================================================================== +--- pulseaudio.orig/src/daemon/daemon-conf.c 2015-05-13 15:00:23.030148165 +0800 ++++ pulseaudio/src/daemon/daemon-conf.c 2015-05-13 15:00:23.026148165 +0800 +@@ -82,7 +82,7 @@ + .log_time = false, + .resample_method = PA_RESAMPLER_AUTO, + .disable_remixing = false, +- .disable_lfe_remixing = true, ++ .disable_lfe_remixing = false, + .lfe_crossover_freq = 120, + .config_file = NULL, + .use_pid_file = true, +Index: pulseaudio/src/daemon/daemon.conf.in +=================================================================== +--- pulseaudio.orig/src/daemon/daemon.conf.in 2015-05-13 15:00:23.030148165 +0800 ++++ pulseaudio/src/daemon/daemon.conf.in 2015-05-13 15:00:23.026148165 +0800 +@@ -54,7 +54,7 @@ + + ; resample-method = speex-float-1 + ; enable-remixing = yes +-; enable-lfe-remixing = no ++; enable-lfe-remixing = yes + ; lfe-crossover-freq = 120 + + flat-volumes = no diff --git a/debian/patches/0309-resampler-Allow-disabling-the-LFE-filter-by-setting-.patch b/debian/patches/0309-resampler-Allow-disabling-the-LFE-filter-by-setting-.patch new file mode 100644 index 0000000..8551782 --- /dev/null +++ b/debian/patches/0309-resampler-Allow-disabling-the-LFE-filter-by-setting-.patch @@ -0,0 +1,43 @@ +From c65a606ae73c9f9fa7bed4aade575395f4ff1890 Mon Sep 17 00:00:00 2001 +From: David Henningsson <[email protected]> +Date: Mon, 30 Mar 2015 11:10:56 +0200 +Subject: [PATCH 309/311] resampler: Allow disabling the LFE filter by setting + crossover to 0 + +When crossover_freq is set to 0, this restores the old behaviour +of letting the LFE channel be the average of the source channels, +without additional processing. This can be useful e g in case the +user already has a hardware crossover. + +Signed-off-by: David Henningsson <[email protected]> +--- + man/pulse-daemon.conf.5.xml.in | 2 +- + src/pulsecore/resampler.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +Index: pulseaudio/man/pulse-daemon.conf.5.xml.in +=================================================================== +--- pulseaudio.orig/man/pulse-daemon.conf.5.xml.in 2015-05-13 15:00:32.082148312 +0800 ++++ pulseaudio/man/pulse-daemon.conf.5.xml.in 2015-05-13 15:00:32.078148312 +0800 +@@ -122,7 +122,7 @@ + + <option> + <p><opt>lfe-crossover-freq=</opt> The crossover frequency (in Hz) for the +- LFE filter. Defaults to 120 Hz.</p> ++ LFE filter. Defaults to 120 Hz. Set it to 0 to disable the LFE filter.</p> + </option> + + <option> +Index: pulseaudio/src/pulsecore/resampler.c +=================================================================== +--- pulseaudio.orig/src/pulsecore/resampler.c 2015-05-13 15:00:32.082148312 +0800 ++++ pulseaudio/src/pulsecore/resampler.c 2015-05-13 15:00:32.078148312 +0800 +@@ -394,7 +394,7 @@ + if (r->map_required) + setup_remap(r, &r->remap, &lfe_filter_required); + +- if (lfe_filter_required) { ++ if (lfe_filter_required && crossover_freq > 0) { + pa_sample_spec wss = r->o_ss; + wss.format = r->work_format; + /* FIXME: For now just hardcode maxrewind to 3 seconds */ diff --git a/debian/patches/0310-resampler-Rename-lfe_filter_required-to-lfe_remixed.patch b/debian/patches/0310-resampler-Rename-lfe_filter_required-to-lfe_remixed.patch new file mode 100644 index 0000000..c4b9117 --- /dev/null +++ b/debian/patches/0310-resampler-Rename-lfe_filter_required-to-lfe_remixed.patch @@ -0,0 +1,92 @@ +From 2cfc5df87faa1ba642afa1ccc7f4c9b920139a79 Mon Sep 17 00:00:00 2001 +From: David Henningsson <[email protected]> +Date: Mon, 30 Mar 2015 15:12:53 +0200 +Subject: [PATCH 310/311] resampler: Rename "lfe_filter_required" to + "lfe_remixed" + +Just refactoring to make the variable name better. + +Signed-off-by: David Henningsson <[email protected]> +--- + src/pulsecore/resampler.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +Index: pulseaudio/src/pulsecore/resampler.c +=================================================================== +--- pulseaudio.orig/src/pulsecore/resampler.c 2015-05-13 15:00:43.758148502 +0800 ++++ pulseaudio/src/pulsecore/resampler.c 2015-05-13 15:00:43.754148501 +0800 +@@ -40,7 +40,7 @@ + + static int copy_init(pa_resampler *r); + +-static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_filter_required); ++static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed); + static void free_remap(pa_remap_t *m); + + static int (* const init_table[])(pa_resampler *r) = { +@@ -303,7 +303,7 @@ + pa_resample_flags_t flags) { + + pa_resampler *r = NULL; +- bool lfe_filter_required = false; ++ bool lfe_remixed = false; + + pa_assert(pool); + pa_assert(a); +@@ -392,9 +392,9 @@ + + /* set up the remap structure */ + if (r->map_required) +- setup_remap(r, &r->remap, &lfe_filter_required); ++ setup_remap(r, &r->remap, &lfe_remixed); + +- if (lfe_filter_required && crossover_freq > 0) { ++ if (lfe_remixed && crossover_freq > 0) { + pa_sample_spec wss = r->o_ss; + wss.format = r->work_format; + /* FIXME: For now just hardcode maxrewind to 3 seconds */ +@@ -764,7 +764,7 @@ + return ON_OTHER; + } + +-static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_filter_required) { ++static void setup_remap(const pa_resampler *r, pa_remap_t *m, bool *lfe_remixed) { + unsigned oc, ic; + unsigned n_oc, n_ic; + bool ic_connected[PA_CHANNELS_MAX]; +@@ -773,7 +773,7 @@ + + pa_assert(r); + pa_assert(m); +- pa_assert(lfe_filter_required); ++ pa_assert(lfe_remixed); + + n_oc = r->o_ss.channels; + n_ic = r->i_ss.channels; +@@ -786,7 +786,7 @@ + memset(m->map_table_i, 0, sizeof(m->map_table_i)); + + memset(ic_connected, 0, sizeof(ic_connected)); +- *lfe_filter_required = false; ++ *lfe_remixed = false; + + if (r->flags & PA_RESAMPLER_NO_REMAP) { + for (oc = 0; oc < PA_MIN(n_ic, n_oc); oc++) +@@ -900,7 +900,7 @@ + ic_connected[ic] = true; + + if (a == PA_CHANNEL_POSITION_MONO && on_lfe(b) && !(r->flags & PA_RESAMPLER_NO_LFE)) +- *lfe_filter_required = true; ++ *lfe_remixed = true; + } + else if (b == PA_CHANNEL_POSITION_MONO) { + m->map_table_f[oc][ic] = 1.0f / (float) n_ic; +@@ -984,7 +984,7 @@ + /* Please note that a channel connected to LFE doesn't + * really count as connected. */ + +- *lfe_filter_required = true; ++ *lfe_remixed = true; + } + } + } diff --git a/debian/patches/series b/debian/patches/series index bfd3520..72fbb61 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -16,3 +16,16 @@ 0209-module-switch-on-connect-adding-parameter-to-allow-s.patch 0210-module-device-restore-adding-property-to-skip.patch 0211-corking-a-sink-input-stream-when-stalled.patch + +# add lfe filter patchset +0300-lfe-filter-Import-code-from-the-Chrome-OS-audio-serv.patch +0301-lfe-filter-Enable-LFE-filter-in-the-resampler.patch +0302-lfe-filter-Cleanup-and-refactor.patch +0303-lfe-filter-change-the-crossover-frequency-as-a-param.patch +0304-memblock-Change-pa_memblock_new_malloced-to-an-inlin.patch +0305-lfe-filter-Add-rewind-support.patch +0306-resampler-Make-some-basic-functions-for-rewinding.patch +0307-tests-adding-lfe-filter-test.patch +0308-daemon-conf-enable-the-lfe-remixing-by-default.patch +0309-resampler-Allow-disabling-the-LFE-filter-by-setting-.patch +0310-resampler-Rename-lfe_filter_required-to-lfe_remixed.patch -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-pulseaudio/pulseaudio.git _______________________________________________ pkg-pulseaudio-devel mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-pulseaudio-devel

