Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(27)

Side by Side Diff: third_party/WebKit/Source/platform/audio/IIRFilter.cpp

Issue 2851873003: Compute the tail time for an IIRFilter from its coefficients (Closed)
Patch Set: Rebase Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698