[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