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