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