|
1 /* |
|
2 * Copyright (C) 2010 Google Inc. All rights reserved. |
|
3 * |
|
4 * Redistribution and use in source and binary forms, with or without |
|
5 * modification, are permitted provided that the following conditions |
|
6 * are met: |
|
7 * |
|
8 * 1. Redistributions of source code must retain the above copyright |
|
9 * notice, this list of conditions and the following disclaimer. |
|
10 * 2. Redistributions in binary form must reproduce the above copyright |
|
11 * notice, this list of conditions and the following disclaimer in the |
|
12 * documentation and/or other materials provided with the distribution. |
|
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
|
14 * its contributors may be used to endorse or promote products derived |
|
15 * from this software without specific prior written permission. |
|
16 * |
|
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
|
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
|
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
27 */ |
|
28 |
|
29 // For M_PI from cmath |
|
30 #ifdef _MSC_VER |
|
31 # define _USE_MATH_DEFINES |
|
32 #endif |
|
33 |
|
34 #include "Biquad.h" |
|
35 |
|
36 #include <cmath> |
|
37 #include <float.h> |
|
38 #include <algorithm> |
|
39 |
|
40 namespace WebCore { |
|
41 |
|
42 Biquad::Biquad() |
|
43 { |
|
44 // Initialize as pass-thru (straight-wire, no filter effect) |
|
45 setNormalizedCoefficients(1, 0, 0, 1, 0, 0); |
|
46 |
|
47 reset(); // clear filter memory |
|
48 } |
|
49 |
|
50 Biquad::~Biquad() |
|
51 { |
|
52 } |
|
53 |
|
54 void Biquad::process(const float* sourceP, float* destP, size_t framesToProcess) |
|
55 { |
|
56 // Create local copies of member variables |
|
57 double x1 = m_x1; |
|
58 double x2 = m_x2; |
|
59 double y1 = m_y1; |
|
60 double y2 = m_y2; |
|
61 |
|
62 double b0 = m_b0; |
|
63 double b1 = m_b1; |
|
64 double b2 = m_b2; |
|
65 double a1 = m_a1; |
|
66 double a2 = m_a2; |
|
67 |
|
68 for (size_t i = 0; i < framesToProcess; ++i) { |
|
69 // FIXME: this can be optimized by pipelining the multiply adds... |
|
70 double x = sourceP[i]; |
|
71 double y = b0*x + b1*x1 + b2*x2 - a1*y1 - a2*y2; |
|
72 |
|
73 destP[i] = y; |
|
74 |
|
75 // Update state variables |
|
76 x2 = x1; |
|
77 x1 = x; |
|
78 y2 = y1; |
|
79 y1 = y; |
|
80 } |
|
81 |
|
82 // Avoid introducing a stream of subnormals when input is silent and the |
|
83 // tail approaches zero. |
|
84 if (x1 == 0.0 && x2 == 0.0 && (y1 != 0.0 || y2 != 0.0) && |
|
85 fabs(y1) < FLT_MIN && fabs(y2) < FLT_MIN) { |
|
86 // Flush future values to zero (until there is new input). |
|
87 y1 = y2 = 0.0; |
|
88 // Flush calculated values. |
|
89 for (int i = framesToProcess; i-- && fabsf(destP[i]) < FLT_MIN; ) { |
|
90 destP[i] = 0.0f; |
|
91 } |
|
92 } |
|
93 // Local variables back to member. |
|
94 m_x1 = x1; |
|
95 m_x2 = x2; |
|
96 m_y1 = y1; |
|
97 m_y2 = y2; |
|
98 } |
|
99 |
|
100 void Biquad::reset() |
|
101 { |
|
102 m_x1 = m_x2 = m_y1 = m_y2 = 0; |
|
103 } |
|
104 |
|
105 void Biquad::setLowpassParams(double cutoff, double resonance) |
|
106 { |
|
107 // Limit cutoff to 0 to 1. |
|
108 cutoff = std::max(0.0, std::min(cutoff, 1.0)); |
|
109 |
|
110 if (cutoff == 1) { |
|
111 // When cutoff is 1, the z-transform is 1. |
|
112 setNormalizedCoefficients(1, 0, 0, |
|
113 1, 0, 0); |
|
114 } else if (cutoff > 0) { |
|
115 // Compute biquad coefficients for lowpass filter |
|
116 resonance = std::max(0.0, resonance); // can't go negative |
|
117 double g = pow(10.0, 0.05 * resonance); |
|
118 double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2); |
|
119 |
|
120 double theta = M_PI * cutoff; |
|
121 double sn = 0.5 * d * sin(theta); |
|
122 double beta = 0.5 * (1 - sn) / (1 + sn); |
|
123 double gamma = (0.5 + beta) * cos(theta); |
|
124 double alpha = 0.25 * (0.5 + beta - gamma); |
|
125 |
|
126 double b0 = 2 * alpha; |
|
127 double b1 = 2 * 2 * alpha; |
|
128 double b2 = 2 * alpha; |
|
129 double a1 = 2 * -gamma; |
|
130 double a2 = 2 * beta; |
|
131 |
|
132 setNormalizedCoefficients(b0, b1, b2, 1, a1, a2); |
|
133 } else { |
|
134 // When cutoff is zero, nothing gets through the filter, so set |
|
135 // coefficients up correctly. |
|
136 setNormalizedCoefficients(0, 0, 0, |
|
137 1, 0, 0); |
|
138 } |
|
139 } |
|
140 |
|
141 void Biquad::setHighpassParams(double cutoff, double resonance) |
|
142 { |
|
143 // Limit cutoff to 0 to 1. |
|
144 cutoff = std::max(0.0, std::min(cutoff, 1.0)); |
|
145 |
|
146 if (cutoff == 1) { |
|
147 // The z-transform is 0. |
|
148 setNormalizedCoefficients(0, 0, 0, |
|
149 1, 0, 0); |
|
150 } else if (cutoff > 0) { |
|
151 // Compute biquad coefficients for highpass filter |
|
152 resonance = std::max(0.0, resonance); // can't go negative |
|
153 double g = pow(10.0, 0.05 * resonance); |
|
154 double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2); |
|
155 |
|
156 double theta = M_PI * cutoff; |
|
157 double sn = 0.5 * d * sin(theta); |
|
158 double beta = 0.5 * (1 - sn) / (1 + sn); |
|
159 double gamma = (0.5 + beta) * cos(theta); |
|
160 double alpha = 0.25 * (0.5 + beta + gamma); |
|
161 |
|
162 double b0 = 2 * alpha; |
|
163 double b1 = 2 * -2 * alpha; |
|
164 double b2 = 2 * alpha; |
|
165 double a1 = 2 * -gamma; |
|
166 double a2 = 2 * beta; |
|
167 |
|
168 setNormalizedCoefficients(b0, b1, b2, 1, a1, a2); |
|
169 } else { |
|
170 // When cutoff is zero, we need to be careful because the above |
|
171 // gives a quadratic divided by the same quadratic, with poles |
|
172 // and zeros on the unit circle in the same place. When cutoff |
|
173 // is zero, the z-transform is 1. |
|
174 setNormalizedCoefficients(1, 0, 0, |
|
175 1, 0, 0); |
|
176 } |
|
177 } |
|
178 |
|
179 void Biquad::setNormalizedCoefficients(double b0, double b1, double b2, double a0, double a1, double a2) |
|
180 { |
|
181 double a0Inverse = 1 / a0; |
|
182 |
|
183 m_b0 = b0 * a0Inverse; |
|
184 m_b1 = b1 * a0Inverse; |
|
185 m_b2 = b2 * a0Inverse; |
|
186 m_a1 = a1 * a0Inverse; |
|
187 m_a2 = a2 * a0Inverse; |
|
188 } |
|
189 |
|
190 void Biquad::setLowShelfParams(double frequency, double dbGain) |
|
191 { |
|
192 // Clip frequencies to between 0 and 1, inclusive. |
|
193 frequency = std::max(0.0, std::min(frequency, 1.0)); |
|
194 |
|
195 double A = pow(10.0, dbGain / 40); |
|
196 |
|
197 if (frequency == 1) { |
|
198 // The z-transform is a constant gain. |
|
199 setNormalizedCoefficients(A * A, 0, 0, |
|
200 1, 0, 0); |
|
201 } else if (frequency > 0) { |
|
202 double w0 = M_PI * frequency; |
|
203 double S = 1; // filter slope (1 is max value) |
|
204 double alpha = 0.5 * sin(w0) * sqrt((A + 1 / A) * (1 / S - 1) + 2); |
|
205 double k = cos(w0); |
|
206 double k2 = 2 * sqrt(A) * alpha; |
|
207 double aPlusOne = A + 1; |
|
208 double aMinusOne = A - 1; |
|
209 |
|
210 double b0 = A * (aPlusOne - aMinusOne * k + k2); |
|
211 double b1 = 2 * A * (aMinusOne - aPlusOne * k); |
|
212 double b2 = A * (aPlusOne - aMinusOne * k - k2); |
|
213 double a0 = aPlusOne + aMinusOne * k + k2; |
|
214 double a1 = -2 * (aMinusOne + aPlusOne * k); |
|
215 double a2 = aPlusOne + aMinusOne * k - k2; |
|
216 |
|
217 setNormalizedCoefficients(b0, b1, b2, a0, a1, a2); |
|
218 } else { |
|
219 // When frequency is 0, the z-transform is 1. |
|
220 setNormalizedCoefficients(1, 0, 0, |
|
221 1, 0, 0); |
|
222 } |
|
223 } |
|
224 |
|
225 void Biquad::setHighShelfParams(double frequency, double dbGain) |
|
226 { |
|
227 // Clip frequencies to between 0 and 1, inclusive. |
|
228 frequency = std::max(0.0, std::min(frequency, 1.0)); |
|
229 |
|
230 double A = pow(10.0, dbGain / 40); |
|
231 |
|
232 if (frequency == 1) { |
|
233 // The z-transform is 1. |
|
234 setNormalizedCoefficients(1, 0, 0, |
|
235 1, 0, 0); |
|
236 } else if (frequency > 0) { |
|
237 double w0 = M_PI * frequency; |
|
238 double S = 1; // filter slope (1 is max value) |
|
239 double alpha = 0.5 * sin(w0) * sqrt((A + 1 / A) * (1 / S - 1) + 2); |
|
240 double k = cos(w0); |
|
241 double k2 = 2 * sqrt(A) * alpha; |
|
242 double aPlusOne = A + 1; |
|
243 double aMinusOne = A - 1; |
|
244 |
|
245 double b0 = A * (aPlusOne + aMinusOne * k + k2); |
|
246 double b1 = -2 * A * (aMinusOne + aPlusOne * k); |
|
247 double b2 = A * (aPlusOne + aMinusOne * k - k2); |
|
248 double a0 = aPlusOne - aMinusOne * k + k2; |
|
249 double a1 = 2 * (aMinusOne - aPlusOne * k); |
|
250 double a2 = aPlusOne - aMinusOne * k - k2; |
|
251 |
|
252 setNormalizedCoefficients(b0, b1, b2, a0, a1, a2); |
|
253 } else { |
|
254 // When frequency = 0, the filter is just a gain, A^2. |
|
255 setNormalizedCoefficients(A * A, 0, 0, |
|
256 1, 0, 0); |
|
257 } |
|
258 } |
|
259 |
|
260 void Biquad::setPeakingParams(double frequency, double Q, double dbGain) |
|
261 { |
|
262 // Clip frequencies to between 0 and 1, inclusive. |
|
263 frequency = std::max(0.0, std::min(frequency, 1.0)); |
|
264 |
|
265 // Don't let Q go negative, which causes an unstable filter. |
|
266 Q = std::max(0.0, Q); |
|
267 |
|
268 double A = pow(10.0, dbGain / 40); |
|
269 |
|
270 if (frequency > 0 && frequency < 1) { |
|
271 if (Q > 0) { |
|
272 double w0 = M_PI * frequency; |
|
273 double alpha = sin(w0) / (2 * Q); |
|
274 double k = cos(w0); |
|
275 |
|
276 double b0 = 1 + alpha * A; |
|
277 double b1 = -2 * k; |
|
278 double b2 = 1 - alpha * A; |
|
279 double a0 = 1 + alpha / A; |
|
280 double a1 = -2 * k; |
|
281 double a2 = 1 - alpha / A; |
|
282 |
|
283 setNormalizedCoefficients(b0, b1, b2, a0, a1, a2); |
|
284 } else { |
|
285 // When Q = 0, the above formulas have problems. If we look at |
|
286 // the z-transform, we can see that the limit as Q->0 is A^2, so |
|
287 // set the filter that way. |
|
288 setNormalizedCoefficients(A * A, 0, 0, |
|
289 1, 0, 0); |
|
290 } |
|
291 } else { |
|
292 // When frequency is 0 or 1, the z-transform is 1. |
|
293 setNormalizedCoefficients(1, 0, 0, |
|
294 1, 0, 0); |
|
295 } |
|
296 } |
|
297 |
|
298 void Biquad::setAllpassParams(double frequency, double Q) |
|
299 { |
|
300 // Clip frequencies to between 0 and 1, inclusive. |
|
301 frequency = std::max(0.0, std::min(frequency, 1.0)); |
|
302 |
|
303 // Don't let Q go negative, which causes an unstable filter. |
|
304 Q = std::max(0.0, Q); |
|
305 |
|
306 if (frequency > 0 && frequency < 1) { |
|
307 if (Q > 0) { |
|
308 double w0 = M_PI * frequency; |
|
309 double alpha = sin(w0) / (2 * Q); |
|
310 double k = cos(w0); |
|
311 |
|
312 double b0 = 1 - alpha; |
|
313 double b1 = -2 * k; |
|
314 double b2 = 1 + alpha; |
|
315 double a0 = 1 + alpha; |
|
316 double a1 = -2 * k; |
|
317 double a2 = 1 - alpha; |
|
318 |
|
319 setNormalizedCoefficients(b0, b1, b2, a0, a1, a2); |
|
320 } else { |
|
321 // When Q = 0, the above formulas have problems. If we look at |
|
322 // the z-transform, we can see that the limit as Q->0 is -1, so |
|
323 // set the filter that way. |
|
324 setNormalizedCoefficients(-1, 0, 0, |
|
325 1, 0, 0); |
|
326 } |
|
327 } else { |
|
328 // When frequency is 0 or 1, the z-transform is 1. |
|
329 setNormalizedCoefficients(1, 0, 0, |
|
330 1, 0, 0); |
|
331 } |
|
332 } |
|
333 |
|
334 void Biquad::setNotchParams(double frequency, double Q) |
|
335 { |
|
336 // Clip frequencies to between 0 and 1, inclusive. |
|
337 frequency = std::max(0.0, std::min(frequency, 1.0)); |
|
338 |
|
339 // Don't let Q go negative, which causes an unstable filter. |
|
340 Q = std::max(0.0, Q); |
|
341 |
|
342 if (frequency > 0 && frequency < 1) { |
|
343 if (Q > 0) { |
|
344 double w0 = M_PI * frequency; |
|
345 double alpha = sin(w0) / (2 * Q); |
|
346 double k = cos(w0); |
|
347 |
|
348 double b0 = 1; |
|
349 double b1 = -2 * k; |
|
350 double b2 = 1; |
|
351 double a0 = 1 + alpha; |
|
352 double a1 = -2 * k; |
|
353 double a2 = 1 - alpha; |
|
354 |
|
355 setNormalizedCoefficients(b0, b1, b2, a0, a1, a2); |
|
356 } else { |
|
357 // When Q = 0, the above formulas have problems. If we look at |
|
358 // the z-transform, we can see that the limit as Q->0 is 0, so |
|
359 // set the filter that way. |
|
360 setNormalizedCoefficients(0, 0, 0, |
|
361 1, 0, 0); |
|
362 } |
|
363 } else { |
|
364 // When frequency is 0 or 1, the z-transform is 1. |
|
365 setNormalizedCoefficients(1, 0, 0, |
|
366 1, 0, 0); |
|
367 } |
|
368 } |
|
369 |
|
370 void Biquad::setBandpassParams(double frequency, double Q) |
|
371 { |
|
372 // No negative frequencies allowed. |
|
373 frequency = std::max(0.0, frequency); |
|
374 |
|
375 // Don't let Q go negative, which causes an unstable filter. |
|
376 Q = std::max(0.0, Q); |
|
377 |
|
378 if (frequency > 0 && frequency < 1) { |
|
379 double w0 = M_PI * frequency; |
|
380 if (Q > 0) { |
|
381 double alpha = sin(w0) / (2 * Q); |
|
382 double k = cos(w0); |
|
383 |
|
384 double b0 = alpha; |
|
385 double b1 = 0; |
|
386 double b2 = -alpha; |
|
387 double a0 = 1 + alpha; |
|
388 double a1 = -2 * k; |
|
389 double a2 = 1 - alpha; |
|
390 |
|
391 setNormalizedCoefficients(b0, b1, b2, a0, a1, a2); |
|
392 } else { |
|
393 // When Q = 0, the above formulas have problems. If we look at |
|
394 // the z-transform, we can see that the limit as Q->0 is 1, so |
|
395 // set the filter that way. |
|
396 setNormalizedCoefficients(1, 0, 0, |
|
397 1, 0, 0); |
|
398 } |
|
399 } else { |
|
400 // When the cutoff is zero, the z-transform approaches 0, if Q |
|
401 // > 0. When both Q and cutoff are zero, the z-transform is |
|
402 // pretty much undefined. What should we do in this case? |
|
403 // For now, just make the filter 0. When the cutoff is 1, the |
|
404 // z-transform also approaches 0. |
|
405 setNormalizedCoefficients(0, 0, 0, |
|
406 1, 0, 0); |
|
407 } |
|
408 } |
|
409 |
|
410 void Biquad::setZeroPolePairs(const Complex &zero, const Complex &pole) |
|
411 { |
|
412 double b0 = 1; |
|
413 double b1 = -2 * zero.real(); |
|
414 |
|
415 double zeroMag = abs(zero); |
|
416 double b2 = zeroMag * zeroMag; |
|
417 |
|
418 double a1 = -2 * pole.real(); |
|
419 |
|
420 double poleMag = abs(pole); |
|
421 double a2 = poleMag * poleMag; |
|
422 setNormalizedCoefficients(b0, b1, b2, 1, a1, a2); |
|
423 } |
|
424 |
|
425 void Biquad::setAllpassPole(const Complex &pole) |
|
426 { |
|
427 Complex zero = Complex(1, 0) / pole; |
|
428 setZeroPolePairs(zero, pole); |
|
429 } |
|
430 |
|
431 void Biquad::getFrequencyResponse(int nFrequencies, |
|
432 const float* frequency, |
|
433 float* magResponse, |
|
434 float* phaseResponse) |
|
435 { |
|
436 // Evaluate the Z-transform of the filter at given normalized |
|
437 // frequency from 0 to 1. (1 corresponds to the Nyquist |
|
438 // frequency.) |
|
439 // |
|
440 // The z-transform of the filter is |
|
441 // |
|
442 // H(z) = (b0 + b1*z^(-1) + b2*z^(-2))/(1 + a1*z^(-1) + a2*z^(-2)) |
|
443 // |
|
444 // Evaluate as |
|
445 // |
|
446 // b0 + (b1 + b2*z1)*z1 |
|
447 // -------------------- |
|
448 // 1 + (a1 + a2*z1)*z1 |
|
449 // |
|
450 // with z1 = 1/z and z = exp(j*pi*frequency). Hence z1 = exp(-j*pi*frequency) |
|
451 |
|
452 // Make local copies of the coefficients as a micro-optimization. |
|
453 double b0 = m_b0; |
|
454 double b1 = m_b1; |
|
455 double b2 = m_b2; |
|
456 double a1 = m_a1; |
|
457 double a2 = m_a2; |
|
458 |
|
459 for (int k = 0; k < nFrequencies; ++k) { |
|
460 double omega = -M_PI * frequency[k]; |
|
461 Complex z = Complex(cos(omega), sin(omega)); |
|
462 Complex numerator = b0 + (b1 + b2 * z) * z; |
|
463 Complex denominator = Complex(1, 0) + (a1 + a2 * z) * z; |
|
464 Complex response = numerator / denominator; |
|
465 magResponse[k] = static_cast<float>(abs(response)); |
|
466 phaseResponse[k] = static_cast<float>(atan2(imag(response), real(response))); |
|
467 } |
|
468 } |
|
469 |
|
470 } // namespace WebCore |
|
471 |