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