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
35namespace 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.
41static const double MaxBiquadDelayTime = 0.2;
42
43void 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
67void 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
113void 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
133void 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
183double BiquadDSPKernel::tailTime() const
184{
185    return MaxBiquadDelayTime;
186}
187
188double BiquadDSPKernel::latencyTime() const
189{
190    return 0;
191}
192
193} // namespace blink
194
195#endif // ENABLE(WEB_AUDIO)
196