LCOV - code coverage report
Current view: top level - dom/media/webaudio/blink - IIRFilter.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 67 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 7 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13