[pulseaudio-discuss] [PATCH 1/6] lfe-filter: Import code from the Chrome OS audio server
David Henningsson
david.henningsson at canonical.com
Mon Feb 9 12:57:57 PST 2015
Thanks for the review!
On 2015-02-01 15:37, Alexander E. Patrakov wrote:
> 29.01.2015 03:14, David Henningsson wrote:
>> 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 <david.henningsson at canonical.com>
>> ---
>> 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);
>> + }
>> +}
>
> This definitely comes from some book, so a reference would be nice.
>
> Also, even though the function is static, it would be nice to describe
> the meaning (including units) of each parameter. And why do we have
> "double resonance" with some decibel-conversion applied to it, instead
> of the more-common Q-factor? Does this function come from the "matched
> Z-transform", "bilinear transform" or "impulse invariance" method of
> digital filter design? [just to check the understanding]
>
> (well, by comparing with a known set of filter coefficients for certain
> parameters, I can tell that a bilinear transform and frequency-warping
> is apparently used here, and that "resonance == 0", as passed from
> crossover.c, indeed results in a Butterworth filter)
>
> IOW, too much "scientific code" without proper comments, references, or
> simple testcases.
>
>> +
>> +static void biquad_highpass(struct biquad *bq, double cutoff, double
>> resonance)
>
> (trimming the quotation, because the above is enough to illustrate the
> point)
>
>> +static void biquad_bandpass(struct biquad *bq, double frequency,
>> double Q)
> ...
>
>> +static void biquad_peaking(struct biquad *bq, double frequency,
>> double Q,
>> + double db_gain)
>> +void biquad_set(struct biquad *bq, enum biquad_type type, double
>> freq, double Q,
>> + double gain)
>
> Of the above, you use only lowpass and highpass biquad filters. I.e. too
> much dead code. Please delete, because we are not implementing Web Audio
> API, and because the math cannot change and thus does not have to be
> maintained.
>
> Besides, the function uses a very non-standard definition of Q. In this
> function, to get a Butterworth filter, Q == 0 is passed, while,
> according to the traditional definition, Q == 0.707 should result in a
> Butterworth filter.
>
> Currently, we only need to be able to design 2nd order highpass and
> lowpass Butterworth filters with a given cutoff frequency, which is
> supposed to be very low as compared to the sample rate, so that the
> "bilinear transform vs other alternatives" distinction does not matter.
> Later, for the "virtual headphone" effect that is also based on IIR
> filters, I would need to set the biquad coefficients directly (e.g.,
> load from a file).
>
> If adding proper comments to all the math is a too-hard requirement,
> then, in v2, I'd suggest to (temporarily?) squash patches 1 and 2 for
> easier review, and to remove all unused functions and parameters.
So to answer to these more general thoughts:
- Yes, I think removing dead code makes sense. I was thinking that
maybe it makes sense to have it in case we wanted to implement more
filters later, but perhaps we should take that problem when it happens.
- Adding comments and references is going to be difficult as I'm not
an DSP expert.
- As for squashing patches, I think it's important that patch 1/6
remains the way it is for tracking/Copyright purposes (i e who wrote
what code). I can commit to that in v2, I'll make three patches - the
two first ones and a third to remove dead code - and also post the
combined diff of these three for easier reviewing.
>> diff --git a/src/pulsecore/filter/biquad.h
>> b/src/pulsecore/filter/biquad.h
>
> Not reviewing, as all criticisms are already expressed above.
>
>> +/* 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.
>> + */
>
> Yes, this is the kind of comments that I am looking for.
>
>> +struct biquad {
>> + float b0, b1, b2;
>> + float a1, a2;
>> + float x1, x2;
>> + float y1, y2;
>> +};
>
> You don't seem to use this directly, but copy the coefficients and the
> history into struct lr4. Maybe it makes sense to remove x1, x2, y1, y2
> from this structure, and use a "struct biquad coeffs;" inside struct lr4?
>
>> +static void lr4_set(struct lr4 *lr4, enum biquad_type type, float freq)
>> +{
>> + struct biquad q;
>> + biquad_set(&q, type, freq, 0, 0);
>
> I have verified that passing 0, 0 as the last two parameters indeed
> yields a Butterworth filter of 2nd order.
If the definition of Q is non-standard (0 instead of 2^-0.5), and Q is
never anything else than 0, maybe we should just remove Q as a function
argument here.
>> +/* Split input data using two LR4 filters, put the result into the
>> input array
>> + * and another array.
>> + *
>> + * data0 --+-- lp --> data0
>> + * |
>> + * \-- hp --> data1
>
> Why? Later you seem to fight with the in-place processing in PATCH 5/6,
> "Make sure we get a copy - we want to keep our original unchanged".
Good point.
>> + */
>> +static void lr4_split(struct lr4 *lp, struct lr4 *hp, int count,
>> float *data0,
>> + float *data1)
>> +{
>
> The implementation of this strange function is correct.
>
>> +/* 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)
>> +{
>
> Also correct.
>
> But later (in PATCH 2/6) you write your own functions that contain the
> same math. Again, this is an argument for squashing the first two
> patches (at least for review purposes).
>
>> 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.
>> + */
>
> Do we actually need this three-band filter on any known hardware?
This is also dead code. ChromeOS uses this for a multiband compressor [1].
--
David Henningsson, Canonical Ltd.
https://launchpad.net/~diwic
[1] https://github.com/drinkcat/adhd/blob/master/cras/src/dsp/drc.h
More information about the pulseaudio-discuss
mailing list