initial import
[vuplus_webkit] / 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(bool isStereo, double sampleRate)
44     : m_isStereo(isStereo)
45     , m_sampleRate(sampleRate)
46     , m_compressor(sampleRate)
47 {
48     // Uninitialized state - for parameter recalculation.
49     m_lastFilterStageRatio = -1;
50     m_lastAnchor = -1;
51     m_lastFilterStageGain = -1;
52
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.003; // seconds
63     m_parameters[ParamRelease] = 0.250; // seconds
64     m_parameters[ParamPreDelay] = 0.006; // seconds
65
66     // Release zone values 0 -> 1.
67     m_parameters[ParamReleaseZone1] = 0.09;
68     m_parameters[ParamReleaseZone2] = 0.16;
69     m_parameters[ParamReleaseZone3] = 0.42;
70     m_parameters[ParamReleaseZone4] = 0.98;
71
72     m_parameters[ParamFilterStageGain] = 4.4; // 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 double 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 = exp(-f1 * piDouble);
94     float r2 = exp(-f2 * piDouble);
95
96     // Set pre-filter zero and pole to create an emphasis filter.
97     m_preFilter[stageIndex].setZero(r1);
98     m_preFilter[stageIndex].setPole(r2);
99     m_preFilterR[stageIndex].setZero(r1);
100     m_preFilterR[stageIndex].setPole(r2);
101
102     // Set post-filter with zero and pole reversed to create the de-emphasis filter.
103     // If there were no compressor kernel in between, they would cancel each other out (allpass filter).
104     m_postFilter[stageIndex].setZero(r2);
105     m_postFilter[stageIndex].setPole(r1);
106     m_postFilterR[stageIndex].setZero(r2);
107     m_postFilterR[stageIndex].setPole(r1);
108 }
109
110 void DynamicsCompressor::setEmphasisParameters(float gain, float anchorFreq, float filterStageRatio)
111 {
112     setEmphasisStageParameters(0, gain, anchorFreq);
113     setEmphasisStageParameters(1, gain, anchorFreq / filterStageRatio);
114     setEmphasisStageParameters(2, gain, anchorFreq / (filterStageRatio * filterStageRatio));
115     setEmphasisStageParameters(3, gain, anchorFreq / (filterStageRatio * filterStageRatio * filterStageRatio));
116 }
117
118 void DynamicsCompressor::process(AudioBus* sourceBus, AudioBus* destinationBus, unsigned framesToProcess)
119 {
120     float* sourceL = sourceBus->channel(0)->data();
121     float* sourceR;
122
123     if (sourceBus->numberOfChannels() > 1)
124         sourceR = sourceBus->channel(1)->data();
125     else
126         sourceR = sourceL;
127
128     ASSERT(destinationBus->numberOfChannels() == 2);
129
130     float* destinationL = destinationBus->channel(0)->data();
131     float* destinationR = destinationBus->channel(1)->data();
132
133     float filterStageGain = parameterValue(ParamFilterStageGain);
134     float filterStageRatio = parameterValue(ParamFilterStageRatio);
135     float anchor = parameterValue(ParamFilterAnchor);
136
137     if (filterStageGain != m_lastFilterStageGain || filterStageRatio != m_lastFilterStageRatio || anchor != m_lastAnchor) {
138         m_lastFilterStageGain = filterStageGain;
139         m_lastFilterStageRatio = filterStageRatio;
140         m_lastAnchor = anchor;
141
142         setEmphasisParameters(filterStageGain, anchor, filterStageRatio);
143     }
144
145     // Apply pre-emphasis filter.
146     // Note that the final three stages are computed in-place in the destination buffer.
147     m_preFilter[0].process(sourceL, destinationL, framesToProcess);
148     m_preFilter[1].process(destinationL, destinationL, framesToProcess);
149     m_preFilter[2].process(destinationL, destinationL, framesToProcess);
150     m_preFilter[3].process(destinationL, destinationL, framesToProcess);
151
152     if (isStereo()) {
153         m_preFilterR[0].process(sourceR, destinationR, framesToProcess);
154         m_preFilterR[1].process(destinationR, destinationR, framesToProcess);
155         m_preFilterR[2].process(destinationR, destinationR, framesToProcess);
156         m_preFilterR[3].process(destinationR, destinationR, framesToProcess);
157     }
158
159     float dbThreshold = parameterValue(ParamThreshold);
160     float dbHeadroom = parameterValue(ParamHeadroom);
161     float attackTime = parameterValue(ParamAttack);
162     float releaseTime = parameterValue(ParamRelease);
163     float preDelayTime = parameterValue(ParamPreDelay);
164
165     // This is effectively a master volume on the compressed signal (pre-blending).
166     float dbPostGain = parameterValue(ParamPostGain);
167
168     // Linear blending value from dry to completely processed (0 -> 1)
169     // 0 means the signal is completely unprocessed.
170     // 1 mixes in only the compressed signal.
171     float effectBlend = parameterValue(ParamEffectBlend);
172
173     double releaseZone1 = parameterValue(ParamReleaseZone1);
174     double releaseZone2 = parameterValue(ParamReleaseZone2);
175     double releaseZone3 = parameterValue(ParamReleaseZone3);
176     double releaseZone4 = parameterValue(ParamReleaseZone4);
177
178     // Apply compression to the pre-filtered signal.
179     // The processing is performed in place.
180     m_compressor.process(destinationL,
181                          destinationL,
182                          destinationR,
183                          destinationR,
184                          framesToProcess,
185
186                          dbThreshold,
187                          dbHeadroom,
188                          attackTime,
189                          releaseTime,
190                          preDelayTime,
191                          dbPostGain,
192                          effectBlend,
193
194                          releaseZone1,
195                          releaseZone2,
196                          releaseZone3,
197                          releaseZone4
198                          );
199
200     // Apply de-emphasis filter.
201     m_postFilter[0].process(destinationL, destinationL, framesToProcess);
202     m_postFilter[1].process(destinationL, destinationL, framesToProcess);
203     m_postFilter[2].process(destinationL, destinationL, framesToProcess);
204     m_postFilter[3].process(destinationL, destinationL, framesToProcess);
205
206     if (isStereo()) {
207         m_postFilterR[0].process(destinationR, destinationR, framesToProcess);
208         m_postFilterR[1].process(destinationR, destinationR, framesToProcess);
209         m_postFilterR[2].process(destinationR, destinationR, framesToProcess);
210         m_postFilterR[3].process(destinationR, destinationR, framesToProcess);
211     }
212 }
213
214 void DynamicsCompressor::reset()
215 {
216     m_lastFilterStageRatio = -1; // for recalc
217     m_lastAnchor = -1;
218     m_lastFilterStageGain = -1;
219
220     for (unsigned i = 0; i < 4; ++i) {
221         m_preFilter[i].reset();
222         m_preFilterR[i].reset();
223         m_postFilter[i].reset();
224         m_postFilterR[i].reset();
225     }
226
227     m_compressor.reset();
228 }
229
230 } // namespace WebCore
231
232 #endif // ENABLE(WEB_AUDIO)