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 <complex> | 7 #include <complex> |
8 #include "platform/audio/AudioUtilities.h" | |
9 #include "platform/audio/VectorMath.h" | |
8 #include "platform/wtf/MathExtras.h" | 10 #include "platform/wtf/MathExtras.h" |
9 | 11 |
10 namespace blink { | 12 namespace blink { |
11 | 13 |
12 // The length of the memory buffers for the IIR filter. This MUST be a power of | 14 // The length of the memory buffers for the IIR filter. This MUST be a power of |
13 // two and must be greater than the possible length of the filter coefficients. | 15 // two and must be greater than the possible length of the filter coefficients. |
14 const int kBufferLength = 32; | 16 const int kBufferLength = 32; |
15 static_assert(kBufferLength >= IIRFilter::kMaxOrder + 1, | 17 static_assert(kBufferLength >= IIRFilter::kMaxOrder + 1, |
16 "Internal IIR buffer length must be greater than maximum IIR " | 18 "Internal IIR buffer length must be greater than maximum IIR " |
17 "Filter order."); | 19 "Filter order."); |
18 | 20 |
19 IIRFilter::IIRFilter(const AudioDoubleArray* feedforward, | 21 IIRFilter::IIRFilter(const AudioDoubleArray* feedforward, |
20 const AudioDoubleArray* feedback) | 22 const AudioDoubleArray* feedback) |
21 : buffer_index_(0), feedback_(feedback), feedforward_(feedforward) { | 23 : buffer_index_(0), feedback_(feedback), feedforward_(feedforward) { |
22 // These are guaranteed to be zero-initialized. | 24 // These are guaranteed to be zero-initialized. |
23 x_buffer_.Allocate(kBufferLength); | 25 x_buffer_.Allocate(kBufferLength); |
24 y_buffer_.Allocate(kBufferLength); | 26 y_buffer_.Allocate(kBufferLength); |
25 } | 27 } |
26 | 28 |
27 IIRFilter::~IIRFilter() {} | 29 IIRFilter::~IIRFilter() {} |
28 | 30 |
29 void IIRFilter::Reset() { | 31 void IIRFilter::Reset() { |
30 x_buffer_.Zero(); | 32 x_buffer_.Zero(); |
31 y_buffer_.Zero(); | 33 y_buffer_.Zero(); |
34 buffer_index_ = 0; | |
32 } | 35 } |
33 | 36 |
34 static std::complex<double> EvaluatePolynomial(const double* coef, | 37 static std::complex<double> EvaluatePolynomial(const double* coef, |
35 std::complex<double> z, | 38 std::complex<double> z, |
36 int order) { | 39 int order) { |
37 // Use Horner's method to evaluate the polynomial P(z) = sum(coef[k]*z^k, k, | 40 // Use Horner's method to evaluate the polynomial P(z) = sum(coef[k]*z^k, k, |
38 // 0, order); | 41 // 0, order); |
39 std::complex<double> result = 0; | 42 std::complex<double> result = 0; |
40 | 43 |
41 for (int k = order; k >= 0; --k) | 44 for (int k = order; k >= 0; --k) |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
132 feedforward_->Data(), z_recip, feedforward_->size() - 1); | 135 feedforward_->Data(), z_recip, feedforward_->size() - 1); |
133 std::complex<double> denominator = | 136 std::complex<double> denominator = |
134 EvaluatePolynomial(feedback_->Data(), z_recip, feedback_->size() - 1); | 137 EvaluatePolynomial(feedback_->Data(), z_recip, feedback_->size() - 1); |
135 std::complex<double> response = numerator / denominator; | 138 std::complex<double> response = numerator / denominator; |
136 mag_response[k] = static_cast<float>(abs(response)); | 139 mag_response[k] = static_cast<float>(abs(response)); |
137 phase_response[k] = | 140 phase_response[k] = |
138 static_cast<float>(atan2(imag(response), real(response))); | 141 static_cast<float>(atan2(imag(response), real(response))); |
139 } | 142 } |
140 } | 143 } |
141 | 144 |
145 double IIRFilter::TailTime(double sample_rate) { | |
146 // The maximum tail time. This is somewhat arbitrary, but we're assuming that | |
147 // no one is going to expect the IIRFilter to produce an output after this | |
148 // much time after the inputs have stopped. | |
149 const double kMaxTailTime = 60; | |
hongchan
2017/05/12 16:24:05
Let's pull these constants out of this function -
Raymond Toy
2017/05/12 19:42:51
But they're not relevant to any other part of the
| |
150 | |
151 // If the maximum amplitude of the impulse response is less than this, we | |
152 // assume that we've reached the tail of the response. Currently, this means | |
153 // that the impulse is less than 1 bit of a 16-bit PCM value. | |
154 const float kMaxTailAmplitude = 1 / 32768.0; | |
hongchan
2017/05/12 16:24:05
Ditto.
| |
155 | |
156 // How to compute the tail time? We're going to filter an impulse | |
157 // for |kMaxTailTime| seconds, in blocks of kRenderQuantumFrames at | |
158 // a time. The maximum magnitude of this block is saved. After all | |
159 // of the samples have been computed, find the last block with a | |
160 // maximum magnitude greater than |kMaxTaileAmplitude|. That block | |
161 // index + 1 will be the tail time. We don't need to be | |
162 // super-accurate in computing the tail time since we process on | |
163 // blocks, block accuracy is good enough, and the value just needs | |
164 // to be larger than the "real" tail time, so we don't prematurely | |
165 // zero out the output of the node. | |
166 | |
167 // Number of render quanta needed to reach the max tail time. | |
168 int number_of_blocks = std::ceil(sample_rate * kMaxTailTime / | |
169 AudioUtilities::kRenderQuantumFrames); | |
170 | |
171 // Input and output buffers for filtering. | |
172 AudioFloatArray input(AudioUtilities::kRenderQuantumFrames); | |
173 AudioFloatArray output(AudioUtilities::kRenderQuantumFrames); | |
174 | |
175 // Array to hold the max magnitudes | |
176 AudioFloatArray magnitudes(number_of_blocks); | |
177 | |
178 // Create the impulse input signal. | |
179 input[0] = 1; | |
180 | |
181 // Process the first block and get the max magnitude of the output. | |
182 Process(input.Data(), output.Data(), AudioUtilities::kRenderQuantumFrames); | |
183 VectorMath::Vmaxmgv(output.Data(), 1, &magnitudes[0], | |
184 AudioUtilities::kRenderQuantumFrames); | |
185 | |
186 // Process the rest of the signal, getting the max magnitude of the | |
187 // output for each block. | |
188 input[0] = 0; | |
189 | |
190 for (int k = 1; k < number_of_blocks; ++k) { | |
191 Process(input.Data(), output.Data(), AudioUtilities::kRenderQuantumFrames); | |
192 VectorMath::Vmaxmgv(output.Data(), 1, &magnitudes[k], | |
193 AudioUtilities::kRenderQuantumFrames); | |
194 } | |
195 | |
196 // Done computing the impulse response; reset the state so the actual node | |
197 // starts in the expected initial state. | |
198 Reset(); | |
199 | |
200 // Find the last block with amplitude greater than the threshold. | |
201 int index = number_of_blocks - 1; | |
202 for (int k = number_of_blocks - 1; k >= 0; --k) { | |
hongchan
2017/05/12 16:24:04
|number_of_blocks -1| can be |index| here.
Raymond Toy
2017/05/12 19:42:51
Done.
| |
203 if (magnitudes[k] > kMaxTailAmplitude) { | |
204 index = k; | |
205 break; | |
206 } | |
207 } | |
208 | |
209 // The magnitude first become lower than the threshold at the next block. | |
210 // Compute the corresponding time value value; that's the tail time. | |
211 double time = | |
212 (index + 1) * AudioUtilities::kRenderQuantumFrames / sample_rate; | |
213 | |
214 return time; | |
hongchan
2017/05/12 16:24:05
We don't need |time| here. Right?
Raymond Toy
2017/05/12 19:42:51
Done.
| |
215 } | |
216 | |
142 } // namespace blink | 217 } // namespace blink |
OLD | NEW |