Fri, 16 Jan 2015 04:50:19 +0100
Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32
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 */
29 // For M_PI from cmath
30 #ifdef _MSC_VER
31 # define _USE_MATH_DEFINES
32 #endif
34 #include "Biquad.h"
36 #include <cmath>
37 #include <float.h>
38 #include <algorithm>
40 namespace WebCore {
42 Biquad::Biquad()
43 {
44 // Initialize as pass-thru (straight-wire, no filter effect)
45 setNormalizedCoefficients(1, 0, 0, 1, 0, 0);
47 reset(); // clear filter memory
48 }
50 Biquad::~Biquad()
51 {
52 }
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;
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;
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;
73 destP[i] = y;
75 // Update state variables
76 x2 = x1;
77 x1 = x;
78 y2 = y1;
79 y1 = y;
80 }
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 }
100 void Biquad::reset()
101 {
102 m_x1 = m_x2 = m_y1 = m_y2 = 0;
103 }
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));
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);
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);
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;
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 }
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));
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);
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);
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;
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 }
179 void Biquad::setNormalizedCoefficients(double b0, double b1, double b2, double a0, double a1, double a2)
180 {
181 double a0Inverse = 1 / a0;
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 }
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));
195 double A = pow(10.0, dbGain / 40);
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;
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;
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 }
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));
230 double A = pow(10.0, dbGain / 40);
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;
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;
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 }
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));
265 // Don't let Q go negative, which causes an unstable filter.
266 Q = std::max(0.0, Q);
268 double A = pow(10.0, dbGain / 40);
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);
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;
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 }
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));
303 // Don't let Q go negative, which causes an unstable filter.
304 Q = std::max(0.0, Q);
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);
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;
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 }
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));
339 // Don't let Q go negative, which causes an unstable filter.
340 Q = std::max(0.0, Q);
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);
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;
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 }
370 void Biquad::setBandpassParams(double frequency, double Q)
371 {
372 // No negative frequencies allowed.
373 frequency = std::max(0.0, frequency);
375 // Don't let Q go negative, which causes an unstable filter.
376 Q = std::max(0.0, Q);
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);
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;
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 }
410 void Biquad::setZeroPolePairs(const Complex &zero, const Complex &pole)
411 {
412 double b0 = 1;
413 double b1 = -2 * zero.real();
415 double zeroMag = abs(zero);
416 double b2 = zeroMag * zeroMag;
418 double a1 = -2 * pole.real();
420 double poleMag = abs(pole);
421 double a2 = poleMag * poleMag;
422 setNormalizedCoefficients(b0, b1, b2, 1, a1, a2);
423 }
425 void Biquad::setAllpassPole(const Complex &pole)
426 {
427 Complex zero = Complex(1, 0) / pole;
428 setZeroPolePairs(zero, pole);
429 }
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)
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;
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 }
470 } // namespace WebCore