Biquad.cpp (14289B)
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 #include "Biquad.h" 30 31 #include <float.h> 32 #include <math.h> 33 34 #include <algorithm> 35 36 #include "DenormalDisabler.h" 37 #include "fdlibm.h" 38 39 namespace WebCore { 40 41 Biquad::Biquad() { 42 // Initialize as pass-thru (straight-wire, no filter effect) 43 setNormalizedCoefficients(1, 0, 0, 1, 0, 0); 44 45 reset(); // clear filter memory 46 } 47 48 Biquad::~Biquad() = default; 49 50 void Biquad::process(const float* sourceP, float* destP, 51 size_t framesToProcess) { 52 // Create local copies of member variables 53 double x1 = m_x1; 54 double x2 = m_x2; 55 double y1 = m_y1; 56 double y2 = m_y2; 57 58 double b0 = m_b0; 59 double b1 = m_b1; 60 double b2 = m_b2; 61 double a1 = m_a1; 62 double a2 = m_a2; 63 64 for (size_t i = 0; i < framesToProcess; ++i) { 65 // FIXME: this can be optimized by pipelining the multiply adds... 66 double x = sourceP[i]; 67 double y = b0 * x + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2; 68 69 destP[i] = y; 70 71 // Update state variables 72 x2 = x1; 73 x1 = x; 74 y2 = y1; 75 y1 = y; 76 } 77 78 // Avoid introducing a stream of subnormals when input is silent and the 79 // tail approaches zero. 80 if (x1 == 0.0 && x2 == 0.0 && (y1 != 0.0 || y2 != 0.0) && 81 fabs(y1) < FLT_MIN && fabs(y2) < FLT_MIN) { 82 // Flush future values to zero (until there is new input). 83 y1 = y2 = 0.0; 84 // Flush calculated values. 85 #ifndef HAVE_DENORMAL 86 for (int i = framesToProcess; i-- && fabsf(destP[i]) < FLT_MIN;) { 87 destP[i] = 0.0f; 88 } 89 #endif 90 } 91 // Local variables back to member. 92 m_x1 = x1; 93 m_x2 = x2; 94 m_y1 = y1; 95 m_y2 = y2; 96 } 97 98 void Biquad::reset() { m_x1 = m_x2 = m_y1 = m_y2 = 0; } 99 100 void Biquad::setLowpassParams(double cutoff, double resonance) { 101 // Limit cutoff to 0 to 1. 102 cutoff = std::max(0.0, std::min(cutoff, 1.0)); 103 104 if (cutoff == 1) { 105 // When cutoff is 1, the z-transform is 1. 106 setNormalizedCoefficients(1, 0, 0, 1, 0, 0); 107 } else if (cutoff > 0) { 108 // Compute biquad coefficients for lowpass filter 109 double g = fdlibm_pow(10.0, -0.05 * resonance); 110 double w0 = M_PI * cutoff; 111 double cos_w0 = fdlibm_cos(w0); 112 double alpha = 0.5 * fdlibm_sin(w0) * g; 113 114 double b1 = 1.0 - cos_w0; 115 double b0 = 0.5 * b1; 116 double b2 = b0; 117 double a0 = 1.0 + alpha; 118 double a1 = -2.0 * cos_w0; 119 double a2 = 1.0 - alpha; 120 121 setNormalizedCoefficients(b0, b1, b2, a0, a1, a2); 122 } else { 123 // When cutoff is zero, nothing gets through the filter, so set 124 // coefficients up correctly. 125 setNormalizedCoefficients(0, 0, 0, 1, 0, 0); 126 } 127 } 128 129 void Biquad::setHighpassParams(double cutoff, double resonance) { 130 // Limit cutoff to 0 to 1. 131 cutoff = std::max(0.0, std::min(cutoff, 1.0)); 132 133 if (cutoff == 1) { 134 // The z-transform is 0. 135 setNormalizedCoefficients(0, 0, 0, 1, 0, 0); 136 } else if (cutoff > 0) { 137 // Compute biquad coefficients for highpass filter 138 double g = fdlibm_pow(10.0, -0.05 * resonance); 139 double w0 = M_PI * cutoff; 140 double cos_w0 = fdlibm_cos(w0); 141 double alpha = 0.5 * fdlibm_sin(w0) * g; 142 143 double b1 = -1.0 - cos_w0; 144 double b0 = -0.5 * b1; 145 double b2 = b0; 146 double a0 = 1.0 + alpha; 147 double a1 = -2.0 * cos_w0; 148 double a2 = 1.0 - alpha; 149 150 setNormalizedCoefficients(b0, b1, b2, a0, a1, a2); 151 } else { 152 // When cutoff is zero, we need to be careful because the above 153 // gives a quadratic divided by the same quadratic, with poles 154 // and zeros on the unit circle in the same place. When cutoff 155 // is zero, the z-transform is 1. 156 setNormalizedCoefficients(1, 0, 0, 1, 0, 0); 157 } 158 } 159 160 void Biquad::setNormalizedCoefficients(double b0, double b1, double b2, 161 double a0, double a1, double a2) { 162 double a0Inverse = 1 / a0; 163 164 m_b0 = b0 * a0Inverse; 165 m_b1 = b1 * a0Inverse; 166 m_b2 = b2 * a0Inverse; 167 m_a1 = a1 * a0Inverse; 168 m_a2 = a2 * a0Inverse; 169 } 170 171 void Biquad::setLowShelfParams(double frequency, double dbGain) { 172 // Clip frequencies to between 0 and 1, inclusive. 173 frequency = std::max(0.0, std::min(frequency, 1.0)); 174 175 double A = fdlibm_pow(10.0, dbGain / 40); 176 177 if (frequency == 1) { 178 // The z-transform is a constant gain. 179 setNormalizedCoefficients(A * A, 0, 0, 1, 0, 0); 180 } else if (frequency > 0) { 181 double w0 = M_PI * frequency; 182 double S = 1; // filter slope (1 is max value) 183 double alpha = 0.5 * fdlibm_sin(w0) * sqrt((A + 1 / A) * (1 / S - 1) + 2); 184 double k = fdlibm_cos(w0); 185 double k2 = 2 * sqrt(A) * alpha; 186 double aPlusOne = A + 1; 187 double aMinusOne = A - 1; 188 189 double b0 = A * (aPlusOne - aMinusOne * k + k2); 190 double b1 = 2 * A * (aMinusOne - aPlusOne * k); 191 double b2 = A * (aPlusOne - aMinusOne * k - k2); 192 double a0 = aPlusOne + aMinusOne * k + k2; 193 double a1 = -2 * (aMinusOne + aPlusOne * k); 194 double a2 = aPlusOne + aMinusOne * k - k2; 195 196 setNormalizedCoefficients(b0, b1, b2, a0, a1, a2); 197 } else { 198 // When frequency is 0, the z-transform is 1. 199 setNormalizedCoefficients(1, 0, 0, 1, 0, 0); 200 } 201 } 202 203 void Biquad::setHighShelfParams(double frequency, double dbGain) { 204 // Clip frequencies to between 0 and 1, inclusive. 205 frequency = std::max(0.0, std::min(frequency, 1.0)); 206 207 double A = fdlibm_pow(10.0, dbGain / 40); 208 209 if (frequency == 1) { 210 // The z-transform is 1. 211 setNormalizedCoefficients(1, 0, 0, 1, 0, 0); 212 } else if (frequency > 0) { 213 double w0 = M_PI * frequency; 214 double S = 1; // filter slope (1 is max value) 215 double alpha = 0.5 * fdlibm_sin(w0) * sqrt((A + 1 / A) * (1 / S - 1) + 2); 216 double k = fdlibm_cos(w0); 217 double k2 = 2 * sqrt(A) * alpha; 218 double aPlusOne = A + 1; 219 double aMinusOne = A - 1; 220 221 double b0 = A * (aPlusOne + aMinusOne * k + k2); 222 double b1 = -2 * A * (aMinusOne + aPlusOne * k); 223 double b2 = A * (aPlusOne + aMinusOne * k - k2); 224 double a0 = aPlusOne - aMinusOne * k + k2; 225 double a1 = 2 * (aMinusOne - aPlusOne * k); 226 double a2 = aPlusOne - aMinusOne * k - k2; 227 228 setNormalizedCoefficients(b0, b1, b2, a0, a1, a2); 229 } else { 230 // When frequency = 0, the filter is just a gain, A^2. 231 setNormalizedCoefficients(A * A, 0, 0, 1, 0, 0); 232 } 233 } 234 235 void Biquad::setPeakingParams(double frequency, double Q, double dbGain) { 236 // Clip frequencies to between 0 and 1, inclusive. 237 frequency = std::max(0.0, std::min(frequency, 1.0)); 238 239 // Don't let Q go negative, which causes an unstable filter. 240 Q = std::max(0.0, Q); 241 242 double A = fdlibm_pow(10.0, dbGain / 40); 243 244 if (frequency > 0 && frequency < 1) { 245 if (Q > 0) { 246 double w0 = M_PI * frequency; 247 double alpha = fdlibm_sin(w0) / (2 * Q); 248 double k = fdlibm_cos(w0); 249 250 double b0 = 1 + alpha * A; 251 double b1 = -2 * k; 252 double b2 = 1 - alpha * A; 253 double a0 = 1 + alpha / A; 254 double a1 = -2 * k; 255 double a2 = 1 - alpha / A; 256 257 setNormalizedCoefficients(b0, b1, b2, a0, a1, a2); 258 } else { 259 // When Q = 0, the above formulas have problems. If we look at 260 // the z-transform, we can see that the limit as Q->0 is A^2, so 261 // set the filter that way. 262 setNormalizedCoefficients(A * A, 0, 0, 1, 0, 0); 263 } 264 } else { 265 // When frequency is 0 or 1, the z-transform is 1. 266 setNormalizedCoefficients(1, 0, 0, 1, 0, 0); 267 } 268 } 269 270 void Biquad::setAllpassParams(double frequency, double Q) { 271 // Clip frequencies to between 0 and 1, inclusive. 272 frequency = std::max(0.0, std::min(frequency, 1.0)); 273 274 // Don't let Q go negative, which causes an unstable filter. 275 Q = std::max(0.0, Q); 276 277 if (frequency > 0 && frequency < 1) { 278 if (Q > 0) { 279 double w0 = M_PI * frequency; 280 double alpha = fdlibm_sin(w0) / (2 * Q); 281 double k = fdlibm_cos(w0); 282 283 double b0 = 1 - alpha; 284 double b1 = -2 * k; 285 double b2 = 1 + alpha; 286 double a0 = 1 + alpha; 287 double a1 = -2 * k; 288 double a2 = 1 - alpha; 289 290 setNormalizedCoefficients(b0, b1, b2, a0, a1, a2); 291 } else { 292 // When Q = 0, the above formulas have problems. If we look at 293 // the z-transform, we can see that the limit as Q->0 is -1, so 294 // set the filter that way. 295 setNormalizedCoefficients(-1, 0, 0, 1, 0, 0); 296 } 297 } else { 298 // When frequency is 0 or 1, the z-transform is 1. 299 setNormalizedCoefficients(1, 0, 0, 1, 0, 0); 300 } 301 } 302 303 void Biquad::setNotchParams(double frequency, double Q) { 304 // Clip frequencies to between 0 and 1, inclusive. 305 frequency = std::max(0.0, std::min(frequency, 1.0)); 306 307 // Don't let Q go negative, which causes an unstable filter. 308 Q = std::max(0.0, Q); 309 310 if (frequency > 0 && frequency < 1) { 311 if (Q > 0) { 312 double w0 = M_PI * frequency; 313 double alpha = fdlibm_sin(w0) / (2 * Q); 314 double k = fdlibm_cos(w0); 315 316 double b0 = 1; 317 double b1 = -2 * k; 318 double b2 = 1; 319 double a0 = 1 + alpha; 320 double a1 = -2 * k; 321 double a2 = 1 - alpha; 322 323 setNormalizedCoefficients(b0, b1, b2, a0, a1, a2); 324 } else { 325 // When Q = 0, the above formulas have problems. If we look at 326 // the z-transform, we can see that the limit as Q->0 is 0, so 327 // set the filter that way. 328 setNormalizedCoefficients(0, 0, 0, 1, 0, 0); 329 } 330 } else { 331 // When frequency is 0 or 1, the z-transform is 1. 332 setNormalizedCoefficients(1, 0, 0, 1, 0, 0); 333 } 334 } 335 336 void Biquad::setBandpassParams(double frequency, double Q) { 337 // No negative frequencies allowed. 338 frequency = std::max(0.0, frequency); 339 340 // Don't let Q go negative, which causes an unstable filter. 341 Q = std::max(0.0, Q); 342 343 if (frequency > 0 && frequency < 1) { 344 double w0 = M_PI * frequency; 345 if (Q > 0) { 346 double alpha = fdlibm_sin(w0) / (2 * Q); 347 double k = fdlibm_cos(w0); 348 349 double b0 = alpha; 350 double b1 = 0; 351 double b2 = -alpha; 352 double a0 = 1 + alpha; 353 double a1 = -2 * k; 354 double a2 = 1 - alpha; 355 356 setNormalizedCoefficients(b0, b1, b2, a0, a1, a2); 357 } else { 358 // When Q = 0, the above formulas have problems. If we look at 359 // the z-transform, we can see that the limit as Q->0 is 1, so 360 // set the filter that way. 361 setNormalizedCoefficients(1, 0, 0, 1, 0, 0); 362 } 363 } else { 364 // When the cutoff is zero, the z-transform approaches 0, if Q 365 // > 0. When both Q and cutoff are zero, the z-transform is 366 // pretty much undefined. What should we do in this case? 367 // For now, just make the filter 0. When the cutoff is 1, the 368 // z-transform also approaches 0. 369 setNormalizedCoefficients(0, 0, 0, 1, 0, 0); 370 } 371 } 372 373 void Biquad::setZeroPolePairs(const Complex& zero, const Complex& pole) { 374 double b0 = 1; 375 double b1 = -2 * zero.real(); 376 377 double zeroMag = abs(zero); 378 double b2 = zeroMag * zeroMag; 379 380 double a1 = -2 * pole.real(); 381 382 double poleMag = abs(pole); 383 double a2 = poleMag * poleMag; 384 setNormalizedCoefficients(b0, b1, b2, 1, a1, a2); 385 } 386 387 void Biquad::setAllpassPole(const Complex& pole) { 388 Complex zero = Complex(1, 0) / pole; 389 setZeroPolePairs(zero, pole); 390 } 391 392 void Biquad::getFrequencyResponse(int nFrequencies, const float* frequency, 393 float* magResponse, float* phaseResponse) { 394 // Evaluate the Z-transform of the filter at given normalized 395 // frequency from 0 to 1. (1 corresponds to the Nyquist 396 // frequency.) 397 // 398 // The z-transform of the filter is 399 // 400 // H(z) = (b0 + b1*z^(-1) + b2*z^(-2))/(1 + a1*z^(-1) + a2*z^(-2)) 401 // 402 // Evaluate as 403 // 404 // b0 + (b1 + b2*z1)*z1 405 // -------------------- 406 // 1 + (a1 + a2*z1)*z1 407 // 408 // with z1 = 1/z and z = exp(j*pi*frequency). Hence z1 = exp(-j*pi*frequency) 409 410 // Make local copies of the coefficients as a micro-optimization. 411 double b0 = m_b0; 412 double b1 = m_b1; 413 double b2 = m_b2; 414 double a1 = m_a1; 415 double a2 = m_a2; 416 417 for (int k = 0; k < nFrequencies; ++k) { 418 double omega = -M_PI * frequency[k]; 419 Complex z = Complex(fdlibm_cos(omega), fdlibm_sin(omega)); 420 Complex numerator = b0 + (b1 + b2 * z) * z; 421 Complex denominator = Complex(1, 0) + (a1 + a2 * z) * z; 422 // Strangely enough, using complex division: 423 // e.g. Complex response = numerator / denominator; 424 // fails on our test machines, yielding infinities and NaNs, so we do 425 // things the long way here. 426 double n = norm(denominator); 427 double r = (real(numerator) * real(denominator) + 428 imag(numerator) * imag(denominator)) / 429 n; 430 double i = (imag(numerator) * real(denominator) - 431 real(numerator) * imag(denominator)) / 432 n; 433 std::complex<double> response = std::complex<double>(r, i); 434 435 magResponse[k] = 436 static_cast<float>(fdlibm_hypot(real(response), imag(response))); 437 phaseResponse[k] = 438 static_cast<float>(fdlibm_atan2(imag(response), real(response))); 439 } 440 } 441 442 } // namespace WebCore