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: #ifndef MOZILLA_AUDIONODEENGINE_H_ michael@0: #define MOZILLA_AUDIONODEENGINE_H_ michael@0: michael@0: #include "AudioSegment.h" michael@0: #include "mozilla/dom/AudioNode.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/Mutex.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: namespace dom { michael@0: struct ThreeDPoint; michael@0: class AudioParamTimeline; michael@0: class DelayNodeEngine; michael@0: } michael@0: michael@0: class AudioNodeStream; michael@0: michael@0: /** michael@0: * This class holds onto a set of immutable channel buffers. The storage michael@0: * for the buffers must be malloced, but the buffer pointers and the malloc michael@0: * pointers can be different (e.g. if the buffers are contained inside michael@0: * some malloced object). michael@0: */ michael@0: class ThreadSharedFloatArrayBufferList : public ThreadSharedObject { michael@0: public: michael@0: /** michael@0: * Construct with null data. michael@0: */ michael@0: ThreadSharedFloatArrayBufferList(uint32_t aCount) michael@0: { michael@0: mContents.SetLength(aCount); michael@0: } michael@0: michael@0: struct Storage { michael@0: Storage() michael@0: { michael@0: mDataToFree = nullptr; michael@0: mSampleData = nullptr; michael@0: } michael@0: ~Storage() { free(mDataToFree); } michael@0: size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: // NB: mSampleData might not be owned, if it is it just points to michael@0: // mDataToFree. michael@0: return aMallocSizeOf(mDataToFree); michael@0: } michael@0: void* mDataToFree; michael@0: const float* mSampleData; michael@0: }; michael@0: michael@0: /** michael@0: * This can be called on any thread. michael@0: */ michael@0: uint32_t GetChannels() const { return mContents.Length(); } michael@0: /** michael@0: * This can be called on any thread. michael@0: */ michael@0: const float* GetData(uint32_t aIndex) const { return mContents[aIndex].mSampleData; } michael@0: michael@0: /** michael@0: * Call this only during initialization, before the object is handed to michael@0: * any other thread. michael@0: */ michael@0: void SetData(uint32_t aIndex, void* aDataToFree, const float* aData) michael@0: { michael@0: Storage* s = &mContents[aIndex]; michael@0: free(s->mDataToFree); michael@0: s->mDataToFree = aDataToFree; michael@0: s->mSampleData = aData; michael@0: } michael@0: michael@0: /** michael@0: * Put this object into an error state where there are no channels. michael@0: */ michael@0: void Clear() { mContents.Clear(); } michael@0: michael@0: virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE michael@0: { michael@0: size_t amount = ThreadSharedObject::SizeOfExcludingThis(aMallocSizeOf); michael@0: amount += mContents.SizeOfExcludingThis(aMallocSizeOf); michael@0: for (size_t i = 0; i < mContents.Length(); i++) { michael@0: amount += mContents[i].SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: return amount; michael@0: } michael@0: michael@0: private: michael@0: AutoFallibleTArray mContents; michael@0: }; michael@0: michael@0: /** michael@0: * Allocates an AudioChunk with fresh buffers of WEBAUDIO_BLOCK_SIZE float samples. michael@0: * AudioChunk::mChannelData's entries can be cast to float* for writing. michael@0: */ michael@0: void AllocateAudioBlock(uint32_t aChannelCount, AudioChunk* aChunk); michael@0: michael@0: /** michael@0: * aChunk must have been allocated by AllocateAudioBlock. michael@0: */ michael@0: void WriteZeroesToAudioBlock(AudioChunk* aChunk, uint32_t aStart, uint32_t aLength); michael@0: michael@0: /** michael@0: * Copy with scale. aScale == 1.0f should be optimized. michael@0: */ michael@0: void AudioBufferCopyWithScale(const float* aInput, michael@0: float aScale, michael@0: float* aOutput, michael@0: uint32_t aSize); michael@0: michael@0: /** michael@0: * Pointwise multiply-add operation. aScale == 1.0f should be optimized. michael@0: */ michael@0: void AudioBufferAddWithScale(const float* aInput, michael@0: float aScale, michael@0: float* aOutput, michael@0: uint32_t aSize); michael@0: michael@0: /** michael@0: * Pointwise multiply-add operation. aScale == 1.0f should be optimized. michael@0: */ michael@0: void AudioBlockAddChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE], michael@0: float aScale, michael@0: float aOutput[WEBAUDIO_BLOCK_SIZE]); michael@0: michael@0: /** michael@0: * Pointwise copy-scaled operation. aScale == 1.0f should be optimized. michael@0: * michael@0: * Buffer size is implicitly assumed to be WEBAUDIO_BLOCK_SIZE. michael@0: */ michael@0: void AudioBlockCopyChannelWithScale(const float* aInput, michael@0: float aScale, michael@0: float* aOutput); michael@0: michael@0: /** michael@0: * Vector copy-scaled operation. michael@0: */ michael@0: void AudioBlockCopyChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE], michael@0: const float aScale[WEBAUDIO_BLOCK_SIZE], michael@0: float aOutput[WEBAUDIO_BLOCK_SIZE]); michael@0: michael@0: /** michael@0: * Vector complex multiplication on arbitrary sized buffers. michael@0: */ michael@0: void BufferComplexMultiply(const float* aInput, michael@0: const float* aScale, michael@0: float* aOutput, michael@0: uint32_t aSize); michael@0: michael@0: /** michael@0: * Vector maximum element magnitude ( max(abs(aInput)) ). michael@0: */ michael@0: float AudioBufferPeakValue(const float* aInput, uint32_t aSize); michael@0: michael@0: /** michael@0: * In place gain. aScale == 1.0f should be optimized. michael@0: */ michael@0: void AudioBlockInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE], michael@0: float aScale); michael@0: michael@0: /** michael@0: * In place gain. aScale == 1.0f should be optimized. michael@0: */ michael@0: void AudioBufferInPlaceScale(float* aBlock, michael@0: float aScale, michael@0: uint32_t aSize); michael@0: michael@0: /** michael@0: * Upmix a mono input to a stereo output, scaling the two output channels by two michael@0: * different gain value. michael@0: * This algorithm is specified in the WebAudio spec. michael@0: */ michael@0: void michael@0: AudioBlockPanMonoToStereo(const float aInput[WEBAUDIO_BLOCK_SIZE], michael@0: float aGainL, float aGainR, michael@0: float aOutputL[WEBAUDIO_BLOCK_SIZE], michael@0: float aOutputR[WEBAUDIO_BLOCK_SIZE]); michael@0: /** michael@0: * Pan a stereo source according to right and left gain, and the position michael@0: * (whether the listener is on the left of the source or not). michael@0: * This algorithm is specified in the WebAudio spec. michael@0: */ michael@0: void michael@0: AudioBlockPanStereoToStereo(const float aInputL[WEBAUDIO_BLOCK_SIZE], michael@0: const float aInputR[WEBAUDIO_BLOCK_SIZE], michael@0: float aGainL, float aGainR, bool aIsOnTheLeft, michael@0: float aOutputL[WEBAUDIO_BLOCK_SIZE], michael@0: float aOutputR[WEBAUDIO_BLOCK_SIZE]); michael@0: michael@0: /** michael@0: * Return the sum of squares of all of the samples in the input. michael@0: */ michael@0: float michael@0: AudioBufferSumOfSquares(const float* aInput, uint32_t aLength); michael@0: michael@0: /** michael@0: * All methods of this class and its subclasses are called on the michael@0: * MediaStreamGraph thread. michael@0: */ michael@0: class AudioNodeEngine { michael@0: public: michael@0: // This should be compatible with AudioNodeStream::OutputChunks. michael@0: typedef nsAutoTArray OutputChunks; michael@0: michael@0: explicit AudioNodeEngine(dom::AudioNode* aNode) michael@0: : mNode(aNode) michael@0: , mNodeMutex("AudioNodeEngine::mNodeMutex") michael@0: , mInputCount(aNode ? aNode->NumberOfInputs() : 1) michael@0: , mOutputCount(aNode ? aNode->NumberOfOutputs() : 0) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_COUNT_CTOR(AudioNodeEngine); michael@0: } michael@0: virtual ~AudioNodeEngine() michael@0: { michael@0: MOZ_ASSERT(!mNode, "The node reference must be already cleared"); michael@0: MOZ_COUNT_DTOR(AudioNodeEngine); michael@0: } michael@0: michael@0: virtual dom::DelayNodeEngine* AsDelayNodeEngine() { return nullptr; } michael@0: michael@0: virtual void SetStreamTimeParameter(uint32_t aIndex, TrackTicks aParam) michael@0: { michael@0: NS_ERROR("Invalid SetStreamTimeParameter index"); michael@0: } michael@0: virtual void SetDoubleParameter(uint32_t aIndex, double aParam) michael@0: { michael@0: NS_ERROR("Invalid SetDoubleParameter index"); michael@0: } michael@0: virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam) michael@0: { michael@0: NS_ERROR("Invalid SetInt32Parameter index"); michael@0: } michael@0: virtual void SetTimelineParameter(uint32_t aIndex, michael@0: const dom::AudioParamTimeline& aValue, michael@0: TrackRate aSampleRate) michael@0: { michael@0: NS_ERROR("Invalid SetTimelineParameter index"); michael@0: } michael@0: virtual void SetThreeDPointParameter(uint32_t aIndex, michael@0: const dom::ThreeDPoint& aValue) michael@0: { michael@0: NS_ERROR("Invalid SetThreeDPointParameter index"); michael@0: } michael@0: virtual void SetBuffer(already_AddRefed aBuffer) michael@0: { michael@0: NS_ERROR("SetBuffer called on engine that doesn't support it"); michael@0: } michael@0: // This consumes the contents of aData. aData will be emptied after this returns. michael@0: virtual void SetRawArrayData(nsTArray& aData) michael@0: { michael@0: NS_ERROR("SetRawArrayData called on an engine that doesn't support it"); michael@0: } michael@0: michael@0: /** michael@0: * Produce the next block of audio samples, given input samples aInput michael@0: * (the mixed data for input 0). michael@0: * aInput is guaranteed to have float sample format (if it has samples at all) michael@0: * and to have been resampled to the sampling rate for the stream, and to have michael@0: * exactly WEBAUDIO_BLOCK_SIZE samples. michael@0: * *aFinished is set to false by the caller. If the callee sets it to true, michael@0: * we'll finish the stream and not call this again. michael@0: */ michael@0: virtual void ProcessBlock(AudioNodeStream* aStream, michael@0: const AudioChunk& aInput, michael@0: AudioChunk* aOutput, michael@0: bool* aFinished) michael@0: { michael@0: MOZ_ASSERT(mInputCount <= 1 && mOutputCount <= 1); michael@0: *aOutput = aInput; michael@0: } michael@0: /** michael@0: * Produce the next block of audio samples, before input is provided. michael@0: * ProcessBlock() will be called later, and it then should not change michael@0: * aOutput. This is used only for DelayNodeEngine in a feedback loop. michael@0: */ michael@0: virtual void ProduceBlockBeforeInput(AudioChunk* aOutput) michael@0: { michael@0: NS_NOTREACHED("ProduceBlockBeforeInput called on wrong engine\n"); michael@0: } michael@0: michael@0: /** michael@0: * Produce the next block of audio samples, given input samples in the aInput michael@0: * array. There is one input sample per active port in aInput, in order. michael@0: * This is the multi-input/output version of ProcessBlock. Only one kind michael@0: * of ProcessBlock is called on each node, depending on whether the michael@0: * number of inputs and outputs are both 1 or not. michael@0: * michael@0: * aInput is always guaranteed to not contain more input AudioChunks than the michael@0: * maximum number of inputs for the node. It is the responsibility of the michael@0: * overrides of this function to make sure they will only add a maximum number michael@0: * of AudioChunks to aOutput as advertized by the AudioNode implementation. michael@0: * An engine may choose to produce fewer inputs than advertizes by the michael@0: * corresponding AudioNode, in which case it will be interpreted as a channel michael@0: * of silence. michael@0: */ michael@0: virtual void ProcessBlocksOnPorts(AudioNodeStream* aStream, michael@0: const OutputChunks& aInput, michael@0: OutputChunks& aOutput, michael@0: bool* aFinished) michael@0: { michael@0: MOZ_ASSERT(mInputCount > 1 || mOutputCount > 1); michael@0: // Only produce one output port, and drop all other input ports. michael@0: aOutput[0] = aInput[0]; michael@0: } michael@0: michael@0: Mutex& NodeMutex() { return mNodeMutex;} michael@0: michael@0: bool HasNode() const michael@0: { michael@0: return !!mNode; michael@0: } michael@0: michael@0: dom::AudioNode* Node() const michael@0: { michael@0: mNodeMutex.AssertCurrentThreadOwns(); michael@0: return mNode; michael@0: } michael@0: michael@0: dom::AudioNode* NodeMainThread() const michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: return mNode; michael@0: } michael@0: michael@0: void ClearNode() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(mNode != nullptr); michael@0: mNodeMutex.AssertCurrentThreadOwns(); michael@0: mNode = nullptr; michael@0: } michael@0: michael@0: uint16_t InputCount() const { return mInputCount; } michael@0: uint16_t OutputCount() const { return mOutputCount; } michael@0: michael@0: virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: // NB: |mNode| is tracked separately so it is excluded here. michael@0: return 0; michael@0: } michael@0: michael@0: virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: void SizeOfIncludingThis(MallocSizeOf aMallocSizeOf, michael@0: AudioNodeSizes& aUsage) const michael@0: { michael@0: aUsage.mEngine = SizeOfIncludingThis(aMallocSizeOf); michael@0: aUsage.mDomNode = mNode->SizeOfIncludingThis(aMallocSizeOf); michael@0: aUsage.mNodeType = mNode->NodeType(); michael@0: } michael@0: michael@0: private: michael@0: dom::AudioNode* mNode; michael@0: Mutex mNodeMutex; michael@0: const uint16_t mInputCount; michael@0: const uint16_t mOutputCount; michael@0: }; michael@0: michael@0: } michael@0: michael@0: #endif /* MOZILLA_AUDIONODEENGINE_H_ */