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