tor-browser

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

DelayNode.cpp (8030B)


      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 "DelayNode.h"
      8 
      9 #include "AudioDestinationNode.h"
     10 #include "AudioNodeEngine.h"
     11 #include "AudioNodeTrack.h"
     12 #include "DelayBuffer.h"
     13 #include "PlayingRefChangeHandler.h"
     14 #include "Tracing.h"
     15 #include "WebAudioUtils.h"
     16 #include "mozilla/dom/DelayNodeBinding.h"
     17 
     18 namespace mozilla::dom {
     19 
     20 NS_IMPL_CYCLE_COLLECTION_INHERITED(DelayNode, AudioNode, mDelay)
     21 
     22 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DelayNode)
     23 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
     24 
     25 NS_IMPL_ADDREF_INHERITED(DelayNode, AudioNode)
     26 NS_IMPL_RELEASE_INHERITED(DelayNode, AudioNode)
     27 
     28 class DelayNodeEngine final : public AudioNodeEngine {
     29  typedef PlayingRefChangeHandler PlayingRefChanged;
     30 
     31 public:
     32  DelayNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination,
     33                  float aMaxDelayTicks)
     34      : AudioNodeEngine(aNode),
     35        mDestination(aDestination->Track())
     36        // Keep the default value in sync with the default value in
     37        // DelayNode::DelayNode.
     38        ,
     39        mDelay(0.f)
     40        // Use a smoothing range of 20ms
     41        ,
     42        mBuffer(
     43            std::max(aMaxDelayTicks, static_cast<float>(WEBAUDIO_BLOCK_SIZE))),
     44        mMaxDelay(aMaxDelayTicks),
     45        mHaveProducedBeforeInput(false),
     46        mLeftOverData(INT32_MIN) {}
     47 
     48  DelayNodeEngine* AsDelayNodeEngine() override { return this; }
     49 
     50  enum Parameters {
     51    DELAY,
     52  };
     53  void RecvTimelineEvent(uint32_t aIndex, AudioParamEvent& aEvent) override {
     54    MOZ_ASSERT(mDestination);
     55    aEvent.ConvertToTicks(mDestination);
     56 
     57    switch (aIndex) {
     58      case DELAY:
     59        mDelay.InsertEvent<int64_t>(aEvent);
     60        break;
     61      default:
     62        NS_ERROR("Bad DelayNodeEngine TimelineParameter");
     63    }
     64  }
     65 
     66  void ProcessBlock(AudioNodeTrack* aTrack, GraphTime aFrom,
     67                    const AudioBlock& aInput, AudioBlock* aOutput,
     68                    bool* aFinished) override {
     69    MOZ_ASSERT(aTrack->mSampleRate == mDestination->mSampleRate);
     70    TRACE("DelayNodeEngine::ProcessBlock");
     71 
     72    if (!aInput.IsSilentOrSubnormal()) {
     73      if (mLeftOverData <= 0) {
     74        RefPtr<PlayingRefChanged> refchanged =
     75            new PlayingRefChanged(aTrack, PlayingRefChanged::ADDREF);
     76        aTrack->Graph()->DispatchToMainThreadStableState(refchanged.forget());
     77      }
     78      mLeftOverData = mBuffer.MaxDelayTicks();
     79    } else if (mLeftOverData > 0) {
     80      mLeftOverData -= WEBAUDIO_BLOCK_SIZE;
     81    } else {
     82      if (mLeftOverData != INT32_MIN) {
     83        mLeftOverData = INT32_MIN;
     84        aTrack->ScheduleCheckForInactive();
     85 
     86        // Delete our buffered data now we no longer need it
     87        mBuffer.Reset();
     88 
     89        RefPtr<PlayingRefChanged> refchanged =
     90            new PlayingRefChanged(aTrack, PlayingRefChanged::RELEASE);
     91        aTrack->Graph()->DispatchToMainThreadStableState(refchanged.forget());
     92      }
     93      aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
     94      return;
     95    }
     96 
     97    mBuffer.Write(aInput);
     98 
     99    // Skip output update if mLastChunks has already been set by
    100    // ProduceBlockBeforeInput() when in a cycle.
    101    if (!mHaveProducedBeforeInput) {
    102      UpdateOutputBlock(aTrack, aFrom, aOutput, 0.0);
    103    }
    104    mHaveProducedBeforeInput = false;
    105    mBuffer.NextBlock();
    106  }
    107 
    108  void UpdateOutputBlock(AudioNodeTrack* aTrack, GraphTime aFrom,
    109                         AudioBlock* aOutput, float minDelay) {
    110    float maxDelay = mMaxDelay;
    111    float sampleRate = aTrack->mSampleRate;
    112    ChannelInterpretation channelInterpretation =
    113        aTrack->GetChannelInterpretation();
    114    if (mDelay.HasSimpleValue()) {
    115      // If this DelayNode is in a cycle, make sure the delay value is at least
    116      // one block, even if that is greater than maxDelay.
    117      float delayFrames = mDelay.GetValue() * sampleRate;
    118      float delayFramesClamped =
    119          std::max(minDelay, std::min(delayFrames, maxDelay));
    120      mBuffer.Read(delayFramesClamped, aOutput, channelInterpretation);
    121    } else {
    122      // Compute the delay values for the duration of the input AudioChunk
    123      // If this DelayNode is in a cycle, make sure the delay value is at least
    124      // one block.
    125      TrackTime tick = mDestination->GraphTimeToTrackTime(aFrom);
    126      float values[WEBAUDIO_BLOCK_SIZE];
    127      mDelay.GetValuesAtTime(tick, values, WEBAUDIO_BLOCK_SIZE);
    128 
    129      float computedDelay[WEBAUDIO_BLOCK_SIZE];
    130      for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
    131        float delayAtTick = values[counter] * sampleRate;
    132        float delayAtTickClamped =
    133            std::max(minDelay, std::min(delayAtTick, maxDelay));
    134        computedDelay[counter] = delayAtTickClamped;
    135      }
    136      mBuffer.Read(computedDelay, aOutput, channelInterpretation);
    137    }
    138  }
    139 
    140  void ProduceBlockBeforeInput(AudioNodeTrack* aTrack, GraphTime aFrom,
    141                               AudioBlock* aOutput) override {
    142    if (mLeftOverData <= 0) {
    143      aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
    144    } else {
    145      UpdateOutputBlock(aTrack, aFrom, aOutput, WEBAUDIO_BLOCK_SIZE);
    146    }
    147    mHaveProducedBeforeInput = true;
    148  }
    149 
    150  bool IsActive() const override { return mLeftOverData != INT32_MIN; }
    151 
    152  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override {
    153    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
    154    // Not owned:
    155    // - mDestination - probably not owned
    156    // - mDelay - shares ref with AudioNode, don't count
    157    amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
    158    return amount;
    159  }
    160 
    161  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
    162    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
    163  }
    164 
    165  RefPtr<AudioNodeTrack> mDestination;
    166  AudioParamTimeline mDelay;
    167  DelayBuffer mBuffer;
    168  float mMaxDelay;
    169  bool mHaveProducedBeforeInput;
    170  // How much data we have in our buffer which needs to be flushed out when our
    171  // inputs finish.
    172  int32_t mLeftOverData;
    173 };
    174 
    175 DelayNode::DelayNode(AudioContext* aContext, double aMaxDelay)
    176    : AudioNode(aContext, 2, ChannelCountMode::Max,
    177                ChannelInterpretation::Speakers) {
    178  mDelay = CreateAudioParam(DelayNodeEngine::DELAY, u"delayTime"_ns, 0.0f, 0.f,
    179                            aMaxDelay);
    180  DelayNodeEngine* engine = new DelayNodeEngine(
    181      this, aContext->Destination(), aContext->SampleRate() * aMaxDelay);
    182  mTrack = AudioNodeTrack::Create(
    183      aContext, engine, AudioNodeTrack::NO_TRACK_FLAGS, aContext->Graph());
    184 }
    185 
    186 /* static */
    187 already_AddRefed<DelayNode> DelayNode::Create(AudioContext& aAudioContext,
    188                                              const DelayOptions& aOptions,
    189                                              ErrorResult& aRv) {
    190  if (aOptions.mMaxDelayTime <= 0. || aOptions.mMaxDelayTime >= 180.) {
    191    aRv.ThrowNotSupportedError(
    192        nsPrintfCString("\"maxDelayTime\" (%g) is not in the range (0,180)",
    193                        aOptions.mMaxDelayTime));
    194    return nullptr;
    195  }
    196 
    197  RefPtr<DelayNode> audioNode =
    198      new DelayNode(&aAudioContext, aOptions.mMaxDelayTime);
    199 
    200  audioNode->Initialize(aOptions, aRv);
    201  if (NS_WARN_IF(aRv.Failed())) {
    202    return nullptr;
    203  }
    204 
    205  audioNode->DelayTime()->SetInitialValue(aOptions.mDelayTime);
    206  return audioNode.forget();
    207 }
    208 
    209 size_t DelayNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
    210  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
    211  amount += mDelay->SizeOfIncludingThis(aMallocSizeOf);
    212  return amount;
    213 }
    214 
    215 size_t DelayNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
    216  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
    217 }
    218 
    219 JSObject* DelayNode::WrapObject(JSContext* aCx,
    220                                JS::Handle<JSObject*> aGivenProto) {
    221  return DelayNode_Binding::Wrap(aCx, this, aGivenProto);
    222 }
    223 
    224 }  // namespace mozilla::dom