tor-browser

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

DynamicsCompressorNode.cpp (8271B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "DynamicsCompressorNode.h"
      8 
      9 #include "AudioDestinationNode.h"
     10 #include "AudioNodeEngine.h"
     11 #include "AudioNodeTrack.h"
     12 #include "Tracing.h"
     13 #include "WebAudioUtils.h"
     14 #include "blink/DynamicsCompressor.h"
     15 #include "mozilla/dom/DynamicsCompressorNodeBinding.h"
     16 
     17 using WebCore::DynamicsCompressor;
     18 
     19 namespace mozilla::dom {
     20 
     21 NS_IMPL_CYCLE_COLLECTION_INHERITED(DynamicsCompressorNode, AudioNode,
     22                                   mThreshold, mKnee, mRatio, mAttack, mRelease)
     23 
     24 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DynamicsCompressorNode)
     25 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
     26 
     27 NS_IMPL_ADDREF_INHERITED(DynamicsCompressorNode, AudioNode)
     28 NS_IMPL_RELEASE_INHERITED(DynamicsCompressorNode, AudioNode)
     29 
     30 class DynamicsCompressorNodeEngine final : public AudioNodeEngine {
     31 public:
     32  explicit DynamicsCompressorNodeEngine(AudioNode* aNode,
     33                                        AudioDestinationNode* aDestination)
     34      : AudioNodeEngine(aNode),
     35        mDestination(aDestination->Track())
     36        // Keep the default value in sync with the default value in
     37        // DynamicsCompressorNode::DynamicsCompressorNode.
     38        ,
     39        mThreshold(-24.f),
     40        mKnee(30.f),
     41        mRatio(12.f),
     42        mAttack(0.003f),
     43        mRelease(0.25f),
     44        mCompressor(new DynamicsCompressor(mDestination->mSampleRate, 2)) {}
     45 
     46  enum Parameters { THRESHOLD, KNEE, RATIO, ATTACK, RELEASE };
     47  void RecvTimelineEvent(uint32_t aIndex, AudioParamEvent& aEvent) override {
     48    MOZ_ASSERT(mDestination);
     49 
     50    aEvent.ConvertToTicks(mDestination);
     51 
     52    switch (aIndex) {
     53      case THRESHOLD:
     54        mThreshold.InsertEvent<int64_t>(aEvent);
     55        break;
     56      case KNEE:
     57        mKnee.InsertEvent<int64_t>(aEvent);
     58        break;
     59      case RATIO:
     60        mRatio.InsertEvent<int64_t>(aEvent);
     61        break;
     62      case ATTACK:
     63        mAttack.InsertEvent<int64_t>(aEvent);
     64        break;
     65      case RELEASE:
     66        mRelease.InsertEvent<int64_t>(aEvent);
     67        break;
     68      default:
     69        NS_ERROR("Bad DynamicsCompresssorNodeEngine TimelineParameter");
     70    }
     71  }
     72 
     73  void ProcessBlock(AudioNodeTrack* aTrack, GraphTime aFrom,
     74                    const AudioBlock& aInput, AudioBlock* aOutput,
     75                    bool* aFinished) override {
     76    TRACE("DynamicsCompressorNodeEngine::ProcessBlock");
     77    if (aInput.IsNull()) {
     78      // Just output silence
     79      *aOutput = aInput;
     80      return;
     81    }
     82 
     83    const uint32_t channelCount = aInput.ChannelCount();
     84    if (mCompressor->numberOfChannels() != channelCount) {
     85      // Create a new compressor object with a new channel count
     86      mCompressor = MakeUnique<WebCore::DynamicsCompressor>(
     87          aTrack->mSampleRate, aInput.ChannelCount());
     88    }
     89 
     90    TrackTime pos = mDestination->GraphTimeToTrackTime(aFrom);
     91    mCompressor->setParameterValue(DynamicsCompressor::ParamThreshold,
     92                                   mThreshold.GetValueAtTime(pos));
     93    mCompressor->setParameterValue(DynamicsCompressor::ParamKnee,
     94                                   mKnee.GetValueAtTime(pos));
     95    mCompressor->setParameterValue(DynamicsCompressor::ParamRatio,
     96                                   mRatio.GetValueAtTime(pos));
     97    mCompressor->setParameterValue(DynamicsCompressor::ParamAttack,
     98                                   mAttack.GetValueAtTime(pos));
     99    mCompressor->setParameterValue(DynamicsCompressor::ParamRelease,
    100                                   mRelease.GetValueAtTime(pos));
    101 
    102    aOutput->AllocateChannels(channelCount);
    103    mCompressor->process(&aInput, aOutput, aInput.GetDuration());
    104 
    105    SendReductionParamToMainThread(
    106        aTrack,
    107        mCompressor->parameterValue(DynamicsCompressor::ParamReduction));
    108  }
    109 
    110  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override {
    111    // Not owned:
    112    // - mDestination (probably)
    113    // - Don't count the AudioParamTimelines, their inner refs are owned by the
    114    // AudioNode.
    115    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
    116    amount += mCompressor->sizeOfIncludingThis(aMallocSizeOf);
    117    return amount;
    118  }
    119 
    120  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
    121    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
    122  }
    123 
    124 private:
    125  void SendReductionParamToMainThread(AudioNodeTrack* aTrack,
    126                                      float aReduction) {
    127    MOZ_ASSERT(!NS_IsMainThread());
    128 
    129    class Command final : public Runnable {
    130     public:
    131      Command(AudioNodeTrack* aTrack, float aReduction)
    132          : mozilla::Runnable("Command"),
    133            mTrack(aTrack),
    134            mReduction(aReduction) {}
    135 
    136      NS_IMETHOD Run() override {
    137        RefPtr<DynamicsCompressorNode> node =
    138            static_cast<DynamicsCompressorNode*>(
    139                mTrack->Engine()->NodeMainThread());
    140        if (node) {
    141          node->SetReduction(mReduction);
    142        }
    143        return NS_OK;
    144      }
    145 
    146     private:
    147      RefPtr<AudioNodeTrack> mTrack;
    148      float mReduction;
    149    };
    150 
    151    AbstractThread::MainThread()->Dispatch(
    152        do_AddRef(new Command(aTrack, aReduction)));
    153  }
    154 
    155 private:
    156  RefPtr<AudioNodeTrack> mDestination;
    157  AudioParamTimeline mThreshold;
    158  AudioParamTimeline mKnee;
    159  AudioParamTimeline mRatio;
    160  AudioParamTimeline mAttack;
    161  AudioParamTimeline mRelease;
    162  UniquePtr<DynamicsCompressor> mCompressor;
    163 };
    164 
    165 DynamicsCompressorNode::DynamicsCompressorNode(AudioContext* aContext)
    166    : AudioNode(aContext, 2, ChannelCountMode::Clamped_max,
    167                ChannelInterpretation::Speakers),
    168      mReduction(0) {
    169  mThreshold = CreateAudioParam(DynamicsCompressorNodeEngine::THRESHOLD,
    170                                u"threshold"_ns, -24.f, -100.f, 0.f);
    171  mKnee = CreateAudioParam(DynamicsCompressorNodeEngine::KNEE, u"knee"_ns, 30.f,
    172                           0.f, 40.f);
    173  mRatio = CreateAudioParam(DynamicsCompressorNodeEngine::RATIO, u"ratio"_ns,
    174                            12.f, 1.f, 20.f);
    175  mAttack = CreateAudioParam(DynamicsCompressorNodeEngine::ATTACK, u"attack"_ns,
    176                             0.003f, 0.f, 1.f);
    177  mRelease = CreateAudioParam(DynamicsCompressorNodeEngine::RELEASE,
    178                              u"release"_ns, 0.25f, 0.f, 1.f);
    179  DynamicsCompressorNodeEngine* engine =
    180      new DynamicsCompressorNodeEngine(this, aContext->Destination());
    181  mTrack = AudioNodeTrack::Create(
    182      aContext, engine, AudioNodeTrack::NO_TRACK_FLAGS, aContext->Graph());
    183 }
    184 
    185 /* static */
    186 already_AddRefed<DynamicsCompressorNode> DynamicsCompressorNode::Create(
    187    AudioContext& aAudioContext, const DynamicsCompressorOptions& aOptions,
    188    ErrorResult& aRv) {
    189  RefPtr<DynamicsCompressorNode> audioNode =
    190      new DynamicsCompressorNode(&aAudioContext);
    191 
    192  audioNode->Initialize(aOptions, aRv);
    193  if (NS_WARN_IF(aRv.Failed())) {
    194    return nullptr;
    195  }
    196 
    197  audioNode->Attack()->SetInitialValue(aOptions.mAttack);
    198  audioNode->Knee()->SetInitialValue(aOptions.mKnee);
    199  audioNode->Ratio()->SetInitialValue(aOptions.mRatio);
    200  audioNode->GetRelease()->SetInitialValue(aOptions.mRelease);
    201  audioNode->Threshold()->SetInitialValue(aOptions.mThreshold);
    202 
    203  return audioNode.forget();
    204 }
    205 
    206 size_t DynamicsCompressorNode::SizeOfExcludingThis(
    207    MallocSizeOf aMallocSizeOf) const {
    208  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
    209  amount += mThreshold->SizeOfIncludingThis(aMallocSizeOf);
    210  amount += mKnee->SizeOfIncludingThis(aMallocSizeOf);
    211  amount += mRatio->SizeOfIncludingThis(aMallocSizeOf);
    212  amount += mAttack->SizeOfIncludingThis(aMallocSizeOf);
    213  amount += mRelease->SizeOfIncludingThis(aMallocSizeOf);
    214  return amount;
    215 }
    216 
    217 size_t DynamicsCompressorNode::SizeOfIncludingThis(
    218    MallocSizeOf aMallocSizeOf) const {
    219  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
    220 }
    221 
    222 JSObject* DynamicsCompressorNode::WrapObject(
    223    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
    224  return DynamicsCompressorNode_Binding::Wrap(aCx, this, aGivenProto);
    225 }
    226 
    227 }  // namespace mozilla::dom