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

     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  */
    29 #include "Reverb.h"
    30 #include "ReverbConvolverStage.h"
    32 #include <math.h>
    33 #include "ReverbConvolver.h"
    34 #include "mozilla/FloatingPoint.h"
    36 using namespace mozilla;
    38 namespace WebCore {
    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;
    44 // A minimum power value to when normalizing a silent (or very quiet) impulse response
    45 const float MinPower = 0.000125f;
    47 static float calculateNormalizationScale(ThreadSharedFloatArrayBufferList* response, size_t aLength, float sampleRate)
    48 {
    49     // Normalize by RMS power
    50     size_t numberOfChannels = response->GetChannels();
    52     float power = 0;
    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     }
    59     power = sqrt(power / (numberOfChannels * aLength));
    61     // Protect against accidental overload
    62     if (!IsFinite(power) || IsNaN(power) || power < MinPower)
    63         power = MinPower;
    65     float scale = 1 / power;
    67     scale *= powf(10, GainCalibration * 0.05f); // calibrate to make perceived volume same as unprocessed
    69     // Scale depends on sample-rate.
    70     if (sampleRate)
    71         scale *= GainCalibrationSampleRate / sampleRate;
    73     // True-stereo compensation
    74     if (response->GetChannels() == 4)
    75         scale *= 0.5f;
    77     return scale;
    78 }
    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;
    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;
    90     if (normalize) {
    91         scale = calculateNormalizationScale(impulseResponse, impulseResponseBufferLength, sampleRate);
    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     }
   104     initialize(irChannels, impulseResponseBufferLength, renderSliceSize,
   105                maxFFTSize, numberOfChannels, useBackgroundThreads);
   106 }
   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     }
   118     amount += m_tempBuffer.SizeOfExcludingThis(aMallocSizeOf, false);
   119     return amount;
   120 }
   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;
   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);
   133     int convolverRenderPhase = 0;
   134     for (size_t i = 0; i < numResponseChannels; ++i) {
   135         const float* channel = impulseResponseBuffer[i];
   136         size_t length = impulseResponseBufferLength;
   138         nsAutoPtr<ReverbConvolver> convolver(new ReverbConvolver(channel, length, renderSliceSize, maxFFTSize, convolverRenderPhase, useBackgroundThreads));
   139         m_convolvers.AppendElement(convolver.forget());
   141         convolverRenderPhase += renderSliceSize;
   142     }
   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 }
   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);
   159     MOZ_ASSERT(isSafeToProcess);
   160     if (!isSafeToProcess)
   161         return;
   163     // For now only handle mono or stereo output
   164     MOZ_ASSERT(destinationBus->mChannelData.Length() <= 2);
   166     float* destinationChannelL = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[0]));
   167     const float* sourceBusL = static_cast<const float*>(sourceBus->mChannelData[0]);
   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();
   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);
   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]));
   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]));
   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);
   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);
   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]));
   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]));
   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);
   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);
   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 }
   243 void Reverb::reset()
   244 {
   245     for (size_t i = 0; i < m_convolvers.Length(); ++i)
   246         m_convolvers[i]->reset();
   247 }
   249 size_t Reverb::latencyFrames() const
   250 {
   251     return !m_convolvers.IsEmpty() ? m_convolvers[0]->latencyFrames() : 0;
   252 }
   254 } // namespace WebCore

mercurial