content/media/webaudio/blink/Reverb.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:ee06592e7470
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 = -58;
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(ThreadSharedFloatArrayBufferList* response, size_t aLength, float sampleRate)
48 {
49 // Normalize by RMS power
50 size_t numberOfChannels = response->GetChannels();
51
52 float power = 0;
53
54 for (size_t i = 0; i < numberOfChannels; ++i) {
55 float channelPower = AudioBufferSumOfSquares(static_cast<const float*>(response->GetData(i)), aLength);
56 power += channelPower;
57 }
58
59 power = sqrt(power / (numberOfChannels * aLength));
60
61 // Protect against accidental overload
62 if (!IsFinite(power) || IsNaN(power) || power < MinPower)
63 power = MinPower;
64
65 float scale = 1 / power;
66
67 scale *= powf(10, GainCalibration * 0.05f); // calibrate to make perceived volume same as unprocessed
68
69 // Scale depends on sample-rate.
70 if (sampleRate)
71 scale *= GainCalibrationSampleRate / sampleRate;
72
73 // True-stereo compensation
74 if (response->GetChannels() == 4)
75 scale *= 0.5f;
76
77 return scale;
78 }
79
80 Reverb::Reverb(ThreadSharedFloatArrayBufferList* impulseResponse, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize, float sampleRate)
81 {
82 float scale = 1;
83
84 nsAutoTArray<const float*,4> irChannels;
85 for (size_t i = 0; i < impulseResponse->GetChannels(); ++i) {
86 irChannels.AppendElement(impulseResponse->GetData(i));
87 }
88 nsAutoTArray<float,1024> tempBuf;
89
90 if (normalize) {
91 scale = calculateNormalizationScale(impulseResponse, impulseResponseBufferLength, sampleRate);
92
93 if (scale) {
94 tempBuf.SetLength(irChannels.Length()*impulseResponseBufferLength);
95 for (uint32_t i = 0; i < irChannels.Length(); ++i) {
96 float* buf = &tempBuf[i*impulseResponseBufferLength];
97 AudioBufferCopyWithScale(irChannels[i], scale, buf,
98 impulseResponseBufferLength);
99 irChannels[i] = buf;
100 }
101 }
102 }
103
104 initialize(irChannels, impulseResponseBufferLength, renderSliceSize,
105 maxFFTSize, numberOfChannels, useBackgroundThreads);
106 }
107
108 size_t Reverb::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
109 {
110 size_t amount = aMallocSizeOf(this);
111 amount += m_convolvers.SizeOfExcludingThis(aMallocSizeOf);
112 for (size_t i = 0; i < m_convolvers.Length(); i++) {
113 if (m_convolvers[i]) {
114 amount += m_convolvers[i]->sizeOfIncludingThis(aMallocSizeOf);
115 }
116 }
117
118 amount += m_tempBuffer.SizeOfExcludingThis(aMallocSizeOf, false);
119 return amount;
120 }
121
122
123 void Reverb::initialize(const nsTArray<const float*>& impulseResponseBuffer,
124 size_t impulseResponseBufferLength, size_t renderSliceSize,
125 size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads)
126 {
127 m_impulseResponseLength = impulseResponseBufferLength;
128
129 // The reverb can handle a mono impulse response and still do stereo processing
130 size_t numResponseChannels = impulseResponseBuffer.Length();
131 m_convolvers.SetCapacity(numberOfChannels);
132
133 int convolverRenderPhase = 0;
134 for (size_t i = 0; i < numResponseChannels; ++i) {
135 const float* channel = impulseResponseBuffer[i];
136 size_t length = impulseResponseBufferLength;
137
138 nsAutoPtr<ReverbConvolver> convolver(new ReverbConvolver(channel, length, renderSliceSize, maxFFTSize, convolverRenderPhase, useBackgroundThreads));
139 m_convolvers.AppendElement(convolver.forget());
140
141 convolverRenderPhase += renderSliceSize;
142 }
143
144 // For "True" stereo processing we allocate a temporary buffer to avoid repeatedly allocating it in the process() method.
145 // It can be bad to allocate memory in a real-time thread.
146 if (numResponseChannels == 4) {
147 AllocateAudioBlock(2, &m_tempBuffer);
148 WriteZeroesToAudioBlock(&m_tempBuffer, 0, WEBAUDIO_BLOCK_SIZE);
149 }
150 }
151
152 void Reverb::process(const AudioChunk* sourceBus, AudioChunk* destinationBus, size_t framesToProcess)
153 {
154 // Do a fairly comprehensive sanity check.
155 // If these conditions are satisfied, all of the source and destination pointers will be valid for the various matrixing cases.
156 bool isSafeToProcess = sourceBus && destinationBus && sourceBus->mChannelData.Length() > 0 && destinationBus->mChannelData.Length() > 0
157 && framesToProcess <= MaxFrameSize && framesToProcess <= size_t(sourceBus->mDuration) && framesToProcess <= size_t(destinationBus->mDuration);
158
159 MOZ_ASSERT(isSafeToProcess);
160 if (!isSafeToProcess)
161 return;
162
163 // For now only handle mono or stereo output
164 MOZ_ASSERT(destinationBus->mChannelData.Length() <= 2);
165
166 float* destinationChannelL = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[0]));
167 const float* sourceBusL = static_cast<const float*>(sourceBus->mChannelData[0]);
168
169 // Handle input -> output matrixing...
170 size_t numInputChannels = sourceBus->mChannelData.Length();
171 size_t numOutputChannels = destinationBus->mChannelData.Length();
172 size_t numReverbChannels = m_convolvers.Length();
173
174 if (numInputChannels == 2 && numReverbChannels == 2 && numOutputChannels == 2) {
175 // 2 -> 2 -> 2
176 const float* sourceBusR = static_cast<const float*>(sourceBus->mChannelData[1]);
177 float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
178 m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess);
179 m_convolvers[1]->process(sourceBusR, sourceBus->mDuration, destinationChannelR, destinationBus->mDuration, framesToProcess);
180 } else if (numInputChannels == 1 && numOutputChannels == 2 && numReverbChannels == 2) {
181 // 1 -> 2 -> 2
182 for (int i = 0; i < 2; ++i) {
183 float* destinationChannel = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[i]));
184 m_convolvers[i]->process(sourceBusL, sourceBus->mDuration, destinationChannel, destinationBus->mDuration, framesToProcess);
185 }
186 } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 2) {
187 // 1 -> 1 -> 2
188 m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess);
189
190 // simply copy L -> R
191 float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
192 bool isCopySafe = destinationChannelL && destinationChannelR && size_t(destinationBus->mDuration) >= framesToProcess && size_t(destinationBus->mDuration) >= framesToProcess;
193 MOZ_ASSERT(isCopySafe);
194 if (!isCopySafe)
195 return;
196 PodCopy(destinationChannelR, destinationChannelL, framesToProcess);
197 } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 1) {
198 // 1 -> 1 -> 1
199 m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess);
200 } else if (numInputChannels == 2 && numReverbChannels == 4 && numOutputChannels == 2) {
201 // 2 -> 4 -> 2 ("True" stereo)
202 const float* sourceBusR = static_cast<const float*>(sourceBus->mChannelData[1]);
203 float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
204
205 float* tempChannelL = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[0]));
206 float* tempChannelR = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[1]));
207
208 // Process left virtual source
209 m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess);
210 m_convolvers[1]->process(sourceBusL, sourceBus->mDuration, destinationChannelR, destinationBus->mDuration, framesToProcess);
211
212 // Process right virtual source
213 m_convolvers[2]->process(sourceBusR, sourceBus->mDuration, tempChannelL, m_tempBuffer.mDuration, framesToProcess);
214 m_convolvers[3]->process(sourceBusR, sourceBus->mDuration, tempChannelR, m_tempBuffer.mDuration, framesToProcess);
215
216 AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL, sourceBus->mDuration);
217 AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR, sourceBus->mDuration);
218 } else if (numInputChannels == 1 && numReverbChannels == 4 && numOutputChannels == 2) {
219 // 1 -> 4 -> 2 (Processing mono with "True" stereo impulse response)
220 // This is an inefficient use of a four-channel impulse response, but we should handle the case.
221 float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
222
223 float* tempChannelL = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[0]));
224 float* tempChannelR = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[1]));
225
226 // Process left virtual source
227 m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess);
228 m_convolvers[1]->process(sourceBusL, sourceBus->mDuration, destinationChannelR, destinationBus->mDuration, framesToProcess);
229
230 // Process right virtual source
231 m_convolvers[2]->process(sourceBusL, sourceBus->mDuration, tempChannelL, m_tempBuffer.mDuration, framesToProcess);
232 m_convolvers[3]->process(sourceBusL, sourceBus->mDuration, tempChannelR, m_tempBuffer.mDuration, framesToProcess);
233
234 AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL, sourceBus->mDuration);
235 AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR, sourceBus->mDuration);
236 } else {
237 // Handle gracefully any unexpected / unsupported matrixing
238 // FIXME: add code for 5.1 support...
239 destinationBus->SetNull(destinationBus->mDuration);
240 }
241 }
242
243 void Reverb::reset()
244 {
245 for (size_t i = 0; i < m_convolvers.Length(); ++i)
246 m_convolvers[i]->reset();
247 }
248
249 size_t Reverb::latencyFrames() const
250 {
251 return !m_convolvers.IsEmpty() ? m_convolvers[0]->latencyFrames() : 0;
252 }
253
254 } // namespace WebCore

mercurial