content/media/webaudio/blink/Reverb.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

michael@0 1 /*
michael@0 2 * Copyright (C) 2010 Google Inc. All rights reserved.
michael@0 3 *
michael@0 4 * Redistribution and use in source and binary forms, with or without
michael@0 5 * modification, are permitted provided that the following conditions
michael@0 6 * are met:
michael@0 7 *
michael@0 8 * 1. Redistributions of source code must retain the above copyright
michael@0 9 * notice, this list of conditions and the following disclaimer.
michael@0 10 * 2. Redistributions in binary form must reproduce the above copyright
michael@0 11 * notice, this list of conditions and the following disclaimer in the
michael@0 12 * documentation and/or other materials provided with the distribution.
michael@0 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
michael@0 14 * its contributors may be used to endorse or promote products derived
michael@0 15 * from this software without specific prior written permission.
michael@0 16 *
michael@0 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
michael@0 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
michael@0 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
michael@0 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
michael@0 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
michael@0 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
michael@0 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
michael@0 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
michael@0 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 27 */
michael@0 28
michael@0 29 #include "Reverb.h"
michael@0 30 #include "ReverbConvolverStage.h"
michael@0 31
michael@0 32 #include <math.h>
michael@0 33 #include "ReverbConvolver.h"
michael@0 34 #include "mozilla/FloatingPoint.h"
michael@0 35
michael@0 36 using namespace mozilla;
michael@0 37
michael@0 38 namespace WebCore {
michael@0 39
michael@0 40 // Empirical gain calibration tested across many impulse responses to ensure perceived volume is same as dry (unprocessed) signal
michael@0 41 const float GainCalibration = -58;
michael@0 42 const float GainCalibrationSampleRate = 44100;
michael@0 43
michael@0 44 // A minimum power value to when normalizing a silent (or very quiet) impulse response
michael@0 45 const float MinPower = 0.000125f;
michael@0 46
michael@0 47 static float calculateNormalizationScale(ThreadSharedFloatArrayBufferList* response, size_t aLength, float sampleRate)
michael@0 48 {
michael@0 49 // Normalize by RMS power
michael@0 50 size_t numberOfChannels = response->GetChannels();
michael@0 51
michael@0 52 float power = 0;
michael@0 53
michael@0 54 for (size_t i = 0; i < numberOfChannels; ++i) {
michael@0 55 float channelPower = AudioBufferSumOfSquares(static_cast<const float*>(response->GetData(i)), aLength);
michael@0 56 power += channelPower;
michael@0 57 }
michael@0 58
michael@0 59 power = sqrt(power / (numberOfChannels * aLength));
michael@0 60
michael@0 61 // Protect against accidental overload
michael@0 62 if (!IsFinite(power) || IsNaN(power) || power < MinPower)
michael@0 63 power = MinPower;
michael@0 64
michael@0 65 float scale = 1 / power;
michael@0 66
michael@0 67 scale *= powf(10, GainCalibration * 0.05f); // calibrate to make perceived volume same as unprocessed
michael@0 68
michael@0 69 // Scale depends on sample-rate.
michael@0 70 if (sampleRate)
michael@0 71 scale *= GainCalibrationSampleRate / sampleRate;
michael@0 72
michael@0 73 // True-stereo compensation
michael@0 74 if (response->GetChannels() == 4)
michael@0 75 scale *= 0.5f;
michael@0 76
michael@0 77 return scale;
michael@0 78 }
michael@0 79
michael@0 80 Reverb::Reverb(ThreadSharedFloatArrayBufferList* impulseResponse, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize, float sampleRate)
michael@0 81 {
michael@0 82 float scale = 1;
michael@0 83
michael@0 84 nsAutoTArray<const float*,4> irChannels;
michael@0 85 for (size_t i = 0; i < impulseResponse->GetChannels(); ++i) {
michael@0 86 irChannels.AppendElement(impulseResponse->GetData(i));
michael@0 87 }
michael@0 88 nsAutoTArray<float,1024> tempBuf;
michael@0 89
michael@0 90 if (normalize) {
michael@0 91 scale = calculateNormalizationScale(impulseResponse, impulseResponseBufferLength, sampleRate);
michael@0 92
michael@0 93 if (scale) {
michael@0 94 tempBuf.SetLength(irChannels.Length()*impulseResponseBufferLength);
michael@0 95 for (uint32_t i = 0; i < irChannels.Length(); ++i) {
michael@0 96 float* buf = &tempBuf[i*impulseResponseBufferLength];
michael@0 97 AudioBufferCopyWithScale(irChannels[i], scale, buf,
michael@0 98 impulseResponseBufferLength);
michael@0 99 irChannels[i] = buf;
michael@0 100 }
michael@0 101 }
michael@0 102 }
michael@0 103
michael@0 104 initialize(irChannels, impulseResponseBufferLength, renderSliceSize,
michael@0 105 maxFFTSize, numberOfChannels, useBackgroundThreads);
michael@0 106 }
michael@0 107
michael@0 108 size_t Reverb::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
michael@0 109 {
michael@0 110 size_t amount = aMallocSizeOf(this);
michael@0 111 amount += m_convolvers.SizeOfExcludingThis(aMallocSizeOf);
michael@0 112 for (size_t i = 0; i < m_convolvers.Length(); i++) {
michael@0 113 if (m_convolvers[i]) {
michael@0 114 amount += m_convolvers[i]->sizeOfIncludingThis(aMallocSizeOf);
michael@0 115 }
michael@0 116 }
michael@0 117
michael@0 118 amount += m_tempBuffer.SizeOfExcludingThis(aMallocSizeOf, false);
michael@0 119 return amount;
michael@0 120 }
michael@0 121
michael@0 122
michael@0 123 void Reverb::initialize(const nsTArray<const float*>& impulseResponseBuffer,
michael@0 124 size_t impulseResponseBufferLength, size_t renderSliceSize,
michael@0 125 size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads)
michael@0 126 {
michael@0 127 m_impulseResponseLength = impulseResponseBufferLength;
michael@0 128
michael@0 129 // The reverb can handle a mono impulse response and still do stereo processing
michael@0 130 size_t numResponseChannels = impulseResponseBuffer.Length();
michael@0 131 m_convolvers.SetCapacity(numberOfChannels);
michael@0 132
michael@0 133 int convolverRenderPhase = 0;
michael@0 134 for (size_t i = 0; i < numResponseChannels; ++i) {
michael@0 135 const float* channel = impulseResponseBuffer[i];
michael@0 136 size_t length = impulseResponseBufferLength;
michael@0 137
michael@0 138 nsAutoPtr<ReverbConvolver> convolver(new ReverbConvolver(channel, length, renderSliceSize, maxFFTSize, convolverRenderPhase, useBackgroundThreads));
michael@0 139 m_convolvers.AppendElement(convolver.forget());
michael@0 140
michael@0 141 convolverRenderPhase += renderSliceSize;
michael@0 142 }
michael@0 143
michael@0 144 // For "True" stereo processing we allocate a temporary buffer to avoid repeatedly allocating it in the process() method.
michael@0 145 // It can be bad to allocate memory in a real-time thread.
michael@0 146 if (numResponseChannels == 4) {
michael@0 147 AllocateAudioBlock(2, &m_tempBuffer);
michael@0 148 WriteZeroesToAudioBlock(&m_tempBuffer, 0, WEBAUDIO_BLOCK_SIZE);
michael@0 149 }
michael@0 150 }
michael@0 151
michael@0 152 void Reverb::process(const AudioChunk* sourceBus, AudioChunk* destinationBus, size_t framesToProcess)
michael@0 153 {
michael@0 154 // Do a fairly comprehensive sanity check.
michael@0 155 // If these conditions are satisfied, all of the source and destination pointers will be valid for the various matrixing cases.
michael@0 156 bool isSafeToProcess = sourceBus && destinationBus && sourceBus->mChannelData.Length() > 0 && destinationBus->mChannelData.Length() > 0
michael@0 157 && framesToProcess <= MaxFrameSize && framesToProcess <= size_t(sourceBus->mDuration) && framesToProcess <= size_t(destinationBus->mDuration);
michael@0 158
michael@0 159 MOZ_ASSERT(isSafeToProcess);
michael@0 160 if (!isSafeToProcess)
michael@0 161 return;
michael@0 162
michael@0 163 // For now only handle mono or stereo output
michael@0 164 MOZ_ASSERT(destinationBus->mChannelData.Length() <= 2);
michael@0 165
michael@0 166 float* destinationChannelL = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[0]));
michael@0 167 const float* sourceBusL = static_cast<const float*>(sourceBus->mChannelData[0]);
michael@0 168
michael@0 169 // Handle input -> output matrixing...
michael@0 170 size_t numInputChannels = sourceBus->mChannelData.Length();
michael@0 171 size_t numOutputChannels = destinationBus->mChannelData.Length();
michael@0 172 size_t numReverbChannels = m_convolvers.Length();
michael@0 173
michael@0 174 if (numInputChannels == 2 && numReverbChannels == 2 && numOutputChannels == 2) {
michael@0 175 // 2 -> 2 -> 2
michael@0 176 const float* sourceBusR = static_cast<const float*>(sourceBus->mChannelData[1]);
michael@0 177 float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
michael@0 178 m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess);
michael@0 179 m_convolvers[1]->process(sourceBusR, sourceBus->mDuration, destinationChannelR, destinationBus->mDuration, framesToProcess);
michael@0 180 } else if (numInputChannels == 1 && numOutputChannels == 2 && numReverbChannels == 2) {
michael@0 181 // 1 -> 2 -> 2
michael@0 182 for (int i = 0; i < 2; ++i) {
michael@0 183 float* destinationChannel = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[i]));
michael@0 184 m_convolvers[i]->process(sourceBusL, sourceBus->mDuration, destinationChannel, destinationBus->mDuration, framesToProcess);
michael@0 185 }
michael@0 186 } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 2) {
michael@0 187 // 1 -> 1 -> 2
michael@0 188 m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess);
michael@0 189
michael@0 190 // simply copy L -> R
michael@0 191 float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
michael@0 192 bool isCopySafe = destinationChannelL && destinationChannelR && size_t(destinationBus->mDuration) >= framesToProcess && size_t(destinationBus->mDuration) >= framesToProcess;
michael@0 193 MOZ_ASSERT(isCopySafe);
michael@0 194 if (!isCopySafe)
michael@0 195 return;
michael@0 196 PodCopy(destinationChannelR, destinationChannelL, framesToProcess);
michael@0 197 } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 1) {
michael@0 198 // 1 -> 1 -> 1
michael@0 199 m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess);
michael@0 200 } else if (numInputChannels == 2 && numReverbChannels == 4 && numOutputChannels == 2) {
michael@0 201 // 2 -> 4 -> 2 ("True" stereo)
michael@0 202 const float* sourceBusR = static_cast<const float*>(sourceBus->mChannelData[1]);
michael@0 203 float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
michael@0 204
michael@0 205 float* tempChannelL = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[0]));
michael@0 206 float* tempChannelR = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[1]));
michael@0 207
michael@0 208 // Process left virtual source
michael@0 209 m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess);
michael@0 210 m_convolvers[1]->process(sourceBusL, sourceBus->mDuration, destinationChannelR, destinationBus->mDuration, framesToProcess);
michael@0 211
michael@0 212 // Process right virtual source
michael@0 213 m_convolvers[2]->process(sourceBusR, sourceBus->mDuration, tempChannelL, m_tempBuffer.mDuration, framesToProcess);
michael@0 214 m_convolvers[3]->process(sourceBusR, sourceBus->mDuration, tempChannelR, m_tempBuffer.mDuration, framesToProcess);
michael@0 215
michael@0 216 AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL, sourceBus->mDuration);
michael@0 217 AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR, sourceBus->mDuration);
michael@0 218 } else if (numInputChannels == 1 && numReverbChannels == 4 && numOutputChannels == 2) {
michael@0 219 // 1 -> 4 -> 2 (Processing mono with "True" stereo impulse response)
michael@0 220 // This is an inefficient use of a four-channel impulse response, but we should handle the case.
michael@0 221 float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
michael@0 222
michael@0 223 float* tempChannelL = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[0]));
michael@0 224 float* tempChannelR = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[1]));
michael@0 225
michael@0 226 // Process left virtual source
michael@0 227 m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess);
michael@0 228 m_convolvers[1]->process(sourceBusL, sourceBus->mDuration, destinationChannelR, destinationBus->mDuration, framesToProcess);
michael@0 229
michael@0 230 // Process right virtual source
michael@0 231 m_convolvers[2]->process(sourceBusL, sourceBus->mDuration, tempChannelL, m_tempBuffer.mDuration, framesToProcess);
michael@0 232 m_convolvers[3]->process(sourceBusL, sourceBus->mDuration, tempChannelR, m_tempBuffer.mDuration, framesToProcess);
michael@0 233
michael@0 234 AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL, sourceBus->mDuration);
michael@0 235 AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR, sourceBus->mDuration);
michael@0 236 } else {
michael@0 237 // Handle gracefully any unexpected / unsupported matrixing
michael@0 238 // FIXME: add code for 5.1 support...
michael@0 239 destinationBus->SetNull(destinationBus->mDuration);
michael@0 240 }
michael@0 241 }
michael@0 242
michael@0 243 void Reverb::reset()
michael@0 244 {
michael@0 245 for (size_t i = 0; i < m_convolvers.Length(); ++i)
michael@0 246 m_convolvers[i]->reset();
michael@0 247 }
michael@0 248
michael@0 249 size_t Reverb::latencyFrames() const
michael@0 250 {
michael@0 251 return !m_convolvers.IsEmpty() ? m_convolvers[0]->latencyFrames() : 0;
michael@0 252 }
michael@0 253
michael@0 254 } // namespace WebCore

mercurial