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 WebCore { 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::setEmphasisStageParameters(unsigned stageIndex, float gain, float normalizedFrequency /* 0 -> 1 */) 99 { 100 float gk = 1 - gain / 20; 101 float f1 = normalizedFrequency * gk; 102 float f2 = normalizedFrequency / gk; 103 float r1 = expf(-f1 * piFloat); 104 float r2 = expf(-f2 * piFloat); 105 106 ASSERT(m_numberOfChannels == m_preFilterPacks.size()); 107 108 for (unsigned i = 0; i < m_numberOfChannels; ++i) { 109 // Set pre-filter zero and pole to create an emphasis filter. 110 ZeroPole& preFilter = m_preFilterPacks[i]->filters[stageIndex]; 111 preFilter.setZero(r1); 112 preFilter.setPole(r2); 113 114 // Set post-filter with zero and pole reversed to create the de-emphasis filter. 115 // If there were no compressor kernel in between, they would cancel each other out (allpass filter). 116 ZeroPole& postFilter = m_postFilterPacks[i]->filters[stageIndex]; 117 postFilter.setZero(r2); 118 postFilter.setPole(r1); 119 } 120 } 121 122 void DynamicsCompressor::setEmphasisParameters(float gain, float anchorFreq, float filterStageRatio) 123 { 124 setEmphasisStageParameters(0, gain, anchorFreq); 125 setEmphasisStageParameters(1, gain, anchorFreq / filterStageRatio); 126 setEmphasisStageParameters(2, gain, anchorFreq / (filterStageRatio * filterStageRatio)); 127 setEmphasisStageParameters(3, gain, anchorFreq / (filterStageRatio * filterStageRatio * filterStageRatio)); 128 } 129 130 void DynamicsCompressor::process(const AudioBus* sourceBus, AudioBus* destinationBus, unsigned framesToProcess) 131 { 132 // Though numberOfChannels is retrived from destinationBus, we still name it numberOfChannels instead of numberOfDestinationChannels. 133 // It's because we internally match sourceChannels's size to destinationBus by channel up/down mix. Thus we need numberOfChannels 134 // to do the loop work for both m_sourceChannels and m_destinationChannels. 135 136 unsigned numberOfChannels = destinationBus->numberOfChannels(); 137 unsigned numberOfSourceChannels = sourceBus->numberOfChannels(); 138 139 ASSERT(numberOfChannels == m_numberOfChannels && numberOfSourceChannels); 140 141 if (numberOfChannels != m_numberOfChannels || !numberOfSourceChannels) { 142 destinationBus->zero(); 143 return; 144 } 145 146 switch (numberOfChannels) { 147 case 2: // stereo 148 m_sourceChannels[0] = sourceBus->channel(0)->data(); 149 150 if (numberOfSourceChannels > 1) 151 m_sourceChannels[1] = sourceBus->channel(1)->data(); 152 else 153 // Simply duplicate mono channel input data to right channel for stereo processing. 154 m_sourceChannels[1] = m_sourceChannels[0]; 155 156 break; 157 default: 158 // FIXME : support other number of channels. 159 ASSERT_NOT_REACHED(); 160 destinationBus->zero(); 161 return; 162 } 163 164 for (unsigned i = 0; i < numberOfChannels; ++i) 165 m_destinationChannels[i] = destinationBus->channel(i)->mutableData(); 166 167 float filterStageGain = parameterValue(ParamFilterStageGain); 168 float filterStageRatio = parameterValue(ParamFilterStageRatio); 169 float anchor = parameterValue(ParamFilterAnchor); 170 171 if (filterStageGain != m_lastFilterStageGain || filterStageRatio != m_lastFilterStageRatio || anchor != m_lastAnchor) { 172 m_lastFilterStageGain = filterStageGain; 173 m_lastFilterStageRatio = filterStageRatio; 174 m_lastAnchor = anchor; 175 176 setEmphasisParameters(filterStageGain, anchor, filterStageRatio); 177 } 178 179 // Apply pre-emphasis filter. 180 // Note that the final three stages are computed in-place in the destination buffer. 181 for (unsigned i = 0; i < numberOfChannels; ++i) { 182 const float* sourceData = m_sourceChannels[i]; 183 float* destinationData = m_destinationChannels[i]; 184 ZeroPole* preFilters = m_preFilterPacks[i]->filters; 185 186 preFilters[0].process(sourceData, destinationData, framesToProcess); 187 preFilters[1].process(destinationData, destinationData, framesToProcess); 188 preFilters[2].process(destinationData, destinationData, framesToProcess); 189 preFilters[3].process(destinationData, destinationData, framesToProcess); 190 } 191 192 float dbThreshold = parameterValue(ParamThreshold); 193 float dbKnee = parameterValue(ParamKnee); 194 float ratio = parameterValue(ParamRatio); 195 float attackTime = parameterValue(ParamAttack); 196 float releaseTime = parameterValue(ParamRelease); 197 float preDelayTime = parameterValue(ParamPreDelay); 198 199 // This is effectively a master volume on the compressed signal (pre-blending). 200 float dbPostGain = parameterValue(ParamPostGain); 201 202 // Linear blending value from dry to completely processed (0 -> 1) 203 // 0 means the signal is completely unprocessed. 204 // 1 mixes in only the compressed signal. 205 float effectBlend = parameterValue(ParamEffectBlend); 206 207 float releaseZone1 = parameterValue(ParamReleaseZone1); 208 float releaseZone2 = parameterValue(ParamReleaseZone2); 209 float releaseZone3 = parameterValue(ParamReleaseZone3); 210 float releaseZone4 = parameterValue(ParamReleaseZone4); 211 212 // Apply compression to the pre-filtered signal. 213 // The processing is performed in place. 214 m_compressor.process(m_destinationChannels.get(), 215 m_destinationChannels.get(), 216 numberOfChannels, 217 framesToProcess, 218 219 dbThreshold, 220 dbKnee, 221 ratio, 222 attackTime, 223 releaseTime, 224 preDelayTime, 225 dbPostGain, 226 effectBlend, 227 228 releaseZone1, 229 releaseZone2, 230 releaseZone3, 231 releaseZone4 232 ); 233 234 // Update the compression amount. 235 setParameterValue(ParamReduction, m_compressor.meteringGain()); 236 237 // Apply de-emphasis filter. 238 for (unsigned i = 0; i < numberOfChannels; ++i) { 239 float* destinationData = m_destinationChannels[i]; 240 ZeroPole* postFilters = m_postFilterPacks[i]->filters; 241 242 postFilters[0].process(destinationData, destinationData, framesToProcess); 243 postFilters[1].process(destinationData, destinationData, framesToProcess); 244 postFilters[2].process(destinationData, destinationData, framesToProcess); 245 postFilters[3].process(destinationData, destinationData, framesToProcess); 246 } 247 } 248 249 void DynamicsCompressor::reset() 250 { 251 m_lastFilterStageRatio = -1; // for recalc 252 m_lastAnchor = -1; 253 m_lastFilterStageGain = -1; 254 255 for (unsigned channel = 0; channel < m_numberOfChannels; ++channel) { 256 for (unsigned stageIndex = 0; stageIndex < 4; ++stageIndex) { 257 m_preFilterPacks[channel]->filters[stageIndex].reset(); 258 m_postFilterPacks[channel]->filters[stageIndex].reset(); 259 } 260 } 261 262 m_compressor.reset(); 263 } 264 265 void DynamicsCompressor::setNumberOfChannels(unsigned numberOfChannels) 266 { 267 if (m_preFilterPacks.size() == numberOfChannels) 268 return; 269 270 m_preFilterPacks.clear(); 271 m_postFilterPacks.clear(); 272 for (unsigned i = 0; i < numberOfChannels; ++i) { 273 m_preFilterPacks.append(adoptPtr(new ZeroPoleFilterPack4())); 274 m_postFilterPacks.append(adoptPtr(new ZeroPoleFilterPack4())); 275 } 276 277 m_sourceChannels = adoptArrayPtr(new const float* [numberOfChannels]); 278 m_destinationChannels = adoptArrayPtr(new float* [numberOfChannels]); 279 280 m_compressor.setNumberOfChannels(numberOfChannels); 281 m_numberOfChannels = numberOfChannels; 282 } 283 284 } // namespace WebCore 285 286 #endif // ENABLE(WEB_AUDIO) 287