~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/platform/audio/DynamicsCompressor.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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_sampleRate(sampleRate)
 
46
    , m_compressor(sampleRate, numberOfChannels)
 
47
{
 
48
    // Uninitialized state - for parameter recalculation.
 
49
    m_lastFilterStageRatio = -1;
 
50
    m_lastAnchor = -1;
 
51
    m_lastFilterStageGain = -1;
 
52
 
 
53
    setNumberOfChannels(numberOfChannels);
 
54
    initializeParameters();
 
55
}
 
56
 
 
57
void DynamicsCompressor::setParameterValue(unsigned parameterID, float value)
 
58
{
 
59
    ASSERT(parameterID < ParamLast);
 
60
    if (parameterID < ParamLast)
 
61
        m_parameters[parameterID] = value;
 
62
}
 
63
 
 
64
void DynamicsCompressor::initializeParameters()
 
65
{
 
66
    // Initializes compressor to default values.
 
67
    
 
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
 
74
 
 
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;
 
80
 
 
81
    m_parameters[ParamFilterStageGain] = 4.4f; // dB
 
82
    m_parameters[ParamFilterStageRatio] = 2;
 
83
    m_parameters[ParamFilterAnchor] = 15000 / nyquist();
 
84
    
 
85
    m_parameters[ParamPostGain] = 0; // dB
 
86
    m_parameters[ParamReduction] = 0; // dB
 
87
 
 
88
    // Linear crossfade (0 -> 1).
 
89
    m_parameters[ParamEffectBlend] = 1;
 
90
}
 
91
 
 
92
float DynamicsCompressor::parameterValue(unsigned parameterID)
 
93
{
 
94
    ASSERT(parameterID < ParamLast);
 
95
    return m_parameters[parameterID];
 
96
}
 
97
 
 
98
void DynamicsCompressor::setEmphasisStageParameters(unsigned stageIndex, float gain, float normalizedFrequency /* 0 -> 1 */)
 
99
{
 
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);
 
105
 
 
106
    ASSERT(m_numberOfChannels == m_preFilterPacks.size());
 
107
 
 
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);
 
113
 
 
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);
 
119
    }
 
120
}
 
121
 
 
122
void DynamicsCompressor::setEmphasisParameters(float gain, float anchorFreq, float filterStageRatio)
 
123
{
 
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));
 
128
}
 
129
 
 
130
void DynamicsCompressor::process(const AudioBus* sourceBus, AudioBus* destinationBus, unsigned framesToProcess)
 
131
{
 
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.
 
135
 
 
136
    unsigned numberOfChannels = destinationBus->numberOfChannels();
 
137
    unsigned numberOfSourceChannels = sourceBus->numberOfChannels();
 
138
 
 
139
    ASSERT(numberOfChannels == m_numberOfChannels && numberOfSourceChannels);
 
140
 
 
141
    if (numberOfChannels != m_numberOfChannels || !numberOfSourceChannels) {
 
142
        destinationBus->zero();
 
143
        return;
 
144
    }
 
145
 
 
146
    switch (numberOfChannels) {
 
147
    case 2: // stereo
 
148
        m_sourceChannels[0] = sourceBus->channel(0)->data();
 
149
 
 
150
        if (numberOfSourceChannels > 1)
 
151
            m_sourceChannels[1] = sourceBus->channel(1)->data();
 
152
        else
 
153
            // Simply duplicate mono channel input data to right channel for stereo processing.
 
154
            m_sourceChannels[1] = m_sourceChannels[0];
 
155
 
 
156
        break;
 
157
    default:
 
158
        // FIXME : support other number of channels.
 
159
        ASSERT_NOT_REACHED();
 
160
        destinationBus->zero();
 
161
        return;
 
162
    }
 
163
 
 
164
    for (unsigned i = 0; i < numberOfChannels; ++i)
 
165
        m_destinationChannels[i] = destinationBus->channel(i)->mutableData();
 
166
 
 
167
    float filterStageGain = parameterValue(ParamFilterStageGain);
 
168
    float filterStageRatio = parameterValue(ParamFilterStageRatio);
 
169
    float anchor = parameterValue(ParamFilterAnchor);
 
170
 
 
171
    if (filterStageGain != m_lastFilterStageGain || filterStageRatio != m_lastFilterStageRatio || anchor != m_lastAnchor) {
 
172
        m_lastFilterStageGain = filterStageGain;
 
173
        m_lastFilterStageRatio = filterStageRatio;
 
174
        m_lastAnchor = anchor;
 
175
 
 
176
        setEmphasisParameters(filterStageGain, anchor, filterStageRatio);
 
177
    }
 
178
 
 
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;
 
185
 
 
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);
 
190
    }
 
191
 
 
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);
 
198
 
 
199
    // This is effectively a master volume on the compressed signal (pre-blending).
 
200
    float dbPostGain = parameterValue(ParamPostGain);
 
201
 
 
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);
 
206
 
 
207
    float releaseZone1 = parameterValue(ParamReleaseZone1);
 
208
    float releaseZone2 = parameterValue(ParamReleaseZone2);
 
209
    float releaseZone3 = parameterValue(ParamReleaseZone3);
 
210
    float releaseZone4 = parameterValue(ParamReleaseZone4);
 
211
 
 
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(),
 
216
                         numberOfChannels,
 
217
                         framesToProcess,
 
218
 
 
219
                         dbThreshold,
 
220
                         dbKnee,
 
221
                         ratio,
 
222
                         attackTime,
 
223
                         releaseTime,
 
224
                         preDelayTime,
 
225
                         dbPostGain,
 
226
                         effectBlend,
 
227
 
 
228
                         releaseZone1,
 
229
                         releaseZone2,
 
230
                         releaseZone3,
 
231
                         releaseZone4
 
232
                         );
 
233
                         
 
234
    // Update the compression amount.                     
 
235
    setParameterValue(ParamReduction, m_compressor.meteringGain());
 
236
 
 
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;
 
241
 
 
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);
 
246
    }
 
247
}
 
248
 
 
249
void DynamicsCompressor::reset()
 
250
{
 
251
    m_lastFilterStageRatio = -1; // for recalc
 
252
    m_lastAnchor = -1;
 
253
    m_lastFilterStageGain = -1;
 
254
 
 
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();
 
259
        }
 
260
    }
 
261
 
 
262
    m_compressor.reset();
 
263
}
 
264
 
 
265
void DynamicsCompressor::setNumberOfChannels(unsigned numberOfChannels)
 
266
{
 
267
    if (m_preFilterPacks.size() == numberOfChannels)
 
268
        return;
 
269
 
 
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()));
 
275
    }
 
276
 
 
277
    m_sourceChannels = adoptArrayPtr(new const float* [numberOfChannels]);
 
278
    m_destinationChannels = adoptArrayPtr(new float* [numberOfChannels]);
 
279
 
 
280
    m_compressor.setNumberOfChannels(numberOfChannels);
 
281
    m_numberOfChannels = numberOfChannels;
 
282
}
 
283
 
 
284
} // namespace WebCore
 
285
 
 
286
#endif // ENABLE(WEB_AUDIO)