tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Biquad.cpp (14289B)


      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 <float.h>
     32 #include <math.h>
     33 
     34 #include <algorithm>
     35 
     36 #include "DenormalDisabler.h"
     37 #include "fdlibm.h"
     38 
     39 namespace WebCore {
     40 
     41 Biquad::Biquad() {
     42  // Initialize as pass-thru (straight-wire, no filter effect)
     43  setNormalizedCoefficients(1, 0, 0, 1, 0, 0);
     44 
     45  reset();  // clear filter memory
     46 }
     47 
     48 Biquad::~Biquad() = default;
     49 
     50 void Biquad::process(const float* sourceP, float* destP,
     51                     size_t framesToProcess) {
     52  // Create local copies of member variables
     53  double x1 = m_x1;
     54  double x2 = m_x2;
     55  double y1 = m_y1;
     56  double y2 = m_y2;
     57 
     58  double b0 = m_b0;
     59  double b1 = m_b1;
     60  double b2 = m_b2;
     61  double a1 = m_a1;
     62  double a2 = m_a2;
     63 
     64  for (size_t i = 0; i < framesToProcess; ++i) {
     65    // FIXME: this can be optimized by pipelining the multiply adds...
     66    double x = sourceP[i];
     67    double y = b0 * x + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2;
     68 
     69    destP[i] = y;
     70 
     71    // Update state variables
     72    x2 = x1;
     73    x1 = x;
     74    y2 = y1;
     75    y1 = y;
     76  }
     77 
     78  // Avoid introducing a stream of subnormals when input is silent and the
     79  // tail approaches zero.
     80  if (x1 == 0.0 && x2 == 0.0 && (y1 != 0.0 || y2 != 0.0) &&
     81      fabs(y1) < FLT_MIN && fabs(y2) < FLT_MIN) {
     82    // Flush future values to zero (until there is new input).
     83    y1 = y2 = 0.0;
     84 // Flush calculated values.
     85 #ifndef HAVE_DENORMAL
     86    for (int i = framesToProcess; i-- && fabsf(destP[i]) < FLT_MIN;) {
     87      destP[i] = 0.0f;
     88    }
     89 #endif
     90  }
     91  // Local variables back to member.
     92  m_x1 = x1;
     93  m_x2 = x2;
     94  m_y1 = y1;
     95  m_y2 = y2;
     96 }
     97 
     98 void Biquad::reset() { m_x1 = m_x2 = m_y1 = m_y2 = 0; }
     99 
    100 void Biquad::setLowpassParams(double cutoff, double resonance) {
    101  // Limit cutoff to 0 to 1.
    102  cutoff = std::max(0.0, std::min(cutoff, 1.0));
    103 
    104  if (cutoff == 1) {
    105    // When cutoff is 1, the z-transform is 1.
    106    setNormalizedCoefficients(1, 0, 0, 1, 0, 0);
    107  } else if (cutoff > 0) {
    108    // Compute biquad coefficients for lowpass filter
    109    double g = fdlibm_pow(10.0, -0.05 * resonance);
    110    double w0 = M_PI * cutoff;
    111    double cos_w0 = fdlibm_cos(w0);
    112    double alpha = 0.5 * fdlibm_sin(w0) * g;
    113 
    114    double b1 = 1.0 - cos_w0;
    115    double b0 = 0.5 * b1;
    116    double b2 = b0;
    117    double a0 = 1.0 + alpha;
    118    double a1 = -2.0 * cos_w0;
    119    double a2 = 1.0 - alpha;
    120 
    121    setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
    122  } else {
    123    // When cutoff is zero, nothing gets through the filter, so set
    124    // coefficients up correctly.
    125    setNormalizedCoefficients(0, 0, 0, 1, 0, 0);
    126  }
    127 }
    128 
    129 void Biquad::setHighpassParams(double cutoff, double resonance) {
    130  // Limit cutoff to 0 to 1.
    131  cutoff = std::max(0.0, std::min(cutoff, 1.0));
    132 
    133  if (cutoff == 1) {
    134    // The z-transform is 0.
    135    setNormalizedCoefficients(0, 0, 0, 1, 0, 0);
    136  } else if (cutoff > 0) {
    137    // Compute biquad coefficients for highpass filter
    138    double g = fdlibm_pow(10.0, -0.05 * resonance);
    139    double w0 = M_PI * cutoff;
    140    double cos_w0 = fdlibm_cos(w0);
    141    double alpha = 0.5 * fdlibm_sin(w0) * g;
    142 
    143    double b1 = -1.0 - cos_w0;
    144    double b0 = -0.5 * b1;
    145    double b2 = b0;
    146    double a0 = 1.0 + alpha;
    147    double a1 = -2.0 * cos_w0;
    148    double a2 = 1.0 - alpha;
    149 
    150    setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
    151  } else {
    152    // When cutoff is zero, we need to be careful because the above
    153    // gives a quadratic divided by the same quadratic, with poles
    154    // and zeros on the unit circle in the same place. When cutoff
    155    // is zero, the z-transform is 1.
    156    setNormalizedCoefficients(1, 0, 0, 1, 0, 0);
    157  }
    158 }
    159 
    160 void Biquad::setNormalizedCoefficients(double b0, double b1, double b2,
    161                                       double a0, double a1, double a2) {
    162  double a0Inverse = 1 / a0;
    163 
    164  m_b0 = b0 * a0Inverse;
    165  m_b1 = b1 * a0Inverse;
    166  m_b2 = b2 * a0Inverse;
    167  m_a1 = a1 * a0Inverse;
    168  m_a2 = a2 * a0Inverse;
    169 }
    170 
    171 void Biquad::setLowShelfParams(double frequency, double dbGain) {
    172  // Clip frequencies to between 0 and 1, inclusive.
    173  frequency = std::max(0.0, std::min(frequency, 1.0));
    174 
    175  double A = fdlibm_pow(10.0, dbGain / 40);
    176 
    177  if (frequency == 1) {
    178    // The z-transform is a constant gain.
    179    setNormalizedCoefficients(A * A, 0, 0, 1, 0, 0);
    180  } else if (frequency > 0) {
    181    double w0 = M_PI * frequency;
    182    double S = 1;  // filter slope (1 is max value)
    183    double alpha = 0.5 * fdlibm_sin(w0) * sqrt((A + 1 / A) * (1 / S - 1) + 2);
    184    double k = fdlibm_cos(w0);
    185    double k2 = 2 * sqrt(A) * alpha;
    186    double aPlusOne = A + 1;
    187    double aMinusOne = A - 1;
    188 
    189    double b0 = A * (aPlusOne - aMinusOne * k + k2);
    190    double b1 = 2 * A * (aMinusOne - aPlusOne * k);
    191    double b2 = A * (aPlusOne - aMinusOne * k - k2);
    192    double a0 = aPlusOne + aMinusOne * k + k2;
    193    double a1 = -2 * (aMinusOne + aPlusOne * k);
    194    double a2 = aPlusOne + aMinusOne * k - k2;
    195 
    196    setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
    197  } else {
    198    // When frequency is 0, the z-transform is 1.
    199    setNormalizedCoefficients(1, 0, 0, 1, 0, 0);
    200  }
    201 }
    202 
    203 void Biquad::setHighShelfParams(double frequency, double dbGain) {
    204  // Clip frequencies to between 0 and 1, inclusive.
    205  frequency = std::max(0.0, std::min(frequency, 1.0));
    206 
    207  double A = fdlibm_pow(10.0, dbGain / 40);
    208 
    209  if (frequency == 1) {
    210    // The z-transform is 1.
    211    setNormalizedCoefficients(1, 0, 0, 1, 0, 0);
    212  } else if (frequency > 0) {
    213    double w0 = M_PI * frequency;
    214    double S = 1;  // filter slope (1 is max value)
    215    double alpha = 0.5 * fdlibm_sin(w0) * sqrt((A + 1 / A) * (1 / S - 1) + 2);
    216    double k = fdlibm_cos(w0);
    217    double k2 = 2 * sqrt(A) * alpha;
    218    double aPlusOne = A + 1;
    219    double aMinusOne = A - 1;
    220 
    221    double b0 = A * (aPlusOne + aMinusOne * k + k2);
    222    double b1 = -2 * A * (aMinusOne + aPlusOne * k);
    223    double b2 = A * (aPlusOne + aMinusOne * k - k2);
    224    double a0 = aPlusOne - aMinusOne * k + k2;
    225    double a1 = 2 * (aMinusOne - aPlusOne * k);
    226    double a2 = aPlusOne - aMinusOne * k - k2;
    227 
    228    setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
    229  } else {
    230    // When frequency = 0, the filter is just a gain, A^2.
    231    setNormalizedCoefficients(A * A, 0, 0, 1, 0, 0);
    232  }
    233 }
    234 
    235 void Biquad::setPeakingParams(double frequency, double Q, double dbGain) {
    236  // Clip frequencies to between 0 and 1, inclusive.
    237  frequency = std::max(0.0, std::min(frequency, 1.0));
    238 
    239  // Don't let Q go negative, which causes an unstable filter.
    240  Q = std::max(0.0, Q);
    241 
    242  double A = fdlibm_pow(10.0, dbGain / 40);
    243 
    244  if (frequency > 0 && frequency < 1) {
    245    if (Q > 0) {
    246      double w0 = M_PI * frequency;
    247      double alpha = fdlibm_sin(w0) / (2 * Q);
    248      double k = fdlibm_cos(w0);
    249 
    250      double b0 = 1 + alpha * A;
    251      double b1 = -2 * k;
    252      double b2 = 1 - alpha * A;
    253      double a0 = 1 + alpha / A;
    254      double a1 = -2 * k;
    255      double a2 = 1 - alpha / A;
    256 
    257      setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
    258    } else {
    259      // When Q = 0, the above formulas have problems. If we look at
    260      // the z-transform, we can see that the limit as Q->0 is A^2, so
    261      // set the filter that way.
    262      setNormalizedCoefficients(A * A, 0, 0, 1, 0, 0);
    263    }
    264  } else {
    265    // When frequency is 0 or 1, the z-transform is 1.
    266    setNormalizedCoefficients(1, 0, 0, 1, 0, 0);
    267  }
    268 }
    269 
    270 void Biquad::setAllpassParams(double frequency, double Q) {
    271  // Clip frequencies to between 0 and 1, inclusive.
    272  frequency = std::max(0.0, std::min(frequency, 1.0));
    273 
    274  // Don't let Q go negative, which causes an unstable filter.
    275  Q = std::max(0.0, Q);
    276 
    277  if (frequency > 0 && frequency < 1) {
    278    if (Q > 0) {
    279      double w0 = M_PI * frequency;
    280      double alpha = fdlibm_sin(w0) / (2 * Q);
    281      double k = fdlibm_cos(w0);
    282 
    283      double b0 = 1 - alpha;
    284      double b1 = -2 * k;
    285      double b2 = 1 + alpha;
    286      double a0 = 1 + alpha;
    287      double a1 = -2 * k;
    288      double a2 = 1 - alpha;
    289 
    290      setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
    291    } else {
    292      // When Q = 0, the above formulas have problems. If we look at
    293      // the z-transform, we can see that the limit as Q->0 is -1, so
    294      // set the filter that way.
    295      setNormalizedCoefficients(-1, 0, 0, 1, 0, 0);
    296    }
    297  } else {
    298    // When frequency is 0 or 1, the z-transform is 1.
    299    setNormalizedCoefficients(1, 0, 0, 1, 0, 0);
    300  }
    301 }
    302 
    303 void Biquad::setNotchParams(double frequency, double Q) {
    304  // Clip frequencies to between 0 and 1, inclusive.
    305  frequency = std::max(0.0, std::min(frequency, 1.0));
    306 
    307  // Don't let Q go negative, which causes an unstable filter.
    308  Q = std::max(0.0, Q);
    309 
    310  if (frequency > 0 && frequency < 1) {
    311    if (Q > 0) {
    312      double w0 = M_PI * frequency;
    313      double alpha = fdlibm_sin(w0) / (2 * Q);
    314      double k = fdlibm_cos(w0);
    315 
    316      double b0 = 1;
    317      double b1 = -2 * k;
    318      double b2 = 1;
    319      double a0 = 1 + alpha;
    320      double a1 = -2 * k;
    321      double a2 = 1 - alpha;
    322 
    323      setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
    324    } else {
    325      // When Q = 0, the above formulas have problems. If we look at
    326      // the z-transform, we can see that the limit as Q->0 is 0, so
    327      // set the filter that way.
    328      setNormalizedCoefficients(0, 0, 0, 1, 0, 0);
    329    }
    330  } else {
    331    // When frequency is 0 or 1, the z-transform is 1.
    332    setNormalizedCoefficients(1, 0, 0, 1, 0, 0);
    333  }
    334 }
    335 
    336 void Biquad::setBandpassParams(double frequency, double Q) {
    337  // No negative frequencies allowed.
    338  frequency = std::max(0.0, frequency);
    339 
    340  // Don't let Q go negative, which causes an unstable filter.
    341  Q = std::max(0.0, Q);
    342 
    343  if (frequency > 0 && frequency < 1) {
    344    double w0 = M_PI * frequency;
    345    if (Q > 0) {
    346      double alpha = fdlibm_sin(w0) / (2 * Q);
    347      double k = fdlibm_cos(w0);
    348 
    349      double b0 = alpha;
    350      double b1 = 0;
    351      double b2 = -alpha;
    352      double a0 = 1 + alpha;
    353      double a1 = -2 * k;
    354      double a2 = 1 - alpha;
    355 
    356      setNormalizedCoefficients(b0, b1, b2, a0, a1, a2);
    357    } else {
    358      // When Q = 0, the above formulas have problems. If we look at
    359      // the z-transform, we can see that the limit as Q->0 is 1, so
    360      // set the filter that way.
    361      setNormalizedCoefficients(1, 0, 0, 1, 0, 0);
    362    }
    363  } else {
    364    // When the cutoff is zero, the z-transform approaches 0, if Q
    365    // > 0. When both Q and cutoff are zero, the z-transform is
    366    // pretty much undefined. What should we do in this case?
    367    // For now, just make the filter 0. When the cutoff is 1, the
    368    // z-transform also approaches 0.
    369    setNormalizedCoefficients(0, 0, 0, 1, 0, 0);
    370  }
    371 }
    372 
    373 void Biquad::setZeroPolePairs(const Complex& zero, const Complex& pole) {
    374  double b0 = 1;
    375  double b1 = -2 * zero.real();
    376 
    377  double zeroMag = abs(zero);
    378  double b2 = zeroMag * zeroMag;
    379 
    380  double a1 = -2 * pole.real();
    381 
    382  double poleMag = abs(pole);
    383  double a2 = poleMag * poleMag;
    384  setNormalizedCoefficients(b0, b1, b2, 1, a1, a2);
    385 }
    386 
    387 void Biquad::setAllpassPole(const Complex& pole) {
    388  Complex zero = Complex(1, 0) / pole;
    389  setZeroPolePairs(zero, pole);
    390 }
    391 
    392 void Biquad::getFrequencyResponse(int nFrequencies, const float* frequency,
    393                                  float* magResponse, float* phaseResponse) {
    394  // Evaluate the Z-transform of the filter at given normalized
    395  // frequency from 0 to 1.  (1 corresponds to the Nyquist
    396  // frequency.)
    397  //
    398  // The z-transform of the filter is
    399  //
    400  // H(z) = (b0 + b1*z^(-1) + b2*z^(-2))/(1 + a1*z^(-1) + a2*z^(-2))
    401  //
    402  // Evaluate as
    403  //
    404  // b0 + (b1 + b2*z1)*z1
    405  // --------------------
    406  // 1 + (a1 + a2*z1)*z1
    407  //
    408  // with z1 = 1/z and z = exp(j*pi*frequency). Hence z1 = exp(-j*pi*frequency)
    409 
    410  // Make local copies of the coefficients as a micro-optimization.
    411  double b0 = m_b0;
    412  double b1 = m_b1;
    413  double b2 = m_b2;
    414  double a1 = m_a1;
    415  double a2 = m_a2;
    416 
    417  for (int k = 0; k < nFrequencies; ++k) {
    418    double omega = -M_PI * frequency[k];
    419    Complex z = Complex(fdlibm_cos(omega), fdlibm_sin(omega));
    420    Complex numerator = b0 + (b1 + b2 * z) * z;
    421    Complex denominator = Complex(1, 0) + (a1 + a2 * z) * z;
    422    // Strangely enough, using complex division:
    423    // e.g. Complex response = numerator / denominator;
    424    // fails on our test machines, yielding infinities and NaNs, so we do
    425    // things the long way here.
    426    double n = norm(denominator);
    427    double r = (real(numerator) * real(denominator) +
    428                imag(numerator) * imag(denominator)) /
    429               n;
    430    double i = (imag(numerator) * real(denominator) -
    431                real(numerator) * imag(denominator)) /
    432               n;
    433    std::complex<double> response = std::complex<double>(r, i);
    434 
    435    magResponse[k] =
    436        static_cast<float>(fdlibm_hypot(real(response), imag(response)));
    437    phaseResponse[k] =
    438        static_cast<float>(fdlibm_atan2(imag(response), real(response)));
    439  }
    440 }
    441 
    442 }  // namespace WebCore