summaryrefslogtreecommitdiffstats
path: root/dom/media/webaudio/AudioNodeStream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webaudio/AudioNodeStream.cpp')
-rw-r--r--dom/media/webaudio/AudioNodeStream.cpp783
1 files changed, 783 insertions, 0 deletions
diff --git a/dom/media/webaudio/AudioNodeStream.cpp b/dom/media/webaudio/AudioNodeStream.cpp
new file mode 100644
index 000000000..0e5aa3fc7
--- /dev/null
+++ b/dom/media/webaudio/AudioNodeStream.cpp
@@ -0,0 +1,783 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AudioNodeStream.h"
+
+#include "MediaStreamGraphImpl.h"
+#include "MediaStreamListener.h"
+#include "AudioNodeEngine.h"
+#include "ThreeDPoint.h"
+#include "AudioChannelFormat.h"
+#include "AudioParamTimeline.h"
+#include "AudioContext.h"
+#include "nsMathUtils.h"
+
+using namespace mozilla::dom;
+
+namespace mozilla {
+
+/**
+ * An AudioNodeStream produces a single audio track with ID
+ * AUDIO_TRACK. This track has rate AudioContext::sIdealAudioRate
+ * for regular audio contexts, and the rate requested by the web content
+ * for offline audio contexts.
+ * Each chunk in the track is a single block of WEBAUDIO_BLOCK_SIZE samples.
+ * Note: This must be a different value than MEDIA_STREAM_DEST_TRACK_ID
+ */
+
+AudioNodeStream::AudioNodeStream(AudioNodeEngine* aEngine,
+ Flags aFlags,
+ TrackRate aSampleRate)
+ : ProcessedMediaStream(),
+ mEngine(aEngine),
+ mSampleRate(aSampleRate),
+ mFlags(aFlags),
+ mNumberOfInputChannels(2),
+ mIsActive(aEngine->IsActive()),
+ mMarkAsFinishedAfterThisBlock(false),
+ mAudioParamStream(false),
+ mPassThrough(false)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ mSuspendedCount = !(mIsActive || mFlags & EXTERNAL_OUTPUT);
+ mChannelCountMode = ChannelCountMode::Max;
+ mChannelInterpretation = ChannelInterpretation::Speakers;
+ // AudioNodes are always producing data
+ mHasCurrentData = true;
+ mLastChunks.SetLength(std::max(uint16_t(1), mEngine->OutputCount()));
+ MOZ_COUNT_CTOR(AudioNodeStream);
+}
+
+AudioNodeStream::~AudioNodeStream()
+{
+ MOZ_ASSERT(mActiveInputCount == 0);
+ MOZ_COUNT_DTOR(AudioNodeStream);
+}
+
+void
+AudioNodeStream::DestroyImpl()
+{
+ // These are graph thread objects, so clean up on graph thread.
+ mInputChunks.Clear();
+ mLastChunks.Clear();
+
+ ProcessedMediaStream::DestroyImpl();
+}
+
+/* static */ already_AddRefed<AudioNodeStream>
+AudioNodeStream::Create(AudioContext* aCtx, AudioNodeEngine* aEngine,
+ Flags aFlags, MediaStreamGraph* aGraph)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(aGraph);
+
+ // MediaRecorders use an AudioNodeStream, but no AudioNode
+ AudioNode* node = aEngine->NodeMainThread();
+
+ RefPtr<AudioNodeStream> stream =
+ new AudioNodeStream(aEngine, aFlags, aGraph->GraphRate());
+ stream->mSuspendedCount += aCtx->ShouldSuspendNewStream();
+ if (node) {
+ stream->SetChannelMixingParametersImpl(node->ChannelCount(),
+ node->ChannelCountModeValue(),
+ node->ChannelInterpretationValue());
+ }
+ aGraph->AddStream(stream);
+ return stream.forget();
+}
+
+size_t
+AudioNodeStream::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ size_t amount = 0;
+
+ // Not reported:
+ // - mEngine
+
+ amount += ProcessedMediaStream::SizeOfExcludingThis(aMallocSizeOf);
+ amount += mLastChunks.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (size_t i = 0; i < mLastChunks.Length(); i++) {
+ // NB: This is currently unshared only as there are instances of
+ // double reporting in DMD otherwise.
+ amount += mLastChunks[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+
+ return amount;
+}
+
+size_t
+AudioNodeStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
+void
+AudioNodeStream::SizeOfAudioNodesIncludingThis(MallocSizeOf aMallocSizeOf,
+ AudioNodeSizes& aUsage) const
+{
+ // Explicitly separate out the stream memory.
+ aUsage.mStream = SizeOfIncludingThis(aMallocSizeOf);
+
+ if (mEngine) {
+ // This will fill out the rest of |aUsage|.
+ mEngine->SizeOfIncludingThis(aMallocSizeOf, aUsage);
+ }
+}
+
+void
+AudioNodeStream::SetStreamTimeParameter(uint32_t aIndex, AudioContext* aContext,
+ double aStreamTime)
+{
+ class Message final : public ControlMessage
+ {
+ public:
+ Message(AudioNodeStream* aStream, uint32_t aIndex, MediaStream* aRelativeToStream,
+ double aStreamTime)
+ : ControlMessage(aStream), mStreamTime(aStreamTime),
+ mRelativeToStream(aRelativeToStream), mIndex(aIndex)
+ {}
+ void Run() override
+ {
+ static_cast<AudioNodeStream*>(mStream)->
+ SetStreamTimeParameterImpl(mIndex, mRelativeToStream, mStreamTime);
+ }
+ double mStreamTime;
+ MediaStream* mRelativeToStream;
+ uint32_t mIndex;
+ };
+
+ GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex,
+ aContext->DestinationStream(),
+ aStreamTime));
+}
+
+void
+AudioNodeStream::SetStreamTimeParameterImpl(uint32_t aIndex, MediaStream* aRelativeToStream,
+ double aStreamTime)
+{
+ StreamTime ticks = aRelativeToStream->SecondsToNearestStreamTime(aStreamTime);
+ mEngine->SetStreamTimeParameter(aIndex, ticks);
+}
+
+void
+AudioNodeStream::SetDoubleParameter(uint32_t aIndex, double aValue)
+{
+ class Message final : public ControlMessage
+ {
+ public:
+ Message(AudioNodeStream* aStream, uint32_t aIndex, double aValue)
+ : ControlMessage(aStream), mValue(aValue), mIndex(aIndex)
+ {}
+ void Run() override
+ {
+ static_cast<AudioNodeStream*>(mStream)->Engine()->
+ SetDoubleParameter(mIndex, mValue);
+ }
+ double mValue;
+ uint32_t mIndex;
+ };
+
+ GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex, aValue));
+}
+
+void
+AudioNodeStream::SetInt32Parameter(uint32_t aIndex, int32_t aValue)
+{
+ class Message final : public ControlMessage
+ {
+ public:
+ Message(AudioNodeStream* aStream, uint32_t aIndex, int32_t aValue)
+ : ControlMessage(aStream), mValue(aValue), mIndex(aIndex)
+ {}
+ void Run() override
+ {
+ static_cast<AudioNodeStream*>(mStream)->Engine()->
+ SetInt32Parameter(mIndex, mValue);
+ }
+ int32_t mValue;
+ uint32_t mIndex;
+ };
+
+ GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex, aValue));
+}
+
+void
+AudioNodeStream::SendTimelineEvent(uint32_t aIndex,
+ const AudioTimelineEvent& aEvent)
+{
+ class Message final : public ControlMessage
+ {
+ public:
+ Message(AudioNodeStream* aStream, uint32_t aIndex,
+ const AudioTimelineEvent& aEvent)
+ : ControlMessage(aStream),
+ mEvent(aEvent),
+ mSampleRate(aStream->SampleRate()),
+ mIndex(aIndex)
+ {}
+ void Run() override
+ {
+ static_cast<AudioNodeStream*>(mStream)->Engine()->
+ RecvTimelineEvent(mIndex, mEvent);
+ }
+ AudioTimelineEvent mEvent;
+ TrackRate mSampleRate;
+ uint32_t mIndex;
+ };
+ GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex, aEvent));
+}
+
+void
+AudioNodeStream::SetThreeDPointParameter(uint32_t aIndex, const ThreeDPoint& aValue)
+{
+ class Message final : public ControlMessage
+ {
+ public:
+ Message(AudioNodeStream* aStream, uint32_t aIndex, const ThreeDPoint& aValue)
+ : ControlMessage(aStream), mValue(aValue), mIndex(aIndex)
+ {}
+ void Run() override
+ {
+ static_cast<AudioNodeStream*>(mStream)->Engine()->
+ SetThreeDPointParameter(mIndex, mValue);
+ }
+ ThreeDPoint mValue;
+ uint32_t mIndex;
+ };
+
+ GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex, aValue));
+}
+
+void
+AudioNodeStream::SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList>&& aBuffer)
+{
+ class Message final : public ControlMessage
+ {
+ public:
+ Message(AudioNodeStream* aStream,
+ already_AddRefed<ThreadSharedFloatArrayBufferList>& aBuffer)
+ : ControlMessage(aStream), mBuffer(aBuffer)
+ {}
+ void Run() override
+ {
+ static_cast<AudioNodeStream*>(mStream)->Engine()->
+ SetBuffer(mBuffer.forget());
+ }
+ RefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
+ };
+
+ GraphImpl()->AppendMessage(MakeUnique<Message>(this, aBuffer));
+}
+
+void
+AudioNodeStream::SetRawArrayData(nsTArray<float>& aData)
+{
+ class Message final : public ControlMessage
+ {
+ public:
+ Message(AudioNodeStream* aStream,
+ nsTArray<float>& aData)
+ : ControlMessage(aStream)
+ {
+ mData.SwapElements(aData);
+ }
+ void Run() override
+ {
+ static_cast<AudioNodeStream*>(mStream)->Engine()->SetRawArrayData(mData);
+ }
+ nsTArray<float> mData;
+ };
+
+ GraphImpl()->AppendMessage(MakeUnique<Message>(this, aData));
+}
+
+void
+AudioNodeStream::SetChannelMixingParameters(uint32_t aNumberOfChannels,
+ ChannelCountMode aChannelCountMode,
+ ChannelInterpretation aChannelInterpretation)
+{
+ class Message final : public ControlMessage
+ {
+ public:
+ Message(AudioNodeStream* aStream,
+ uint32_t aNumberOfChannels,
+ ChannelCountMode aChannelCountMode,
+ ChannelInterpretation aChannelInterpretation)
+ : ControlMessage(aStream),
+ mNumberOfChannels(aNumberOfChannels),
+ mChannelCountMode(aChannelCountMode),
+ mChannelInterpretation(aChannelInterpretation)
+ {}
+ void Run() override
+ {
+ static_cast<AudioNodeStream*>(mStream)->
+ SetChannelMixingParametersImpl(mNumberOfChannels, mChannelCountMode,
+ mChannelInterpretation);
+ }
+ uint32_t mNumberOfChannels;
+ ChannelCountMode mChannelCountMode;
+ ChannelInterpretation mChannelInterpretation;
+ };
+
+ GraphImpl()->AppendMessage(MakeUnique<Message>(this, aNumberOfChannels,
+ aChannelCountMode,
+ aChannelInterpretation));
+}
+
+void
+AudioNodeStream::SetPassThrough(bool aPassThrough)
+{
+ class Message final : public ControlMessage
+ {
+ public:
+ Message(AudioNodeStream* aStream, bool aPassThrough)
+ : ControlMessage(aStream), mPassThrough(aPassThrough)
+ {}
+ void Run() override
+ {
+ static_cast<AudioNodeStream*>(mStream)->mPassThrough = mPassThrough;
+ }
+ bool mPassThrough;
+ };
+
+ GraphImpl()->AppendMessage(MakeUnique<Message>(this, aPassThrough));
+}
+
+void
+AudioNodeStream::SetChannelMixingParametersImpl(uint32_t aNumberOfChannels,
+ ChannelCountMode aChannelCountMode,
+ ChannelInterpretation aChannelInterpretation)
+{
+ // Make sure that we're not clobbering any significant bits by fitting these
+ // values in 16 bits.
+ MOZ_ASSERT(int(aChannelCountMode) < INT16_MAX);
+ MOZ_ASSERT(int(aChannelInterpretation) < INT16_MAX);
+
+ mNumberOfInputChannels = aNumberOfChannels;
+ mChannelCountMode = aChannelCountMode;
+ mChannelInterpretation = aChannelInterpretation;
+}
+
+uint32_t
+AudioNodeStream::ComputedNumberOfChannels(uint32_t aInputChannelCount)
+{
+ switch (mChannelCountMode) {
+ case ChannelCountMode::Explicit:
+ // Disregard the channel count we've calculated from inputs, and just use
+ // mNumberOfInputChannels.
+ return mNumberOfInputChannels;
+ case ChannelCountMode::Clamped_max:
+ // Clamp the computed output channel count to mNumberOfInputChannels.
+ return std::min(aInputChannelCount, mNumberOfInputChannels);
+ default:
+ case ChannelCountMode::Max:
+ // Nothing to do here, just shut up the compiler warning.
+ return aInputChannelCount;
+ }
+}
+
+class AudioNodeStream::AdvanceAndResumeMessage final : public ControlMessage {
+public:
+ AdvanceAndResumeMessage(AudioNodeStream* aStream, StreamTime aAdvance) :
+ ControlMessage(aStream), mAdvance(aAdvance) {}
+ void Run() override
+ {
+ auto ns = static_cast<AudioNodeStream*>(mStream);
+ ns->mTracksStartTime -= mAdvance;
+
+ StreamTracks::Track* track = ns->EnsureTrack(AUDIO_TRACK);
+ track->Get<AudioSegment>()->AppendNullData(mAdvance);
+
+ ns->GraphImpl()->DecrementSuspendCount(mStream);
+ }
+private:
+ StreamTime mAdvance;
+};
+
+void
+AudioNodeStream::AdvanceAndResume(StreamTime aAdvance)
+{
+ mMainThreadCurrentTime += aAdvance;
+ GraphImpl()->AppendMessage(MakeUnique<AdvanceAndResumeMessage>(this, aAdvance));
+}
+
+void
+AudioNodeStream::ObtainInputBlock(AudioBlock& aTmpChunk,
+ uint32_t aPortIndex)
+{
+ uint32_t inputCount = mInputs.Length();
+ uint32_t outputChannelCount = 1;
+ AutoTArray<const AudioBlock*,250> inputChunks;
+ for (uint32_t i = 0; i < inputCount; ++i) {
+ if (aPortIndex != mInputs[i]->InputNumber()) {
+ // This input is connected to a different port
+ continue;
+ }
+ MediaStream* s = mInputs[i]->GetSource();
+ AudioNodeStream* a = static_cast<AudioNodeStream*>(s);
+ MOZ_ASSERT(a == s->AsAudioNodeStream());
+ if (a->IsAudioParamStream()) {
+ continue;
+ }
+
+ const AudioBlock* chunk = &a->mLastChunks[mInputs[i]->OutputNumber()];
+ MOZ_ASSERT(chunk);
+ if (chunk->IsNull() || chunk->mChannelData.IsEmpty()) {
+ continue;
+ }
+
+ inputChunks.AppendElement(chunk);
+ outputChannelCount =
+ GetAudioChannelsSuperset(outputChannelCount, chunk->ChannelCount());
+ }
+
+ outputChannelCount = ComputedNumberOfChannels(outputChannelCount);
+
+ uint32_t inputChunkCount = inputChunks.Length();
+ if (inputChunkCount == 0 ||
+ (inputChunkCount == 1 && inputChunks[0]->ChannelCount() == 0)) {
+ aTmpChunk.SetNull(WEBAUDIO_BLOCK_SIZE);
+ return;
+ }
+
+ if (inputChunkCount == 1 &&
+ inputChunks[0]->ChannelCount() == outputChannelCount) {
+ aTmpChunk = *inputChunks[0];
+ return;
+ }
+
+ if (outputChannelCount == 0) {
+ aTmpChunk.SetNull(WEBAUDIO_BLOCK_SIZE);
+ return;
+ }
+
+ aTmpChunk.AllocateChannels(outputChannelCount);
+ DownmixBufferType downmixBuffer;
+ ASSERT_ALIGNED16(downmixBuffer.Elements());
+
+ for (uint32_t i = 0; i < inputChunkCount; ++i) {
+ AccumulateInputChunk(i, *inputChunks[i], &aTmpChunk, &downmixBuffer);
+ }
+}
+
+void
+AudioNodeStream::AccumulateInputChunk(uint32_t aInputIndex,
+ const AudioBlock& aChunk,
+ AudioBlock* aBlock,
+ DownmixBufferType* aDownmixBuffer)
+{
+ AutoTArray<const float*,GUESS_AUDIO_CHANNELS> channels;
+ UpMixDownMixChunk(&aChunk, aBlock->ChannelCount(), channels, *aDownmixBuffer);
+
+ for (uint32_t c = 0; c < channels.Length(); ++c) {
+ const float* inputData = static_cast<const float*>(channels[c]);
+ float* outputData = aBlock->ChannelFloatsForWrite(c);
+ if (inputData) {
+ if (aInputIndex == 0) {
+ AudioBlockCopyChannelWithScale(inputData, aChunk.mVolume, outputData);
+ } else {
+ AudioBlockAddChannelWithScale(inputData, aChunk.mVolume, outputData);
+ }
+ } else {
+ if (aInputIndex == 0) {
+ PodZero(outputData, WEBAUDIO_BLOCK_SIZE);
+ }
+ }
+ }
+}
+
+void
+AudioNodeStream::UpMixDownMixChunk(const AudioBlock* aChunk,
+ uint32_t aOutputChannelCount,
+ nsTArray<const float*>& aOutputChannels,
+ DownmixBufferType& aDownmixBuffer)
+{
+ for (uint32_t i = 0; i < aChunk->ChannelCount(); i++) {
+ aOutputChannels.AppendElement(static_cast<const float*>(aChunk->mChannelData[i]));
+ }
+ if (aOutputChannels.Length() < aOutputChannelCount) {
+ if (mChannelInterpretation == ChannelInterpretation::Speakers) {
+ AudioChannelsUpMix<float>(&aOutputChannels, aOutputChannelCount, nullptr);
+ NS_ASSERTION(aOutputChannelCount == aOutputChannels.Length(),
+ "We called GetAudioChannelsSuperset to avoid this");
+ } else {
+ // Fill up the remaining aOutputChannels by zeros
+ for (uint32_t j = aOutputChannels.Length(); j < aOutputChannelCount; ++j) {
+ aOutputChannels.AppendElement(nullptr);
+ }
+ }
+ } else if (aOutputChannels.Length() > aOutputChannelCount) {
+ if (mChannelInterpretation == ChannelInterpretation::Speakers) {
+ AutoTArray<float*,GUESS_AUDIO_CHANNELS> outputChannels;
+ outputChannels.SetLength(aOutputChannelCount);
+ aDownmixBuffer.SetLength(aOutputChannelCount * WEBAUDIO_BLOCK_SIZE);
+ for (uint32_t j = 0; j < aOutputChannelCount; ++j) {
+ outputChannels[j] = &aDownmixBuffer[j * WEBAUDIO_BLOCK_SIZE];
+ }
+
+ AudioChannelsDownMix(aOutputChannels, outputChannels.Elements(),
+ aOutputChannelCount, WEBAUDIO_BLOCK_SIZE);
+
+ aOutputChannels.SetLength(aOutputChannelCount);
+ for (uint32_t j = 0; j < aOutputChannels.Length(); ++j) {
+ aOutputChannels[j] = outputChannels[j];
+ }
+ } else {
+ // Drop the remaining aOutputChannels
+ aOutputChannels.RemoveElementsAt(aOutputChannelCount,
+ aOutputChannels.Length() - aOutputChannelCount);
+ }
+ }
+}
+
+// The MediaStreamGraph guarantees that this is actually one block, for
+// AudioNodeStreams.
+void
+AudioNodeStream::ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
+{
+ uint16_t outputCount = mLastChunks.Length();
+ MOZ_ASSERT(outputCount == std::max(uint16_t(1), mEngine->OutputCount()));
+
+ if (!mIsActive) {
+ // mLastChunks are already null.
+#ifdef DEBUG
+ for (const auto& chunk : mLastChunks) {
+ MOZ_ASSERT(chunk.IsNull());
+ }
+#endif
+ } else if (InMutedCycle()) {
+ mInputChunks.Clear();
+ for (uint16_t i = 0; i < outputCount; ++i) {
+ mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
+ }
+ } else {
+ // We need to generate at least one input
+ uint16_t maxInputs = std::max(uint16_t(1), mEngine->InputCount());
+ mInputChunks.SetLength(maxInputs);
+ for (uint16_t i = 0; i < maxInputs; ++i) {
+ ObtainInputBlock(mInputChunks[i], i);
+ }
+ bool finished = false;
+ if (mPassThrough) {
+ MOZ_ASSERT(outputCount == 1, "For now, we only support nodes that have one output port");
+ mLastChunks[0] = mInputChunks[0];
+ } else {
+ if (maxInputs <= 1 && outputCount <= 1) {
+ mEngine->ProcessBlock(this, aFrom,
+ mInputChunks[0], &mLastChunks[0], &finished);
+ } else {
+ mEngine->ProcessBlocksOnPorts(this, mInputChunks, mLastChunks, &finished);
+ }
+ }
+ for (uint16_t i = 0; i < outputCount; ++i) {
+ NS_ASSERTION(mLastChunks[i].GetDuration() == WEBAUDIO_BLOCK_SIZE,
+ "Invalid WebAudio chunk size");
+ }
+ if (finished) {
+ mMarkAsFinishedAfterThisBlock = true;
+ if (mIsActive) {
+ ScheduleCheckForInactive();
+ }
+ }
+
+ if (GetDisabledTrackMode(static_cast<TrackID>(AUDIO_TRACK)) != DisabledTrackMode::ENABLED) {
+ for (uint32_t i = 0; i < outputCount; ++i) {
+ mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
+ }
+ }
+ }
+
+ if (!mFinished) {
+ // Don't output anything while finished
+ if (mFlags & EXTERNAL_OUTPUT) {
+ AdvanceOutputSegment();
+ }
+ if (mMarkAsFinishedAfterThisBlock && (aFlags & ALLOW_FINISH)) {
+ // This stream was finished the last time that we looked at it, and all
+ // of the depending streams have finished their output as well, so now
+ // it's time to mark this stream as finished.
+ if (mFlags & EXTERNAL_OUTPUT) {
+ FinishOutput();
+ }
+ FinishOnGraphThread();
+ }
+ }
+}
+
+void
+AudioNodeStream::ProduceOutputBeforeInput(GraphTime aFrom)
+{
+ MOZ_ASSERT(mEngine->AsDelayNodeEngine());
+ MOZ_ASSERT(mEngine->OutputCount() == 1,
+ "DelayNodeEngine output count should be 1");
+ MOZ_ASSERT(!InMutedCycle(), "DelayNodes should break cycles");
+ MOZ_ASSERT(mLastChunks.Length() == 1);
+
+ if (!mIsActive) {
+ mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
+ } else {
+ mEngine->ProduceBlockBeforeInput(this, aFrom, &mLastChunks[0]);
+ NS_ASSERTION(mLastChunks[0].GetDuration() == WEBAUDIO_BLOCK_SIZE,
+ "Invalid WebAudio chunk size");
+ if (GetDisabledTrackMode(static_cast<TrackID>(AUDIO_TRACK)) != DisabledTrackMode::ENABLED) {
+ mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
+ }
+ }
+}
+
+void
+AudioNodeStream::AdvanceOutputSegment()
+{
+ StreamTracks::Track* track = EnsureTrack(AUDIO_TRACK);
+ // No more tracks will be coming
+ mTracks.AdvanceKnownTracksTime(STREAM_TIME_MAX);
+
+ AudioSegment* segment = track->Get<AudioSegment>();
+
+ if (!mLastChunks[0].IsNull()) {
+ segment->AppendAndConsumeChunk(mLastChunks[0].AsMutableChunk());
+ } else {
+ segment->AppendNullData(mLastChunks[0].GetDuration());
+ }
+
+ for (uint32_t j = 0; j < mListeners.Length(); ++j) {
+ MediaStreamListener* l = mListeners[j];
+ AudioChunk copyChunk = mLastChunks[0].AsAudioChunk();
+ AudioSegment tmpSegment;
+ tmpSegment.AppendAndConsumeChunk(&copyChunk);
+ l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK,
+ segment->GetDuration(), TrackEventCommand::TRACK_EVENT_NONE, tmpSegment);
+ }
+}
+
+void
+AudioNodeStream::FinishOutput()
+{
+ StreamTracks::Track* track = EnsureTrack(AUDIO_TRACK);
+ track->SetEnded();
+
+ for (uint32_t j = 0; j < mListeners.Length(); ++j) {
+ MediaStreamListener* l = mListeners[j];
+ AudioSegment emptySegment;
+ l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK,
+ track->GetSegment()->GetDuration(),
+ TrackEventCommand::TRACK_EVENT_ENDED, emptySegment);
+ }
+}
+
+void
+AudioNodeStream::AddInput(MediaInputPort* aPort)
+{
+ ProcessedMediaStream::AddInput(aPort);
+ AudioNodeStream* ns = aPort->GetSource()->AsAudioNodeStream();
+ // Streams that are not AudioNodeStreams are considered active.
+ if (!ns || (ns->mIsActive && !ns->IsAudioParamStream())) {
+ IncrementActiveInputCount();
+ }
+}
+void
+AudioNodeStream::RemoveInput(MediaInputPort* aPort)
+{
+ ProcessedMediaStream::RemoveInput(aPort);
+ AudioNodeStream* ns = aPort->GetSource()->AsAudioNodeStream();
+ // Streams that are not AudioNodeStreams are considered active.
+ if (!ns || (ns->mIsActive && !ns->IsAudioParamStream())) {
+ DecrementActiveInputCount();
+ }
+}
+
+void
+AudioNodeStream::SetActive()
+{
+ if (mIsActive || mMarkAsFinishedAfterThisBlock) {
+ return;
+ }
+
+ mIsActive = true;
+ if (!(mFlags & EXTERNAL_OUTPUT)) {
+ GraphImpl()->DecrementSuspendCount(this);
+ }
+ if (IsAudioParamStream()) {
+ // Consumers merely influence stream order.
+ // They do not read from the stream.
+ return;
+ }
+
+ for (const auto& consumer : mConsumers) {
+ AudioNodeStream* ns = consumer->GetDestination()->AsAudioNodeStream();
+ if (ns) {
+ ns->IncrementActiveInputCount();
+ }
+ }
+}
+
+class AudioNodeStream::CheckForInactiveMessage final : public ControlMessage
+{
+public:
+ explicit CheckForInactiveMessage(AudioNodeStream* aStream) :
+ ControlMessage(aStream) {}
+ void Run() override
+ {
+ auto ns = static_cast<AudioNodeStream*>(mStream);
+ ns->CheckForInactive();
+ }
+};
+
+void
+AudioNodeStream::ScheduleCheckForInactive()
+{
+ if (mActiveInputCount > 0 && !mMarkAsFinishedAfterThisBlock) {
+ return;
+ }
+
+ auto message = MakeUnique<CheckForInactiveMessage>(this);
+ GraphImpl()->RunMessageAfterProcessing(Move(message));
+}
+
+void
+AudioNodeStream::CheckForInactive()
+{
+ if (((mActiveInputCount > 0 || mEngine->IsActive()) &&
+ !mMarkAsFinishedAfterThisBlock) ||
+ !mIsActive) {
+ return;
+ }
+
+ mIsActive = false;
+ mInputChunks.Clear(); // not required for foreseeable future
+ for (auto& chunk : mLastChunks) {
+ chunk.SetNull(WEBAUDIO_BLOCK_SIZE);
+ }
+ if (!(mFlags & EXTERNAL_OUTPUT)) {
+ GraphImpl()->IncrementSuspendCount(this);
+ }
+ if (IsAudioParamStream()) {
+ return;
+ }
+
+ for (const auto& consumer : mConsumers) {
+ AudioNodeStream* ns = consumer->GetDestination()->AsAudioNodeStream();
+ if (ns) {
+ ns->DecrementActiveInputCount();
+ }
+ }
+}
+
+void
+AudioNodeStream::IncrementActiveInputCount()
+{
+ ++mActiveInputCount;
+ SetActive();
+}
+
+void
+AudioNodeStream::DecrementActiveInputCount()
+{
+ MOZ_ASSERT(mActiveInputCount > 0);
+ --mActiveInputCount;
+ CheckForInactive();
+}
+
+} // namespace mozilla