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 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 "DynamicsCompressor.h"
34
35#include "AudioBus.h"
36#include "AudioUtilities.h"
37#include <wtf/MathExtras.h>
38#include <wtf/StdLibExtras.h>
39
40namespace WebCore {
41
42using namespace AudioUtilities;
43
44DynamicsCompressor::DynamicsCompressor(float sampleRate, unsigned numberOfChannels)
45 : m_numberOfChannels(numberOfChannels)
46 , m_sampleRate(sampleRate)
47 , m_compressor(sampleRate, numberOfChannels)
48{
49 // Uninitialized state - for parameter recalculation.
50 m_lastFilterStageRatio = -1;
51 m_lastAnchor = -1;
52 m_lastFilterStageGain = -1;
53
54 setNumberOfChannels(numberOfChannels);
55 initializeParameters();
56}
57
58void DynamicsCompressor::setParameterValue(unsigned parameterID, float value)
59{
60 ASSERT(parameterID < ParamLast);
61 if (parameterID < ParamLast)
62 m_parameters[parameterID] = value;
63}
64
65void DynamicsCompressor::initializeParameters()
66{
67 // Initializes compressor to default values.
68
69 m_parameters[ParamThreshold] = -24; // dB
70 m_parameters[ParamKnee] = 30; // dB
71 m_parameters[ParamRatio] = 12; // unit-less
72 m_parameters[ParamAttack] = 0.003f; // seconds
73 m_parameters[ParamRelease] = 0.250f; // seconds
74 m_parameters[ParamPreDelay] = 0.006f; // seconds
75
76 // Release zone values 0 -> 1.
77 m_parameters[ParamReleaseZone1] = 0.09f;
78 m_parameters[ParamReleaseZone2] = 0.16f;
79 m_parameters[ParamReleaseZone3] = 0.42f;
80 m_parameters[ParamReleaseZone4] = 0.98f;
81
82 m_parameters[ParamFilterStageGain] = 4.4f; // dB
83 m_parameters[ParamFilterStageRatio] = 2;
84 m_parameters[ParamFilterAnchor] = 15000 / nyquist();
85
86 m_parameters[ParamPostGain] = 0; // dB
87 m_parameters[ParamReduction] = 0; // dB
88
89 // Linear crossfade (0 -> 1).
90 m_parameters[ParamEffectBlend] = 1;
91}
92
93float DynamicsCompressor::parameterValue(unsigned parameterID)
94{
95 ASSERT(parameterID < ParamLast);
96 return m_parameters[parameterID];
97}
98
99void DynamicsCompressor::setEmphasisStageParameters(unsigned stageIndex, float gain, float normalizedFrequency /* 0 -> 1 */)
100{
101 float gk = 1 - gain / 20;
102 float f1 = normalizedFrequency * gk;
103 float f2 = normalizedFrequency / gk;
104 float r1 = expf(-f1 * piFloat);
105 float r2 = expf(-f2 * piFloat);
106
107 ASSERT(m_numberOfChannels == m_preFilterPacks.size());
108
109 for (unsigned i = 0; i < m_numberOfChannels; ++i) {
110 // Set pre-filter zero and pole to create an emphasis filter.
111 ZeroPole& preFilter = m_preFilterPacks[i]->filters[stageIndex];
112 preFilter.setZero(r1);
113 preFilter.setPole(r2);
114
115 // Set post-filter with zero and pole reversed to create the de-emphasis filter.
116 // If there were no compressor kernel in between, they would cancel each other out (allpass filter).
117 ZeroPole& postFilter = m_postFilterPacks[i]->filters[stageIndex];
118 postFilter.setZero(r2);
119 postFilter.setPole(r1);
120 }
121}
122
123void DynamicsCompressor::setEmphasisParameters(float gain, float anchorFreq, float filterStageRatio)
124{
125 setEmphasisStageParameters(0, gain, anchorFreq);
126 setEmphasisStageParameters(1, gain, anchorFreq / filterStageRatio);
127 setEmphasisStageParameters(2, gain, anchorFreq / (filterStageRatio * filterStageRatio));
128 setEmphasisStageParameters(3, gain, anchorFreq / (filterStageRatio * filterStageRatio * filterStageRatio));
129}
130
131void DynamicsCompressor::process(const AudioBus* sourceBus, AudioBus* destinationBus, unsigned framesToProcess)
132{
133 // Though numberOfChannels is retrived from destinationBus, we still name it numberOfChannels instead of numberOfDestinationChannels.
134 // It's because we internally match sourceChannels's size to destinationBus by channel up/down mix. Thus we need numberOfChannels
135 // to do the loop work for both m_sourceChannels and m_destinationChannels.
136
137 unsigned numberOfChannels = destinationBus->numberOfChannels();
138 unsigned numberOfSourceChannels = sourceBus->numberOfChannels();
139
140 ASSERT(numberOfChannels == m_numberOfChannels && numberOfSourceChannels);
141
142 if (numberOfChannels != m_numberOfChannels || !numberOfSourceChannels) {
143 destinationBus->zero();
144 return;
145 }
146
147 switch (numberOfChannels) {
148 case 2: // stereo
149 m_sourceChannels[0] = sourceBus->channel(0)->data();
150
151 if (numberOfSourceChannels > 1)
152 m_sourceChannels[1] = sourceBus->channel(1)->data();
153 else
154 // Simply duplicate mono channel input data to right channel for stereo processing.
155 m_sourceChannels[1] = m_sourceChannels[0];
156
157 break;
158 default:
159 // FIXME : support other number of channels.
160 ASSERT_NOT_REACHED();
161 destinationBus->zero();
162 return;
163 }
164
165 for (unsigned i = 0; i < numberOfChannels; ++i)
166 m_destinationChannels[i] = destinationBus->channel(i)->mutableData();
167
168 float filterStageGain = parameterValue(ParamFilterStageGain);
169 float filterStageRatio = parameterValue(ParamFilterStageRatio);
170 float anchor = parameterValue(ParamFilterAnchor);
171
172 if (filterStageGain != m_lastFilterStageGain || filterStageRatio != m_lastFilterStageRatio || anchor != m_lastAnchor) {
173 m_lastFilterStageGain = filterStageGain;
174 m_lastFilterStageRatio = filterStageRatio;
175 m_lastAnchor = anchor;
176
177 setEmphasisParameters(filterStageGain, anchor, filterStageRatio);
178 }
179
180 // Apply pre-emphasis filter.
181 // Note that the final three stages are computed in-place in the destination buffer.
182 for (unsigned i = 0; i < numberOfChannels; ++i) {
183 const float* sourceData = m_sourceChannels[i];
184 float* destinationData = m_destinationChannels[i];
185 ZeroPole* preFilters = m_preFilterPacks[i]->filters;
186
187 preFilters[0].process(sourceData, destinationData, framesToProcess);
188 preFilters[1].process(destinationData, destinationData, framesToProcess);
189 preFilters[2].process(destinationData, destinationData, framesToProcess);
190 preFilters[3].process(destinationData, destinationData, framesToProcess);
191 }
192
193 float dbThreshold = parameterValue(ParamThreshold);
194 float dbKnee = parameterValue(ParamKnee);
195 float ratio = parameterValue(ParamRatio);
196 float attackTime = parameterValue(ParamAttack);
197 float releaseTime = parameterValue(ParamRelease);
198 float preDelayTime = parameterValue(ParamPreDelay);
199
200 // This is effectively a master volume on the compressed signal (pre-blending).
201 float dbPostGain = parameterValue(ParamPostGain);
202
203 // Linear blending value from dry to completely processed (0 -> 1)
204 // 0 means the signal is completely unprocessed.
205 // 1 mixes in only the compressed signal.
206 float effectBlend = parameterValue(ParamEffectBlend);
207
208 float releaseZone1 = parameterValue(ParamReleaseZone1);
209 float releaseZone2 = parameterValue(ParamReleaseZone2);
210 float releaseZone3 = parameterValue(ParamReleaseZone3);
211 float releaseZone4 = parameterValue(ParamReleaseZone4);
212
213 // Apply compression to the pre-filtered signal.
214 // The processing is performed in place.
215 m_compressor.process(m_destinationChannels.get(),
216 m_destinationChannels.get(),
217 numberOfChannels,
218 framesToProcess,
219
220 dbThreshold,
221 dbKnee,
222 ratio,
223 attackTime,
224 releaseTime,
225 preDelayTime,
226 dbPostGain,
227 effectBlend,
228
229 releaseZone1,
230 releaseZone2,
231 releaseZone3,
232 releaseZone4
233 );
234
235 // Update the compression amount.
236 setParameterValue(ParamReduction, m_compressor.meteringGain());
237
238 // Apply de-emphasis filter.
239 for (unsigned i = 0; i < numberOfChannels; ++i) {
240 float* destinationData = m_destinationChannels[i];
241 ZeroPole* postFilters = m_postFilterPacks[i]->filters;
242
243 postFilters[0].process(destinationData, destinationData, framesToProcess);
244 postFilters[1].process(destinationData, destinationData, framesToProcess);
245 postFilters[2].process(destinationData, destinationData, framesToProcess);
246 postFilters[3].process(destinationData, destinationData, framesToProcess);
247 }
248}
249
250void DynamicsCompressor::reset()
251{
252 m_lastFilterStageRatio = -1; // for recalc
253 m_lastAnchor = -1;
254 m_lastFilterStageGain = -1;
255
256 for (unsigned channel = 0; channel < m_numberOfChannels; ++channel) {
257 for (unsigned stageIndex = 0; stageIndex < 4; ++stageIndex) {
258 m_preFilterPacks[channel]->filters[stageIndex].reset();
259 m_postFilterPacks[channel]->filters[stageIndex].reset();
260 }
261 }
262
263 m_compressor.reset();
264}
265
266void DynamicsCompressor::setNumberOfChannels(unsigned numberOfChannels)
267{
268 if (m_preFilterPacks.size() == numberOfChannels)
269 return;
270
271 m_preFilterPacks.clear();
272 m_postFilterPacks.clear();
273 for (unsigned i = 0; i < numberOfChannels; ++i) {
274 m_preFilterPacks.append(std::make_unique<ZeroPoleFilterPack4>());
275 m_postFilterPacks.append(std::make_unique<ZeroPoleFilterPack4>());
276 }
277
278 m_sourceChannels = makeUniqueArray<const float*>(numberOfChannels);
279 m_destinationChannels = makeUniqueArray<float*>(numberOfChannels);
280
281 m_compressor.setNumberOfChannels(numberOfChannels);
282 m_numberOfChannels = numberOfChannels;
283}
284
285} // namespace WebCore
286
287#endif // ENABLE(WEB_AUDIO)
288