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; |
| 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; |
| 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 = index; k >= 0; --k) { |
| 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 return (index + 1) * AudioUtilities::kRenderQuantumFrames / sample_rate; |
| 212 } |
| 213 |
142 } // namespace blink | 214 } // namespace blink |
OLD | NEW |