tor-browser

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

IIRFilter.cpp (6430B)


      1 // Copyright 2016 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "IIRFilter.h"
      6 
      7 #include <mozilla/Assertions.h>
      8 
      9 #include <complex>
     10 
     11 #include "DenormalDisabler.h"
     12 #include "fdlibm.h"
     13 
     14 namespace blink {
     15 
     16 // The length of the memory buffers for the IIR filter.  This MUST be a power of
     17 // two and must be greater than the possible length of the filter coefficients.
     18 const int kBufferLength = 32;
     19 static_assert(kBufferLength >= IIRFilter::kMaxOrder + 1,
     20              "Internal IIR buffer length must be greater than maximum IIR "
     21              "Filter order.");
     22 
     23 IIRFilter::IIRFilter(const AudioDoubleArray* feedforwardCoef,
     24                     const AudioDoubleArray* feedbackCoef)
     25    : m_bufferIndex(0),
     26      m_feedback(feedbackCoef),
     27      m_feedforward(feedforwardCoef) {
     28  m_xBuffer.SetLength(kBufferLength);
     29  m_yBuffer.SetLength(kBufferLength);
     30  reset();
     31 }
     32 
     33 IIRFilter::~IIRFilter() = default;
     34 
     35 void IIRFilter::reset() {
     36  memset(m_xBuffer.Elements(), 0, m_xBuffer.Length() * sizeof(double));
     37  memset(m_yBuffer.Elements(), 0, m_yBuffer.Length() * sizeof(double));
     38 }
     39 
     40 static std::complex<double> evaluatePolynomial(const double* coef,
     41                                               std::complex<double> z,
     42                                               int order) {
     43  // Use Horner's method to evaluate the polynomial P(z) = sum(coef[k]*z^k, k,
     44  // 0, order);
     45  std::complex<double> result = 0;
     46 
     47  for (int k = order; k >= 0; --k)
     48    result = result * z + std::complex<double>(coef[k]);
     49 
     50  return result;
     51 }
     52 
     53 void IIRFilter::process(const float* sourceP, float* destP,
     54                        size_t framesToProcess) {
     55  // Compute
     56  //
     57  //   y[n] = sum(b[k] * x[n - k], k = 0, M) - sum(a[k] * y[n - k], k = 1, N)
     58  //
     59  // where b[k] are the feedforward coefficients and a[k] are the feedback
     60  // coefficients of the filter.
     61 
     62  // This is a Direct Form I implementation of an IIR Filter.  Should we
     63  // consider doing a different implementation such as Transposed Direct Form
     64  // II?
     65  const double* feedback = m_feedback->Elements();
     66  const double* feedforward = m_feedforward->Elements();
     67 
     68  MOZ_ASSERT(feedback);
     69  MOZ_ASSERT(feedforward);
     70 
     71  // Sanity check to see if the feedback coefficients have been scaled
     72  // appropriately. It must be EXACTLY 1!
     73  MOZ_ASSERT(feedback[0] == 1);
     74 
     75  int feedbackLength = m_feedback->Length();
     76  int feedforwardLength = m_feedforward->Length();
     77  int minLength = std::min(feedbackLength, feedforwardLength);
     78 
     79  double* xBuffer = m_xBuffer.Elements();
     80  double* yBuffer = m_yBuffer.Elements();
     81 
     82  for (size_t n = 0; n < framesToProcess; ++n) {
     83    // To help minimize roundoff, we compute using double's, even though the
     84    // filter coefficients only have single precision values.
     85    double yn = feedforward[0] * sourceP[n];
     86 
     87    // Run both the feedforward and feedback terms together, when possible.
     88    for (int k = 1; k < minLength; ++k) {
     89      int n = (m_bufferIndex - k) & (kBufferLength - 1);
     90      yn += feedforward[k] * xBuffer[n];
     91      yn -= feedback[k] * yBuffer[n];
     92    }
     93 
     94    // Handle any remaining feedforward or feedback terms.
     95    for (int k = minLength; k < feedforwardLength; ++k)
     96      yn += feedforward[k] * xBuffer[(m_bufferIndex - k) & (kBufferLength - 1)];
     97 
     98    for (int k = minLength; k < feedbackLength; ++k)
     99      yn -= feedback[k] * yBuffer[(m_bufferIndex - k) & (kBufferLength - 1)];
    100 
    101    // Save the current input and output values in the memory buffers for the
    102    // next output.
    103    m_xBuffer[m_bufferIndex] = sourceP[n];
    104    m_yBuffer[m_bufferIndex] = yn;
    105 
    106    m_bufferIndex = (m_bufferIndex + 1) & (kBufferLength - 1);
    107 
    108    // Avoid introducing a stream of subnormals
    109    destP[n] = WebCore::DenormalDisabler::flushDenormalFloatToZero(yn);
    110    MOZ_ASSERT(destP[n] == 0.0 || std::fabs(destP[n]) > FLT_MIN ||
    111                   std::isnan(destP[n]),
    112               "output should not be subnormal, but can be NaN");
    113  }
    114 }
    115 
    116 void IIRFilter::getFrequencyResponse(int nFrequencies, const float* frequency,
    117                                     float* magResponse, float* phaseResponse) {
    118  // Evaluate the z-transform of the filter at the given normalized frequencies
    119  // from 0 to 1. (One corresponds to the Nyquist frequency.)
    120  //
    121  // The z-tranform of the filter is
    122  //
    123  // H(z) = sum(b[k]*z^(-k), k, 0, M) / sum(a[k]*z^(-k), k, 0, N);
    124  //
    125  // The desired frequency response is H(exp(j*omega)), where omega is in
    126  // [0, 1).
    127  //
    128  // Let P(x) = sum(c[k]*x^k, k, 0, P) be a polynomial of order P.  Then each of
    129  // the sums in H(z) is equivalent to evaluating a polynomial at the point 1/z.
    130 
    131  for (int k = 0; k < nFrequencies; ++k) {
    132    // zRecip = 1/z = exp(-j*frequency)
    133    double omega = -M_PI * frequency[k];
    134    std::complex<double> zRecip =
    135        std::complex<double>(fdlibm_cos(omega), fdlibm_sin(omega));
    136 
    137    std::complex<double> numerator = evaluatePolynomial(
    138        m_feedforward->Elements(), zRecip, m_feedforward->Length() - 1);
    139    std::complex<double> denominator = evaluatePolynomial(
    140        m_feedback->Elements(), zRecip, m_feedback->Length() - 1);
    141    // Strangely enough, using complex division:
    142    // e.g. Complex response = numerator / denominator;
    143    // fails on our test machines, yielding infinities and NaNs, so we do
    144    // things the long way here.
    145    double n = norm(denominator);
    146    double r = (real(numerator) * real(denominator) +
    147                imag(numerator) * imag(denominator)) /
    148               n;
    149    double i = (imag(numerator) * real(denominator) -
    150                real(numerator) * imag(denominator)) /
    151               n;
    152    std::complex<double> response = std::complex<double>(r, i);
    153 
    154    magResponse[k] =
    155        static_cast<float>(fdlibm_hypot(real(response), imag(response)));
    156    phaseResponse[k] =
    157        static_cast<float>(fdlibm_atan2(imag(response), real(response)));
    158  }
    159 }
    160 
    161 bool IIRFilter::buffersAreZero() {
    162  double* xBuffer = m_xBuffer.Elements();
    163  double* yBuffer = m_yBuffer.Elements();
    164 
    165  for (size_t k = 0; k < m_feedforward->Length(); ++k) {
    166    if (xBuffer[(m_bufferIndex - k) & (kBufferLength - 1)] != 0.0) {
    167      return false;
    168    }
    169  }
    170 
    171  for (size_t k = 0; k < m_feedback->Length(); ++k) {
    172    if (fabs(yBuffer[(m_bufferIndex - k) & (kBufferLength - 1)]) >= FLT_MIN) {
    173      return false;
    174    }
    175  }
    176 
    177  return true;
    178 }
    179 
    180 }  // namespace blink