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