• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DynamicsCompressor(float sampleRate,unsigned numberOfChannels)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 
setParameterValue(unsigned parameterID,float value)57 void DynamicsCompressor::setParameterValue(unsigned parameterID, float value)
58 {
59     ASSERT(parameterID < ParamLast);
60     if (parameterID < ParamLast)
61         m_parameters[parameterID] = value;
62 }
63 
initializeParameters()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 
parameterValue(unsigned parameterID)92 float DynamicsCompressor::parameterValue(unsigned parameterID)
93 {
94     ASSERT(parameterID < ParamLast);
95     return m_parameters[parameterID];
96 }
97 
setEmphasisStageParameters(unsigned stageIndex,float gain,float normalizedFrequency)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 
setEmphasisParameters(float gain,float anchorFreq,float filterStageRatio)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 
process(const AudioBus * sourceBus,AudioBus * destinationBus,unsigned framesToProcess)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 
reset()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 
setNumberOfChannels(unsigned numberOfChannels)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