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 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY 17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25 #include "config.h" 26 27 #if ENABLE(WEB_AUDIO) 28 29 #include "modules/webaudio/BiquadDSPKernel.h" 30 31 #include "platform/FloatConversion.h" 32 #include <limits.h> 33 #include "wtf/Vector.h" 34 35 namespace blink { 36 37 // FIXME: As a recursive linear filter, depending on its parameters, a biquad filter can have 38 // an infinite tailTime. In practice, Biquad filters do not usually (except for very high resonance values) 39 // have a tailTime of longer than approx. 200ms. This value could possibly be calculated based on the 40 // settings of the Biquad. 41 static const double MaxBiquadDelayTime = 0.2; 42 43 void BiquadDSPKernel::updateCoefficientsIfNecessary() 44 { 45 if (biquadProcessor()->filterCoefficientsDirty()) { 46 double cutoffFrequency; 47 double Q; 48 double gain; 49 double detune; // in Cents 50 51 if (biquadProcessor()->hasSampleAccurateValues()) { 52 cutoffFrequency = biquadProcessor()->parameter1()->finalValue(); 53 Q = biquadProcessor()->parameter2()->finalValue(); 54 gain = biquadProcessor()->parameter3()->finalValue(); 55 detune = biquadProcessor()->parameter4()->finalValue(); 56 } else { 57 cutoffFrequency = biquadProcessor()->parameter1()->smoothedValue(); 58 Q = biquadProcessor()->parameter2()->smoothedValue(); 59 gain = biquadProcessor()->parameter3()->smoothedValue(); 60 detune = biquadProcessor()->parameter4()->smoothedValue(); 61 } 62 63 updateCoefficients(cutoffFrequency, Q, gain, detune); 64 } 65 } 66 67 void BiquadDSPKernel::updateCoefficients(double cutoffFrequency, double Q, double gain, double detune) 68 { 69 // Convert from Hertz to normalized frequency 0 -> 1. 70 double nyquist = this->nyquist(); 71 double normalizedFrequency = cutoffFrequency / nyquist; 72 73 // Offset frequency by detune. 74 if (detune) 75 normalizedFrequency *= pow(2, detune / 1200); 76 77 // Configure the biquad with the new filter parameters for the appropriate type of filter. 78 switch (biquadProcessor()->type()) { 79 case BiquadProcessor::LowPass: 80 m_biquad.setLowpassParams(normalizedFrequency, Q); 81 break; 82 83 case BiquadProcessor::HighPass: 84 m_biquad.setHighpassParams(normalizedFrequency, Q); 85 break; 86 87 case BiquadProcessor::BandPass: 88 m_biquad.setBandpassParams(normalizedFrequency, Q); 89 break; 90 91 case BiquadProcessor::LowShelf: 92 m_biquad.setLowShelfParams(normalizedFrequency, gain); 93 break; 94 95 case BiquadProcessor::HighShelf: 96 m_biquad.setHighShelfParams(normalizedFrequency, gain); 97 break; 98 99 case BiquadProcessor::Peaking: 100 m_biquad.setPeakingParams(normalizedFrequency, Q, gain); 101 break; 102 103 case BiquadProcessor::Notch: 104 m_biquad.setNotchParams(normalizedFrequency, Q); 105 break; 106 107 case BiquadProcessor::Allpass: 108 m_biquad.setAllpassParams(normalizedFrequency, Q); 109 break; 110 } 111 } 112 113 void BiquadDSPKernel::process(const float* source, float* destination, size_t framesToProcess) 114 { 115 ASSERT(source && destination && biquadProcessor()); 116 117 // Recompute filter coefficients if any of the parameters have changed. 118 // FIXME: as an optimization, implement a way that a Biquad object can simply copy its internal filter coefficients from another Biquad object. 119 // Then re-factor this code to only run for the first BiquadDSPKernel of each BiquadProcessor. 120 121 122 // The audio thread can't block on this lock; skip updating the coefficients for this block if 123 // necessary. We'll get them the next time around. 124 { 125 MutexTryLocker tryLocker(m_processLock); 126 if (tryLocker.locked()) 127 updateCoefficientsIfNecessary(); 128 } 129 130 m_biquad.process(source, destination, framesToProcess); 131 } 132 133 void BiquadDSPKernel::getFrequencyResponse(int nFrequencies, 134 const float* frequencyHz, 135 float* magResponse, 136 float* phaseResponse) 137 { 138 bool isGood = nFrequencies > 0 && frequencyHz && magResponse && phaseResponse; 139 ASSERT(isGood); 140 if (!isGood) 141 return; 142 143 Vector<float> frequency(nFrequencies); 144 145 double nyquist = this->nyquist(); 146 147 // Convert from frequency in Hz to normalized frequency (0 -> 1), 148 // with 1 equal to the Nyquist frequency. 149 for (int k = 0; k < nFrequencies; ++k) 150 frequency[k] = narrowPrecisionToFloat(frequencyHz[k] / nyquist); 151 152 double cutoffFrequency; 153 double Q; 154 double gain; 155 double detune; // in Cents 156 157 { 158 // Get a copy of the current biquad filter coefficients so we can update the biquad with 159 // these values. We need to synchronize with process() to prevent process() from updating 160 // the filter coefficients while we're trying to access them. The process will update it 161 // next time around. 162 // 163 // The BiquadDSPKernel object here (along with it's Biquad object) is for querying the 164 // frequency response and is NOT the same as the one in process() which is used for 165 // performing the actual filtering. This one is is created in 166 // BiquadProcessor::getFrequencyResponse for this purpose. Both, however, point to the same 167 // BiquadProcessor object. 168 // 169 // FIXME: Simplify this: crbug.com/390266 170 MutexLocker processLocker(m_processLock); 171 172 cutoffFrequency = biquadProcessor()->parameter1()->value(); 173 Q = biquadProcessor()->parameter2()->value(); 174 gain = biquadProcessor()->parameter3()->value(); 175 detune = biquadProcessor()->parameter4()->value(); 176 } 177 178 updateCoefficients(cutoffFrequency, Q, gain, detune); 179 180 m_biquad.getFrequencyResponse(nFrequencies, frequency.data(), magResponse, phaseResponse); 181 } 182 183 double BiquadDSPKernel::tailTime() const 184 { 185 return MaxBiquadDelayTime; 186 } 187 188 double BiquadDSPKernel::latencyTime() const 189 { 190 return 0; 191 } 192 193 } // namespace blink 194 195 #endif // ENABLE(WEB_AUDIO) 196