Home | History | Annotate | Download | only in webaudio
      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