Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/webaudio/blink/Reverb.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2010 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 "Reverb.h"
30
#include "ReverbConvolverStage.h"
31
32
#include <math.h>
33
#include "ReverbConvolver.h"
34
#include "mozilla/FloatingPoint.h"
35
36
using namespace mozilla;
37
38
namespace WebCore {
39
40
// Empirical gain calibration tested across many impulse responses to ensure perceived volume is same as dry (unprocessed) signal
41
const float GainCalibration = 0.00125f;
42
const float GainCalibrationSampleRate = 44100;
43
44
// A minimum power value to when normalizing a silent (or very quiet) impulse response
45
const float MinPower = 0.000125f;
46
47
static float calculateNormalizationScale(const nsTArray<const float*>& response, size_t aLength, float sampleRate)
48
0
{
49
0
    // Normalize by RMS power
50
0
    size_t numberOfChannels = response.Length();
51
0
52
0
    float power = 0;
53
0
54
0
    for (size_t i = 0; i < numberOfChannels; ++i) {
55
0
        float channelPower = AudioBufferSumOfSquares(response[i], aLength);
56
0
        power += channelPower;
57
0
    }
58
0
59
0
    power = sqrt(power / (numberOfChannels * aLength));
60
0
61
0
    // Protect against accidental overload
62
0
    if (!IsFinite(power) || IsNaN(power) || power < MinPower)
63
0
        power = MinPower;
64
0
65
0
    float scale = 1 / power;
66
0
67
0
    scale *= GainCalibration; // calibrate to make perceived volume same as unprocessed
68
0
69
0
    // Scale depends on sample-rate.
70
0
    if (sampleRate)
71
0
        scale *= GainCalibrationSampleRate / sampleRate;
72
0
73
0
    // True-stereo compensation
74
0
    if (numberOfChannels == 4)
75
0
        scale *= 0.5f;
76
0
77
0
    return scale;
78
0
}
79
80
Reverb::Reverb(const AudioChunk& impulseResponse, size_t maxFFTSize, bool useBackgroundThreads, bool normalize, float sampleRate)
81
0
{
82
0
    size_t impulseResponseBufferLength = impulseResponse.mDuration;
83
0
    float scale = impulseResponse.mVolume;
84
0
85
0
    AutoTArray<const float*,4> irChannels(impulseResponse.ChannelData<float>());
86
0
    AutoTArray<float,1024> tempBuf;
87
0
88
0
    if (normalize) {
89
0
        scale = calculateNormalizationScale(irChannels, impulseResponseBufferLength, sampleRate);
90
0
    }
91
0
92
0
    if (scale != 1.0f) {
93
0
        tempBuf.SetLength(irChannels.Length()*impulseResponseBufferLength);
94
0
        for (uint32_t i = 0; i < irChannels.Length(); ++i) {
95
0
            float* buf = &tempBuf[i*impulseResponseBufferLength];
96
0
            AudioBufferCopyWithScale(irChannels[i], scale, buf,
97
0
                                     impulseResponseBufferLength);
98
0
            irChannels[i] = buf;
99
0
        }
100
0
    }
101
0
102
0
    initialize(irChannels, impulseResponseBufferLength,
103
0
               maxFFTSize, useBackgroundThreads);
104
0
}
105
106
size_t Reverb::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
107
0
{
108
0
    size_t amount = aMallocSizeOf(this);
109
0
    amount += m_convolvers.ShallowSizeOfExcludingThis(aMallocSizeOf);
110
0
    for (size_t i = 0; i < m_convolvers.Length(); i++) {
111
0
        if (m_convolvers[i]) {
112
0
            amount += m_convolvers[i]->sizeOfIncludingThis(aMallocSizeOf);
113
0
        }
114
0
    }
115
0
116
0
    amount += m_tempBuffer.SizeOfExcludingThis(aMallocSizeOf, false);
117
0
    return amount;
118
0
}
119
120
121
void Reverb::initialize(const nsTArray<const float*>& impulseResponseBuffer,
122
                        size_t impulseResponseBufferLength,
123
                        size_t maxFFTSize, bool useBackgroundThreads)
124
0
{
125
0
    m_impulseResponseLength = impulseResponseBufferLength;
126
0
127
0
    // The reverb can handle a mono impulse response and still do stereo processing
128
0
    size_t numResponseChannels = impulseResponseBuffer.Length();
129
0
    MOZ_ASSERT(numResponseChannels > 0);
130
0
    // The number of convolvers required is at least the number of audio
131
0
    // channels.  Even if there is initially only one audio channel, another
132
0
    // may be added later, and so a second convolver is created now while the
133
0
    // impulse response is available.
134
0
    size_t numConvolvers = std::max<size_t>(numResponseChannels, 2);
135
0
    m_convolvers.SetCapacity(numConvolvers);
136
0
137
0
    int convolverRenderPhase = 0;
138
0
    for (size_t i = 0; i < numConvolvers; ++i) {
139
0
        size_t channelIndex = i < numResponseChannels ? i : 0;
140
0
        const float* channel = impulseResponseBuffer[channelIndex];
141
0
        size_t length = impulseResponseBufferLength;
142
0
143
0
        nsAutoPtr<ReverbConvolver> convolver(new ReverbConvolver(channel, length, maxFFTSize, convolverRenderPhase, useBackgroundThreads));
144
0
        m_convolvers.AppendElement(convolver.forget());
145
0
146
0
        convolverRenderPhase += WEBAUDIO_BLOCK_SIZE;
147
0
    }
148
0
149
0
    // For "True" stereo processing we allocate a temporary buffer to avoid repeatedly allocating it in the process() method.
150
0
    // It can be bad to allocate memory in a real-time thread.
151
0
    if (numResponseChannels == 4) {
152
0
        m_tempBuffer.AllocateChannels(2);
153
0
        WriteZeroesToAudioBlock(&m_tempBuffer, 0, WEBAUDIO_BLOCK_SIZE);
154
0
    }
155
0
}
156
157
void Reverb::process(const AudioBlock* sourceBus, AudioBlock* destinationBus)
158
0
{
159
0
    // Do a fairly comprehensive sanity check.
160
0
    // If these conditions are satisfied, all of the source and destination pointers will be valid for the various matrixing cases.
161
0
    bool isSafeToProcess =
162
0
      sourceBus && destinationBus && sourceBus->ChannelCount() > 0 &&
163
0
      destinationBus->mChannelData.Length() > 0 &&
164
0
      WEBAUDIO_BLOCK_SIZE <= MaxFrameSize &&
165
0
      WEBAUDIO_BLOCK_SIZE <= size_t(sourceBus->GetDuration()) &&
166
0
      WEBAUDIO_BLOCK_SIZE <= size_t(destinationBus->GetDuration());
167
0
168
0
    MOZ_ASSERT(isSafeToProcess);
169
0
    if (!isSafeToProcess)
170
0
        return;
171
0
172
0
    // For now only handle mono or stereo output
173
0
    MOZ_ASSERT(destinationBus->ChannelCount() <= 2);
174
0
175
0
    float* destinationChannelL = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[0]));
176
0
    const float* sourceBusL = static_cast<const float*>(sourceBus->mChannelData[0]);
177
0
178
0
    // Handle input -> output matrixing...
179
0
    size_t numInputChannels = sourceBus->ChannelCount();
180
0
    size_t numOutputChannels = destinationBus->ChannelCount();
181
0
    size_t numReverbChannels = m_convolvers.Length();
182
0
183
0
    if (numInputChannels == 2 && numReverbChannels == 2 && numOutputChannels == 2) {
184
0
        // 2 -> 2 -> 2
185
0
        const float* sourceBusR = static_cast<const float*>(sourceBus->mChannelData[1]);
186
0
        float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
187
0
        m_convolvers[0]->process(sourceBusL, destinationChannelL);
188
0
        m_convolvers[1]->process(sourceBusR, destinationChannelR);
189
0
    } else  if (numInputChannels == 1 && numOutputChannels == 2 && numReverbChannels == 2) {
190
0
        // 1 -> 2 -> 2
191
0
        for (int i = 0; i < 2; ++i) {
192
0
            float* destinationChannel = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[i]));
193
0
            m_convolvers[i]->process(sourceBusL, destinationChannel);
194
0
        }
195
0
    } else if (numInputChannels == 1 && numOutputChannels == 1) {
196
0
        // 1 -> 1 -> 1 (Only one of the convolvers is used.)
197
0
        m_convolvers[0]->process(sourceBusL, destinationChannelL);
198
0
    } else if (numInputChannels == 2 && numReverbChannels == 4 && numOutputChannels == 2) {
199
0
        // 2 -> 4 -> 2 ("True" stereo)
200
0
        const float* sourceBusR = static_cast<const float*>(sourceBus->mChannelData[1]);
201
0
        float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
202
0
203
0
        float* tempChannelL = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[0]));
204
0
        float* tempChannelR = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[1]));
205
0
206
0
        // Process left virtual source
207
0
        m_convolvers[0]->process(sourceBusL, destinationChannelL);
208
0
        m_convolvers[1]->process(sourceBusL, destinationChannelR);
209
0
210
0
        // Process right virtual source
211
0
        m_convolvers[2]->process(sourceBusR, tempChannelL);
212
0
        m_convolvers[3]->process(sourceBusR, tempChannelR);
213
0
214
0
        AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL, sourceBus->GetDuration());
215
0
        AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR, sourceBus->GetDuration());
216
0
    } else if (numInputChannels == 1 && numReverbChannels == 4 && numOutputChannels == 2) {
217
0
        // 1 -> 4 -> 2 (Processing mono with "True" stereo impulse response)
218
0
        // This is an inefficient use of a four-channel impulse response, but we should handle the case.
219
0
        float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
220
0
221
0
        float* tempChannelL = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[0]));
222
0
        float* tempChannelR = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[1]));
223
0
224
0
        // Process left virtual source
225
0
        m_convolvers[0]->process(sourceBusL, destinationChannelL);
226
0
        m_convolvers[1]->process(sourceBusL, destinationChannelR);
227
0
228
0
        // Process right virtual source
229
0
        m_convolvers[2]->process(sourceBusL, tempChannelL);
230
0
        m_convolvers[3]->process(sourceBusL, tempChannelR);
231
0
232
0
        AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL, sourceBus->GetDuration());
233
0
        AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR, sourceBus->GetDuration());
234
0
    } else {
235
0
        MOZ_ASSERT_UNREACHABLE("Unexpected Reverb configuration");
236
0
        destinationBus->SetNull(destinationBus->GetDuration());
237
0
    }
238
0
}
239
240
} // namespace WebCore