Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/webaudio/blink/Biquad.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2010 Google Inc. All rights reserved.
3
 *
4
 * Redistribution and use in source and binary forms, with or without
5
 * modification, are permitted provided that the following conditions
6
 * are met:
7
 *
8
 * 1.  Redistributions of source code must retain the above copyright
9
 *     notice, this list of conditions and the following disclaimer.
10
 * 2.  Redistributions in binary form must reproduce the above copyright
11
 *     notice, this list of conditions and the following disclaimer in the
12
 *     documentation and/or other materials provided with the distribution.
13
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14
 *     its contributors may be used to endorse or promote products derived
15
 *     from this software without specific prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 */
28
29
#include "Biquad.h"
30
31
#include "DenormalDisabler.h"
32
33
#include <float.h>
34
#include <algorithm>
35
#include <math.h>
36
37
namespace WebCore {
38
39
Biquad::Biquad()
40
0
{
41
0
    // Initialize as pass-thru (straight-wire, no filter effect)
42
0
    setNormalizedCoefficients(1, 0, 0, 1, 0, 0);
43
0
44
0
    reset(); // clear filter memory
45
0
}
46
47
Biquad::~Biquad()
48
0
{
49
0
}
50
51
void Biquad::process(const float* sourceP, float* destP, size_t framesToProcess)
52
0
{
53
0
    // Create local copies of member variables
54
0
    double x1 = m_x1;
55
0
    double x2 = m_x2;
56
0
    double y1 = m_y1;
57
0
    double y2 = m_y2;
58
0
59
0
    double b0 = m_b0;
60
0
    double b1 = m_b1;
61
0
    double b2 = m_b2;
62
0
    double a1 = m_a1;
63
0
    double a2 = m_a2;
64
0
65
0
    for (size_t i = 0; i < framesToProcess; ++i) {
66
0
        // FIXME: this can be optimized by pipelining the multiply adds...
67
0
        double x = sourceP[i];
68
0
        double y = b0*x + b1*x1 + b2*x2 - a1*y1 - a2*y2;
69
0
70
0
        destP[i] = y;
71
0
72
0
        // Update state variables
73
0
        x2 = x1;
74
0
        x1 = x;
75
0
        y2 = y1;
76
0
        y1 = y;
77
0
    }
78
0
79
0
    // Avoid introducing a stream of subnormals when input is silent and the
80
0
    // tail approaches zero.
81
0
    if (x1 == 0.0 && x2 == 0.0 && (y1 != 0.0 || y2 != 0.0) &&
82
0
        fabs(y1) < FLT_MIN && fabs(y2) < FLT_MIN) {
83
0
      // Flush future values to zero (until there is new input).
84
0
      y1 = y2 = 0.0;
85
0
      // Flush calculated values.
86
      #ifndef HAVE_DENORMAL
87
      for (int i = framesToProcess; i-- && fabsf(destP[i]) < FLT_MIN; ) {
88
        destP[i] = 0.0f;
89
      }
90
      #endif
91
    }
92
0
    // Local variables back to member.
93
0
    m_x1 = x1;
94
0
    m_x2 = x2;
95
0
    m_y1 = y1;
96
0
    m_y2 = y2;
97
0
}
98
99
void Biquad::reset()
100
0
{
101
0
    m_x1 = m_x2 = m_y1 = m_y2 = 0;
102
0
}
103
104
void Biquad::setLowpassParams(double cutoff, double resonance)
105
0
{
106
0
    // Limit cutoff to 0 to 1.
107
0
    cutoff = std::max(0.0, std::min(cutoff, 1.0));
108
0
109
0
    if (cutoff == 1) {
110
0
        // When cutoff is 1, the z-transform is 1.
111
0
        setNormalizedCoefficients(1, 0, 0,
112
0
                                  1, 0, 0);
113
0
    } else if (cutoff > 0) {
114
0
        // Compute biquad coefficients for lowpass filter
115
0
        resonance = std::max(0.0, resonance); // can't go negative
116
0
        double g = pow(10.0, -0.05 * resonance);
117
0
        double w0 = M_PI * cutoff;
118
0
        double cos_w0 = cos(w0);
119
0
        double alpha = 0.5 * sin(w0) * g;
120
0
121
0
        double b1 = 1.0 - cos_w0;
122
0
        double b0 = 0.5 * b1;
123
0
        double b2 = b0;
124
0
        double a0 = 1.0 + alpha;
125
0
        double a1 = -2.0 * cos_w0;
126
0
        double a2 = 1.0 - alpha;
127
0
128
0
        setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
129
0
    } else {
130
0
        // When cutoff is zero, nothing gets through the filter, so set
131
0
        // coefficients up correctly.
132
0
        setNormalizedCoefficients(0, 0, 0,
133
0
                                  1, 0, 0);
134
0
    }
135
0
}
136
137
void Biquad::setHighpassParams(double cutoff, double resonance)
138
0
{
139
0
    // Limit cutoff to 0 to 1.
140
0
    cutoff = std::max(0.0, std::min(cutoff, 1.0));
141
0
142
0
    if (cutoff == 1) {
143
0
        // The z-transform is 0.
144
0
        setNormalizedCoefficients(0, 0, 0,
145
0
                                  1, 0, 0);
146
0
    } else if (cutoff > 0) {
147
0
        // Compute biquad coefficients for highpass filter
148
0
        resonance = std::max(0.0, resonance); // can't go negative
149
0
        double g = pow(10.0, -0.05 * resonance);
150
0
        double w0 = M_PI * cutoff;
151
0
        double cos_w0 = cos(w0);
152
0
        double alpha = 0.5 * sin(w0) * g;
153
0
154
0
        double b1 = -1.0 - cos_w0;
155
0
        double b0 = -0.5 * b1;
156
0
        double b2 = b0;
157
0
        double a0 = 1.0 + alpha;
158
0
        double a1 = -2.0 * cos_w0;
159
0
        double a2 = 1.0 - alpha;
160
0
161
0
        setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
162
0
    } else {
163
0
      // When cutoff is zero, we need to be careful because the above
164
0
      // gives a quadratic divided by the same quadratic, with poles
165
0
      // and zeros on the unit circle in the same place. When cutoff
166
0
      // is zero, the z-transform is 1.
167
0
        setNormalizedCoefficients(1, 0, 0,
168
0
                                  1, 0, 0);
169
0
    }
170
0
}
171
172
void Biquad::setNormalizedCoefficients(double b0, double b1, double b2, double a0, double a1, double a2)
173
0
{
174
0
    double a0Inverse = 1 / a0;
175
0
176
0
    m_b0 = b0 * a0Inverse;
177
0
    m_b1 = b1 * a0Inverse;
178
0
    m_b2 = b2 * a0Inverse;
179
0
    m_a1 = a1 * a0Inverse;
180
0
    m_a2 = a2 * a0Inverse;
181
0
}
182
183
void Biquad::setLowShelfParams(double frequency, double dbGain)
184
0
{
185
0
    // Clip frequencies to between 0 and 1, inclusive.
186
0
    frequency = std::max(0.0, std::min(frequency, 1.0));
187
0
188
0
    double A = pow(10.0, dbGain / 40);
189
0
190
0
    if (frequency == 1) {
191
0
        // The z-transform is a constant gain.
192
0
        setNormalizedCoefficients(A * A, 0, 0,
193
0
                                  1, 0, 0);
194
0
    } else if (frequency > 0) {
195
0
        double w0 = M_PI * frequency;
196
0
        double S = 1; // filter slope (1 is max value)
197
0
        double alpha = 0.5 * sin(w0) * sqrt((A + 1 / A) * (1 / S - 1) + 2);
198
0
        double k = cos(w0);
199
0
        double k2 = 2 * sqrt(A) * alpha;
200
0
        double aPlusOne = A + 1;
201
0
        double aMinusOne = A - 1;
202
0
203
0
        double b0 = A * (aPlusOne - aMinusOne * k + k2);
204
0
        double b1 = 2 * A * (aMinusOne - aPlusOne * k);
205
0
        double b2 = A * (aPlusOne - aMinusOne * k - k2);
206
0
        double a0 = aPlusOne + aMinusOne * k + k2;
207
0
        double a1 = -2 * (aMinusOne + aPlusOne * k);
208
0
        double a2 = aPlusOne + aMinusOne * k - k2;
209
0
210
0
        setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
211
0
    } else {
212
0
        // When frequency is 0, the z-transform is 1.
213
0
        setNormalizedCoefficients(1, 0, 0,
214
0
                                  1, 0, 0);
215
0
    }
216
0
}
217
218
void Biquad::setHighShelfParams(double frequency, double dbGain)
219
0
{
220
0
    // Clip frequencies to between 0 and 1, inclusive.
221
0
    frequency = std::max(0.0, std::min(frequency, 1.0));
222
0
223
0
    double A = pow(10.0, dbGain / 40);
224
0
225
0
    if (frequency == 1) {
226
0
        // The z-transform is 1.
227
0
        setNormalizedCoefficients(1, 0, 0,
228
0
                                  1, 0, 0);
229
0
    } else if (frequency > 0) {
230
0
        double w0 = M_PI * frequency;
231
0
        double S = 1; // filter slope (1 is max value)
232
0
        double alpha = 0.5 * sin(w0) * sqrt((A + 1 / A) * (1 / S - 1) + 2);
233
0
        double k = cos(w0);
234
0
        double k2 = 2 * sqrt(A) * alpha;
235
0
        double aPlusOne = A + 1;
236
0
        double aMinusOne = A - 1;
237
0
238
0
        double b0 = A * (aPlusOne + aMinusOne * k + k2);
239
0
        double b1 = -2 * A * (aMinusOne + aPlusOne * k);
240
0
        double b2 = A * (aPlusOne + aMinusOne * k - k2);
241
0
        double a0 = aPlusOne - aMinusOne * k + k2;
242
0
        double a1 = 2 * (aMinusOne - aPlusOne * k);
243
0
        double a2 = aPlusOne - aMinusOne * k - k2;
244
0
245
0
        setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
246
0
    } else {
247
0
        // When frequency = 0, the filter is just a gain, A^2.
248
0
        setNormalizedCoefficients(A * A, 0, 0,
249
0
                                  1, 0, 0);
250
0
    }
251
0
}
252
253
void Biquad::setPeakingParams(double frequency, double Q, double dbGain)
254
0
{
255
0
    // Clip frequencies to between 0 and 1, inclusive.
256
0
    frequency = std::max(0.0, std::min(frequency, 1.0));
257
0
258
0
    // Don't let Q go negative, which causes an unstable filter.
259
0
    Q = std::max(0.0, Q);
260
0
261
0
    double A = pow(10.0, dbGain / 40);
262
0
263
0
    if (frequency > 0 && frequency < 1) {
264
0
        if (Q > 0) {
265
0
            double w0 = M_PI * frequency;
266
0
            double alpha = sin(w0) / (2 * Q);
267
0
            double k = cos(w0);
268
0
269
0
            double b0 = 1 + alpha * A;
270
0
            double b1 = -2 * k;
271
0
            double b2 = 1 - alpha * A;
272
0
            double a0 = 1 + alpha / A;
273
0
            double a1 = -2 * k;
274
0
            double a2 = 1 - alpha / A;
275
0
276
0
            setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
277
0
        } else {
278
0
            // When Q = 0, the above formulas have problems. If we look at
279
0
            // the z-transform, we can see that the limit as Q->0 is A^2, so
280
0
            // set the filter that way.
281
0
            setNormalizedCoefficients(A * A, 0, 0,
282
0
                                      1, 0, 0);
283
0
        }
284
0
    } else {
285
0
        // When frequency is 0 or 1, the z-transform is 1.
286
0
        setNormalizedCoefficients(1, 0, 0,
287
0
                                  1, 0, 0);
288
0
    }
289
0
}
290
291
void Biquad::setAllpassParams(double frequency, double Q)
292
0
{
293
0
    // Clip frequencies to between 0 and 1, inclusive.
294
0
    frequency = std::max(0.0, std::min(frequency, 1.0));
295
0
296
0
    // Don't let Q go negative, which causes an unstable filter.
297
0
    Q = std::max(0.0, Q);
298
0
299
0
    if (frequency > 0 && frequency < 1) {
300
0
        if (Q > 0) {
301
0
            double w0 = M_PI * frequency;
302
0
            double alpha = sin(w0) / (2 * Q);
303
0
            double k = cos(w0);
304
0
305
0
            double b0 = 1 - alpha;
306
0
            double b1 = -2 * k;
307
0
            double b2 = 1 + alpha;
308
0
            double a0 = 1 + alpha;
309
0
            double a1 = -2 * k;
310
0
            double a2 = 1 - alpha;
311
0
312
0
            setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
313
0
        } else {
314
0
            // When Q = 0, the above formulas have problems. If we look at
315
0
            // the z-transform, we can see that the limit as Q->0 is -1, so
316
0
            // set the filter that way.
317
0
            setNormalizedCoefficients(-1, 0, 0,
318
0
                                      1, 0, 0);
319
0
        }
320
0
    } else {
321
0
        // When frequency is 0 or 1, the z-transform is 1.
322
0
        setNormalizedCoefficients(1, 0, 0,
323
0
                                  1, 0, 0);
324
0
    }
325
0
}
326
327
void Biquad::setNotchParams(double frequency, double Q)
328
0
{
329
0
    // Clip frequencies to between 0 and 1, inclusive.
330
0
    frequency = std::max(0.0, std::min(frequency, 1.0));
331
0
332
0
    // Don't let Q go negative, which causes an unstable filter.
333
0
    Q = std::max(0.0, Q);
334
0
335
0
    if (frequency > 0 && frequency < 1) {
336
0
        if (Q > 0) {
337
0
            double w0 = M_PI * frequency;
338
0
            double alpha = sin(w0) / (2 * Q);
339
0
            double k = cos(w0);
340
0
341
0
            double b0 = 1;
342
0
            double b1 = -2 * k;
343
0
            double b2 = 1;
344
0
            double a0 = 1 + alpha;
345
0
            double a1 = -2 * k;
346
0
            double a2 = 1 - alpha;
347
0
348
0
            setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
349
0
        } else {
350
0
            // When Q = 0, the above formulas have problems. If we look at
351
0
            // the z-transform, we can see that the limit as Q->0 is 0, so
352
0
            // set the filter that way.
353
0
            setNormalizedCoefficients(0, 0, 0,
354
0
                                      1, 0, 0);
355
0
        }
356
0
    } else {
357
0
        // When frequency is 0 or 1, the z-transform is 1.
358
0
        setNormalizedCoefficients(1, 0, 0,
359
0
                                  1, 0, 0);
360
0
    }
361
0
}
362
363
void Biquad::setBandpassParams(double frequency, double Q)
364
0
{
365
0
    // No negative frequencies allowed.
366
0
    frequency = std::max(0.0, frequency);
367
0
368
0
    // Don't let Q go negative, which causes an unstable filter.
369
0
    Q = std::max(0.0, Q);
370
0
371
0
    if (frequency > 0 && frequency < 1) {
372
0
        double w0 = M_PI * frequency;
373
0
        if (Q > 0) {
374
0
            double alpha = sin(w0) / (2 * Q);
375
0
            double k = cos(w0);
376
0
377
0
            double b0 = alpha;
378
0
            double b1 = 0;
379
0
            double b2 = -alpha;
380
0
            double a0 = 1 + alpha;
381
0
            double a1 = -2 * k;
382
0
            double a2 = 1 - alpha;
383
0
384
0
            setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
385
0
        } else {
386
0
            // When Q = 0, the above formulas have problems. If we look at
387
0
            // the z-transform, we can see that the limit as Q->0 is 1, so
388
0
            // set the filter that way.
389
0
            setNormalizedCoefficients(1, 0, 0,
390
0
                                      1, 0, 0);
391
0
        }
392
0
    } else {
393
0
        // When the cutoff is zero, the z-transform approaches 0, if Q
394
0
        // > 0. When both Q and cutoff are zero, the z-transform is
395
0
        // pretty much undefined. What should we do in this case?
396
0
        // For now, just make the filter 0. When the cutoff is 1, the
397
0
        // z-transform also approaches 0.
398
0
        setNormalizedCoefficients(0, 0, 0,
399
0
                                  1, 0, 0);
400
0
    }
401
0
}
402
403
void Biquad::setZeroPolePairs(const Complex &zero, const Complex &pole)
404
0
{
405
0
    double b0 = 1;
406
0
    double b1 = -2 * zero.real();
407
0
408
0
    double zeroMag = abs(zero);
409
0
    double b2 = zeroMag * zeroMag;
410
0
411
0
    double a1 = -2 * pole.real();
412
0
413
0
    double poleMag = abs(pole);
414
0
    double a2 = poleMag * poleMag;
415
0
    setNormalizedCoefficients(b0, b1, b2, 1, a1, a2);
416
0
}
417
418
void Biquad::setAllpassPole(const Complex &pole)
419
0
{
420
0
    Complex zero = Complex(1, 0) / pole;
421
0
    setZeroPolePairs(zero, pole);
422
0
}
423
424
void Biquad::getFrequencyResponse(int nFrequencies,
425
                                  const float* frequency,
426
                                  float* magResponse,
427
                                  float* phaseResponse)
428
0
{
429
0
    // Evaluate the Z-transform of the filter at given normalized
430
0
    // frequency from 0 to 1.  (1 corresponds to the Nyquist
431
0
    // frequency.)
432
0
    //
433
0
    // The z-transform of the filter is
434
0
    //
435
0
    // H(z) = (b0 + b1*z^(-1) + b2*z^(-2))/(1 + a1*z^(-1) + a2*z^(-2))
436
0
    //
437
0
    // Evaluate as
438
0
    //
439
0
    // b0 + (b1 + b2*z1)*z1
440
0
    // --------------------
441
0
    // 1 + (a1 + a2*z1)*z1
442
0
    //
443
0
    // with z1 = 1/z and z = exp(j*pi*frequency). Hence z1 = exp(-j*pi*frequency)
444
0
445
0
    // Make local copies of the coefficients as a micro-optimization.
446
0
    double b0 = m_b0;
447
0
    double b1 = m_b1;
448
0
    double b2 = m_b2;
449
0
    double a1 = m_a1;
450
0
    double a2 = m_a2;
451
0
452
0
    for (int k = 0; k < nFrequencies; ++k) {
453
0
        double omega = -M_PI * frequency[k];
454
0
        Complex z = Complex(cos(omega), sin(omega));
455
0
        Complex numerator = b0 + (b1 + b2 * z) * z;
456
0
        Complex denominator = Complex(1, 0) + (a1 + a2 * z) * z;
457
0
        // Strangely enough, using complex division:
458
0
        // e.g. Complex response = numerator / denominator;
459
0
        // fails on our test machines, yielding infinities and NaNs, so we do
460
0
        // things the long way here.
461
0
        double n = norm(denominator);
462
0
        double r = (real(numerator)*real(denominator) + imag(numerator)*imag(denominator)) / n;
463
0
        double i = (imag(numerator)*real(denominator) - real(numerator)*imag(denominator)) / n;
464
0
        std::complex<double> response = std::complex<double>(r, i);
465
0
466
0
        magResponse[k] = static_cast<float>(abs(response));
467
0
        phaseResponse[k] = static_cast<float>(atan2(imag(response), real(response)));
468
0
    }
469
0
}
470
471
} // namespace WebCore
472