tor-browser

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

AudioNodeTrack.cpp (20539B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "AudioNodeTrack.h"
      7 
      8 #include "AlignmentUtils.h"
      9 #include "AudioChannelFormat.h"
     10 #include "AudioContext.h"
     11 #include "AudioNodeEngine.h"
     12 #include "AudioParamTimeline.h"
     13 #include "MediaTrackGraph.h"
     14 #include "MediaTrackListener.h"
     15 #include "ThreeDPoint.h"
     16 #include "Tracing.h"
     17 #include "blink/Reverb.h"
     18 #include "nsMathUtils.h"
     19 
     20 using namespace mozilla::dom;
     21 
     22 namespace mozilla {
     23 
     24 /**
     25 * An AudioNodeTrack produces a single audio track with ID
     26 * AUDIO_TRACK. This track has rate AudioContext::sIdealAudioRate
     27 * for regular audio contexts, and the rate requested by the web content
     28 * for offline audio contexts.
     29 * Each chunk in the track is a single block of WEBAUDIO_BLOCK_SIZE samples.
     30 * Note: This must be a different value than MEDIA_STREAM_DEST_TRACK_ID
     31 */
     32 
     33 AudioNodeTrack::AudioNodeTrack(AudioNodeEngine* aEngine, Flags aFlags,
     34                               TrackRate aSampleRate)
     35    : ProcessedMediaTrack(
     36          aSampleRate, MediaSegment::AUDIO,
     37          (aFlags & EXTERNAL_OUTPUT) ? new AudioSegment() : nullptr),
     38      mEngine(aEngine),
     39      mFlags(aFlags),
     40      mNumberOfInputChannels(2),
     41      mIsActive(aEngine->IsActive()),
     42      mMarkAsEndedAfterThisBlock(false),
     43      mAudioParamTrack(false),
     44      mPassThrough(false) {
     45  MOZ_ASSERT(NS_IsMainThread());
     46  mSuspendedCount = !(mIsActive || mFlags & EXTERNAL_OUTPUT);
     47  mChannelCountMode = ChannelCountMode::Max;
     48  mChannelInterpretation = ChannelInterpretation::Speakers;
     49  mLastChunks.SetLength(std::max(uint16_t(1), mEngine->OutputCount()));
     50  MOZ_COUNT_CTOR(AudioNodeTrack);
     51 }
     52 
     53 AudioNodeTrack::~AudioNodeTrack() {
     54  MOZ_ASSERT(mActiveInputCount == 0);
     55  MOZ_COUNT_DTOR(AudioNodeTrack);
     56 }
     57 
     58 void AudioNodeTrack::OnGraphThreadDone() { mEngine->OnGraphThreadDone(); }
     59 
     60 void AudioNodeTrack::DestroyImpl() {
     61  // These are graph thread objects, so clean up on graph thread.
     62  mInputChunks.Clear();
     63  mLastChunks.Clear();
     64 
     65  ProcessedMediaTrack::DestroyImpl();
     66 }
     67 
     68 /* static */
     69 already_AddRefed<AudioNodeTrack> AudioNodeTrack::Create(
     70    AudioContext* aCtx, AudioNodeEngine* aEngine, Flags aFlags,
     71    MediaTrackGraph* aGraph) {
     72  MOZ_ASSERT(NS_IsMainThread());
     73  MOZ_RELEASE_ASSERT(aGraph);
     74 
     75  // MediaRecorders use an AudioNodeTrack, but no AudioNode
     76  AudioNode* node = aEngine->NodeMainThread();
     77 
     78  RefPtr<AudioNodeTrack> track =
     79      new AudioNodeTrack(aEngine, aFlags, aGraph->GraphRate());
     80  if (node) {
     81    track->SetChannelMixingParametersImpl(node->ChannelCount(),
     82                                          node->ChannelCountModeValue(),
     83                                          node->ChannelInterpretationValue());
     84  }
     85  // All realtime tracks are initially suspended.
     86  // ApplyAudioContextOperation() is used to start tracks so that a new track
     87  // will not be started before the existing tracks, which may be awaiting an
     88  // AudioCallbackDriver to resume.
     89  bool isRealtime = !aCtx->IsOffline();
     90  track->mSuspendedCount += isRealtime;
     91  aGraph->AddTrack(track);
     92  if (isRealtime && !aCtx->ShouldSuspendNewTrack()) {
     93    nsTArray<RefPtr<mozilla::MediaTrack>> tracks;
     94    tracks.AppendElement(track);
     95    aGraph->ApplyAudioContextOperation(aCtx->DestinationTrack(),
     96                                       std::move(tracks),
     97                                       AudioContextOperation::Resume);
     98  }
     99  return track.forget();
    100 }
    101 
    102 size_t AudioNodeTrack::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
    103  size_t amount = 0;
    104 
    105  // Not reported:
    106  // - mEngine
    107 
    108  amount += ProcessedMediaTrack::SizeOfExcludingThis(aMallocSizeOf);
    109  amount += mLastChunks.ShallowSizeOfExcludingThis(aMallocSizeOf);
    110  for (size_t i = 0; i < mLastChunks.Length(); i++) {
    111    // NB: This is currently unshared only as there are instances of
    112    //     double reporting in DMD otherwise.
    113    amount += mLastChunks[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
    114  }
    115 
    116  return amount;
    117 }
    118 
    119 size_t AudioNodeTrack::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
    120  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
    121 }
    122 
    123 void AudioNodeTrack::SizeOfAudioNodesIncludingThis(
    124    MallocSizeOf aMallocSizeOf, AudioNodeSizes& aUsage) const {
    125  // Explicitly separate out the track memory.
    126  aUsage.mTrack = SizeOfIncludingThis(aMallocSizeOf);
    127 
    128  if (mEngine) {
    129    // This will fill out the rest of |aUsage|.
    130    mEngine->SizeOfIncludingThis(aMallocSizeOf, aUsage);
    131  }
    132 }
    133 
    134 void AudioNodeTrack::SetTrackTimeParameter(uint32_t aIndex,
    135                                           AudioContext* aContext,
    136                                           double aTrackTime) {
    137  QueueControlMessageWithNoShutdown(
    138      [self = RefPtr{this}, this, aIndex,
    139       relativeToTrack = RefPtr{aContext->DestinationTrack()}, aTrackTime] {
    140        TRACE("AudioNodeTrack::SetTrackTimeParameterImpl");
    141        SetTrackTimeParameterImpl(aIndex, relativeToTrack, aTrackTime);
    142      });
    143 }
    144 
    145 void AudioNodeTrack::SetTrackTimeParameterImpl(uint32_t aIndex,
    146                                               MediaTrack* aRelativeToTrack,
    147                                               double aTrackTime) {
    148  TrackTime ticks = aRelativeToTrack->SecondsToNearestTrackTime(aTrackTime);
    149  mEngine->SetTrackTimeParameter(aIndex, ticks);
    150 }
    151 
    152 void AudioNodeTrack::SetDoubleParameter(uint32_t aIndex, double aValue) {
    153  QueueControlMessageWithNoShutdown(
    154      [self = RefPtr{this}, this, aIndex, aValue] {
    155        TRACE("AudioNodeTrack::SetDoubleParameter");
    156        Engine()->SetDoubleParameter(aIndex, aValue);
    157      });
    158 }
    159 
    160 void AudioNodeTrack::SetInt32Parameter(uint32_t aIndex, int32_t aValue) {
    161  QueueControlMessageWithNoShutdown(
    162      [self = RefPtr{this}, this, aIndex, aValue] {
    163        TRACE("AudioNodeTrack::SetInt32Parameter");
    164        Engine()->SetInt32Parameter(aIndex, aValue);
    165      });
    166 }
    167 
    168 void AudioNodeTrack::SendTimelineEvent(uint32_t aIndex,
    169                                       const AudioParamEvent& aEvent) {
    170  QueueControlMessageWithNoShutdown(
    171      [self = RefPtr{this}, this, aIndex, event = aEvent]() mutable {
    172        TRACE("AudioNodeTrack::RecvTimelineEvent");
    173        Engine()->RecvTimelineEvent(aIndex, event);
    174      });
    175 }
    176 
    177 void AudioNodeTrack::SetBuffer(AudioChunk&& aBuffer) {
    178  QueueControlMessageWithNoShutdown(
    179      [self = RefPtr{this}, this, buffer = std::move(aBuffer)]() mutable {
    180        TRACE("AudioNodeTrack::SetBuffer");
    181        Engine()->SetBuffer(std::move(buffer));
    182      });
    183 }
    184 
    185 void AudioNodeTrack::SetReverb(WebCore::Reverb* aReverb,
    186                               uint32_t aImpulseChannelCount) {
    187  QueueControlMessageWithNoShutdown([self = RefPtr{this}, this,
    188                                     reverb = WrapUnique(aReverb),
    189                                     aImpulseChannelCount]() mutable {
    190    TRACE("AudioNodeTrack::SetReverb");
    191    Engine()->SetReverb(reverb.release(), aImpulseChannelCount);
    192  });
    193 }
    194 
    195 void AudioNodeTrack::SetRawArrayData(nsTArray<float>&& aData) {
    196  QueueControlMessageWithNoShutdown(
    197      [self = RefPtr{this}, this, data = std::move(aData)]() mutable {
    198        TRACE("AudioNodeTrack::SetRawArrayData");
    199        Engine()->SetRawArrayData(std::move(data));
    200      });
    201 }
    202 
    203 void AudioNodeTrack::SetChannelMixingParameters(
    204    uint32_t aNumberOfChannels, ChannelCountMode aChannelCountMode,
    205    ChannelInterpretation aChannelInterpretation) {
    206  QueueControlMessageWithNoShutdown([self = RefPtr{this}, this,
    207                                     aNumberOfChannels, aChannelCountMode,
    208                                     aChannelInterpretation] {
    209    TRACE("AudioNodeTrack::SetChannelMixingParameters");
    210    SetChannelMixingParametersImpl(aNumberOfChannels, aChannelCountMode,
    211                                   aChannelInterpretation);
    212  });
    213 }
    214 
    215 void AudioNodeTrack::SetPassThrough(bool aPassThrough) {
    216  QueueControlMessageWithNoShutdown([self = RefPtr{this}, this, aPassThrough] {
    217    TRACE("AudioNodeTrack::SetPassThrough");
    218    mPassThrough = aPassThrough;
    219  });
    220 }
    221 
    222 void AudioNodeTrack::SendRunnable(already_AddRefed<nsIRunnable> aRunnable) {
    223  QueueControlMessageWithNoShutdown([runnable = nsCOMPtr{aRunnable}] {
    224    TRACE("AudioNodeTrack::SendRunnable");
    225    runnable->Run();
    226  });
    227 }
    228 
    229 void AudioNodeTrack::SetChannelMixingParametersImpl(
    230    uint32_t aNumberOfChannels, ChannelCountMode aChannelCountMode,
    231    ChannelInterpretation aChannelInterpretation) {
    232  mNumberOfInputChannels = aNumberOfChannels;
    233  mChannelCountMode = aChannelCountMode;
    234  mChannelInterpretation = aChannelInterpretation;
    235 }
    236 
    237 uint32_t AudioNodeTrack::ComputedNumberOfChannels(uint32_t aInputChannelCount) {
    238  switch (mChannelCountMode) {
    239    case ChannelCountMode::Explicit:
    240      // Disregard the channel count we've calculated from inputs, and just use
    241      // mNumberOfInputChannels.
    242      return mNumberOfInputChannels;
    243    case ChannelCountMode::Clamped_max:
    244      // Clamp the computed output channel count to mNumberOfInputChannels.
    245      return std::min(aInputChannelCount, mNumberOfInputChannels);
    246    default:
    247    case ChannelCountMode::Max:
    248      // Nothing to do here, just shut up the compiler warning.
    249      return aInputChannelCount;
    250  }
    251 }
    252 
    253 uint32_t AudioNodeTrack::NumberOfChannels() const {
    254  AssertOnGraphThread();
    255 
    256  return mNumberOfInputChannels;
    257 }
    258 
    259 void AudioNodeTrack::AdvanceAndResume(TrackTime aAdvance) {
    260  mMainThreadCurrentTime += aAdvance;
    261  QueueControlMessageWithNoShutdown([self = RefPtr{this}, this, aAdvance] {
    262    TRACE("AudioNodeTrack::AdvanceAndResumeMessage");
    263    mStartTime -= aAdvance;
    264    mSegment->AppendNullData(aAdvance);
    265    DecrementSuspendCount();
    266  });
    267 }
    268 
    269 void AudioNodeTrack::ObtainInputBlock(AudioBlock& aTmpChunk,
    270                                      uint32_t aPortIndex) {
    271  uint32_t inputCount = mInputs.Length();
    272  uint32_t outputChannelCount = 1;
    273  AutoTArray<const AudioBlock*, 250> inputChunks;
    274  for (uint32_t i = 0; i < inputCount; ++i) {
    275    if (aPortIndex != mInputs[i]->InputNumber()) {
    276      // This input is connected to a different port
    277      continue;
    278    }
    279    MediaTrack* t = mInputs[i]->GetSource();
    280    AudioNodeTrack* a = static_cast<AudioNodeTrack*>(t);
    281    MOZ_ASSERT(a == t->AsAudioNodeTrack());
    282    if (a->IsAudioParamTrack()) {
    283      continue;
    284    }
    285 
    286    const AudioBlock* chunk = &a->mLastChunks[mInputs[i]->OutputNumber()];
    287    MOZ_ASSERT(chunk);
    288    if (chunk->IsNull() || chunk->mChannelData.IsEmpty()) {
    289      continue;
    290    }
    291 
    292    inputChunks.AppendElement(chunk);
    293    outputChannelCount =
    294        GetAudioChannelsSuperset(outputChannelCount, chunk->ChannelCount());
    295  }
    296 
    297  outputChannelCount = ComputedNumberOfChannels(outputChannelCount);
    298 
    299  uint32_t inputChunkCount = inputChunks.Length();
    300  if (inputChunkCount == 0 ||
    301      (inputChunkCount == 1 && inputChunks[0]->ChannelCount() == 0)) {
    302    aTmpChunk.SetNull(WEBAUDIO_BLOCK_SIZE);
    303    return;
    304  }
    305 
    306  if (inputChunkCount == 1 &&
    307      inputChunks[0]->ChannelCount() == outputChannelCount) {
    308    aTmpChunk = *inputChunks[0];
    309    return;
    310  }
    311 
    312  if (outputChannelCount == 0) {
    313    aTmpChunk.SetNull(WEBAUDIO_BLOCK_SIZE);
    314    return;
    315  }
    316 
    317  aTmpChunk.AllocateChannels(outputChannelCount);
    318  DownmixBufferType downmixBuffer;
    319  ASSERT_ALIGNED16(downmixBuffer.Elements());
    320 
    321  for (uint32_t i = 0; i < inputChunkCount; ++i) {
    322    AccumulateInputChunk(i, *inputChunks[i], &aTmpChunk, &downmixBuffer);
    323  }
    324 }
    325 
    326 void AudioNodeTrack::AccumulateInputChunk(uint32_t aInputIndex,
    327                                          const AudioBlock& aChunk,
    328                                          AudioBlock* aBlock,
    329                                          DownmixBufferType* aDownmixBuffer) {
    330  AutoTArray<const float*, GUESS_AUDIO_CHANNELS> channels;
    331  UpMixDownMixChunk(&aChunk, aBlock->ChannelCount(), channels, *aDownmixBuffer);
    332 
    333  for (uint32_t c = 0; c < channels.Length(); ++c) {
    334    const float* inputData = static_cast<const float*>(channels[c]);
    335    float* outputData = aBlock->ChannelFloatsForWrite(c);
    336    if (inputData) {
    337      if (aInputIndex == 0) {
    338        AudioBlockCopyChannelWithScale(inputData, aChunk.mVolume, outputData);
    339      } else {
    340        AudioBlockAddChannelWithScale(inputData, aChunk.mVolume, outputData);
    341      }
    342    } else {
    343      if (aInputIndex == 0) {
    344        PodZero(outputData, WEBAUDIO_BLOCK_SIZE);
    345      }
    346    }
    347  }
    348 }
    349 
    350 void AudioNodeTrack::UpMixDownMixChunk(const AudioBlock* aChunk,
    351                                       uint32_t aOutputChannelCount,
    352                                       nsTArray<const float*>& aOutputChannels,
    353                                       DownmixBufferType& aDownmixBuffer) {
    354  for (uint32_t i = 0; i < aChunk->ChannelCount(); i++) {
    355    aOutputChannels.AppendElement(
    356        static_cast<const float*>(aChunk->mChannelData[i]));
    357  }
    358  if (aOutputChannels.Length() < aOutputChannelCount) {
    359    if (mChannelInterpretation == ChannelInterpretation::Speakers) {
    360      AudioChannelsUpMix<float>(&aOutputChannels, aOutputChannelCount, nullptr);
    361      NS_ASSERTION(aOutputChannelCount == aOutputChannels.Length(),
    362                   "We called GetAudioChannelsSuperset to avoid this");
    363    } else {
    364      // Fill up the remaining aOutputChannels by zeros
    365      for (uint32_t j = aOutputChannels.Length(); j < aOutputChannelCount;
    366           ++j) {
    367        aOutputChannels.AppendElement(nullptr);
    368      }
    369    }
    370  } else if (aOutputChannels.Length() > aOutputChannelCount) {
    371    if (mChannelInterpretation == ChannelInterpretation::Speakers) {
    372      AutoTArray<float*, GUESS_AUDIO_CHANNELS> outputChannels;
    373      outputChannels.SetLength(aOutputChannelCount);
    374      aDownmixBuffer.SetLength(aOutputChannelCount * WEBAUDIO_BLOCK_SIZE);
    375      for (uint32_t j = 0; j < aOutputChannelCount; ++j) {
    376        outputChannels[j] = &aDownmixBuffer[j * WEBAUDIO_BLOCK_SIZE];
    377      }
    378 
    379      AudioChannelsDownMix<float, float>(aOutputChannels, outputChannels,
    380                                         WEBAUDIO_BLOCK_SIZE);
    381 
    382      aOutputChannels.SetLength(aOutputChannelCount);
    383      for (uint32_t j = 0; j < aOutputChannels.Length(); ++j) {
    384        aOutputChannels[j] = outputChannels[j];
    385      }
    386    } else {
    387      // Drop the remaining aOutputChannels
    388      aOutputChannels.RemoveLastElements(aOutputChannels.Length() -
    389                                         aOutputChannelCount);
    390    }
    391  }
    392 }
    393 
    394 // The MediaTrackGraph guarantees that this is actually one block, for
    395 // AudioNodeTracks.
    396 void AudioNodeTrack::ProcessInput(GraphTime aFrom, GraphTime aTo,
    397                                  uint32_t aFlags) {
    398  MOZ_ASSERT(aTo - aFrom == WEBAUDIO_BLOCK_SIZE);
    399  uint16_t outputCount = mLastChunks.Length();
    400  MOZ_ASSERT(outputCount == std::max(uint16_t(1), mEngine->OutputCount()));
    401 
    402  if (!mIsActive) {
    403    // mLastChunks are already null.
    404 #ifdef DEBUG
    405    for (const auto& chunk : mLastChunks) {
    406      MOZ_ASSERT(chunk.IsNull());
    407    }
    408 #endif
    409  } else if (InMutedCycle()) {
    410    mInputChunks.Clear();
    411    for (uint16_t i = 0; i < outputCount; ++i) {
    412      mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
    413    }
    414  } else {
    415    // We need to generate at least one input
    416    uint16_t maxInputs = std::max(uint16_t(1), mEngine->InputCount());
    417    mInputChunks.SetLength(maxInputs);
    418    for (uint16_t i = 0; i < maxInputs; ++i) {
    419      ObtainInputBlock(mInputChunks[i], i);
    420    }
    421    bool finished = false;
    422    if (mPassThrough) {
    423      MOZ_ASSERT(outputCount == 1,
    424                 "For now, we only support nodes that have one output port");
    425      mLastChunks[0] = mInputChunks[0];
    426    } else {
    427      if (maxInputs <= 1 && outputCount <= 1) {
    428        mEngine->ProcessBlock(this, aFrom, mInputChunks[0], &mLastChunks[0],
    429                              &finished);
    430      } else {
    431        mEngine->ProcessBlocksOnPorts(
    432            this, aFrom, Span(mInputChunks.Elements(), mEngine->InputCount()),
    433            Span(mLastChunks.Elements(), mEngine->OutputCount()), &finished);
    434      }
    435    }
    436    for (uint16_t i = 0; i < outputCount; ++i) {
    437      NS_ASSERTION(mLastChunks[i].GetDuration() == WEBAUDIO_BLOCK_SIZE,
    438                   "Invalid WebAudio chunk size");
    439    }
    440    if (finished && !mMarkAsEndedAfterThisBlock) {
    441      mMarkAsEndedAfterThisBlock = true;
    442      if (mIsActive) {
    443        ScheduleCheckForInactive();
    444      }
    445    }
    446 
    447    if (mDisabledMode != DisabledTrackMode::ENABLED) {
    448      for (uint32_t i = 0; i < outputCount; ++i) {
    449        mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
    450      }
    451    }
    452  }
    453 
    454  if (!mEnded) {
    455    // Don't output anything while finished
    456    if (mFlags & EXTERNAL_OUTPUT) {
    457      AdvanceOutputSegment();
    458    }
    459    if (mMarkAsEndedAfterThisBlock && (aFlags & ALLOW_END)) {
    460      // This track was ended the last time that we looked at it, and all
    461      // of the depending tracks have ended their output as well, so now
    462      // it's time to mark this track as ended.
    463      mEnded = true;
    464    }
    465  }
    466 }
    467 
    468 void AudioNodeTrack::ProduceOutputBeforeInput(GraphTime aFrom) {
    469  MOZ_ASSERT(mEngine->AsDelayNodeEngine());
    470  MOZ_ASSERT(mEngine->OutputCount() == 1,
    471             "DelayNodeEngine output count should be 1");
    472  MOZ_ASSERT(!InMutedCycle(), "DelayNodes should break cycles");
    473  MOZ_ASSERT(mLastChunks.Length() == 1);
    474 
    475  if (!mIsActive) {
    476    mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
    477  } else {
    478    mEngine->ProduceBlockBeforeInput(this, aFrom, &mLastChunks[0]);
    479    NS_ASSERTION(mLastChunks[0].GetDuration() == WEBAUDIO_BLOCK_SIZE,
    480                 "Invalid WebAudio chunk size");
    481    if (mDisabledMode != DisabledTrackMode::ENABLED) {
    482      mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
    483    }
    484  }
    485 }
    486 
    487 void AudioNodeTrack::AdvanceOutputSegment() {
    488  AudioSegment* segment = GetData<AudioSegment>();
    489 
    490  AudioChunk copyChunk = *mLastChunks[0].AsMutableChunk();
    491  AudioSegment tmpSegment;
    492  tmpSegment.AppendAndConsumeChunk(std::move(copyChunk));
    493 
    494  for (const auto& l : mTrackListeners) {
    495    // Notify MediaTrackListeners.
    496    l->NotifyQueuedChanges(Graph(), segment->GetDuration(), tmpSegment);
    497  }
    498 
    499  if (mLastChunks[0].IsNull()) {
    500    segment->AppendNullData(tmpSegment.GetDuration());
    501  } else {
    502    segment->AppendFrom(&tmpSegment);
    503  }
    504 }
    505 
    506 void AudioNodeTrack::AddInput(MediaInputPort* aPort) {
    507  ProcessedMediaTrack::AddInput(aPort);
    508  AudioNodeTrack* ns = aPort->GetSource()->AsAudioNodeTrack();
    509  // Tracks that are not AudioNodeTracks are considered active.
    510  if (!ns || (ns->mIsActive && !ns->IsAudioParamTrack())) {
    511    IncrementActiveInputCount();
    512  }
    513 }
    514 void AudioNodeTrack::RemoveInput(MediaInputPort* aPort) {
    515  ProcessedMediaTrack::RemoveInput(aPort);
    516  AudioNodeTrack* ns = aPort->GetSource()->AsAudioNodeTrack();
    517  // Tracks that are not AudioNodeTracks are considered active.
    518  if (!ns || (ns->mIsActive && !ns->IsAudioParamTrack())) {
    519    DecrementActiveInputCount();
    520  }
    521 }
    522 
    523 void AudioNodeTrack::SetActive() {
    524  if (mIsActive || mMarkAsEndedAfterThisBlock) {
    525    return;
    526  }
    527 
    528  mIsActive = true;
    529  if (!(mFlags & EXTERNAL_OUTPUT)) {
    530    DecrementSuspendCount();
    531  }
    532  if (IsAudioParamTrack()) {
    533    // Consumers merely influence track order.
    534    // They do not read from the track.
    535    return;
    536  }
    537 
    538  for (const auto& consumer : mConsumers) {
    539    AudioNodeTrack* ns = consumer->GetDestination()->AsAudioNodeTrack();
    540    if (ns) {
    541      ns->IncrementActiveInputCount();
    542    }
    543  }
    544 }
    545 
    546 void AudioNodeTrack::ScheduleCheckForInactive() {
    547  if (mActiveInputCount > 0 && !mMarkAsEndedAfterThisBlock) {
    548    return;
    549  }
    550 
    551  RunAfterProcessing([self = RefPtr{this}, this] {
    552    TRACE("AudioNodeTrack::CheckForInactive");
    553    CheckForInactive();
    554  });
    555 }
    556 
    557 void AudioNodeTrack::CheckForInactive() {
    558  if (((mActiveInputCount > 0 || mEngine->IsActive()) &&
    559       !mMarkAsEndedAfterThisBlock) ||
    560      !mIsActive) {
    561    return;
    562  }
    563 
    564  mIsActive = false;
    565  mInputChunks.Clear();  // not required for foreseeable future
    566  for (auto& chunk : mLastChunks) {
    567    chunk.SetNull(WEBAUDIO_BLOCK_SIZE);
    568  }
    569  if (!(mFlags & EXTERNAL_OUTPUT)) {
    570    IncrementSuspendCount();
    571  }
    572  if (IsAudioParamTrack()) {
    573    return;
    574  }
    575 
    576  for (const auto& consumer : mConsumers) {
    577    AudioNodeTrack* ns = consumer->GetDestination()->AsAudioNodeTrack();
    578    if (ns) {
    579      ns->DecrementActiveInputCount();
    580    }
    581  }
    582 }
    583 
    584 void AudioNodeTrack::IncrementActiveInputCount() {
    585  ++mActiveInputCount;
    586  SetActive();
    587 }
    588 
    589 void AudioNodeTrack::DecrementActiveInputCount() {
    590  MOZ_ASSERT(mActiveInputCount > 0);
    591  --mActiveInputCount;
    592  CheckForInactive();
    593 }
    594 
    595 }  // namespace mozilla