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
updateCoefficientsIfNecessary()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
updateCoefficients(double cutoffFrequency,double Q,double gain,double detune)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
process(const float * source,float * destination,size_t framesToProcess)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
getFrequencyResponse(int nFrequencies,const float * frequencyHz,float * magResponse,float * phaseResponse)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
tailTime() const183 double BiquadDSPKernel::tailTime() const
184 {
185 return MaxBiquadDelayTime;
186 }
187
latencyTime() const188 double BiquadDSPKernel::latencyTime() const
189 {
190 return 0;
191 }
192
193 } // namespace blink
194
195 #endif // ENABLE(WEB_AUDIO)
196