2
* Copyright (C) 2011 Google Inc. All rights reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
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.
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.
33
#include "DynamicsCompressor.h"
36
#include "AudioUtilities.h"
37
#include <wtf/MathExtras.h>
41
using namespace AudioUtilities;
43
DynamicsCompressor::DynamicsCompressor(float sampleRate, unsigned numberOfChannels)
44
: m_numberOfChannels(numberOfChannels)
45
, m_sampleRate(sampleRate)
46
, m_compressor(sampleRate, numberOfChannels)
48
// Uninitialized state - for parameter recalculation.
49
m_lastFilterStageRatio = -1;
51
m_lastFilterStageGain = -1;
53
setNumberOfChannels(numberOfChannels);
54
initializeParameters();
57
void DynamicsCompressor::setParameterValue(unsigned parameterID, float value)
59
ASSERT(parameterID < ParamLast);
60
if (parameterID < ParamLast)
61
m_parameters[parameterID] = value;
64
void DynamicsCompressor::initializeParameters()
66
// Initializes compressor to default values.
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
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;
81
m_parameters[ParamFilterStageGain] = 4.4f; // dB
82
m_parameters[ParamFilterStageRatio] = 2;
83
m_parameters[ParamFilterAnchor] = 15000 / nyquist();
85
m_parameters[ParamPostGain] = 0; // dB
86
m_parameters[ParamReduction] = 0; // dB
88
// Linear crossfade (0 -> 1).
89
m_parameters[ParamEffectBlend] = 1;
92
float DynamicsCompressor::parameterValue(unsigned parameterID)
94
ASSERT(parameterID < ParamLast);
95
return m_parameters[parameterID];
98
void DynamicsCompressor::setEmphasisStageParameters(unsigned stageIndex, float gain, float normalizedFrequency /* 0 -> 1 */)
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);
106
ASSERT(m_numberOfChannels == m_preFilterPacks.size());
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);
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);
122
void DynamicsCompressor::setEmphasisParameters(float gain, float anchorFreq, float filterStageRatio)
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));
130
void DynamicsCompressor::process(const AudioBus* sourceBus, AudioBus* destinationBus, unsigned framesToProcess)
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.
136
unsigned numberOfChannels = destinationBus->numberOfChannels();
137
unsigned numberOfSourceChannels = sourceBus->numberOfChannels();
139
ASSERT(numberOfChannels == m_numberOfChannels && numberOfSourceChannels);
141
if (numberOfChannels != m_numberOfChannels || !numberOfSourceChannels) {
142
destinationBus->zero();
146
switch (numberOfChannels) {
148
m_sourceChannels[0] = sourceBus->channel(0)->data();
150
if (numberOfSourceChannels > 1)
151
m_sourceChannels[1] = sourceBus->channel(1)->data();
153
// Simply duplicate mono channel input data to right channel for stereo processing.
154
m_sourceChannels[1] = m_sourceChannels[0];
158
// FIXME : support other number of channels.
159
ASSERT_NOT_REACHED();
160
destinationBus->zero();
164
for (unsigned i = 0; i < numberOfChannels; ++i)
165
m_destinationChannels[i] = destinationBus->channel(i)->mutableData();
167
float filterStageGain = parameterValue(ParamFilterStageGain);
168
float filterStageRatio = parameterValue(ParamFilterStageRatio);
169
float anchor = parameterValue(ParamFilterAnchor);
171
if (filterStageGain != m_lastFilterStageGain || filterStageRatio != m_lastFilterStageRatio || anchor != m_lastAnchor) {
172
m_lastFilterStageGain = filterStageGain;
173
m_lastFilterStageRatio = filterStageRatio;
174
m_lastAnchor = anchor;
176
setEmphasisParameters(filterStageGain, anchor, filterStageRatio);
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;
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);
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);
199
// This is effectively a master volume on the compressed signal (pre-blending).
200
float dbPostGain = parameterValue(ParamPostGain);
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);
207
float releaseZone1 = parameterValue(ParamReleaseZone1);
208
float releaseZone2 = parameterValue(ParamReleaseZone2);
209
float releaseZone3 = parameterValue(ParamReleaseZone3);
210
float releaseZone4 = parameterValue(ParamReleaseZone4);
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(),
234
// Update the compression amount.
235
setParameterValue(ParamReduction, m_compressor.meteringGain());
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;
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);
249
void DynamicsCompressor::reset()
251
m_lastFilterStageRatio = -1; // for recalc
253
m_lastFilterStageGain = -1;
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();
262
m_compressor.reset();
265
void DynamicsCompressor::setNumberOfChannels(unsigned numberOfChannels)
267
if (m_preFilterPacks.size() == numberOfChannels)
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()));
277
m_sourceChannels = adoptArrayPtr(new const float* [numberOfChannels]);
278
m_destinationChannels = adoptArrayPtr(new float* [numberOfChannels]);
280
m_compressor.setNumberOfChannels(numberOfChannels);
281
m_numberOfChannels = numberOfChannels;
284
} // namespace WebCore
286
#endif // ENABLE(WEB_AUDIO)