| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "platform/audio/IIRFilter.h" | 5 #include "platform/audio/IIRFilter.h" |
| 6 | 6 |
| 7 #include "wtf/MathExtras.h" | 7 #include "wtf/MathExtras.h" |
| 8 #include <complex> | 8 #include <complex> |
| 9 | 9 |
| 10 namespace blink { | 10 namespace blink { |
| 11 | 11 |
| 12 // The length of the memory buffers for the IIR filter. This MUST be a power of
two and must be | 12 // The length of the memory buffers for the IIR filter. This MUST be a power of |
| 13 // greater than the possible length of the filter coefficients. | 13 // two and must be greater than the possible length of the filter coefficients. |
| 14 const int kBufferLength = 32; | 14 const int kBufferLength = 32; |
| 15 static_assert(kBufferLength >= IIRFilter::kMaxOrder + 1, | 15 static_assert(kBufferLength >= IIRFilter::kMaxOrder + 1, |
| 16 "Internal IIR buffer length must be greater than maximum IIR " | 16 "Internal IIR buffer length must be greater than maximum IIR " |
| 17 "Filter order."); | 17 "Filter order."); |
| 18 | 18 |
| 19 IIRFilter::IIRFilter(const AudioDoubleArray* feedforward, | 19 IIRFilter::IIRFilter(const AudioDoubleArray* feedforward, |
| 20 const AudioDoubleArray* feedback) | 20 const AudioDoubleArray* feedback) |
| 21 : m_bufferIndex(0), m_feedback(feedback), m_feedforward(feedforward) { | 21 : m_bufferIndex(0), m_feedback(feedback), m_feedforward(feedforward) { |
| 22 // These are guaranteed to be zero-initialized. | 22 // These are guaranteed to be zero-initialized. |
| 23 m_xBuffer.allocate(kBufferLength); | 23 m_xBuffer.allocate(kBufferLength); |
| 24 m_yBuffer.allocate(kBufferLength); | 24 m_yBuffer.allocate(kBufferLength); |
| 25 } | 25 } |
| 26 | 26 |
| 27 IIRFilter::~IIRFilter() {} | 27 IIRFilter::~IIRFilter() {} |
| 28 | 28 |
| 29 void IIRFilter::reset() { | 29 void IIRFilter::reset() { |
| 30 m_xBuffer.zero(); | 30 m_xBuffer.zero(); |
| 31 m_yBuffer.zero(); | 31 m_yBuffer.zero(); |
| 32 } | 32 } |
| 33 | 33 |
| 34 static std::complex<double> evaluatePolynomial(const double* coef, | 34 static std::complex<double> evaluatePolynomial(const double* coef, |
| 35 std::complex<double> z, | 35 std::complex<double> z, |
| 36 int order) { | 36 int order) { |
| 37 // Use Horner's method to evaluate the polynomial P(z) = sum(coef[k]*z^k, k, 0
, order); | 37 // Use Horner's method to evaluate the polynomial P(z) = sum(coef[k]*z^k, k, |
| 38 // 0, order); |
| 38 std::complex<double> result = 0; | 39 std::complex<double> result = 0; |
| 39 | 40 |
| 40 for (int k = order; k >= 0; --k) | 41 for (int k = order; k >= 0; --k) |
| 41 result = result * z + std::complex<double>(coef[k]); | 42 result = result * z + std::complex<double>(coef[k]); |
| 42 | 43 |
| 43 return result; | 44 return result; |
| 44 } | 45 } |
| 45 | 46 |
| 46 void IIRFilter::process(const float* sourceP, | 47 void IIRFilter::process(const float* sourceP, |
| 47 float* destP, | 48 float* destP, |
| 48 size_t framesToProcess) { | 49 size_t framesToProcess) { |
| 49 // Compute | 50 // Compute |
| 50 // | 51 // |
| 51 // y[n] = sum(b[k] * x[n - k], k = 0, M) - sum(a[k] * y[n - k], k = 1, N) | 52 // y[n] = sum(b[k] * x[n - k], k = 0, M) - sum(a[k] * y[n - k], k = 1, N) |
| 52 // | 53 // |
| 53 // where b[k] are the feedforward coefficients and a[k] are the feedback coeff
icients of the | 54 // where b[k] are the feedforward coefficients and a[k] are the feedback |
| 54 // filter. | 55 // coefficients of the filter. |
| 55 | 56 |
| 56 // This is a Direct Form I implementation of an IIR Filter. Should we conside
r doing a | 57 // This is a Direct Form I implementation of an IIR Filter. Should we |
| 57 // different implementation such as Transposed Direct Form II? | 58 // consider doing a different implementation such as Transposed Direct Form |
| 59 // II? |
| 58 const double* feedback = m_feedback->data(); | 60 const double* feedback = m_feedback->data(); |
| 59 const double* feedforward = m_feedforward->data(); | 61 const double* feedforward = m_feedforward->data(); |
| 60 | 62 |
| 61 ASSERT(feedback); | 63 ASSERT(feedback); |
| 62 ASSERT(feedforward); | 64 ASSERT(feedforward); |
| 63 | 65 |
| 64 // Sanity check to see if the feedback coefficients have been scaled appropria
tely. It must | 66 // Sanity check to see if the feedback coefficients have been scaled |
| 65 // be EXACTLY 1! | 67 // appropriately. It must be EXACTLY 1! |
| 66 ASSERT(feedback[0] == 1); | 68 ASSERT(feedback[0] == 1); |
| 67 | 69 |
| 68 int feedbackLength = m_feedback->size(); | 70 int feedbackLength = m_feedback->size(); |
| 69 int feedforwardLength = m_feedforward->size(); | 71 int feedforwardLength = m_feedforward->size(); |
| 70 int minLength = std::min(feedbackLength, feedforwardLength); | 72 int minLength = std::min(feedbackLength, feedforwardLength); |
| 71 | 73 |
| 72 double* xBuffer = m_xBuffer.data(); | 74 double* xBuffer = m_xBuffer.data(); |
| 73 double* yBuffer = m_yBuffer.data(); | 75 double* yBuffer = m_yBuffer.data(); |
| 74 | 76 |
| 75 for (size_t n = 0; n < framesToProcess; ++n) { | 77 for (size_t n = 0; n < framesToProcess; ++n) { |
| 76 // To help minimize roundoff, we compute using double's, even though the fil
ter coefficients | 78 // To help minimize roundoff, we compute using double's, even though the |
| 77 // only have single precision values. | 79 // filter coefficients only have single precision values. |
| 78 double yn = feedforward[0] * sourceP[n]; | 80 double yn = feedforward[0] * sourceP[n]; |
| 79 | 81 |
| 80 // Run both the feedforward and feedback terms together, when possible. | 82 // Run both the feedforward and feedback terms together, when possible. |
| 81 for (int k = 1; k < minLength; ++k) { | 83 for (int k = 1; k < minLength; ++k) { |
| 82 int n = (m_bufferIndex - k) & (kBufferLength - 1); | 84 int n = (m_bufferIndex - k) & (kBufferLength - 1); |
| 83 yn += feedforward[k] * xBuffer[n]; | 85 yn += feedforward[k] * xBuffer[n]; |
| 84 yn -= feedback[k] * yBuffer[n]; | 86 yn -= feedback[k] * yBuffer[n]; |
| 85 } | 87 } |
| 86 | 88 |
| 87 // Handle any remaining feedforward or feedback terms. | 89 // Handle any remaining feedforward or feedback terms. |
| 88 for (int k = minLength; k < feedforwardLength; ++k) | 90 for (int k = minLength; k < feedforwardLength; ++k) |
| 89 yn += feedforward[k] * xBuffer[(m_bufferIndex - k) & (kBufferLength - 1)]; | 91 yn += feedforward[k] * xBuffer[(m_bufferIndex - k) & (kBufferLength - 1)]; |
| 90 | 92 |
| 91 for (int k = minLength; k < feedbackLength; ++k) | 93 for (int k = minLength; k < feedbackLength; ++k) |
| 92 yn -= feedback[k] * yBuffer[(m_bufferIndex - k) & (kBufferLength - 1)]; | 94 yn -= feedback[k] * yBuffer[(m_bufferIndex - k) & (kBufferLength - 1)]; |
| 93 | 95 |
| 94 // Save the current input and output values in the memory buffers for the ne
xt output. | 96 // Save the current input and output values in the memory buffers for the |
| 97 // next output. |
| 95 m_xBuffer[m_bufferIndex] = sourceP[n]; | 98 m_xBuffer[m_bufferIndex] = sourceP[n]; |
| 96 m_yBuffer[m_bufferIndex] = yn; | 99 m_yBuffer[m_bufferIndex] = yn; |
| 97 | 100 |
| 98 m_bufferIndex = (m_bufferIndex + 1) & (kBufferLength - 1); | 101 m_bufferIndex = (m_bufferIndex + 1) & (kBufferLength - 1); |
| 99 | 102 |
| 100 destP[n] = yn; | 103 destP[n] = yn; |
| 101 } | 104 } |
| 102 } | 105 } |
| 103 | 106 |
| 104 void IIRFilter::getFrequencyResponse(int nFrequencies, | 107 void IIRFilter::getFrequencyResponse(int nFrequencies, |
| 105 const float* frequency, | 108 const float* frequency, |
| 106 float* magResponse, | 109 float* magResponse, |
| 107 float* phaseResponse) { | 110 float* phaseResponse) { |
| 108 // Evaluate the z-transform of the filter at the given normalized frequencies
from 0 to 1. (One | 111 // Evaluate the z-transform of the filter at the given normalized frequencies |
| 109 // corresponds to the Nyquist frequency.) | 112 // from 0 to 1. (One corresponds to the Nyquist frequency.) |
| 110 // | 113 // |
| 111 // The z-tranform of the filter is | 114 // The z-tranform of the filter is |
| 112 // | 115 // |
| 113 // H(z) = sum(b[k]*z^(-k), k, 0, M) / sum(a[k]*z^(-k), k, 0, N); | 116 // H(z) = sum(b[k]*z^(-k), k, 0, M) / sum(a[k]*z^(-k), k, 0, N); |
| 114 // | 117 // |
| 115 // The desired frequency response is H(exp(j*omega)), where omega is in [0, 1)
. | 118 // The desired frequency response is H(exp(j*omega)), where omega is in [0, |
| 119 // 1). |
| 116 // | 120 // |
| 117 // 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) | 121 // Let P(x) = sum(c[k]*x^k, k, 0, P) be a polynomial of order P. Then each of |
| 118 // is equivalent to evaluating a polynomial at the point 1/z. | 122 // the sums in H(z) is equivalent to evaluating a polynomial at the point |
| 123 // 1/z. |
| 119 | 124 |
| 120 for (int k = 0; k < nFrequencies; ++k) { | 125 for (int k = 0; k < nFrequencies; ++k) { |
| 121 // zRecip = 1/z = exp(-j*frequency) | 126 // zRecip = 1/z = exp(-j*frequency) |
| 122 double omega = -piDouble * frequency[k]; | 127 double omega = -piDouble * frequency[k]; |
| 123 std::complex<double> zRecip = std::complex<double>(cos(omega), sin(omega)); | 128 std::complex<double> zRecip = std::complex<double>(cos(omega), sin(omega)); |
| 124 | 129 |
| 125 std::complex<double> numerator = evaluatePolynomial( | 130 std::complex<double> numerator = evaluatePolynomial( |
| 126 m_feedforward->data(), zRecip, m_feedforward->size() - 1); | 131 m_feedforward->data(), zRecip, m_feedforward->size() - 1); |
| 127 std::complex<double> denominator = | 132 std::complex<double> denominator = |
| 128 evaluatePolynomial(m_feedback->data(), zRecip, m_feedback->size() - 1); | 133 evaluatePolynomial(m_feedback->data(), zRecip, m_feedback->size() - 1); |
| 129 std::complex<double> response = numerator / denominator; | 134 std::complex<double> response = numerator / denominator; |
| 130 magResponse[k] = static_cast<float>(abs(response)); | 135 magResponse[k] = static_cast<float>(abs(response)); |
| 131 phaseResponse[k] = | 136 phaseResponse[k] = |
| 132 static_cast<float>(atan2(imag(response), real(response))); | 137 static_cast<float>(atan2(imag(response), real(response))); |
| 133 } | 138 } |
| 134 } | 139 } |
| 135 | 140 |
| 136 } // namespace blink | 141 } // namespace blink |
| OLD | NEW |