Home | History | Annotate | Download | only in audio
      1 /*
      2  * Copyright (C) 2011 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 "config.h"
     30 
     31 #if ENABLE(WEB_AUDIO)
     32 
     33 #include "platform/audio/DynamicsCompressor.h"
     34 
     35 #include "platform/audio/AudioBus.h"
     36 #include "platform/audio/AudioUtilities.h"
     37 #include "wtf/MathExtras.h"
     38 
     39 namespace blink {
     40 
     41 using namespace AudioUtilities;
     42 
     43 DynamicsCompressor::DynamicsCompressor(float sampleRate, unsigned numberOfChannels)
     44     : m_numberOfChannels(numberOfChannels)
     45     , m_sampleRate(sampleRate)
     46     , m_compressor(sampleRate, numberOfChannels)
     47 {
     48     // Uninitialized state - for parameter recalculation.
     49     m_lastFilterStageRatio = -1;
     50     m_lastAnchor = -1;
     51     m_lastFilterStageGain = -1;
     52 
     53     setNumberOfChannels(numberOfChannels);
     54     initializeParameters();
     55 }
     56 
     57 void DynamicsCompressor::setParameterValue(unsigned parameterID, float value)
     58 {
     59     ASSERT(parameterID < ParamLast);
     60     if (parameterID < ParamLast)
     61         m_parameters[parameterID] = value;
     62 }
     63 
     64 void DynamicsCompressor::initializeParameters()
     65 {
     66     // Initializes compressor to default values.
     67 
     68     m_parameters[ParamThreshold] = -24; // dB
     69     m_parameters[ParamKnee] = 30; // dB
     70     m_parameters[ParamRatio] = 12; // unit-less
     71     m_parameters[ParamAttack] = 0.003f; // seconds
     72     m_parameters[ParamRelease] = 0.250f; // seconds
     73     m_parameters[ParamPreDelay] = 0.006f; // seconds
     74 
     75     // Release zone values 0 -> 1.
     76     m_parameters[ParamReleaseZone1] = 0.09f;
     77     m_parameters[ParamReleaseZone2] = 0.16f;
     78     m_parameters[ParamReleaseZone3] = 0.42f;
     79     m_parameters[ParamReleaseZone4] = 0.98f;
     80 
     81     m_parameters[ParamFilterStageGain] = 4.4f; // dB
     82     m_parameters[ParamFilterStageRatio] = 2;
     83     m_parameters[ParamFilterAnchor] = 15000 / nyquist();
     84 
     85     m_parameters[ParamPostGain] = 0; // dB
     86     m_parameters[ParamReduction] = 0; // dB
     87 
     88     // Linear crossfade (0 -> 1).
     89     m_parameters[ParamEffectBlend] = 1;
     90 }
     91 
     92 float DynamicsCompressor::parameterValue(unsigned parameterID)
     93 {
     94     ASSERT(parameterID < ParamLast);
     95     return m_parameters[parameterID];
     96 }
     97 
     98 void DynamicsCompressor::process(const AudioBus* sourceBus, AudioBus* destinationBus, unsigned framesToProcess)
     99 {
    100     // Though numberOfChannels is retrived from destinationBus, we still name it numberOfChannels instead of numberOfDestinationChannels.
    101     // It's because we internally match sourceChannels's size to destinationBus by channel up/down mix. Thus we need numberOfChannels
    102     // to do the loop work for both m_sourceChannels and m_destinationChannels.
    103 
    104     unsigned numberOfChannels = destinationBus->numberOfChannels();
    105     unsigned numberOfSourceChannels = sourceBus->numberOfChannels();
    106 
    107     ASSERT(numberOfChannels == m_numberOfChannels && numberOfSourceChannels);
    108 
    109     if (numberOfChannels != m_numberOfChannels || !numberOfSourceChannels) {
    110         destinationBus->zero();
    111         return;
    112     }
    113 
    114     switch (numberOfChannels) {
    115     case 2: // stereo
    116         m_sourceChannels[0] = sourceBus->channel(0)->data();
    117 
    118         if (numberOfSourceChannels > 1)
    119             m_sourceChannels[1] = sourceBus->channel(1)->data();
    120         else
    121             // Simply duplicate mono channel input data to right channel for stereo processing.
    122             m_sourceChannels[1] = m_sourceChannels[0];
    123 
    124         break;
    125     default:
    126         // FIXME : support other number of channels.
    127         ASSERT_NOT_REACHED();
    128         destinationBus->zero();
    129         return;
    130     }
    131 
    132     for (unsigned i = 0; i < numberOfChannels; ++i)
    133         m_destinationChannels[i] = destinationBus->channel(i)->mutableData();
    134 
    135     float filterStageGain = parameterValue(ParamFilterStageGain);
    136     float filterStageRatio = parameterValue(ParamFilterStageRatio);
    137     float anchor = parameterValue(ParamFilterAnchor);
    138 
    139     if (filterStageGain != m_lastFilterStageGain || filterStageRatio != m_lastFilterStageRatio || anchor != m_lastAnchor) {
    140         m_lastFilterStageGain = filterStageGain;
    141         m_lastFilterStageRatio = filterStageRatio;
    142         m_lastAnchor = anchor;
    143 
    144     }
    145 
    146     float dbThreshold = parameterValue(ParamThreshold);
    147     float dbKnee = parameterValue(ParamKnee);
    148     float ratio = parameterValue(ParamRatio);
    149     float attackTime = parameterValue(ParamAttack);
    150     float releaseTime = parameterValue(ParamRelease);
    151     float preDelayTime = parameterValue(ParamPreDelay);
    152 
    153     // This is effectively a master volume on the compressed signal (pre-blending).
    154     float dbPostGain = parameterValue(ParamPostGain);
    155 
    156     // Linear blending value from dry to completely processed (0 -> 1)
    157     // 0 means the signal is completely unprocessed.
    158     // 1 mixes in only the compressed signal.
    159     float effectBlend = parameterValue(ParamEffectBlend);
    160 
    161     float releaseZone1 = parameterValue(ParamReleaseZone1);
    162     float releaseZone2 = parameterValue(ParamReleaseZone2);
    163     float releaseZone3 = parameterValue(ParamReleaseZone3);
    164     float releaseZone4 = parameterValue(ParamReleaseZone4);
    165 
    166     // Apply compression to the source signal.
    167     m_compressor.process(m_sourceChannels.get(),
    168                          m_destinationChannels.get(),
    169                          numberOfChannels,
    170                          framesToProcess,
    171 
    172                          dbThreshold,
    173                          dbKnee,
    174                          ratio,
    175                          attackTime,
    176                          releaseTime,
    177                          preDelayTime,
    178                          dbPostGain,
    179                          effectBlend,
    180 
    181                          releaseZone1,
    182                          releaseZone2,
    183                          releaseZone3,
    184                          releaseZone4
    185                          );
    186 
    187     // Update the compression amount.
    188     setParameterValue(ParamReduction, m_compressor.meteringGain());
    189 
    190 }
    191 
    192 void DynamicsCompressor::reset()
    193 {
    194     m_lastFilterStageRatio = -1; // for recalc
    195     m_lastAnchor = -1;
    196     m_lastFilterStageGain = -1;
    197 
    198     m_compressor.reset();
    199 }
    200 
    201 void DynamicsCompressor::setNumberOfChannels(unsigned numberOfChannels)
    202 {
    203     m_sourceChannels = adoptArrayPtr(new const float* [numberOfChannels]);
    204     m_destinationChannels = adoptArrayPtr(new float* [numberOfChannels]);
    205 
    206     m_compressor.setNumberOfChannels(numberOfChannels);
    207     m_numberOfChannels = numberOfChannels;
    208 }
    209 
    210 } // namespace blink
    211 
    212 #endif // ENABLE(WEB_AUDIO)
    213