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: #include "AudioContext.h" michael@0: michael@0: #include "nsPIDOMWindow.h" michael@0: #include "mozilla/ErrorResult.h" michael@0: #include "mozilla/dom/AnalyserNode.h" michael@0: #include "mozilla/dom/AudioContextBinding.h" michael@0: #include "mozilla/dom/HTMLMediaElement.h" michael@0: #include "mozilla/dom/OfflineAudioContextBinding.h" michael@0: #include "mozilla/dom/OwningNonNull.h" michael@0: #include "MediaStreamGraph.h" michael@0: #include "AudioDestinationNode.h" michael@0: #include "AudioBufferSourceNode.h" michael@0: #include "AudioBuffer.h" michael@0: #include "GainNode.h" michael@0: #include "MediaElementAudioSourceNode.h" michael@0: #include "MediaStreamAudioSourceNode.h" michael@0: #include "DelayNode.h" michael@0: #include "PannerNode.h" michael@0: #include "AudioListener.h" michael@0: #include "DynamicsCompressorNode.h" michael@0: #include "BiquadFilterNode.h" michael@0: #include "ScriptProcessorNode.h" michael@0: #include "ChannelMergerNode.h" michael@0: #include "ChannelSplitterNode.h" michael@0: #include "MediaStreamAudioDestinationNode.h" michael@0: #include "WaveShaperNode.h" michael@0: #include "PeriodicWave.h" michael@0: #include "ConvolverNode.h" michael@0: #include "OscillatorNode.h" michael@0: #include "nsNetUtil.h" michael@0: #include "AudioStream.h" michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(AudioContext) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioContext) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mDestination) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener) michael@0: if (!tmp->mIsStarted) { michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveNodes) michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DOMEventTargetHelper) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AudioContext, michael@0: DOMEventTargetHelper) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDestination) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListener) michael@0: if (!tmp->mIsStarted) { michael@0: MOZ_ASSERT(tmp->mIsOffline, michael@0: "Online AudioContexts should always be started"); michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActiveNodes) michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(AudioContext, DOMEventTargetHelper) michael@0: NS_IMPL_RELEASE_INHERITED(AudioContext, DOMEventTargetHelper) michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioContext) michael@0: NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) michael@0: michael@0: static float GetSampleRateForAudioContext(bool aIsOffline, float aSampleRate) michael@0: { michael@0: if (aIsOffline) { michael@0: return aSampleRate; michael@0: } else { michael@0: AudioStream::InitPreferredSampleRate(); michael@0: return static_cast(AudioStream::PreferredSampleRate()); michael@0: } michael@0: } michael@0: michael@0: AudioContext::AudioContext(nsPIDOMWindow* aWindow, michael@0: bool aIsOffline, michael@0: AudioChannel aChannel, michael@0: uint32_t aNumberOfChannels, michael@0: uint32_t aLength, michael@0: float aSampleRate) michael@0: : DOMEventTargetHelper(aWindow) michael@0: , mSampleRate(GetSampleRateForAudioContext(aIsOffline, aSampleRate)) michael@0: , mNumberOfChannels(aNumberOfChannels) michael@0: , mNodeCount(0) michael@0: , mIsOffline(aIsOffline) michael@0: , mIsStarted(!aIsOffline) michael@0: , mIsShutDown(false) michael@0: { michael@0: aWindow->AddAudioContext(this); michael@0: michael@0: // Note: AudioDestinationNode needs an AudioContext that must already be michael@0: // bound to the window. michael@0: mDestination = new AudioDestinationNode(this, aIsOffline, aChannel, michael@0: aNumberOfChannels, aLength, aSampleRate); michael@0: // We skip calling SetIsOnlyNodeForContext during mDestination's constructor, michael@0: // because we can only call SetIsOnlyNodeForContext after mDestination has michael@0: // been set up. michael@0: mDestination->SetIsOnlyNodeForContext(true); michael@0: } michael@0: michael@0: AudioContext::~AudioContext() michael@0: { michael@0: nsPIDOMWindow* window = GetOwner(); michael@0: if (window) { michael@0: window->RemoveAudioContext(this); michael@0: } michael@0: michael@0: UnregisterWeakMemoryReporter(this); michael@0: } michael@0: michael@0: JSObject* michael@0: AudioContext::WrapObject(JSContext* aCx) michael@0: { michael@0: if (mIsOffline) { michael@0: return OfflineAudioContextBinding::Wrap(aCx, this); michael@0: } else { michael@0: return AudioContextBinding::Wrap(aCx, this); michael@0: } michael@0: } michael@0: michael@0: /* static */ already_AddRefed michael@0: AudioContext::Constructor(const GlobalObject& aGlobal, michael@0: ErrorResult& aRv) michael@0: { michael@0: nsCOMPtr window = do_QueryInterface(aGlobal.GetAsSupports()); michael@0: if (!window) { michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr object = new AudioContext(window, false); michael@0: michael@0: RegisterWeakMemoryReporter(object); michael@0: michael@0: return object.forget(); michael@0: } michael@0: michael@0: /* static */ already_AddRefed michael@0: AudioContext::Constructor(const GlobalObject& aGlobal, michael@0: AudioChannel aChannel, michael@0: ErrorResult& aRv) michael@0: { michael@0: nsCOMPtr window = do_QueryInterface(aGlobal.GetAsSupports()); michael@0: if (!window) { michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr object = new AudioContext(window, false, aChannel); michael@0: michael@0: RegisterWeakMemoryReporter(object); michael@0: michael@0: return object.forget(); michael@0: } michael@0: michael@0: /* static */ already_AddRefed michael@0: AudioContext::Constructor(const GlobalObject& aGlobal, michael@0: uint32_t aNumberOfChannels, michael@0: uint32_t aLength, michael@0: float aSampleRate, michael@0: ErrorResult& aRv) michael@0: { michael@0: nsCOMPtr window = do_QueryInterface(aGlobal.GetAsSupports()); michael@0: if (!window) { michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aNumberOfChannels == 0 || michael@0: aNumberOfChannels > WebAudioUtils::MaxChannelCount || michael@0: aLength == 0 || michael@0: aSampleRate < WebAudioUtils::MinSampleRate || michael@0: aSampleRate > WebAudioUtils::MaxSampleRate) { michael@0: // The DOM binding protects us against infinity and NaN michael@0: aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr object = new AudioContext(window, michael@0: true, michael@0: AudioChannel::Normal, michael@0: aNumberOfChannels, michael@0: aLength, michael@0: aSampleRate); michael@0: michael@0: RegisterWeakMemoryReporter(object); michael@0: michael@0: return object.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreateBufferSource() michael@0: { michael@0: nsRefPtr bufferNode = michael@0: new AudioBufferSourceNode(this); michael@0: return bufferNode.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreateBuffer(JSContext* aJSContext, uint32_t aNumberOfChannels, michael@0: uint32_t aLength, float aSampleRate, michael@0: ErrorResult& aRv) michael@0: { michael@0: if (!aNumberOfChannels) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: return AudioBuffer::Create(this, aNumberOfChannels, aLength, michael@0: aSampleRate, aJSContext, aRv); michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: bool IsValidBufferSize(uint32_t aBufferSize) { michael@0: switch (aBufferSize) { michael@0: case 0: // let the implementation choose the buffer size michael@0: case 256: michael@0: case 512: michael@0: case 1024: michael@0: case 2048: michael@0: case 4096: michael@0: case 8192: michael@0: case 16384: michael@0: return true; michael@0: default: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreateMediaStreamDestination(ErrorResult& aRv) michael@0: { michael@0: if (mIsOffline) { michael@0: aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr node = michael@0: new MediaStreamAudioDestinationNode(this); michael@0: return node.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreateScriptProcessor(uint32_t aBufferSize, michael@0: uint32_t aNumberOfInputChannels, michael@0: uint32_t aNumberOfOutputChannels, michael@0: ErrorResult& aRv) michael@0: { michael@0: if ((aNumberOfInputChannels == 0 && aNumberOfOutputChannels == 0) || michael@0: aNumberOfInputChannels > WebAudioUtils::MaxChannelCount || michael@0: aNumberOfOutputChannels > WebAudioUtils::MaxChannelCount || michael@0: !IsValidBufferSize(aBufferSize)) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr scriptProcessor = michael@0: new ScriptProcessorNode(this, aBufferSize, aNumberOfInputChannels, michael@0: aNumberOfOutputChannels); michael@0: return scriptProcessor.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreateAnalyser() michael@0: { michael@0: nsRefPtr analyserNode = new AnalyserNode(this); michael@0: return analyserNode.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreateMediaElementSource(HTMLMediaElement& aMediaElement, michael@0: ErrorResult& aRv) michael@0: { michael@0: if (mIsOffline) { michael@0: aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); michael@0: return nullptr; michael@0: } michael@0: nsRefPtr stream = aMediaElement.MozCaptureStream(aRv); michael@0: if (aRv.Failed()) { michael@0: return nullptr; michael@0: } michael@0: nsRefPtr mediaElementAudioSourceNode = michael@0: new MediaElementAudioSourceNode(this, stream); michael@0: return mediaElementAudioSourceNode.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreateMediaStreamSource(DOMMediaStream& aMediaStream, michael@0: ErrorResult& aRv) michael@0: { michael@0: if (mIsOffline) { michael@0: aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); michael@0: return nullptr; michael@0: } michael@0: nsRefPtr mediaStreamAudioSourceNode = michael@0: new MediaStreamAudioSourceNode(this, &aMediaStream); michael@0: return mediaStreamAudioSourceNode.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreateGain() michael@0: { michael@0: nsRefPtr gainNode = new GainNode(this); michael@0: return gainNode.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreateWaveShaper() michael@0: { michael@0: nsRefPtr waveShaperNode = new WaveShaperNode(this); michael@0: return waveShaperNode.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreateDelay(double aMaxDelayTime, ErrorResult& aRv) michael@0: { michael@0: if (aMaxDelayTime > 0. && aMaxDelayTime < 180.) { michael@0: nsRefPtr delayNode = new DelayNode(this, aMaxDelayTime); michael@0: return delayNode.forget(); michael@0: } michael@0: aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreatePanner() michael@0: { michael@0: nsRefPtr pannerNode = new PannerNode(this); michael@0: mPannerNodes.PutEntry(pannerNode); michael@0: return pannerNode.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreateConvolver() michael@0: { michael@0: nsRefPtr convolverNode = new ConvolverNode(this); michael@0: return convolverNode.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreateChannelSplitter(uint32_t aNumberOfOutputs, ErrorResult& aRv) michael@0: { michael@0: if (aNumberOfOutputs == 0 || michael@0: aNumberOfOutputs > WebAudioUtils::MaxChannelCount) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr splitterNode = michael@0: new ChannelSplitterNode(this, aNumberOfOutputs); michael@0: return splitterNode.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreateChannelMerger(uint32_t aNumberOfInputs, ErrorResult& aRv) michael@0: { michael@0: if (aNumberOfInputs == 0 || michael@0: aNumberOfInputs > WebAudioUtils::MaxChannelCount) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr mergerNode = michael@0: new ChannelMergerNode(this, aNumberOfInputs); michael@0: return mergerNode.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreateDynamicsCompressor() michael@0: { michael@0: nsRefPtr compressorNode = michael@0: new DynamicsCompressorNode(this); michael@0: return compressorNode.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreateBiquadFilter() michael@0: { michael@0: nsRefPtr filterNode = michael@0: new BiquadFilterNode(this); michael@0: return filterNode.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreateOscillator() michael@0: { michael@0: nsRefPtr oscillatorNode = michael@0: new OscillatorNode(this); michael@0: return oscillatorNode.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: AudioContext::CreatePeriodicWave(const Float32Array& aRealData, michael@0: const Float32Array& aImagData, michael@0: ErrorResult& aRv) michael@0: { michael@0: aRealData.ComputeLengthAndData(); michael@0: aImagData.ComputeLengthAndData(); michael@0: michael@0: if (aRealData.Length() != aImagData.Length() || michael@0: aRealData.Length() == 0 || michael@0: aRealData.Length() > 4096) { michael@0: aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr periodicWave = michael@0: new PeriodicWave(this, aRealData.Data(), aImagData.Data(), michael@0: aImagData.Length(), aRv); michael@0: if (aRv.Failed()) { michael@0: return nullptr; michael@0: } michael@0: return periodicWave.forget(); michael@0: } michael@0: michael@0: AudioListener* michael@0: AudioContext::Listener() michael@0: { michael@0: if (!mListener) { michael@0: mListener = new AudioListener(this); michael@0: } michael@0: return mListener; michael@0: } michael@0: michael@0: void michael@0: AudioContext::DecodeAudioData(const ArrayBuffer& aBuffer, michael@0: DecodeSuccessCallback& aSuccessCallback, michael@0: const Optional >& aFailureCallback) michael@0: { michael@0: AutoJSAPI jsapi; michael@0: JSContext* cx = jsapi.cx(); michael@0: JSAutoCompartment ac(cx, aBuffer.Obj()); michael@0: michael@0: aBuffer.ComputeLengthAndData(); michael@0: michael@0: // Neuter the array buffer michael@0: size_t length = aBuffer.Length(); michael@0: JS::RootedObject obj(cx, aBuffer.Obj()); michael@0: michael@0: uint8_t* data = static_cast(JS_StealArrayBufferContents(cx, obj)); michael@0: michael@0: // Sniff the content of the media. michael@0: // Failed type sniffing will be handled by AsyncDecodeMedia. michael@0: nsAutoCString contentType; michael@0: NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, nullptr, data, length, contentType); michael@0: michael@0: nsRefPtr failureCallback; michael@0: if (aFailureCallback.WasPassed()) { michael@0: failureCallback = &aFailureCallback.Value(); michael@0: } michael@0: nsRefPtr job( michael@0: new WebAudioDecodeJob(contentType, this, michael@0: &aSuccessCallback, failureCallback)); michael@0: mDecoder.AsyncDecodeMedia(contentType.get(), data, length, *job); michael@0: // Transfer the ownership to mDecodeJobs michael@0: mDecodeJobs.AppendElement(job); michael@0: } michael@0: michael@0: void michael@0: AudioContext::RemoveFromDecodeQueue(WebAudioDecodeJob* aDecodeJob) michael@0: { michael@0: mDecodeJobs.RemoveElement(aDecodeJob); michael@0: } michael@0: michael@0: void michael@0: AudioContext::RegisterActiveNode(AudioNode* aNode) michael@0: { michael@0: if (!mIsShutDown) { michael@0: mActiveNodes.PutEntry(aNode); michael@0: } michael@0: } michael@0: michael@0: void michael@0: AudioContext::UnregisterActiveNode(AudioNode* aNode) michael@0: { michael@0: mActiveNodes.RemoveEntry(aNode); michael@0: } michael@0: michael@0: void michael@0: AudioContext::UnregisterAudioBufferSourceNode(AudioBufferSourceNode* aNode) michael@0: { michael@0: UpdatePannerSource(); michael@0: } michael@0: michael@0: void michael@0: AudioContext::UnregisterPannerNode(PannerNode* aNode) michael@0: { michael@0: mPannerNodes.RemoveEntry(aNode); michael@0: if (mListener) { michael@0: mListener->UnregisterPannerNode(aNode); michael@0: } michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: FindConnectedSourcesOn(nsPtrHashKey* aEntry, void* aData) michael@0: { michael@0: aEntry->GetKey()->FindConnectedSources(); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: void michael@0: AudioContext::UpdatePannerSource() michael@0: { michael@0: mPannerNodes.EnumerateEntries(FindConnectedSourcesOn, nullptr); michael@0: } michael@0: michael@0: uint32_t michael@0: AudioContext::MaxChannelCount() const michael@0: { michael@0: return mIsOffline ? mNumberOfChannels : AudioStream::MaxNumberOfChannels(); michael@0: } michael@0: michael@0: MediaStreamGraph* michael@0: AudioContext::Graph() const michael@0: { michael@0: return Destination()->Stream()->Graph(); michael@0: } michael@0: michael@0: MediaStream* michael@0: AudioContext::DestinationStream() const michael@0: { michael@0: if (Destination()) { michael@0: return Destination()->Stream(); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: double michael@0: AudioContext::CurrentTime() const michael@0: { michael@0: return MediaTimeToSeconds(Destination()->Stream()->GetCurrentTime()) + michael@0: ExtraCurrentTime(); michael@0: } michael@0: michael@0: void michael@0: AudioContext::Shutdown() michael@0: { michael@0: mIsShutDown = true; michael@0: michael@0: // We mute rather than suspending, because the delay between the ::Shutdown michael@0: // call and the CC would make us overbuffer in the MediaStreamGraph. michael@0: // See bug 936784 for details. michael@0: if (!mIsOffline) { michael@0: Mute(); michael@0: } michael@0: michael@0: mDecoder.Shutdown(); michael@0: michael@0: // Release references to active nodes. michael@0: // Active AudioNodes don't unregister in destructors, at which point the michael@0: // Node is already unregistered. michael@0: mActiveNodes.Clear(); michael@0: michael@0: // For offline contexts, we can destroy the MediaStreamGraph at this point. michael@0: if (mIsOffline && mDestination) { michael@0: mDestination->OfflineShutdown(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: AudioContext::Suspend() michael@0: { michael@0: MediaStream* ds = DestinationStream(); michael@0: if (ds) { michael@0: ds->ChangeExplicitBlockerCount(1); michael@0: } michael@0: } michael@0: michael@0: void michael@0: AudioContext::Resume() michael@0: { michael@0: MediaStream* ds = DestinationStream(); michael@0: if (ds) { michael@0: ds->ChangeExplicitBlockerCount(-1); michael@0: } michael@0: } michael@0: michael@0: void michael@0: AudioContext::UpdateNodeCount(int32_t aDelta) michael@0: { michael@0: bool firstNode = mNodeCount == 0; michael@0: mNodeCount += aDelta; michael@0: MOZ_ASSERT(mNodeCount >= 0); michael@0: // mDestinationNode may be null when we're destroying nodes unlinked by CC michael@0: if (!firstNode && mDestination) { michael@0: mDestination->SetIsOnlyNodeForContext(mNodeCount == 1); michael@0: } michael@0: } michael@0: michael@0: JSContext* michael@0: AudioContext::GetJSContext() const michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr scriptGlobal = michael@0: do_QueryInterface(GetParentObject()); michael@0: if (!scriptGlobal) { michael@0: return nullptr; michael@0: } michael@0: nsIScriptContext* scriptContext = scriptGlobal->GetContext(); michael@0: if (!scriptContext) { michael@0: return nullptr; michael@0: } michael@0: return scriptContext->GetNativeContext(); michael@0: } michael@0: michael@0: void michael@0: AudioContext::StartRendering(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mIsOffline, "This should only be called on OfflineAudioContext"); michael@0: if (mIsStarted) { michael@0: aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); michael@0: return; michael@0: } michael@0: michael@0: mIsStarted = true; michael@0: mDestination->StartRendering(); michael@0: } michael@0: michael@0: void michael@0: AudioContext::Mute() const michael@0: { michael@0: MOZ_ASSERT(!mIsOffline); michael@0: if (mDestination) { michael@0: mDestination->Mute(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: AudioContext::Unmute() const michael@0: { michael@0: MOZ_ASSERT(!mIsOffline); michael@0: if (mDestination) { michael@0: mDestination->Unmute(); michael@0: } michael@0: } michael@0: michael@0: AudioChannel michael@0: AudioContext::MozAudioChannelType() const michael@0: { michael@0: return mDestination->MozAudioChannelType(); michael@0: } michael@0: michael@0: void michael@0: AudioContext::SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv) michael@0: { michael@0: mDestination->SetMozAudioChannelType(aValue, aRv); michael@0: } michael@0: michael@0: size_t michael@0: AudioContext::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: // AudioNodes are tracked separately because we do not want the AudioContext michael@0: // to track all of the AudioNodes it creates, so we wouldn't be able to michael@0: // traverse them from here. michael@0: michael@0: size_t amount = aMallocSizeOf(this); michael@0: if (mListener) { michael@0: amount += mListener->SizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: amount += mDecoder.SizeOfExcludingThis(aMallocSizeOf); michael@0: amount += mDecodeJobs.SizeOfExcludingThis(aMallocSizeOf); michael@0: for (uint32_t i = 0; i < mDecodeJobs.Length(); ++i) { michael@0: amount += mDecodeJobs[i]->SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: amount += mActiveNodes.SizeOfExcludingThis(nullptr, aMallocSizeOf); michael@0: amount += mPannerNodes.SizeOfExcludingThis(nullptr, aMallocSizeOf); michael@0: return amount; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: AudioContext::CollectReports(nsIHandleReportCallback* aHandleReport, michael@0: nsISupports* aData) michael@0: { michael@0: int64_t amount = SizeOfIncludingThis(MallocSizeOf); michael@0: return MOZ_COLLECT_REPORT("explicit/webaudio/audiocontext", KIND_HEAP, UNITS_BYTES, michael@0: amount, "Memory used by AudioContext objects (Web Audio)."); michael@0: } michael@0: michael@0: double michael@0: AudioContext::ExtraCurrentTime() const michael@0: { michael@0: return mDestination->ExtraCurrentTime(); michael@0: } michael@0: michael@0: } michael@0: }