Line data Source code
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 0 : static float calculateNormalizationScale(ThreadSharedFloatArrayBufferList* response, size_t aLength, float sampleRate)
48 : {
49 : // Normalize by RMS power
50 0 : size_t numberOfChannels = response->GetChannels();
51 :
52 0 : float power = 0;
53 :
54 0 : for (size_t i = 0; i < numberOfChannels; ++i) {
55 0 : float channelPower = AudioBufferSumOfSquares(static_cast<const float*>(response->GetData(i)), aLength);
56 0 : power += channelPower;
57 : }
58 :
59 0 : power = sqrt(power / (numberOfChannels * aLength));
60 :
61 : // Protect against accidental overload
62 0 : if (!IsFinite(power) || IsNaN(power) || power < MinPower)
63 0 : power = MinPower;
64 :
65 0 : float scale = 1 / power;
66 :
67 0 : scale *= powf(10, GainCalibration * 0.05f); // calibrate to make perceived volume same as unprocessed
68 :
69 : // Scale depends on sample-rate.
70 0 : if (sampleRate)
71 0 : scale *= GainCalibrationSampleRate / sampleRate;
72 :
73 : // True-stereo compensation
74 0 : if (response->GetChannels() == 4)
75 0 : scale *= 0.5f;
76 :
77 0 : return scale;
78 : }
79 :
80 0 : Reverb::Reverb(ThreadSharedFloatArrayBufferList* impulseResponse, size_t impulseResponseBufferLength, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize, float sampleRate)
81 : {
82 0 : float scale = 1;
83 :
84 0 : AutoTArray<const float*,4> irChannels;
85 0 : for (size_t i = 0; i < impulseResponse->GetChannels(); ++i) {
86 0 : irChannels.AppendElement(impulseResponse->GetData(i));
87 : }
88 0 : AutoTArray<float,1024> tempBuf;
89 :
90 0 : if (normalize) {
91 0 : scale = calculateNormalizationScale(impulseResponse, impulseResponseBufferLength, sampleRate);
92 :
93 0 : if (scale) {
94 0 : tempBuf.SetLength(irChannels.Length()*impulseResponseBufferLength);
95 0 : for (uint32_t i = 0; i < irChannels.Length(); ++i) {
96 0 : float* buf = &tempBuf[i*impulseResponseBufferLength];
97 0 : AudioBufferCopyWithScale(irChannels[i], scale, buf,
98 0 : impulseResponseBufferLength);
99 0 : irChannels[i] = buf;
100 : }
101 : }
102 : }
103 :
104 0 : initialize(irChannels, impulseResponseBufferLength,
105 0 : maxFFTSize, numberOfChannels, useBackgroundThreads);
106 0 : }
107 :
108 0 : size_t Reverb::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
109 : {
110 0 : size_t amount = aMallocSizeOf(this);
111 0 : amount += m_convolvers.ShallowSizeOfExcludingThis(aMallocSizeOf);
112 0 : for (size_t i = 0; i < m_convolvers.Length(); i++) {
113 0 : if (m_convolvers[i]) {
114 0 : amount += m_convolvers[i]->sizeOfIncludingThis(aMallocSizeOf);
115 : }
116 : }
117 :
118 0 : amount += m_tempBuffer.SizeOfExcludingThis(aMallocSizeOf, false);
119 0 : return amount;
120 : }
121 :
122 :
123 0 : void Reverb::initialize(const nsTArray<const float*>& impulseResponseBuffer,
124 : size_t impulseResponseBufferLength,
125 : size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads)
126 : {
127 0 : m_impulseResponseLength = impulseResponseBufferLength;
128 :
129 : // The reverb can handle a mono impulse response and still do stereo processing
130 0 : size_t numResponseChannels = impulseResponseBuffer.Length();
131 0 : m_convolvers.SetCapacity(numberOfChannels);
132 :
133 0 : int convolverRenderPhase = 0;
134 0 : for (size_t i = 0; i < numResponseChannels; ++i) {
135 0 : const float* channel = impulseResponseBuffer[i];
136 0 : size_t length = impulseResponseBufferLength;
137 :
138 0 : nsAutoPtr<ReverbConvolver> convolver(new ReverbConvolver(channel, length, maxFFTSize, convolverRenderPhase, useBackgroundThreads));
139 0 : m_convolvers.AppendElement(convolver.forget());
140 :
141 0 : convolverRenderPhase += WEBAUDIO_BLOCK_SIZE;
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 0 : if (numResponseChannels == 4) {
147 0 : m_tempBuffer.AllocateChannels(2);
148 0 : WriteZeroesToAudioBlock(&m_tempBuffer, 0, WEBAUDIO_BLOCK_SIZE);
149 : }
150 0 : }
151 :
152 0 : void Reverb::process(const AudioBlock* sourceBus, AudioBlock* destinationBus)
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 0 : bool isSafeToProcess = sourceBus && destinationBus && sourceBus->ChannelCount() > 0 && destinationBus->mChannelData.Length() > 0
157 0 : && WEBAUDIO_BLOCK_SIZE <= MaxFrameSize && WEBAUDIO_BLOCK_SIZE <= size_t(sourceBus->GetDuration()) && WEBAUDIO_BLOCK_SIZE <= size_t(destinationBus->GetDuration());
158 :
159 0 : MOZ_ASSERT(isSafeToProcess);
160 0 : if (!isSafeToProcess)
161 0 : return;
162 :
163 : // For now only handle mono or stereo output
164 0 : MOZ_ASSERT(destinationBus->ChannelCount() <= 2);
165 :
166 0 : float* destinationChannelL = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[0]));
167 0 : const float* sourceBusL = static_cast<const float*>(sourceBus->mChannelData[0]);
168 :
169 : // Handle input -> output matrixing...
170 0 : size_t numInputChannels = sourceBus->ChannelCount();
171 0 : size_t numOutputChannels = destinationBus->ChannelCount();
172 0 : size_t numReverbChannels = m_convolvers.Length();
173 :
174 0 : if (numInputChannels == 2 && numReverbChannels == 2 && numOutputChannels == 2) {
175 : // 2 -> 2 -> 2
176 0 : const float* sourceBusR = static_cast<const float*>(sourceBus->mChannelData[1]);
177 0 : float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
178 0 : m_convolvers[0]->process(sourceBusL, destinationChannelL);
179 0 : m_convolvers[1]->process(sourceBusR, destinationChannelR);
180 0 : } else if (numInputChannels == 1 && numOutputChannels == 2 && numReverbChannels == 2) {
181 : // 1 -> 2 -> 2
182 0 : for (int i = 0; i < 2; ++i) {
183 0 : float* destinationChannel = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[i]));
184 0 : m_convolvers[i]->process(sourceBusL, destinationChannel);
185 0 : }
186 0 : } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 2) {
187 : // 1 -> 1 -> 2
188 0 : m_convolvers[0]->process(sourceBusL, destinationChannelL);
189 :
190 : // simply copy L -> R
191 0 : float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
192 0 : bool isCopySafe = destinationChannelL && destinationChannelR && size_t(destinationBus->GetDuration()) >= WEBAUDIO_BLOCK_SIZE;
193 0 : MOZ_ASSERT(isCopySafe);
194 0 : if (!isCopySafe)
195 0 : return;
196 0 : PodCopy(destinationChannelR, destinationChannelL, WEBAUDIO_BLOCK_SIZE);
197 0 : } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 1) {
198 : // 1 -> 1 -> 1
199 0 : m_convolvers[0]->process(sourceBusL, destinationChannelL);
200 0 : } else if (numInputChannels == 2 && numReverbChannels == 4 && numOutputChannels == 2) {
201 : // 2 -> 4 -> 2 ("True" stereo)
202 0 : const float* sourceBusR = static_cast<const float*>(sourceBus->mChannelData[1]);
203 0 : float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
204 :
205 0 : float* tempChannelL = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[0]));
206 0 : float* tempChannelR = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[1]));
207 :
208 : // Process left virtual source
209 0 : m_convolvers[0]->process(sourceBusL, destinationChannelL);
210 0 : m_convolvers[1]->process(sourceBusL, destinationChannelR);
211 :
212 : // Process right virtual source
213 0 : m_convolvers[2]->process(sourceBusR, tempChannelL);
214 0 : m_convolvers[3]->process(sourceBusR, tempChannelR);
215 :
216 0 : AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL, sourceBus->GetDuration());
217 0 : AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR, sourceBus->GetDuration());
218 0 : } 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 0 : float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
222 :
223 0 : float* tempChannelL = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[0]));
224 0 : float* tempChannelR = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[1]));
225 :
226 : // Process left virtual source
227 0 : m_convolvers[0]->process(sourceBusL, destinationChannelL);
228 0 : m_convolvers[1]->process(sourceBusL, destinationChannelR);
229 :
230 : // Process right virtual source
231 0 : m_convolvers[2]->process(sourceBusL, tempChannelL);
232 0 : m_convolvers[3]->process(sourceBusL, tempChannelR);
233 :
234 0 : AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL, sourceBus->GetDuration());
235 0 : AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR, sourceBus->GetDuration());
236 : } else {
237 : // Handle gracefully any unexpected / unsupported matrixing
238 : // FIXME: add code for 5.1 support...
239 0 : destinationBus->SetNull(destinationBus->GetDuration());
240 : }
241 : }
242 :
243 : } // namespace WebCore
|