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 |