michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef AudioNode_h_ michael@0: #define AudioNode_h_ michael@0: michael@0: #include "mozilla/DOMEventTargetHelper.h" michael@0: #include "mozilla/dom/AudioNodeBinding.h" michael@0: #include "nsCycleCollectionParticipant.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsTArray.h" michael@0: #include "AudioContext.h" michael@0: #include "MediaStreamGraph.h" michael@0: #include "WebAudioUtils.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: namespace dom { michael@0: michael@0: class AudioContext; michael@0: class AudioBufferSourceNode; michael@0: class AudioParam; michael@0: class AudioParamTimeline; michael@0: struct ThreeDPoint; michael@0: michael@0: template michael@0: class SelfReference { michael@0: public: michael@0: SelfReference() : mHeld(false) {} michael@0: ~SelfReference() michael@0: { michael@0: NS_ASSERTION(!mHeld, "Forgot to drop the self reference?"); michael@0: } michael@0: michael@0: void Take(T* t) michael@0: { michael@0: if (!mHeld) { michael@0: mHeld = true; michael@0: t->AddRef(); michael@0: } michael@0: } michael@0: void Drop(T* t) michael@0: { michael@0: if (mHeld) { michael@0: mHeld = false; michael@0: t->Release(); michael@0: } michael@0: } michael@0: michael@0: operator bool() const { return mHeld; } michael@0: michael@0: private: michael@0: bool mHeld; michael@0: }; michael@0: michael@0: /** michael@0: * The DOM object representing a Web Audio AudioNode. michael@0: * michael@0: * Each AudioNode has a MediaStream representing the actual michael@0: * real-time processing and output of this AudioNode. michael@0: * michael@0: * We track the incoming and outgoing connections to other AudioNodes. michael@0: * Outgoing connections have strong ownership. Also, AudioNodes that will michael@0: * produce sound on their output even when they have silent or no input ask michael@0: * the AudioContext to keep playing or tail-time references to keep them alive michael@0: * until the context is finished. michael@0: * michael@0: * Explicit disconnections will only remove references from output nodes after michael@0: * the graph is notified and the main thread receives a reply. Similarly, michael@0: * nodes with playing or tail-time references release these references only michael@0: * after receiving notification from their engine on the graph thread that michael@0: * playing has stopped. Engines notifying the main thread that they have michael@0: * finished do so strictly *after* producing and returning their last block. michael@0: * In this way, an engine that receives non-null input knows that the input michael@0: * comes from nodes that are still alive and will keep their output nodes michael@0: * alive for at least as long as it takes to process messages from the graph michael@0: * thread. i.e. the engine receiving non-null input knows that its node is michael@0: * still alive, and will still be alive when it receives a message from the michael@0: * engine. michael@0: */ michael@0: class AudioNode : public DOMEventTargetHelper michael@0: { michael@0: protected: michael@0: // You can only use refcounting to delete this object michael@0: virtual ~AudioNode(); michael@0: michael@0: public: michael@0: AudioNode(AudioContext* aContext, michael@0: uint32_t aChannelCount, michael@0: ChannelCountMode aChannelCountMode, michael@0: ChannelInterpretation aChannelInterpretation); michael@0: michael@0: // This should be idempotent (safe to call multiple times). michael@0: virtual void DestroyMediaStream(); michael@0: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioNode, michael@0: DOMEventTargetHelper) michael@0: michael@0: virtual AudioBufferSourceNode* AsAudioBufferSourceNode() { michael@0: return nullptr; michael@0: } michael@0: michael@0: virtual const DelayNode* AsDelayNode() const { michael@0: return nullptr; michael@0: } michael@0: michael@0: AudioContext* GetParentObject() const michael@0: { michael@0: return mContext; michael@0: } michael@0: michael@0: AudioContext* Context() const michael@0: { michael@0: return mContext; michael@0: } michael@0: michael@0: virtual void Connect(AudioNode& aDestination, uint32_t aOutput, michael@0: uint32_t aInput, ErrorResult& aRv); michael@0: michael@0: virtual void Connect(AudioParam& aDestination, uint32_t aOutput, michael@0: ErrorResult& aRv); michael@0: michael@0: virtual void Disconnect(uint32_t aOutput, ErrorResult& aRv); michael@0: michael@0: // The following two virtual methods must be implemented by each node type michael@0: // to provide their number of input and output ports. These numbers are michael@0: // constant for the lifetime of the node. Both default to 1. michael@0: virtual uint16_t NumberOfInputs() const { return 1; } michael@0: virtual uint16_t NumberOfOutputs() const { return 1; } michael@0: michael@0: uint32_t ChannelCount() const { return mChannelCount; } michael@0: virtual void SetChannelCount(uint32_t aChannelCount, ErrorResult& aRv) michael@0: { michael@0: if (aChannelCount == 0 || michael@0: aChannelCount > WebAudioUtils::MaxChannelCount) { michael@0: aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); michael@0: return; michael@0: } michael@0: mChannelCount = aChannelCount; michael@0: SendChannelMixingParametersToStream(); michael@0: } michael@0: ChannelCountMode ChannelCountModeValue() const michael@0: { michael@0: return mChannelCountMode; michael@0: } michael@0: virtual void SetChannelCountModeValue(ChannelCountMode aMode, ErrorResult& aRv) michael@0: { michael@0: mChannelCountMode = aMode; michael@0: SendChannelMixingParametersToStream(); michael@0: } michael@0: ChannelInterpretation ChannelInterpretationValue() const michael@0: { michael@0: return mChannelInterpretation; michael@0: } michael@0: void SetChannelInterpretationValue(ChannelInterpretation aMode) michael@0: { michael@0: mChannelInterpretation = aMode; michael@0: SendChannelMixingParametersToStream(); michael@0: } michael@0: michael@0: struct InputNode { michael@0: ~InputNode() michael@0: { michael@0: if (mStreamPort) { michael@0: mStreamPort->Destroy(); michael@0: } michael@0: } michael@0: michael@0: size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t amount = 0; michael@0: if (mStreamPort) { michael@0: amount += mStreamPort->SizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: return amount; michael@0: } michael@0: michael@0: // Weak reference. michael@0: AudioNode* mInputNode; michael@0: nsRefPtr mStreamPort; michael@0: // The index of the input port this node feeds into. michael@0: // This is not used for connections to AudioParams. michael@0: uint32_t mInputPort; michael@0: // The index of the output port this node comes out of. michael@0: uint32_t mOutputPort; michael@0: }; michael@0: michael@0: MediaStream* Stream() { return mStream; } michael@0: michael@0: const nsTArray& InputNodes() const michael@0: { michael@0: return mInputNodes; michael@0: } michael@0: const nsTArray >& OutputNodes() const michael@0: { michael@0: return mOutputNodes; michael@0: } michael@0: const nsTArray >& OutputParams() const michael@0: { michael@0: return mOutputParams; michael@0: } michael@0: michael@0: void RemoveOutputParam(AudioParam* aParam); michael@0: michael@0: // MarkActive() asks the context to keep the AudioNode alive until the michael@0: // context is finished. This takes care of "playing" references and michael@0: // "tail-time" references. michael@0: void MarkActive() { Context()->RegisterActiveNode(this); } michael@0: // Active nodes call MarkInactive() when they have finished producing sound michael@0: // for the foreseeable future. michael@0: // Do not call MarkInactive from a node destructor. If the destructor is michael@0: // called, then the node is already inactive. michael@0: // MarkInactive() may delete |this|. michael@0: void MarkInactive() { Context()->UnregisterActiveNode(this); } michael@0: michael@0: virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const; michael@0: virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; michael@0: michael@0: virtual const char* NodeType() const = 0; michael@0: michael@0: private: michael@0: friend class AudioBufferSourceNode; michael@0: // This could possibly delete 'this'. michael@0: void DisconnectFromGraph(); michael@0: michael@0: protected: michael@0: static void Callback(AudioNode* aNode) { /* not implemented */ } michael@0: michael@0: // Helpers for sending different value types to streams michael@0: void SendDoubleParameterToStream(uint32_t aIndex, double aValue); michael@0: void SendInt32ParameterToStream(uint32_t aIndex, int32_t aValue); michael@0: void SendThreeDPointParameterToStream(uint32_t aIndex, const ThreeDPoint& aValue); michael@0: void SendChannelMixingParametersToStream(); michael@0: static void SendTimelineParameterToStream(AudioNode* aNode, uint32_t aIndex, michael@0: const AudioParamTimeline& aValue); michael@0: michael@0: private: michael@0: nsRefPtr mContext; michael@0: michael@0: protected: michael@0: // Must be set in the constructor. Must not be null. michael@0: // If MaxNumberOfInputs() is > 0, then mStream must be a ProcessedMediaStream. michael@0: nsRefPtr mStream; michael@0: michael@0: private: michael@0: // For every InputNode, there is a corresponding entry in mOutputNodes of the michael@0: // InputNode's mInputNode. michael@0: nsTArray mInputNodes; michael@0: // For every mOutputNode entry, there is a corresponding entry in mInputNodes michael@0: // of the mOutputNode entry. We won't necessarily be able to identify the michael@0: // exact matching entry, since mOutputNodes doesn't include the port michael@0: // identifiers and the same node could be connected on multiple ports. michael@0: nsTArray > mOutputNodes; michael@0: // For every mOutputParams entry, there is a corresponding entry in michael@0: // AudioParam::mInputNodes of the mOutputParams entry. We won't necessarily be michael@0: // able to identify the exact matching entry, since mOutputParams doesn't michael@0: // include the port identifiers and the same node could be connected on michael@0: // multiple ports. michael@0: nsTArray > mOutputParams; michael@0: uint32_t mChannelCount; michael@0: ChannelCountMode mChannelCountMode; michael@0: ChannelInterpretation mChannelInterpretation; michael@0: }; michael@0: michael@0: } michael@0: } michael@0: michael@0: #endif