bool AudioNode::DisconnectFromOutputIfConnected<AudioNode>(uint32_t aOutputNodeIndex, uint32_t aInputIndex) { WEB_AUDIO_API_LOG("%f: %s %u Disconnect()", Context()->CurrentTime(), NodeType(), Id()); AudioNode* destination = mOutputNodes[aOutputNodeIndex]; MOZ_ASSERT(aOutputNodeIndex < mOutputNodes.Length()); MOZ_ASSERT(aInputIndex < destination->InputNodes().Length()); // An upstream node may be starting to play on the graph thread, and the // engine for a downstream node may be sending a PlayingRefChangeHandler // ADDREF message to this (main) thread. Wait for a round trip before // releasing nodes, to give engines receiving sound now time to keep their // nodes alive. class RunnableRelease final : public Runnable { public: explicit RunnableRelease(already_AddRefed<AudioNode> aNode) : mozilla::Runnable("RunnableRelease") , mNode(aNode) { } NS_IMETHOD Run() override { mNode = nullptr; return NS_OK; } private: RefPtr<AudioNode> mNode; }; InputNode& input = destination->mInputNodes[aInputIndex]; if (input.mInputNode != this) { return false; } // Remove one instance of 'dest' from mOutputNodes. There could be // others, and it's not correct to remove them all since some of them // could be for different output ports. RefPtr<AudioNode> output = mOutputNodes[aOutputNodeIndex].forget(); mOutputNodes.RemoveElementAt(aOutputNodeIndex); // Destroying the InputNode here sends a message to the graph thread // to disconnect the streams, which should be sent before the // RunAfterPendingUpdates() call below. destination->mInputNodes.RemoveElementAt(aInputIndex); output->NotifyInputsChanged(); if (mStream) { nsCOMPtr<nsIRunnable> runnable = new RunnableRelease(output.forget()); mStream->RunAfterPendingUpdates(runnable.forget()); } return true; }
AudioNode* AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput, uint32_t aInput, ErrorResult& aRv) { if (aOutput >= NumberOfOutputs() || aInput >= aDestination.NumberOfInputs()) { aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); return nullptr; } if (Context() != aDestination.Context()) { aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); return nullptr; } if (FindIndexOfNodeWithPorts(aDestination.mInputNodes, this, aInput, aOutput) != nsTArray<AudioNode::InputNode>::NoIndex) { // connection already exists. return &aDestination; } WEB_AUDIO_API_LOG("%f: %s %u Connect() to %s %u", Context()->CurrentTime(), NodeType(), Id(), aDestination.NodeType(), aDestination.Id()); // The MediaStreamGraph will handle cycle detection. We don't need to do it // here. mOutputNodes.AppendElement(&aDestination); InputNode* input = aDestination.mInputNodes.AppendElement(); input->mInputNode = this; input->mInputPort = aInput; input->mOutputPort = aOutput; AudioNodeStream* destinationStream = aDestination.mStream; if (mStream && destinationStream) { // Connect streams in the MediaStreamGraph MOZ_ASSERT(aInput <= UINT16_MAX, "Unexpected large input port number"); MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number"); input->mStreamPort = destinationStream-> AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK, TRACK_ANY, static_cast<uint16_t>(aInput), static_cast<uint16_t>(aOutput)); } aDestination.NotifyInputsChanged(); // This connection may have connected a panner and a source. Context()->UpdatePannerSource(); return &aDestination; }
void AudioBufferSourceNode::Start(double aWhen, double aOffset, const Optional<double>& aDuration, ErrorResult& aRv) { if (!WebAudioUtils::IsTimeValid(aWhen) || (aDuration.WasPassed() && !WebAudioUtils::IsTimeValid(aDuration.Value()))) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return; } if (mStartCalled) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } mStartCalled = true; AudioNodeStream* ns = mStream; if (!ns) { // Nothing to play, or we're already dead for some reason return; } // Remember our arguments so that we can use them when we get a new buffer. mOffset = aOffset; mDuration = aDuration.WasPassed() ? aDuration.Value() : std::numeric_limits<double>::min(); WEB_AUDIO_API_LOG("%f: %s %u Start(%f, %g, %g)", Context()->CurrentTime(), NodeType(), Id(), aWhen, aOffset, mDuration); // We can't send these parameters without a buffer because we don't know the // buffer's sample rate or length. if (mBuffer) { SendOffsetAndDurationParametersToStream(ns); } // Don't set parameter unnecessarily if (aWhen > 0.0) { ns->SetDoubleParameter(START, aWhen); } }
void AudioBufferSourceNode::Stop(double aWhen, ErrorResult& aRv) { if (!WebAudioUtils::IsTimeValid(aWhen)) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return; } if (!mStartCalled) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } WEB_AUDIO_API_LOG("%f: %s %u Stop(%f)", Context()->CurrentTime(), NodeType(), Id(), aWhen); AudioNodeStream* ns = mStream; if (!ns || !Context()) { // We've already stopped and had our stream shut down return; } ns->SetStreamTimeParameter(STOP, Context(), std::max(0.0, aWhen)); }
void AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv) { if (aOutput >= NumberOfOutputs()) { aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); return; } WEB_AUDIO_API_LOG("%f: %s %u Disconnect()", Context()->CurrentTime(), NodeType(), Id()); // An upstream node may be starting to play on the graph thread, and the // engine for a downstream node may be sending a PlayingRefChangeHandler // ADDREF message to this (main) thread. Wait for a round trip before // releasing nodes, to give engines receiving sound now time to keep their // nodes alive. class RunnableRelease final : public nsRunnable { public: explicit RunnableRelease(already_AddRefed<AudioNode> aNode) : mNode(aNode) {} NS_IMETHODIMP Run() override { mNode = nullptr; return NS_OK; } private: RefPtr<AudioNode> mNode; }; for (int32_t i = mOutputNodes.Length() - 1; i >= 0; --i) { AudioNode* dest = mOutputNodes[i]; for (int32_t j = dest->mInputNodes.Length() - 1; j >= 0; --j) { InputNode& input = dest->mInputNodes[j]; if (input.mInputNode == this && input.mOutputPort == aOutput) { // Destroying the InputNode here sends a message to the graph thread // to disconnect the streams, which should be sent before the // RunAfterPendingUpdates() call below. dest->mInputNodes.RemoveElementAt(j); // Remove one instance of 'dest' from mOutputNodes. There could be // others, and it's not correct to remove them all since some of them // could be for different output ports. RefPtr<AudioNode> output = mOutputNodes[i].forget(); mOutputNodes.RemoveElementAt(i); output->NotifyInputsChanged(); if (mStream) { RefPtr<nsIRunnable> runnable = new RunnableRelease(output.forget()); mStream->RunAfterPendingUpdates(runnable.forget()); } break; } } } for (int32_t i = mOutputParams.Length() - 1; i >= 0; --i) { AudioParam* dest = mOutputParams[i]; for (int32_t j = dest->InputNodes().Length() - 1; j >= 0; --j) { const InputNode& input = dest->InputNodes()[j]; if (input.mInputNode == this && input.mOutputPort == aOutput) { dest->RemoveInputNode(j); // Remove one instance of 'dest' from mOutputParams. There could be // others, and it's not correct to remove them all since some of them // could be for different output ports. mOutputParams.RemoveElementAt(i); break; } } } // This disconnection may have disconnected a panner and a source. Context()->UpdatePannerSource(); }