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