1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/webaudio/blink/Reverb.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,254 @@ 1.4 +/* 1.5 + * Copyright (C) 2010 Google Inc. All rights reserved. 1.6 + * 1.7 + * Redistribution and use in source and binary forms, with or without 1.8 + * modification, are permitted provided that the following conditions 1.9 + * are met: 1.10 + * 1.11 + * 1. Redistributions of source code must retain the above copyright 1.12 + * notice, this list of conditions and the following disclaimer. 1.13 + * 2. Redistributions in binary form must reproduce the above copyright 1.14 + * notice, this list of conditions and the following disclaimer in the 1.15 + * documentation and/or other materials provided with the distribution. 1.16 + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 1.17 + * its contributors may be used to endorse or promote products derived 1.18 + * from this software without specific prior written permission. 1.19 + * 1.20 + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 1.21 + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 1.22 + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 1.23 + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 1.24 + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 1.25 + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 1.26 + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 1.27 + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.28 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 1.29 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.30 + */ 1.31 + 1.32 +#include "Reverb.h" 1.33 +#include "ReverbConvolverStage.h" 1.34 + 1.35 +#include <math.h> 1.36 +#include "ReverbConvolver.h" 1.37 +#include "mozilla/FloatingPoint.h" 1.38 + 1.39 +using namespace mozilla; 1.40 + 1.41 +namespace WebCore { 1.42 + 1.43 +// Empirical gain calibration tested across many impulse responses to ensure perceived volume is same as dry (unprocessed) signal 1.44 +const float GainCalibration = -58; 1.45 +const float GainCalibrationSampleRate = 44100; 1.46 + 1.47 +// A minimum power value to when normalizing a silent (or very quiet) impulse response 1.48 +const float MinPower = 0.000125f; 1.49 + 1.50 +static float calculateNormalizationScale(ThreadSharedFloatArrayBufferList* response, size_t aLength, float sampleRate) 1.51 +{ 1.52 + // Normalize by RMS power 1.53 + size_t numberOfChannels = response->GetChannels(); 1.54 + 1.55 + float power = 0; 1.56 + 1.57 + for (size_t i = 0; i < numberOfChannels; ++i) { 1.58 + float channelPower = AudioBufferSumOfSquares(static_cast<const float*>(response->GetData(i)), aLength); 1.59 + power += channelPower; 1.60 + } 1.61 + 1.62 + power = sqrt(power / (numberOfChannels * aLength)); 1.63 + 1.64 + // Protect against accidental overload 1.65 + if (!IsFinite(power) || IsNaN(power) || power < MinPower) 1.66 + power = MinPower; 1.67 + 1.68 + float scale = 1 / power; 1.69 + 1.70 + scale *= powf(10, GainCalibration * 0.05f); // calibrate to make perceived volume same as unprocessed 1.71 + 1.72 + // Scale depends on sample-rate. 1.73 + if (sampleRate) 1.74 + scale *= GainCalibrationSampleRate / sampleRate; 1.75 + 1.76 + // True-stereo compensation 1.77 + if (response->GetChannels() == 4) 1.78 + scale *= 0.5f; 1.79 + 1.80 + return scale; 1.81 +} 1.82 + 1.83 +Reverb::Reverb(ThreadSharedFloatArrayBufferList* impulseResponse, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize, float sampleRate) 1.84 +{ 1.85 + float scale = 1; 1.86 + 1.87 + nsAutoTArray<const float*,4> irChannels; 1.88 + for (size_t i = 0; i < impulseResponse->GetChannels(); ++i) { 1.89 + irChannels.AppendElement(impulseResponse->GetData(i)); 1.90 + } 1.91 + nsAutoTArray<float,1024> tempBuf; 1.92 + 1.93 + if (normalize) { 1.94 + scale = calculateNormalizationScale(impulseResponse, impulseResponseBufferLength, sampleRate); 1.95 + 1.96 + if (scale) { 1.97 + tempBuf.SetLength(irChannels.Length()*impulseResponseBufferLength); 1.98 + for (uint32_t i = 0; i < irChannels.Length(); ++i) { 1.99 + float* buf = &tempBuf[i*impulseResponseBufferLength]; 1.100 + AudioBufferCopyWithScale(irChannels[i], scale, buf, 1.101 + impulseResponseBufferLength); 1.102 + irChannels[i] = buf; 1.103 + } 1.104 + } 1.105 + } 1.106 + 1.107 + initialize(irChannels, impulseResponseBufferLength, renderSliceSize, 1.108 + maxFFTSize, numberOfChannels, useBackgroundThreads); 1.109 +} 1.110 + 1.111 +size_t Reverb::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const 1.112 +{ 1.113 + size_t amount = aMallocSizeOf(this); 1.114 + amount += m_convolvers.SizeOfExcludingThis(aMallocSizeOf); 1.115 + for (size_t i = 0; i < m_convolvers.Length(); i++) { 1.116 + if (m_convolvers[i]) { 1.117 + amount += m_convolvers[i]->sizeOfIncludingThis(aMallocSizeOf); 1.118 + } 1.119 + } 1.120 + 1.121 + amount += m_tempBuffer.SizeOfExcludingThis(aMallocSizeOf, false); 1.122 + return amount; 1.123 +} 1.124 + 1.125 + 1.126 +void Reverb::initialize(const nsTArray<const float*>& impulseResponseBuffer, 1.127 + size_t impulseResponseBufferLength, size_t renderSliceSize, 1.128 + size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads) 1.129 +{ 1.130 + m_impulseResponseLength = impulseResponseBufferLength; 1.131 + 1.132 + // The reverb can handle a mono impulse response and still do stereo processing 1.133 + size_t numResponseChannels = impulseResponseBuffer.Length(); 1.134 + m_convolvers.SetCapacity(numberOfChannels); 1.135 + 1.136 + int convolverRenderPhase = 0; 1.137 + for (size_t i = 0; i < numResponseChannels; ++i) { 1.138 + const float* channel = impulseResponseBuffer[i]; 1.139 + size_t length = impulseResponseBufferLength; 1.140 + 1.141 + nsAutoPtr<ReverbConvolver> convolver(new ReverbConvolver(channel, length, renderSliceSize, maxFFTSize, convolverRenderPhase, useBackgroundThreads)); 1.142 + m_convolvers.AppendElement(convolver.forget()); 1.143 + 1.144 + convolverRenderPhase += renderSliceSize; 1.145 + } 1.146 + 1.147 + // For "True" stereo processing we allocate a temporary buffer to avoid repeatedly allocating it in the process() method. 1.148 + // It can be bad to allocate memory in a real-time thread. 1.149 + if (numResponseChannels == 4) { 1.150 + AllocateAudioBlock(2, &m_tempBuffer); 1.151 + WriteZeroesToAudioBlock(&m_tempBuffer, 0, WEBAUDIO_BLOCK_SIZE); 1.152 + } 1.153 +} 1.154 + 1.155 +void Reverb::process(const AudioChunk* sourceBus, AudioChunk* destinationBus, size_t framesToProcess) 1.156 +{ 1.157 + // Do a fairly comprehensive sanity check. 1.158 + // If these conditions are satisfied, all of the source and destination pointers will be valid for the various matrixing cases. 1.159 + bool isSafeToProcess = sourceBus && destinationBus && sourceBus->mChannelData.Length() > 0 && destinationBus->mChannelData.Length() > 0 1.160 + && framesToProcess <= MaxFrameSize && framesToProcess <= size_t(sourceBus->mDuration) && framesToProcess <= size_t(destinationBus->mDuration); 1.161 + 1.162 + MOZ_ASSERT(isSafeToProcess); 1.163 + if (!isSafeToProcess) 1.164 + return; 1.165 + 1.166 + // For now only handle mono or stereo output 1.167 + MOZ_ASSERT(destinationBus->mChannelData.Length() <= 2); 1.168 + 1.169 + float* destinationChannelL = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[0])); 1.170 + const float* sourceBusL = static_cast<const float*>(sourceBus->mChannelData[0]); 1.171 + 1.172 + // Handle input -> output matrixing... 1.173 + size_t numInputChannels = sourceBus->mChannelData.Length(); 1.174 + size_t numOutputChannels = destinationBus->mChannelData.Length(); 1.175 + size_t numReverbChannels = m_convolvers.Length(); 1.176 + 1.177 + if (numInputChannels == 2 && numReverbChannels == 2 && numOutputChannels == 2) { 1.178 + // 2 -> 2 -> 2 1.179 + const float* sourceBusR = static_cast<const float*>(sourceBus->mChannelData[1]); 1.180 + float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1])); 1.181 + m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess); 1.182 + m_convolvers[1]->process(sourceBusR, sourceBus->mDuration, destinationChannelR, destinationBus->mDuration, framesToProcess); 1.183 + } else if (numInputChannels == 1 && numOutputChannels == 2 && numReverbChannels == 2) { 1.184 + // 1 -> 2 -> 2 1.185 + for (int i = 0; i < 2; ++i) { 1.186 + float* destinationChannel = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[i])); 1.187 + m_convolvers[i]->process(sourceBusL, sourceBus->mDuration, destinationChannel, destinationBus->mDuration, framesToProcess); 1.188 + } 1.189 + } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 2) { 1.190 + // 1 -> 1 -> 2 1.191 + m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess); 1.192 + 1.193 + // simply copy L -> R 1.194 + float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1])); 1.195 + bool isCopySafe = destinationChannelL && destinationChannelR && size_t(destinationBus->mDuration) >= framesToProcess && size_t(destinationBus->mDuration) >= framesToProcess; 1.196 + MOZ_ASSERT(isCopySafe); 1.197 + if (!isCopySafe) 1.198 + return; 1.199 + PodCopy(destinationChannelR, destinationChannelL, framesToProcess); 1.200 + } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 1) { 1.201 + // 1 -> 1 -> 1 1.202 + m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess); 1.203 + } else if (numInputChannels == 2 && numReverbChannels == 4 && numOutputChannels == 2) { 1.204 + // 2 -> 4 -> 2 ("True" stereo) 1.205 + const float* sourceBusR = static_cast<const float*>(sourceBus->mChannelData[1]); 1.206 + float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1])); 1.207 + 1.208 + float* tempChannelL = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[0])); 1.209 + float* tempChannelR = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[1])); 1.210 + 1.211 + // Process left virtual source 1.212 + m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess); 1.213 + m_convolvers[1]->process(sourceBusL, sourceBus->mDuration, destinationChannelR, destinationBus->mDuration, framesToProcess); 1.214 + 1.215 + // Process right virtual source 1.216 + m_convolvers[2]->process(sourceBusR, sourceBus->mDuration, tempChannelL, m_tempBuffer.mDuration, framesToProcess); 1.217 + m_convolvers[3]->process(sourceBusR, sourceBus->mDuration, tempChannelR, m_tempBuffer.mDuration, framesToProcess); 1.218 + 1.219 + AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL, sourceBus->mDuration); 1.220 + AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR, sourceBus->mDuration); 1.221 + } else if (numInputChannels == 1 && numReverbChannels == 4 && numOutputChannels == 2) { 1.222 + // 1 -> 4 -> 2 (Processing mono with "True" stereo impulse response) 1.223 + // This is an inefficient use of a four-channel impulse response, but we should handle the case. 1.224 + float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1])); 1.225 + 1.226 + float* tempChannelL = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[0])); 1.227 + float* tempChannelR = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[1])); 1.228 + 1.229 + // Process left virtual source 1.230 + m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess); 1.231 + m_convolvers[1]->process(sourceBusL, sourceBus->mDuration, destinationChannelR, destinationBus->mDuration, framesToProcess); 1.232 + 1.233 + // Process right virtual source 1.234 + m_convolvers[2]->process(sourceBusL, sourceBus->mDuration, tempChannelL, m_tempBuffer.mDuration, framesToProcess); 1.235 + m_convolvers[3]->process(sourceBusL, sourceBus->mDuration, tempChannelR, m_tempBuffer.mDuration, framesToProcess); 1.236 + 1.237 + AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL, sourceBus->mDuration); 1.238 + AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR, sourceBus->mDuration); 1.239 + } else { 1.240 + // Handle gracefully any unexpected / unsupported matrixing 1.241 + // FIXME: add code for 5.1 support... 1.242 + destinationBus->SetNull(destinationBus->mDuration); 1.243 + } 1.244 +} 1.245 + 1.246 +void Reverb::reset() 1.247 +{ 1.248 + for (size_t i = 0; i < m_convolvers.Length(); ++i) 1.249 + m_convolvers[i]->reset(); 1.250 +} 1.251 + 1.252 +size_t Reverb::latencyFrames() const 1.253 +{ 1.254 + return !m_convolvers.IsEmpty() ? m_convolvers[0]->latencyFrames() : 0; 1.255 +} 1.256 + 1.257 +} // namespace WebCore