tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Reverb.cpp (11118B)


      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 
     31 #include <math.h>
     32 
     33 #include "ReverbConvolver.h"
     34 #include "ReverbConvolverStage.h"
     35 
     36 using namespace mozilla;
     37 
     38 namespace WebCore {
     39 
     40 // Empirical gain calibration tested across many impulse responses to ensure
     41 // perceived volume is same as dry (unprocessed) signal
     42 const float GainCalibration = 0.00125f;
     43 const float GainCalibrationSampleRate = 44100;
     44 
     45 // A minimum power value to when normalizing a silent (or very quiet) impulse
     46 // response
     47 const float MinPower = 0.000125f;
     48 
     49 static float calculateNormalizationScale(const nsTArray<const float*>& response,
     50                                         size_t aLength, float sampleRate) {
     51  // Normalize by RMS power
     52  size_t numberOfChannels = response.Length();
     53 
     54  float power = 0;
     55 
     56  for (size_t i = 0; i < numberOfChannels; ++i) {
     57    float channelPower = AudioBufferSumOfSquares(response[i], aLength);
     58    power += channelPower;
     59  }
     60 
     61  power = sqrt(power / (numberOfChannels * aLength));
     62 
     63  // Protect against accidental overload
     64  if (!std::isfinite(power) || std::isnan(power) || power < MinPower)
     65    power = MinPower;
     66 
     67  float scale = 1 / power;
     68 
     69  scale *= GainCalibration;  // calibrate to make perceived volume same as
     70                             // unprocessed
     71 
     72  // Scale depends on sample-rate.
     73  if (sampleRate) scale *= GainCalibrationSampleRate / sampleRate;
     74 
     75  // True-stereo compensation
     76  if (numberOfChannels == 4) scale *= 0.5f;
     77 
     78  return scale;
     79 }
     80 
     81 Reverb::Reverb(const AudioChunk& impulseResponse, size_t maxFFTSize,
     82               bool useBackgroundThreads, bool normalize, float sampleRate,
     83               bool* aAllocationFailure) {
     84  MOZ_ASSERT(aAllocationFailure);
     85  size_t impulseResponseBufferLength = impulseResponse.mDuration;
     86  float scale = impulseResponse.mVolume;
     87 
     88  CopyableAutoTArray<const float*, 4> irChannels;
     89  irChannels.AppendElements(impulseResponse.ChannelData<float>());
     90  AutoTArray<float, 1024> tempBuf;
     91 
     92  if (normalize) {
     93    scale = calculateNormalizationScale(irChannels, impulseResponseBufferLength,
     94                                        sampleRate);
     95  }
     96 
     97  if (scale != 1.0f) {
     98    bool rv = tempBuf.SetLength(
     99        irChannels.Length() * impulseResponseBufferLength, mozilla::fallible);
    100    *aAllocationFailure = !rv;
    101    if (*aAllocationFailure) {
    102      return;
    103    }
    104 
    105    for (uint32_t i = 0; i < irChannels.Length(); ++i) {
    106      float* buf = &tempBuf[i * impulseResponseBufferLength];
    107      AudioBufferCopyWithScale(irChannels[i], scale, buf,
    108                               impulseResponseBufferLength);
    109      irChannels[i] = buf;
    110    }
    111  }
    112 
    113  *aAllocationFailure = !initialize(irChannels, impulseResponseBufferLength,
    114                                    maxFFTSize, useBackgroundThreads);
    115 }
    116 
    117 size_t Reverb::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
    118  size_t amount = aMallocSizeOf(this);
    119  amount += m_convolvers.ShallowSizeOfExcludingThis(aMallocSizeOf);
    120  for (size_t i = 0; i < m_convolvers.Length(); i++) {
    121    if (m_convolvers[i]) {
    122      amount += m_convolvers[i]->sizeOfIncludingThis(aMallocSizeOf);
    123    }
    124  }
    125 
    126  amount += m_tempBuffer.SizeOfExcludingThis(aMallocSizeOf, false);
    127  return amount;
    128 }
    129 
    130 bool Reverb::initialize(const nsTArray<const float*>& impulseResponseBuffer,
    131                        size_t impulseResponseBufferLength, size_t maxFFTSize,
    132                        bool useBackgroundThreads) {
    133  m_impulseResponseLength = impulseResponseBufferLength;
    134 
    135  // The reverb can handle a mono impulse response and still do stereo
    136  // processing
    137  size_t numResponseChannels = impulseResponseBuffer.Length();
    138  MOZ_ASSERT(numResponseChannels > 0);
    139  // The number of convolvers required is at least the number of audio
    140  // channels.  Even if there is initially only one audio channel, another
    141  // may be added later, and so a second convolver is created now while the
    142  // impulse response is available.
    143  size_t numConvolvers = std::max<size_t>(numResponseChannels, 2);
    144  m_convolvers.SetCapacity(numConvolvers);
    145 
    146  int convolverRenderPhase = 0;
    147  for (size_t i = 0; i < numConvolvers; ++i) {
    148    size_t channelIndex = i < numResponseChannels ? i : 0;
    149    const float* channel = impulseResponseBuffer[channelIndex];
    150    size_t length = impulseResponseBufferLength;
    151 
    152    bool allocationFailure;
    153    UniquePtr<ReverbConvolver> convolver(
    154        new ReverbConvolver(channel, length, maxFFTSize, convolverRenderPhase,
    155                            useBackgroundThreads, &allocationFailure));
    156    if (allocationFailure) {
    157      return false;
    158    }
    159    m_convolvers.AppendElement(std::move(convolver));
    160 
    161    convolverRenderPhase += WEBAUDIO_BLOCK_SIZE;
    162  }
    163 
    164  // For "True" stereo processing we allocate a temporary buffer to avoid
    165  // repeatedly allocating it in the process() method. It can be bad to allocate
    166  // memory in a real-time thread.
    167  if (numResponseChannels == 4) {
    168    m_tempBuffer.AllocateChannels(2);
    169    WriteZeroesToAudioBlock(&m_tempBuffer, 0, WEBAUDIO_BLOCK_SIZE);
    170  }
    171  return true;
    172 }
    173 
    174 void Reverb::process(const AudioBlock* sourceBus, AudioBlock* destinationBus) {
    175  // Do a fairly comprehensive sanity check.
    176  // If these conditions are satisfied, all of the source and destination
    177  // pointers will be valid for the various matrixing cases.
    178  bool isSafeToProcess =
    179      sourceBus && destinationBus && sourceBus->ChannelCount() > 0 &&
    180      destinationBus->mChannelData.Length() > 0 &&
    181      WEBAUDIO_BLOCK_SIZE <= MaxFrameSize &&
    182      WEBAUDIO_BLOCK_SIZE <= size_t(sourceBus->GetDuration()) &&
    183      WEBAUDIO_BLOCK_SIZE <= size_t(destinationBus->GetDuration());
    184 
    185  MOZ_ASSERT(isSafeToProcess);
    186  if (!isSafeToProcess) return;
    187 
    188  // For now only handle mono or stereo output
    189  MOZ_ASSERT(destinationBus->ChannelCount() <= 2);
    190 
    191  float* destinationChannelL =
    192      static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[0]));
    193  const float* sourceBusL =
    194      static_cast<const float*>(sourceBus->mChannelData[0]);
    195 
    196  // Handle input -> output matrixing...
    197  size_t numInputChannels = sourceBus->ChannelCount();
    198  size_t numOutputChannels = destinationBus->ChannelCount();
    199  size_t numReverbChannels = m_convolvers.Length();
    200 
    201  if (numInputChannels == 2 && numReverbChannels == 2 &&
    202      numOutputChannels == 2) {
    203    // 2 -> 2 -> 2
    204    const float* sourceBusR =
    205        static_cast<const float*>(sourceBus->mChannelData[1]);
    206    float* destinationChannelR =
    207        static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
    208    m_convolvers[0]->process(sourceBusL, destinationChannelL);
    209    m_convolvers[1]->process(sourceBusR, destinationChannelR);
    210  } else if (numInputChannels == 1 && numOutputChannels == 2 &&
    211             numReverbChannels == 2) {
    212    // 1 -> 2 -> 2
    213    for (int i = 0; i < 2; ++i) {
    214      float* destinationChannel = static_cast<float*>(
    215          const_cast<void*>(destinationBus->mChannelData[i]));
    216      m_convolvers[i]->process(sourceBusL, destinationChannel);
    217    }
    218  } else if (numInputChannels == 1 && numOutputChannels == 1) {
    219    // 1 -> 1 -> 1 (Only one of the convolvers is used.)
    220    m_convolvers[0]->process(sourceBusL, destinationChannelL);
    221  } else if (numInputChannels == 2 && numReverbChannels == 4 &&
    222             numOutputChannels == 2) {
    223    // 2 -> 4 -> 2 ("True" stereo)
    224    const float* sourceBusR =
    225        static_cast<const float*>(sourceBus->mChannelData[1]);
    226    float* destinationChannelR =
    227        static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
    228 
    229    float* tempChannelL =
    230        static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[0]));
    231    float* tempChannelR =
    232        static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[1]));
    233 
    234    // Process left virtual source
    235    m_convolvers[0]->process(sourceBusL, destinationChannelL);
    236    m_convolvers[1]->process(sourceBusL, destinationChannelR);
    237 
    238    // Process right virtual source
    239    m_convolvers[2]->process(sourceBusR, tempChannelL);
    240    m_convolvers[3]->process(sourceBusR, tempChannelR);
    241 
    242    AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL,
    243                            sourceBus->GetDuration());
    244    AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR,
    245                            sourceBus->GetDuration());
    246  } else if (numInputChannels == 1 && numReverbChannels == 4 &&
    247             numOutputChannels == 2) {
    248    // 1 -> 4 -> 2 (Processing mono with "True" stereo impulse response)
    249    // This is an inefficient use of a four-channel impulse response, but we
    250    // should handle the case.
    251    float* destinationChannelR =
    252        static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
    253 
    254    float* tempChannelL =
    255        static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[0]));
    256    float* tempChannelR =
    257        static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[1]));
    258 
    259    // Process left virtual source
    260    m_convolvers[0]->process(sourceBusL, destinationChannelL);
    261    m_convolvers[1]->process(sourceBusL, destinationChannelR);
    262 
    263    // Process right virtual source
    264    m_convolvers[2]->process(sourceBusL, tempChannelL);
    265    m_convolvers[3]->process(sourceBusL, tempChannelR);
    266 
    267    AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL,
    268                            sourceBus->GetDuration());
    269    AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR,
    270                            sourceBus->GetDuration());
    271  } else {
    272    MOZ_ASSERT_UNREACHABLE("Unexpected Reverb configuration");
    273    destinationBus->SetNull(destinationBus->GetDuration());
    274  }
    275 }
    276 
    277 }  // namespace WebCore