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