tor-browser

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

DynamicsCompressor.cpp (12232B)


      1 /*
      2 * Copyright (C) 2011 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 "DynamicsCompressor.h"
     30 
     31 #include <cmath>
     32 
     33 #include "AlignmentUtils.h"
     34 #include "AudioBlock.h"
     35 #include "AudioNodeEngine.h"
     36 #include "nsDebug.h"
     37 
     38 using mozilla::AudioBlockCopyChannelWithScale;
     39 using mozilla::WEBAUDIO_BLOCK_SIZE;
     40 
     41 namespace WebCore {
     42 
     43 DynamicsCompressor::DynamicsCompressor(float sampleRate,
     44                                       unsigned numberOfChannels)
     45    : m_numberOfChannels(numberOfChannels),
     46      m_sampleRate(sampleRate),
     47      m_compressor(sampleRate, numberOfChannels) {
     48  // Uninitialized state - for parameter recalculation.
     49  m_lastFilterStageRatio = -1;
     50  m_lastAnchor = -1;
     51  m_lastFilterStageGain = -1;
     52 
     53  setNumberOfChannels(numberOfChannels);
     54  initializeParameters();
     55 }
     56 
     57 size_t DynamicsCompressor::sizeOfIncludingThis(
     58    mozilla::MallocSizeOf aMallocSizeOf) const {
     59  size_t amount = aMallocSizeOf(this);
     60  amount += m_preFilterPacks.ShallowSizeOfExcludingThis(aMallocSizeOf);
     61  for (size_t i = 0; i < m_preFilterPacks.Length(); i++) {
     62    if (m_preFilterPacks[i]) {
     63      amount += m_preFilterPacks[i]->sizeOfIncludingThis(aMallocSizeOf);
     64    }
     65  }
     66 
     67  amount += m_postFilterPacks.ShallowSizeOfExcludingThis(aMallocSizeOf);
     68  for (size_t i = 0; i < m_postFilterPacks.Length(); i++) {
     69    if (m_postFilterPacks[i]) {
     70      amount += m_postFilterPacks[i]->sizeOfIncludingThis(aMallocSizeOf);
     71    }
     72  }
     73 
     74  amount += aMallocSizeOf(m_sourceChannels.get());
     75  amount += aMallocSizeOf(m_destinationChannels.get());
     76  amount += m_compressor.sizeOfExcludingThis(aMallocSizeOf);
     77  return amount;
     78 }
     79 
     80 void DynamicsCompressor::setParameterValue(unsigned parameterID, float value) {
     81  MOZ_ASSERT(parameterID < ParamLast);
     82  if (parameterID < ParamLast) m_parameters[parameterID] = value;
     83 }
     84 
     85 void DynamicsCompressor::initializeParameters() {
     86  // Initializes compressor to default values.
     87 
     88  m_parameters[ParamThreshold] = -24;    // dB
     89  m_parameters[ParamKnee] = 30;          // dB
     90  m_parameters[ParamRatio] = 12;         // unit-less
     91  m_parameters[ParamAttack] = 0.003f;    // seconds
     92  m_parameters[ParamRelease] = 0.250f;   // seconds
     93  m_parameters[ParamPreDelay] = 0.006f;  // seconds
     94 
     95  // Release zone values 0 -> 1.
     96  m_parameters[ParamReleaseZone1] = 0.09f;
     97  m_parameters[ParamReleaseZone2] = 0.16f;
     98  m_parameters[ParamReleaseZone3] = 0.42f;
     99  m_parameters[ParamReleaseZone4] = 0.98f;
    100 
    101  m_parameters[ParamFilterStageGain] = 4.4f;  // dB
    102  m_parameters[ParamFilterStageRatio] = 2;
    103  m_parameters[ParamFilterAnchor] = 15000 / nyquist();
    104 
    105  m_parameters[ParamPostGain] = 0;   // dB
    106  m_parameters[ParamReduction] = 0;  // dB
    107 
    108  // Linear crossfade (0 -> 1).
    109  m_parameters[ParamEffectBlend] = 1;
    110 }
    111 
    112 float DynamicsCompressor::parameterValue(unsigned parameterID) {
    113  MOZ_ASSERT(parameterID < ParamLast);
    114  return m_parameters[parameterID];
    115 }
    116 
    117 void DynamicsCompressor::setEmphasisStageParameters(
    118    unsigned stageIndex, float gain, float normalizedFrequency /* 0 -> 1 */) {
    119  float gk = 1 - gain / 20;
    120  float f1 = normalizedFrequency * gk;
    121  float f2 = normalizedFrequency / gk;
    122  float r1 = fdlibm_expf(-f1 * M_PI);
    123  float r2 = fdlibm_expf(-f2 * M_PI);
    124 
    125  MOZ_ASSERT(m_numberOfChannels == m_preFilterPacks.Length());
    126 
    127  for (unsigned i = 0; i < m_numberOfChannels; ++i) {
    128    // Set pre-filter zero and pole to create an emphasis filter.
    129    ZeroPole& preFilter = m_preFilterPacks[i]->filters[stageIndex];
    130    preFilter.setZero(r1);
    131    preFilter.setPole(r2);
    132 
    133    // Set post-filter with zero and pole reversed to create the de-emphasis
    134    // filter. If there were no compressor kernel in between, they would cancel
    135    // each other out (allpass filter).
    136    ZeroPole& postFilter = m_postFilterPacks[i]->filters[stageIndex];
    137    postFilter.setZero(r2);
    138    postFilter.setPole(r1);
    139  }
    140 }
    141 
    142 void DynamicsCompressor::setEmphasisParameters(float gain, float anchorFreq,
    143                                               float filterStageRatio) {
    144  setEmphasisStageParameters(0, gain, anchorFreq);
    145  setEmphasisStageParameters(1, gain, anchorFreq / filterStageRatio);
    146  setEmphasisStageParameters(
    147      2, gain, anchorFreq / (filterStageRatio * filterStageRatio));
    148  setEmphasisStageParameters(
    149      3, gain,
    150      anchorFreq / (filterStageRatio * filterStageRatio * filterStageRatio));
    151 }
    152 
    153 void DynamicsCompressor::process(const AudioBlock* sourceChunk,
    154                                 AudioBlock* destinationChunk,
    155                                 unsigned framesToProcess) {
    156  // Though numberOfChannels is retrived from destinationBus, we still name it
    157  // numberOfChannels instead of numberOfDestinationChannels. It's because we
    158  // internally match sourceChannels's size to destinationBus by channel up/down
    159  // mix. Thus we need numberOfChannels to do the loop work for both
    160  // m_sourceChannels and m_destinationChannels.
    161 
    162  unsigned numberOfChannels = destinationChunk->ChannelCount();
    163  unsigned numberOfSourceChannels = sourceChunk->ChannelCount();
    164 
    165  MOZ_ASSERT(numberOfChannels == m_numberOfChannels && numberOfSourceChannels);
    166 
    167  if (numberOfChannels != m_numberOfChannels || !numberOfSourceChannels) {
    168    destinationChunk->SetNull(WEBAUDIO_BLOCK_SIZE);
    169    return;
    170  }
    171 
    172  switch (numberOfChannels) {
    173    case 2:  // stereo
    174      m_sourceChannels[0] =
    175          static_cast<const float*>(sourceChunk->mChannelData[0]);
    176 
    177      if (numberOfSourceChannels > 1)
    178        m_sourceChannels[1] =
    179            static_cast<const float*>(sourceChunk->mChannelData[1]);
    180      else
    181        // Simply duplicate mono channel input data to right channel for stereo
    182        // processing.
    183        m_sourceChannels[1] = m_sourceChannels[0];
    184 
    185      break;
    186    case 1:
    187      m_sourceChannels[0] =
    188          static_cast<const float*>(sourceChunk->mChannelData[0]);
    189      break;
    190    default:
    191      MOZ_CRASH("not supported.");
    192  }
    193 
    194  for (unsigned i = 0; i < numberOfChannels; ++i)
    195    m_destinationChannels[i] = const_cast<float*>(
    196        static_cast<const float*>(destinationChunk->mChannelData[i]));
    197 
    198  float filterStageGain = parameterValue(ParamFilterStageGain);
    199  float filterStageRatio = parameterValue(ParamFilterStageRatio);
    200  float anchor = parameterValue(ParamFilterAnchor);
    201 
    202  if (filterStageGain != m_lastFilterStageGain ||
    203      filterStageRatio != m_lastFilterStageRatio || anchor != m_lastAnchor) {
    204    m_lastFilterStageGain = filterStageGain;
    205    m_lastFilterStageRatio = filterStageRatio;
    206    m_lastAnchor = anchor;
    207 
    208    setEmphasisParameters(filterStageGain, anchor, filterStageRatio);
    209  }
    210 
    211  float sourceWithVolume[WEBAUDIO_BLOCK_SIZE + 4];
    212  float* alignedSourceWithVolume = ALIGNED16(sourceWithVolume);
    213  ASSERT_ALIGNED16(alignedSourceWithVolume);
    214 
    215  // Apply pre-emphasis filter.
    216  // Note that the final three stages are computed in-place in the destination
    217  // buffer.
    218  for (unsigned i = 0; i < numberOfChannels; ++i) {
    219    const float* sourceData;
    220    if (sourceChunk->mVolume == 1.0f) {
    221      // Fast path, the volume scale doesn't need to get taken into account
    222      sourceData = m_sourceChannels[i];
    223    } else {
    224      AudioBlockCopyChannelWithScale(m_sourceChannels[i], sourceChunk->mVolume,
    225                                     alignedSourceWithVolume);
    226      sourceData = alignedSourceWithVolume;
    227    }
    228 
    229    float* destinationData = m_destinationChannels[i];
    230    ZeroPole* preFilters = m_preFilterPacks[i]->filters;
    231 
    232    preFilters[0].process(sourceData, destinationData, framesToProcess);
    233    preFilters[1].process(destinationData, destinationData, framesToProcess);
    234    preFilters[2].process(destinationData, destinationData, framesToProcess);
    235    preFilters[3].process(destinationData, destinationData, framesToProcess);
    236  }
    237 
    238  float dbThreshold = parameterValue(ParamThreshold);
    239  float dbKnee = parameterValue(ParamKnee);
    240  float ratio = parameterValue(ParamRatio);
    241  float attackTime = parameterValue(ParamAttack);
    242  float releaseTime = parameterValue(ParamRelease);
    243  float preDelayTime = parameterValue(ParamPreDelay);
    244 
    245  // This is effectively a master volume on the compressed signal
    246  // (pre-blending).
    247  float dbPostGain = parameterValue(ParamPostGain);
    248 
    249  // Linear blending value from dry to completely processed (0 -> 1)
    250  // 0 means the signal is completely unprocessed.
    251  // 1 mixes in only the compressed signal.
    252  float effectBlend = parameterValue(ParamEffectBlend);
    253 
    254  float releaseZone1 = parameterValue(ParamReleaseZone1);
    255  float releaseZone2 = parameterValue(ParamReleaseZone2);
    256  float releaseZone3 = parameterValue(ParamReleaseZone3);
    257  float releaseZone4 = parameterValue(ParamReleaseZone4);
    258 
    259  // Apply compression to the pre-filtered signal.
    260  // The processing is performed in place.
    261  m_compressor.process(m_destinationChannels.get(), m_destinationChannels.get(),
    262                       numberOfChannels, framesToProcess,
    263 
    264                       dbThreshold, dbKnee, ratio, attackTime, releaseTime,
    265                       preDelayTime, dbPostGain, effectBlend,
    266 
    267                       releaseZone1, releaseZone2, releaseZone3, releaseZone4);
    268 
    269  // Update the compression amount.
    270  setParameterValue(ParamReduction, m_compressor.meteringGain());
    271 
    272  // Apply de-emphasis filter.
    273  for (unsigned i = 0; i < numberOfChannels; ++i) {
    274    float* destinationData = m_destinationChannels[i];
    275    ZeroPole* postFilters = m_postFilterPacks[i]->filters;
    276 
    277    postFilters[0].process(destinationData, destinationData, framesToProcess);
    278    postFilters[1].process(destinationData, destinationData, framesToProcess);
    279    postFilters[2].process(destinationData, destinationData, framesToProcess);
    280    postFilters[3].process(destinationData, destinationData, framesToProcess);
    281  }
    282 }
    283 
    284 void DynamicsCompressor::reset() {
    285  m_lastFilterStageRatio = -1;  // for recalc
    286  m_lastAnchor = -1;
    287  m_lastFilterStageGain = -1;
    288 
    289  for (unsigned channel = 0; channel < m_numberOfChannels; ++channel) {
    290    for (unsigned stageIndex = 0; stageIndex < 4; ++stageIndex) {
    291      m_preFilterPacks[channel]->filters[stageIndex].reset();
    292      m_postFilterPacks[channel]->filters[stageIndex].reset();
    293    }
    294  }
    295 
    296  m_compressor.reset();
    297 }
    298 
    299 void DynamicsCompressor::setNumberOfChannels(unsigned numberOfChannels) {
    300  if (m_preFilterPacks.Length() == numberOfChannels) return;
    301 
    302  m_preFilterPacks.Clear();
    303  m_postFilterPacks.Clear();
    304  for (unsigned i = 0; i < numberOfChannels; ++i) {
    305    m_preFilterPacks.AppendElement(new ZeroPoleFilterPack4());
    306    m_postFilterPacks.AppendElement(new ZeroPoleFilterPack4());
    307  }
    308 
    309  m_sourceChannels = mozilla::MakeUnique<const float*[]>(numberOfChannels);
    310  m_destinationChannels = mozilla::MakeUnique<float*[]>(numberOfChannels);
    311 
    312  m_compressor.setNumberOfChannels(numberOfChannels);
    313  m_numberOfChannels = numberOfChannels;
    314 }
    315 
    316 }  // namespace WebCore