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