mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 13:34:03 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1141282 - DynamicCompressorNode's readonly 'reduction' should be a float. r=ehsan (4685d2cf8) - Bug 1153783 - Implement the `detune` AudioParam for the AudioBufferSourceNode. r=ehsan (8e6b3aca4) - Bug 1134034 - Add a chrome-only parentId and name on AudioParam for devtools. r=ehsan (13815cb94) - Bug 1159290 - "Add final/override to WebAudio classes". r=padenot (83cb303ea) - Bug 1161946 - MainThreadMediaStreamListener should be notified just when the stream is finished - patch 1, r=padenot (c2a126103) - Bug 1161946 - MainThreadMediaStreamListener should be notified just when the stream is finished - patch 2, r=padenot (a1f408cce) - Bug 1161946 - patch 3 - explicit CTOR for NotifyRunnable CLOSED TREE (f304c1ef0) - Bug 974089 - Destroy WebAudio MediaStream when a source finishes. r=padenot (66d7e20df) - Bug 1175510 - Update the AudioBufferSourceNode <=> PannerNode mapping when destroying AudioNodeStream. r=karlt (156741d15) - bug 1179662 uninline DestroyMediaStream overrides so that mStream type need not be complete r=padenot (08d0ebd62) - bug 1179662 specify AudioNode::mStream as AudioNodeStream r=padenot (3aaa3a544) - bug 1179662 handle null AudioNodeStream stream r=padenot (980a8296b) - bug 914392 don't check engine's Node existence in ProcessBlock r=padenot (583176721)
This commit is contained in:
@@ -475,11 +475,10 @@ DOMMediaStream::NotifyMediaStreamGraphShutdown()
|
||||
}
|
||||
|
||||
void
|
||||
DOMMediaStream::NotifyStreamStateChanged()
|
||||
DOMMediaStream::NotifyStreamFinished()
|
||||
{
|
||||
if (IsFinished()) {
|
||||
mConsumersToKeepAlive.Clear();
|
||||
}
|
||||
MOZ_ASSERT(IsFinished());
|
||||
mConsumersToKeepAlive.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -178,9 +178,9 @@ public:
|
||||
*/
|
||||
void NotifyMediaStreamGraphShutdown();
|
||||
/**
|
||||
* Called when the main-thread state of the MediaStream changed.
|
||||
* Called when the main-thread state of the MediaStream goes to finished.
|
||||
*/
|
||||
void NotifyStreamStateChanged();
|
||||
void NotifyStreamFinished();
|
||||
|
||||
// Webrtc allows the remote side to name a stream whatever it wants, and we
|
||||
// need to surface this to content.
|
||||
|
||||
@@ -773,10 +773,13 @@ MediaRecorder::MediaRecorder(AudioNode& aSrcAudioNode,
|
||||
mPipeStream = ctx->Graph()->CreateAudioNodeStream(engine,
|
||||
MediaStreamGraph::EXTERNAL_STREAM,
|
||||
ctx->SampleRate());
|
||||
mInputPort = mPipeStream->AllocateInputPort(aSrcAudioNode.Stream(),
|
||||
MediaInputPort::FLAG_BLOCK_INPUT,
|
||||
0,
|
||||
aSrcOutput);
|
||||
AudioNodeStream* ns = aSrcAudioNode.Stream();
|
||||
if (ns) {
|
||||
mInputPort = mPipeStream->AllocateInputPort(aSrcAudioNode.Stream(),
|
||||
MediaInputPort::FLAG_BLOCK_INPUT,
|
||||
0,
|
||||
aSrcOutput);
|
||||
}
|
||||
}
|
||||
mAudioNode = &aSrcAudioNode;
|
||||
if (!gMediaRecorderLog) {
|
||||
@@ -1153,7 +1156,7 @@ MediaRecorder::GetSourceMediaStream()
|
||||
return mDOMStream->GetStream();
|
||||
}
|
||||
MOZ_ASSERT(mAudioNode != nullptr);
|
||||
return mPipeStream != nullptr ? mPipeStream : mAudioNode->Stream();
|
||||
return mPipeStream != nullptr ? mPipeStream.get() : mAudioNode->Stream();
|
||||
}
|
||||
|
||||
nsIPrincipal*
|
||||
|
||||
@@ -1557,11 +1557,12 @@ MediaStreamGraphImpl::ApplyStreamUpdate(StreamUpdate* aUpdate)
|
||||
stream->mMainThreadCurrentTime = aUpdate->mNextMainThreadCurrentTime;
|
||||
stream->mMainThreadFinished = aUpdate->mNextMainThreadFinished;
|
||||
|
||||
if (stream->mWrapper) {
|
||||
stream->mWrapper->NotifyStreamStateChanged();
|
||||
}
|
||||
for (int32_t i = stream->mMainThreadListeners.Length() - 1; i >= 0; --i) {
|
||||
stream->mMainThreadListeners[i]->NotifyMainThreadStateChanged();
|
||||
if (stream->ShouldNotifyStreamFinished()) {
|
||||
if (stream->mWrapper) {
|
||||
stream->mWrapper->NotifyStreamFinished();
|
||||
}
|
||||
|
||||
stream->NotifyMainThreadListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1930,6 +1931,7 @@ MediaStream::MediaStream(DOMMediaStream* aWrapper)
|
||||
, mWrapper(aWrapper)
|
||||
, mMainThreadCurrentTime(0)
|
||||
, mMainThreadFinished(false)
|
||||
, mFinishedNotificationSent(false)
|
||||
, mMainThreadDestroyed(false)
|
||||
, mGraph(nullptr)
|
||||
, mAudioChannelType(dom::AudioChannel::Normal)
|
||||
@@ -2415,6 +2417,50 @@ MediaStream::ApplyTrackDisabling(TrackID aTrackID, MediaSegment* aSegment, Media
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaStream::AddMainThreadListener(MainThreadMediaStreamListener* aListener)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aListener);
|
||||
MOZ_ASSERT(!mMainThreadListeners.Contains(aListener));
|
||||
|
||||
mMainThreadListeners.AppendElement(aListener);
|
||||
|
||||
// If we have to send the notification or we have a runnable that will do it,
|
||||
// let finish here.
|
||||
if (!mFinishedNotificationSent || mNotificationMainThreadRunnable) {
|
||||
return;
|
||||
}
|
||||
|
||||
class NotifyRunnable final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit NotifyRunnable(MediaStream* aStream)
|
||||
: mStream(aStream)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mStream->mNotificationMainThreadRunnable = nullptr;
|
||||
mStream->NotifyMainThreadListeners();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~NotifyRunnable() {}
|
||||
|
||||
nsRefPtr<MediaStream> mStream;
|
||||
};
|
||||
|
||||
nsRefPtr<nsRunnable> runnable = new NotifyRunnable(this);
|
||||
if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
mNotificationMainThreadRunnable = runnable;
|
||||
}
|
||||
|
||||
void
|
||||
SourceMediaStream::DestroyImpl()
|
||||
{
|
||||
|
||||
@@ -224,7 +224,7 @@ public:
|
||||
*/
|
||||
class MainThreadMediaStreamListener {
|
||||
public:
|
||||
virtual void NotifyMainThreadStateChanged() = 0;
|
||||
virtual void NotifyMainThreadStreamFinished() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -371,20 +371,19 @@ public:
|
||||
// A disabled track has video replaced by black, and audio replaced by
|
||||
// silence.
|
||||
void SetTrackEnabled(TrackID aTrackID, bool aEnabled);
|
||||
// Events will be dispatched by calling methods of aListener. It is the
|
||||
|
||||
// Finish event will be notified by calling methods of aListener. It is the
|
||||
// responsibility of the caller to remove aListener before it is destroyed.
|
||||
void AddMainThreadListener(MainThreadMediaStreamListener* aListener)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
|
||||
mMainThreadListeners.AppendElement(aListener);
|
||||
}
|
||||
void AddMainThreadListener(MainThreadMediaStreamListener* aListener);
|
||||
// It's safe to call this even if aListener is not currently a listener;
|
||||
// the call will be ignored.
|
||||
void RemoveMainThreadListener(MainThreadMediaStreamListener* aListener)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aListener);
|
||||
mMainThreadListeners.RemoveElement(aListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a runnable will run on the main thread after running all pending
|
||||
* updates that were sent from the graph thread or will be sent before the
|
||||
@@ -415,6 +414,7 @@ public:
|
||||
NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
|
||||
return mMainThreadFinished;
|
||||
}
|
||||
|
||||
bool IsDestroyed()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
|
||||
@@ -594,6 +594,27 @@ protected:
|
||||
mBuffer.ForgetUpTo(aCurrentTime - mBufferStartTime);
|
||||
}
|
||||
|
||||
void NotifyMainThreadListeners()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
|
||||
|
||||
for (int32_t i = mMainThreadListeners.Length() - 1; i >= 0; --i) {
|
||||
mMainThreadListeners[i]->NotifyMainThreadStreamFinished();
|
||||
}
|
||||
mMainThreadListeners.Clear();
|
||||
}
|
||||
|
||||
bool ShouldNotifyStreamFinished()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
|
||||
if (!mMainThreadFinished || mFinishedNotificationSent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mFinishedNotificationSent = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// This state is all initialized on the main thread but
|
||||
// otherwise modified only on the media graph thread.
|
||||
|
||||
@@ -622,6 +643,7 @@ protected:
|
||||
TimeVarying<GraphTime,uint32_t,0> mExplicitBlockerCount;
|
||||
nsTArray<nsRefPtr<MediaStreamListener> > mListeners;
|
||||
nsTArray<MainThreadMediaStreamListener*> mMainThreadListeners;
|
||||
nsRefPtr<nsRunnable> mNotificationMainThreadRunnable;
|
||||
nsTArray<TrackID> mDisabledTrackIDs;
|
||||
|
||||
// Precomputed blocking status (over GraphTime).
|
||||
@@ -694,6 +716,7 @@ protected:
|
||||
// Main-thread views of state
|
||||
StreamTime mMainThreadCurrentTime;
|
||||
bool mMainThreadFinished;
|
||||
bool mFinishedNotificationSent;
|
||||
bool mMainThreadDestroyed;
|
||||
|
||||
// Our media stream graph. null if destroyed on the graph thread.
|
||||
|
||||
@@ -16,9 +16,9 @@ namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(AnalyserNode, AudioNode)
|
||||
|
||||
class AnalyserNodeEngine : public AudioNodeEngine
|
||||
class AnalyserNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
class TransferBuffer : public nsRunnable
|
||||
class TransferBuffer final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
TransferBuffer(AudioNodeStream* aStream,
|
||||
@@ -30,15 +30,8 @@ class AnalyserNodeEngine : public AudioNodeEngine
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
nsRefPtr<AnalyserNode> node;
|
||||
{
|
||||
// No need to keep holding the lock for the whole duration of this
|
||||
// function, since we're holding a strong reference to it, so if
|
||||
// we can obtain the reference, we will hold the node alive in
|
||||
// this function.
|
||||
MutexAutoLock lock(mStream->Engine()->NodeMutex());
|
||||
node = static_cast<AnalyserNode*>(mStream->Engine()->Node());
|
||||
}
|
||||
nsRefPtr<AnalyserNode> node =
|
||||
static_cast<AnalyserNode*>(mStream->Engine()->NodeMainThread());
|
||||
if (node) {
|
||||
node->AppendChunk(mChunk);
|
||||
}
|
||||
@@ -64,25 +57,21 @@ public:
|
||||
{
|
||||
*aOutput = aInput;
|
||||
|
||||
MutexAutoLock lock(NodeMutex());
|
||||
|
||||
if (Node()) {
|
||||
// If the input is silent, we sill need to send a silent buffer
|
||||
if (aOutput->IsNull()) {
|
||||
AllocateAudioBlock(1, aOutput);
|
||||
float* samples = static_cast<float*>(
|
||||
const_cast<void*>(aOutput->mChannelData[0]));
|
||||
PodZero(samples, WEBAUDIO_BLOCK_SIZE);
|
||||
}
|
||||
uint32_t channelCount = aOutput->mChannelData.Length();
|
||||
for (uint32_t channel = 0; channel < channelCount; ++channel) {
|
||||
float* samples = static_cast<float*>(
|
||||
const_cast<void*>(aOutput->mChannelData[channel]));
|
||||
AudioBlockInPlaceScale(samples, aOutput->mVolume);
|
||||
}
|
||||
nsRefPtr<TransferBuffer> transfer = new TransferBuffer(aStream, *aOutput);
|
||||
NS_DispatchToMainThread(transfer);
|
||||
// If the input is silent, we sill need to send a silent buffer
|
||||
if (aOutput->IsNull()) {
|
||||
AllocateAudioBlock(1, aOutput);
|
||||
float* samples =
|
||||
static_cast<float*>(const_cast<void*>(aOutput->mChannelData[0]));
|
||||
PodZero(samples, WEBAUDIO_BLOCK_SIZE);
|
||||
}
|
||||
uint32_t channelCount = aOutput->mChannelData.Length();
|
||||
for (uint32_t channel = 0; channel < channelCount; ++channel) {
|
||||
float* samples =
|
||||
static_cast<float*>(const_cast<void*>(aOutput->mChannelData[channel]));
|
||||
AudioBlockInPlaceScale(samples, aOutput->mVolume);
|
||||
}
|
||||
nsRefPtr<TransferBuffer> transfer = new TransferBuffer(aStream, *aOutput);
|
||||
NS_DispatchToMainThread(transfer);
|
||||
}
|
||||
|
||||
virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
|
||||
class AnalyserNode : public AudioNode
|
||||
class AnalyserNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
explicit AnalyserNode(AudioContext* aContext);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "AudioDestinationNode.h"
|
||||
#include "AudioParamTimeline.h"
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@@ -23,19 +24,16 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(AudioBufferSourceNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioBufferSourceNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBuffer)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlaybackRate)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDetune)
|
||||
if (tmp->Context()) {
|
||||
// AudioNode's Unlink implementation disconnects us from the graph
|
||||
// too, but we need to do this right here to make sure that
|
||||
// UnregisterAudioBufferSourceNode can properly untangle us from
|
||||
// the possibly connected PannerNodes.
|
||||
tmp->DisconnectFromGraph();
|
||||
tmp->Context()->UnregisterAudioBufferSourceNode(tmp);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(AudioNode)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AudioBufferSourceNode, AudioNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBuffer)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlaybackRate)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDetune)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioBufferSourceNode)
|
||||
@@ -50,11 +48,11 @@ NS_IMPL_RELEASE_INHERITED(AudioBufferSourceNode, AudioNode)
|
||||
* AudioNodeStream::SetBuffer) and a non-zero mBufferEnd has been set (via
|
||||
* AudioNodeStream::SetInt32Parameter).
|
||||
*/
|
||||
class AudioBufferSourceNodeEngine : public AudioNodeEngine
|
||||
class AudioBufferSourceNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
explicit AudioBufferSourceNodeEngine(AudioNode* aNode,
|
||||
AudioDestinationNode* aDestination) :
|
||||
AudioBufferSourceNodeEngine(AudioNode* aNode,
|
||||
AudioDestinationNode* aDestination) :
|
||||
AudioNodeEngine(aNode),
|
||||
mStart(0.0), mBeginProcessing(0),
|
||||
mStop(STREAM_TIME_MAX),
|
||||
@@ -63,8 +61,10 @@ public:
|
||||
mLoopStart(0), mLoopEnd(0),
|
||||
mBufferSampleRate(0), mBufferPosition(0), mChannels(0),
|
||||
mDopplerShift(1.0f),
|
||||
mDestination(static_cast<AudioNodeStream*>(aDestination->Stream())),
|
||||
mPlaybackRateTimeline(1.0f), mLoop(false)
|
||||
mDestination(aDestination->Stream()),
|
||||
mPlaybackRateTimeline(1.0f),
|
||||
mDetuneTimeline(0.0f),
|
||||
mLoop(false)
|
||||
{}
|
||||
|
||||
~AudioBufferSourceNodeEngine()
|
||||
@@ -88,6 +88,10 @@ public:
|
||||
mPlaybackRateTimeline = aValue;
|
||||
WebAudioUtils::ConvertAudioParamToTicks(mPlaybackRateTimeline, mSource, mDestination);
|
||||
break;
|
||||
case AudioBufferSourceNode::DETUNE:
|
||||
mDetuneTimeline = aValue;
|
||||
WebAudioUtils::ConvertAudioParamToTicks(mDetuneTimeline, mSource, mDestination);
|
||||
break;
|
||||
default:
|
||||
NS_ERROR("Bad AudioBufferSourceNodeEngine TimelineParameter");
|
||||
}
|
||||
@@ -226,7 +230,7 @@ public:
|
||||
}
|
||||
|
||||
// Resamples input data to an output buffer, according to |mBufferSampleRate| and
|
||||
// the playbackRate.
|
||||
// the playbackRate/detune.
|
||||
// The number of frames consumed/produced depends on the amount of space
|
||||
// remaining in both the input and output buffer, and the playback rate (that
|
||||
// is, the ratio between the output samplerate and the input samplerate).
|
||||
@@ -397,30 +401,39 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
int32_t ComputeFinalOutSampleRate(float aPlaybackRate)
|
||||
int32_t ComputeFinalOutSampleRate(float aPlaybackRate, float aDetune)
|
||||
{
|
||||
float computedPlaybackRate = aPlaybackRate * pow(2, aDetune / 1200.f);
|
||||
// Make sure the playback rate and the doppler shift are something
|
||||
// our resampler can work with.
|
||||
int32_t rate = WebAudioUtils::
|
||||
TruncateFloatToInt<int32_t>(mSource->SampleRate() /
|
||||
(aPlaybackRate * mDopplerShift));
|
||||
(computedPlaybackRate * mDopplerShift));
|
||||
return rate ? rate : mBufferSampleRate;
|
||||
}
|
||||
|
||||
void UpdateSampleRateIfNeeded(uint32_t aChannels)
|
||||
{
|
||||
float playbackRate;
|
||||
float detune;
|
||||
|
||||
if (mPlaybackRateTimeline.HasSimpleValue()) {
|
||||
playbackRate = mPlaybackRateTimeline.GetValue();
|
||||
} else {
|
||||
playbackRate = mPlaybackRateTimeline.GetValueAtTime(mSource->GetCurrentPosition());
|
||||
}
|
||||
if (mDetuneTimeline.HasSimpleValue()) {
|
||||
detune = mDetuneTimeline.GetValue();
|
||||
} else {
|
||||
detune = mDetuneTimeline.GetValueAtTime(mSource->GetCurrentPosition());
|
||||
}
|
||||
if (playbackRate <= 0 || mozilla::IsNaN(playbackRate)) {
|
||||
playbackRate = 1.0f;
|
||||
}
|
||||
|
||||
int32_t outRate = ComputeFinalOutSampleRate(playbackRate);
|
||||
detune = std::min(std::max(-1200.f, detune), 1200.f);
|
||||
|
||||
int32_t outRate = ComputeFinalOutSampleRate(playbackRate, detune);
|
||||
UpdateResampler(outRate, aChannels);
|
||||
}
|
||||
|
||||
@@ -440,9 +453,6 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
// WebKit treats the playbackRate as a k-rate parameter in their code,
|
||||
// despite the spec saying that it should be an a-rate parameter. We treat
|
||||
// it as k-rate. Spec bug: https://www.w3.org/Bugs/Public/show_bug.cgi?id=21592
|
||||
UpdateSampleRateIfNeeded(channels);
|
||||
|
||||
uint32_t written = 0;
|
||||
@@ -488,6 +498,7 @@ public:
|
||||
// Not owned:
|
||||
// - mBuffer - shared w/ AudioNode
|
||||
// - mPlaybackRateTimeline - shared w/ AudioNode
|
||||
// - mDetuneTimeline - shared w/ AudioNode
|
||||
|
||||
size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
|
||||
|
||||
@@ -530,6 +541,7 @@ public:
|
||||
AudioNodeStream* mDestination;
|
||||
AudioNodeStream* mSource;
|
||||
AudioParamTimeline mPlaybackRateTimeline;
|
||||
AudioParamTimeline mDetuneTimeline;
|
||||
bool mLoop;
|
||||
};
|
||||
|
||||
@@ -541,19 +553,28 @@ AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* aContext)
|
||||
, mLoopStart(0.0)
|
||||
, mLoopEnd(0.0)
|
||||
// mOffset and mDuration are initialized in Start().
|
||||
, mPlaybackRate(new AudioParam(this, SendPlaybackRateToStream, 1.0f))
|
||||
, mPlaybackRate(new AudioParam(this, SendPlaybackRateToStream, 1.0f, "playbackRate"))
|
||||
, mDetune(new AudioParam(this, SendDetuneToStream, 0.0f, "detune"))
|
||||
, mLoop(false)
|
||||
, mStartCalled(false)
|
||||
, mStopped(false)
|
||||
{
|
||||
AudioBufferSourceNodeEngine* engine = new AudioBufferSourceNodeEngine(this, aContext->Destination());
|
||||
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::SOURCE_STREAM);
|
||||
engine->SetSourceStream(static_cast<AudioNodeStream*>(mStream.get()));
|
||||
engine->SetSourceStream(mStream);
|
||||
mStream->AddMainThreadListener(this);
|
||||
}
|
||||
|
||||
AudioBufferSourceNode::~AudioBufferSourceNode()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
AudioBufferSourceNode::DestroyMediaStream()
|
||||
{
|
||||
if (mStream) {
|
||||
mStream->RemoveMainThreadListener(this);
|
||||
}
|
||||
AudioNode::DestroyMediaStream();
|
||||
if (Context()) {
|
||||
Context()->UnregisterAudioBufferSourceNode(this);
|
||||
}
|
||||
@@ -568,6 +589,7 @@ AudioBufferSourceNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
|
||||
}
|
||||
|
||||
amount += mPlaybackRate->SizeOfIncludingThis(aMallocSizeOf);
|
||||
amount += mDetune->SizeOfIncludingThis(aMallocSizeOf);
|
||||
return amount;
|
||||
}
|
||||
|
||||
@@ -599,7 +621,7 @@ AudioBufferSourceNode::Start(double aWhen, double aOffset,
|
||||
}
|
||||
mStartCalled = true;
|
||||
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
AudioNodeStream* ns = mStream;
|
||||
if (!ns) {
|
||||
// Nothing to play, or we're already dead for some reason
|
||||
return;
|
||||
@@ -624,8 +646,10 @@ AudioBufferSourceNode::Start(double aWhen, double aOffset,
|
||||
void
|
||||
AudioBufferSourceNode::SendBufferParameterToStream(JSContext* aCx)
|
||||
{
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
MOZ_ASSERT(ns, "Why don't we have a stream here?");
|
||||
AudioNodeStream* ns = mStream;
|
||||
if (!ns) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mBuffer) {
|
||||
float rate = mBuffer->SampleRate();
|
||||
@@ -681,7 +705,7 @@ AudioBufferSourceNode::Stop(double aWhen, ErrorResult& aRv)
|
||||
return;
|
||||
}
|
||||
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
AudioNodeStream* ns = mStream;
|
||||
if (!ns || !Context()) {
|
||||
// We've already stopped and had our stream shut down
|
||||
return;
|
||||
@@ -691,56 +715,72 @@ AudioBufferSourceNode::Stop(double aWhen, ErrorResult& aRv)
|
||||
}
|
||||
|
||||
void
|
||||
AudioBufferSourceNode::NotifyMainThreadStateChanged()
|
||||
AudioBufferSourceNode::NotifyMainThreadStreamFinished()
|
||||
{
|
||||
if (mStream->IsFinished()) {
|
||||
class EndedEventDispatcher : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit EndedEventDispatcher(AudioBufferSourceNode* aNode)
|
||||
: mNode(aNode) {}
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
// If it's not safe to run scripts right now, schedule this to run later
|
||||
if (!nsContentUtils::IsSafeToRunScript()) {
|
||||
nsContentUtils::AddScriptRunner(this);
|
||||
return NS_OK;
|
||||
}
|
||||
MOZ_ASSERT(mStream->IsFinished());
|
||||
|
||||
mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
|
||||
class EndedEventDispatcher final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit EndedEventDispatcher(AudioBufferSourceNode* aNode)
|
||||
: mNode(aNode) {}
|
||||
NS_IMETHODIMP Run() override
|
||||
{
|
||||
// If it's not safe to run scripts right now, schedule this to run later
|
||||
if (!nsContentUtils::IsSafeToRunScript()) {
|
||||
nsContentUtils::AddScriptRunner(this);
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsRefPtr<AudioBufferSourceNode> mNode;
|
||||
};
|
||||
if (!mStopped) {
|
||||
// Only dispatch the ended event once
|
||||
NS_DispatchToMainThread(new EndedEventDispatcher(this));
|
||||
mStopped = true;
|
||||
}
|
||||
|
||||
// Drop the playing reference
|
||||
// Warning: The below line might delete this.
|
||||
MarkInactive();
|
||||
}
|
||||
mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
|
||||
// Release stream resources.
|
||||
mNode->DestroyMediaStream();
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsRefPtr<AudioBufferSourceNode> mNode;
|
||||
};
|
||||
|
||||
NS_DispatchToMainThread(new EndedEventDispatcher(this));
|
||||
|
||||
// Drop the playing reference
|
||||
// Warning: The below line might delete this.
|
||||
MarkInactive();
|
||||
}
|
||||
|
||||
void
|
||||
AudioBufferSourceNode::SendPlaybackRateToStream(AudioNode* aNode)
|
||||
{
|
||||
AudioBufferSourceNode* This = static_cast<AudioBufferSourceNode*>(aNode);
|
||||
if (!This->mStream) {
|
||||
return;
|
||||
}
|
||||
SendTimelineParameterToStream(This, PLAYBACKRATE, *This->mPlaybackRate);
|
||||
}
|
||||
|
||||
void
|
||||
AudioBufferSourceNode::SendDetuneToStream(AudioNode* aNode)
|
||||
{
|
||||
AudioBufferSourceNode* This = static_cast<AudioBufferSourceNode*>(aNode);
|
||||
if (!This->mStream) {
|
||||
return;
|
||||
}
|
||||
SendTimelineParameterToStream(This, DETUNE, *This->mDetune);
|
||||
}
|
||||
|
||||
void
|
||||
AudioBufferSourceNode::SendDopplerShiftToStream(double aDopplerShift)
|
||||
{
|
||||
MOZ_ASSERT(mStream, "Should have disconnected panner if no stream");
|
||||
SendDoubleParameterToStream(DOPPLERSHIFT, aDopplerShift);
|
||||
}
|
||||
|
||||
void
|
||||
AudioBufferSourceNode::SendLoopParametersToStream()
|
||||
{
|
||||
if (!mStream) {
|
||||
return;
|
||||
}
|
||||
// Don't compute and set the loop parameters unnecessarily
|
||||
if (mLoop && mBuffer) {
|
||||
float rate = mBuffer->SampleRate();
|
||||
|
||||
@@ -15,19 +15,14 @@ namespace dom {
|
||||
|
||||
class AudioParam;
|
||||
|
||||
class AudioBufferSourceNode : public AudioNode,
|
||||
public MainThreadMediaStreamListener
|
||||
class AudioBufferSourceNode final : public AudioNode,
|
||||
public MainThreadMediaStreamListener
|
||||
{
|
||||
public:
|
||||
explicit AudioBufferSourceNode(AudioContext* aContext);
|
||||
|
||||
virtual void DestroyMediaStream() override
|
||||
{
|
||||
if (mStream) {
|
||||
mStream->RemoveMainThreadListener(this);
|
||||
}
|
||||
AudioNode::DestroyMediaStream();
|
||||
}
|
||||
virtual void DestroyMediaStream() override;
|
||||
|
||||
virtual uint16_t NumberOfInputs() const final override
|
||||
{
|
||||
return 0;
|
||||
@@ -59,6 +54,10 @@ public:
|
||||
{
|
||||
return mPlaybackRate;
|
||||
}
|
||||
AudioParam* Detune() const
|
||||
{
|
||||
return mDetune;
|
||||
}
|
||||
bool Loop() const
|
||||
{
|
||||
return mLoop;
|
||||
@@ -90,7 +89,7 @@ public:
|
||||
|
||||
IMPL_EVENT_HANDLER(ended)
|
||||
|
||||
virtual void NotifyMainThreadStateChanged() override;
|
||||
virtual void NotifyMainThreadStreamFinished() override;
|
||||
|
||||
virtual const char* NodeType() const override
|
||||
{
|
||||
@@ -123,6 +122,7 @@ private:
|
||||
LOOPSTART,
|
||||
LOOPEND,
|
||||
PLAYBACKRATE,
|
||||
DETUNE,
|
||||
DOPPLERSHIFT
|
||||
};
|
||||
|
||||
@@ -130,6 +130,7 @@ private:
|
||||
void SendBufferParameterToStream(JSContext* aCx);
|
||||
void SendOffsetAndDurationParametersToStream(AudioNodeStream* aStream);
|
||||
static void SendPlaybackRateToStream(AudioNode* aNode);
|
||||
static void SendDetuneToStream(AudioNode* aNode);
|
||||
|
||||
private:
|
||||
double mLoopStart;
|
||||
@@ -138,9 +139,9 @@ private:
|
||||
double mDuration;
|
||||
nsRefPtr<AudioBuffer> mBuffer;
|
||||
nsRefPtr<AudioParam> mPlaybackRate;
|
||||
nsRefPtr<AudioParam> mDetune;
|
||||
bool mLoop;
|
||||
bool mStartCalled;
|
||||
bool mStopped;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace dom {
|
||||
|
||||
static uint8_t gWebAudioOutputKey;
|
||||
|
||||
class OfflineDestinationNodeEngine : public AudioNodeEngine
|
||||
class OfflineDestinationNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
typedef AutoFallibleTArray<nsAutoArrayPtr<float>, 2> InputChannels;
|
||||
@@ -134,7 +134,7 @@ public:
|
||||
, mRenderedBuffer(aRenderedBuffer)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
nsRefPtr<OfflineAudioCompletionEvent> event =
|
||||
new OfflineAudioCompletionEvent(mAudioContext, nullptr, nullptr);
|
||||
@@ -209,7 +209,7 @@ private:
|
||||
bool mBufferAllocated;
|
||||
};
|
||||
|
||||
class InputMutedRunnable : public nsRunnable
|
||||
class InputMutedRunnable final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
InputMutedRunnable(AudioNodeStream* aStream,
|
||||
@@ -219,7 +219,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsRefPtr<AudioNode> node = mStream->Engine()->NodeMainThread();
|
||||
@@ -237,7 +237,7 @@ private:
|
||||
bool mInputMuted;
|
||||
};
|
||||
|
||||
class DestinationNodeEngine : public AudioNodeEngine
|
||||
class DestinationNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
explicit DestinationNodeEngine(AudioDestinationNode* aNode)
|
||||
@@ -350,7 +350,6 @@ AudioDestinationNode::AudioDestinationNode(AudioContext* aContext,
|
||||
, mFramesToProduce(aLength)
|
||||
, mAudioChannel(AudioChannel::Normal)
|
||||
, mIsOffline(aIsOffline)
|
||||
, mHasFinished(false)
|
||||
, mAudioChannelAgentPlaying(false)
|
||||
, mExtraCurrentTime(0)
|
||||
, mExtraCurrentTimeSinceLastStartedBlocking(0)
|
||||
@@ -370,7 +369,7 @@ AudioDestinationNode::AudioDestinationNode(AudioContext* aContext,
|
||||
mStream->AddAudioOutput(&gWebAudioOutputKey);
|
||||
|
||||
if (!aIsOffline) {
|
||||
graph->NotifyWhenGraphStarted(mStream->AsAudioNodeStream());
|
||||
graph->NotifyWhenGraphStarted(mStream);
|
||||
}
|
||||
|
||||
if (aChannel != AudioChannel::Normal) {
|
||||
@@ -425,24 +424,22 @@ AudioDestinationNode::DestroyMediaStream()
|
||||
}
|
||||
|
||||
void
|
||||
AudioDestinationNode::NotifyMainThreadStateChanged()
|
||||
AudioDestinationNode::NotifyMainThreadStreamFinished()
|
||||
{
|
||||
if (mStream->IsFinished() && !mHasFinished) {
|
||||
mHasFinished = true;
|
||||
if (mIsOffline) {
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &AudioDestinationNode::FireOfflineCompletionEvent);
|
||||
NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
MOZ_ASSERT(mStream->IsFinished());
|
||||
|
||||
if (mIsOffline) {
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &AudioDestinationNode::FireOfflineCompletionEvent);
|
||||
NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioDestinationNode::FireOfflineCompletionEvent()
|
||||
{
|
||||
AudioNodeStream* stream = static_cast<AudioNodeStream*>(Stream());
|
||||
OfflineDestinationNodeEngine* engine =
|
||||
static_cast<OfflineDestinationNodeEngine*>(stream->Engine());
|
||||
static_cast<OfflineDestinationNodeEngine*>(Stream()->Engine());
|
||||
engine->FireOfflineCompletionEvent(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
|
||||
AudioChannel MozAudioChannelType() const;
|
||||
|
||||
virtual void NotifyMainThreadStateChanged() override;
|
||||
virtual void NotifyMainThreadStreamFinished() override;
|
||||
void FireOfflineCompletionEvent();
|
||||
|
||||
// An amount that should be added to the MediaStream's current time to
|
||||
@@ -109,7 +109,6 @@ private:
|
||||
// Audio Channel Type.
|
||||
AudioChannel mAudioChannel;
|
||||
bool mIsOffline;
|
||||
bool mHasFinished;
|
||||
bool mAudioChannelAgentPlaying;
|
||||
|
||||
TimeStamp mStartedBlockingDueToBeingOnlyNode;
|
||||
|
||||
@@ -21,8 +21,10 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// This is an internal helper class and should not be used outside of this header.
|
||||
struct AudioTimelineEvent {
|
||||
enum Type : uint32_t {
|
||||
struct AudioTimelineEvent final
|
||||
{
|
||||
enum Type : uint32_t
|
||||
{
|
||||
SetValue,
|
||||
LinearRamp,
|
||||
ExponentialRamp,
|
||||
|
||||
@@ -212,15 +212,13 @@ AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput,
|
||||
input->mInputNode = this;
|
||||
input->mInputPort = aInput;
|
||||
input->mOutputPort = aOutput;
|
||||
if (aDestination.mStream) {
|
||||
AudioNodeStream* destinationStream = aDestination.mStream;
|
||||
if (mStream && destinationStream) {
|
||||
// Connect streams in the MediaStreamGraph
|
||||
MOZ_ASSERT(aDestination.mStream->AsProcessedStream());
|
||||
ProcessedMediaStream* ps =
|
||||
static_cast<ProcessedMediaStream*>(aDestination.mStream.get());
|
||||
MOZ_ASSERT(aInput <= UINT16_MAX, "Unexpected large input port number");
|
||||
MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number");
|
||||
input->mStreamPort =
|
||||
ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT,
|
||||
input->mStreamPort = destinationStream->
|
||||
AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT,
|
||||
static_cast<uint16_t>(aInput),
|
||||
static_cast<uint16_t>(aOutput));
|
||||
}
|
||||
@@ -260,43 +258,41 @@ AudioNode::Connect(AudioParam& aDestination, uint32_t aOutput,
|
||||
MediaStream* stream = aDestination.Stream();
|
||||
MOZ_ASSERT(stream->AsProcessedStream());
|
||||
ProcessedMediaStream* ps = static_cast<ProcessedMediaStream*>(stream);
|
||||
|
||||
// Setup our stream as an input to the AudioParam's stream
|
||||
MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number");
|
||||
input->mStreamPort = ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT,
|
||||
0, static_cast<uint16_t>(aOutput));
|
||||
if (mStream) {
|
||||
// Setup our stream as an input to the AudioParam's stream
|
||||
MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number");
|
||||
input->mStreamPort =
|
||||
ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT,
|
||||
0, static_cast<uint16_t>(aOutput));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioNode::SendDoubleParameterToStream(uint32_t aIndex, double aValue)
|
||||
{
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
MOZ_ASSERT(ns, "How come we don't have a stream here?");
|
||||
ns->SetDoubleParameter(aIndex, aValue);
|
||||
MOZ_ASSERT(mStream, "How come we don't have a stream here?");
|
||||
mStream->SetDoubleParameter(aIndex, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
AudioNode::SendInt32ParameterToStream(uint32_t aIndex, int32_t aValue)
|
||||
{
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
MOZ_ASSERT(ns, "How come we don't have a stream here?");
|
||||
ns->SetInt32Parameter(aIndex, aValue);
|
||||
MOZ_ASSERT(mStream, "How come we don't have a stream here?");
|
||||
mStream->SetInt32Parameter(aIndex, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
AudioNode::SendThreeDPointParameterToStream(uint32_t aIndex, const ThreeDPoint& aValue)
|
||||
{
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
MOZ_ASSERT(ns, "How come we don't have a stream here?");
|
||||
ns->SetThreeDPointParameter(aIndex, aValue);
|
||||
MOZ_ASSERT(mStream, "How come we don't have a stream here?");
|
||||
mStream->SetThreeDPointParameter(aIndex, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
AudioNode::SendChannelMixingParametersToStream()
|
||||
{
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
MOZ_ASSERT(ns, "How come we don't have a stream here?");
|
||||
ns->SetChannelMixingParameters(mChannelCount, mChannelCountMode,
|
||||
MOZ_ASSERT(mStream, "How come we don't have a stream here?");
|
||||
mStream->SetChannelMixingParameters(mChannelCount, mChannelCountMode,
|
||||
mChannelInterpretation);
|
||||
}
|
||||
|
||||
@@ -304,7 +300,7 @@ void
|
||||
AudioNode::SendTimelineParameterToStream(AudioNode* aNode, uint32_t aIndex,
|
||||
const AudioParamTimeline& aValue)
|
||||
{
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(aNode->mStream.get());
|
||||
AudioNodeStream* ns = aNode->mStream;
|
||||
MOZ_ASSERT(ns, "How come we don't have a stream here?");
|
||||
ns->SetTimelineParameter(aIndex, aValue);
|
||||
}
|
||||
@@ -322,7 +318,8 @@ AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv)
|
||||
// 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 : public nsRunnable {
|
||||
class RunnableRelease final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit RunnableRelease(already_AddRefed<AudioNode> aNode)
|
||||
: mNode(aNode) {}
|
||||
@@ -348,10 +345,12 @@ AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv)
|
||||
// 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.
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new RunnableRelease(mOutputNodes[i].forget());
|
||||
nsRefPtr<AudioNode> output = mOutputNodes[i].forget();
|
||||
mOutputNodes.RemoveElementAt(i);
|
||||
mStream->RunAfterPendingUpdates(runnable.forget());
|
||||
if (mStream) {
|
||||
nsRefPtr<nsIRunnable> runnable = new RunnableRelease(output.forget());
|
||||
mStream->RunAfterPendingUpdates(runnable.forget());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -380,17 +379,12 @@ void
|
||||
AudioNode::DestroyMediaStream()
|
||||
{
|
||||
if (mStream) {
|
||||
{
|
||||
// Remove the node reference on the engine, and take care to not
|
||||
// hold the lock when the stream gets destroyed, because that will
|
||||
// cause the engine to be destroyed as well, and we don't want to
|
||||
// be holding the lock as we're trying to destroy it!
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
MutexAutoLock lock(ns->Engine()->NodeMutex());
|
||||
MOZ_ASSERT(ns, "How come we don't have a stream here?");
|
||||
MOZ_ASSERT(ns->Engine()->Node() == this, "Invalid node reference");
|
||||
ns->Engine()->ClearNode();
|
||||
}
|
||||
// Remove the node pointer on the engine.
|
||||
AudioNodeStream* ns = mStream;
|
||||
MOZ_ASSERT(ns, "How come we don't have a stream here?");
|
||||
MOZ_ASSERT(ns->Engine()->NodeMainThread() == this,
|
||||
"Invalid node reference");
|
||||
ns->Engine()->ClearNode();
|
||||
|
||||
mStream->Destroy();
|
||||
mStream = nullptr;
|
||||
@@ -422,9 +416,8 @@ AudioNode::SetPassThrough(bool aPassThrough)
|
||||
{
|
||||
MOZ_ASSERT(NumberOfInputs() <= 1 && NumberOfOutputs() == 1);
|
||||
mPassThrough = aPassThrough;
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
MOZ_ASSERT(ns, "How come we don't have a stream here?");
|
||||
ns->SetPassThrough(mPassThrough);
|
||||
MOZ_ASSERT(mStream, "How come we don't have a stream here?");
|
||||
mStream->SetPassThrough(mPassThrough);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -74,7 +74,8 @@ public:
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioNode,
|
||||
DOMEventTargetHelper)
|
||||
|
||||
virtual AudioBufferSourceNode* AsAudioBufferSourceNode() {
|
||||
virtual AudioBufferSourceNode* AsAudioBufferSourceNode()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -137,7 +138,8 @@ public:
|
||||
SendChannelMixingParametersToStream();
|
||||
}
|
||||
|
||||
struct InputNode {
|
||||
struct InputNode final
|
||||
{
|
||||
~InputNode()
|
||||
{
|
||||
if (mStreamPort) {
|
||||
@@ -165,7 +167,7 @@ public:
|
||||
uint32_t mOutputPort;
|
||||
};
|
||||
|
||||
MediaStream* Stream() { return mStream; }
|
||||
AudioNodeStream* Stream() { return mStream; }
|
||||
|
||||
const nsTArray<InputNode>& InputNodes() const
|
||||
{
|
||||
@@ -218,9 +220,8 @@ private:
|
||||
nsRefPtr<AudioContext> mContext;
|
||||
|
||||
protected:
|
||||
// Must be set in the constructor. Must not be null.
|
||||
// If MaxNumberOfInputs() is > 0, then mStream must be a ProcessedMediaStream.
|
||||
nsRefPtr<MediaStream> mStream;
|
||||
// Must be set in the constructor. Must not be null unless finished.
|
||||
nsRefPtr<AudioNodeStream> mStream;
|
||||
|
||||
private:
|
||||
// For every InputNode, there is a corresponding entry in mOutputNodes of the
|
||||
|
||||
@@ -27,7 +27,8 @@ class AudioNodeStream;
|
||||
* pointers can be different (e.g. if the buffers are contained inside
|
||||
* some malloced object).
|
||||
*/
|
||||
class ThreadSharedFloatArrayBufferList : public ThreadSharedObject {
|
||||
class ThreadSharedFloatArrayBufferList final : public ThreadSharedObject
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Construct with null data.
|
||||
@@ -37,7 +38,8 @@ public:
|
||||
mContents.SetLength(aCount);
|
||||
}
|
||||
|
||||
struct Storage {
|
||||
struct Storage final
|
||||
{
|
||||
Storage() :
|
||||
mDataToFree(nullptr),
|
||||
mFree(nullptr),
|
||||
@@ -234,14 +236,14 @@ AudioBufferSumOfSquares(const float* aInput, uint32_t aLength);
|
||||
* All methods of this class and its subclasses are called on the
|
||||
* MediaStreamGraph thread.
|
||||
*/
|
||||
class AudioNodeEngine {
|
||||
class AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
// This should be compatible with AudioNodeStream::OutputChunks.
|
||||
typedef nsAutoTArray<AudioChunk, 1> OutputChunks;
|
||||
|
||||
explicit AudioNodeEngine(dom::AudioNode* aNode)
|
||||
: mNode(aNode)
|
||||
, mNodeMutex("AudioNodeEngine::mNodeMutex")
|
||||
, mInputCount(aNode ? aNode->NumberOfInputs() : 1)
|
||||
, mOutputCount(aNode ? aNode->NumberOfOutputs() : 0)
|
||||
{
|
||||
@@ -341,19 +343,12 @@ public:
|
||||
aOutput[0] = aInput[0];
|
||||
}
|
||||
|
||||
Mutex& NodeMutex() { return mNodeMutex;}
|
||||
|
||||
bool HasNode() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return !!mNode;
|
||||
}
|
||||
|
||||
dom::AudioNode* Node() const
|
||||
{
|
||||
mNodeMutex.AssertCurrentThreadOwns();
|
||||
return mNode;
|
||||
}
|
||||
|
||||
dom::AudioNode* NodeMainThread() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
@@ -364,7 +359,6 @@ public:
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mNode != nullptr);
|
||||
mNodeMutex.AssertCurrentThreadOwns();
|
||||
mNode = nullptr;
|
||||
}
|
||||
|
||||
@@ -394,7 +388,6 @@ public:
|
||||
|
||||
private:
|
||||
dom::AudioNode* mNode;
|
||||
Mutex mNodeMutex;
|
||||
const uint16_t mInputCount;
|
||||
const uint16_t mOutputCount;
|
||||
};
|
||||
|
||||
@@ -18,14 +18,17 @@ namespace mozilla {
|
||||
* input --- handling any number of audio tracks and handling blocking of
|
||||
* the input MediaStream.
|
||||
*/
|
||||
class AudioNodeExternalInputStream : public AudioNodeStream {
|
||||
class AudioNodeExternalInputStream final : public AudioNodeStream
|
||||
{
|
||||
public:
|
||||
AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate, uint32_t aContextId);
|
||||
AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate,
|
||||
uint32_t aContextId);
|
||||
protected:
|
||||
~AudioNodeExternalInputStream();
|
||||
|
||||
public:
|
||||
virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override;
|
||||
virtual void ProcessInput(GraphTime aFrom, GraphTime aTo,
|
||||
uint32_t aFlags) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
|
||||
@@ -95,13 +95,15 @@ void
|
||||
AudioNodeStream::SetStreamTimeParameter(uint32_t aIndex, AudioContext* aContext,
|
||||
double aStreamTime)
|
||||
{
|
||||
class Message : public ControlMessage {
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream, uint32_t aIndex, MediaStream* aRelativeToStream,
|
||||
double aStreamTime)
|
||||
: ControlMessage(aStream), mStreamTime(aStreamTime),
|
||||
mRelativeToStream(aRelativeToStream), mIndex(aIndex) {}
|
||||
virtual void Run()
|
||||
mRelativeToStream(aRelativeToStream), mIndex(aIndex)
|
||||
{}
|
||||
virtual void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->
|
||||
SetStreamTimeParameterImpl(mIndex, mRelativeToStream, mStreamTime);
|
||||
@@ -127,11 +129,13 @@ AudioNodeStream::SetStreamTimeParameterImpl(uint32_t aIndex, MediaStream* aRelat
|
||||
void
|
||||
AudioNodeStream::SetDoubleParameter(uint32_t aIndex, double aValue)
|
||||
{
|
||||
class Message : public ControlMessage {
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream, uint32_t aIndex, double aValue)
|
||||
: ControlMessage(aStream), mValue(aValue), mIndex(aIndex) {}
|
||||
virtual void Run()
|
||||
: ControlMessage(aStream), mValue(aValue), mIndex(aIndex)
|
||||
{}
|
||||
virtual void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->Engine()->
|
||||
SetDoubleParameter(mIndex, mValue);
|
||||
@@ -146,11 +150,13 @@ AudioNodeStream::SetDoubleParameter(uint32_t aIndex, double aValue)
|
||||
void
|
||||
AudioNodeStream::SetInt32Parameter(uint32_t aIndex, int32_t aValue)
|
||||
{
|
||||
class Message : public ControlMessage {
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream, uint32_t aIndex, int32_t aValue)
|
||||
: ControlMessage(aStream), mValue(aValue), mIndex(aIndex) {}
|
||||
virtual void Run()
|
||||
: ControlMessage(aStream), mValue(aValue), mIndex(aIndex)
|
||||
{}
|
||||
virtual void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->Engine()->
|
||||
SetInt32Parameter(mIndex, mValue);
|
||||
@@ -166,15 +172,17 @@ void
|
||||
AudioNodeStream::SetTimelineParameter(uint32_t aIndex,
|
||||
const AudioParamTimeline& aValue)
|
||||
{
|
||||
class Message : public ControlMessage {
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream, uint32_t aIndex,
|
||||
const AudioParamTimeline& aValue)
|
||||
: ControlMessage(aStream),
|
||||
mValue(aValue),
|
||||
mSampleRate(aStream->SampleRate()),
|
||||
mIndex(aIndex) {}
|
||||
virtual void Run()
|
||||
mIndex(aIndex)
|
||||
{}
|
||||
virtual void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->Engine()->
|
||||
SetTimelineParameter(mIndex, mValue, mSampleRate);
|
||||
@@ -189,11 +197,13 @@ AudioNodeStream::SetTimelineParameter(uint32_t aIndex,
|
||||
void
|
||||
AudioNodeStream::SetThreeDPointParameter(uint32_t aIndex, const ThreeDPoint& aValue)
|
||||
{
|
||||
class Message : public ControlMessage {
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream, uint32_t aIndex, const ThreeDPoint& aValue)
|
||||
: ControlMessage(aStream), mValue(aValue), mIndex(aIndex) {}
|
||||
virtual void Run()
|
||||
: ControlMessage(aStream), mValue(aValue), mIndex(aIndex)
|
||||
{}
|
||||
virtual void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->Engine()->
|
||||
SetThreeDPointParameter(mIndex, mValue);
|
||||
@@ -208,12 +218,14 @@ AudioNodeStream::SetThreeDPointParameter(uint32_t aIndex, const ThreeDPoint& aVa
|
||||
void
|
||||
AudioNodeStream::SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList>&& aBuffer)
|
||||
{
|
||||
class Message : public ControlMessage {
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream,
|
||||
already_AddRefed<ThreadSharedFloatArrayBufferList>& aBuffer)
|
||||
: ControlMessage(aStream), mBuffer(aBuffer) {}
|
||||
virtual void Run()
|
||||
: ControlMessage(aStream), mBuffer(aBuffer)
|
||||
{}
|
||||
virtual void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->Engine()->
|
||||
SetBuffer(mBuffer.forget());
|
||||
@@ -227,7 +239,8 @@ AudioNodeStream::SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList>&&
|
||||
void
|
||||
AudioNodeStream::SetRawArrayData(nsTArray<float>& aData)
|
||||
{
|
||||
class Message : public ControlMessage {
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream,
|
||||
nsTArray<float>& aData)
|
||||
@@ -235,7 +248,7 @@ AudioNodeStream::SetRawArrayData(nsTArray<float>& aData)
|
||||
{
|
||||
mData.SwapElements(aData);
|
||||
}
|
||||
virtual void Run()
|
||||
virtual void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->Engine()->SetRawArrayData(mData);
|
||||
}
|
||||
@@ -250,7 +263,8 @@ AudioNodeStream::SetChannelMixingParameters(uint32_t aNumberOfChannels,
|
||||
ChannelCountMode aChannelCountMode,
|
||||
ChannelInterpretation aChannelInterpretation)
|
||||
{
|
||||
class Message : public ControlMessage {
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream,
|
||||
uint32_t aNumberOfChannels,
|
||||
@@ -261,7 +275,7 @@ AudioNodeStream::SetChannelMixingParameters(uint32_t aNumberOfChannels,
|
||||
mChannelCountMode(aChannelCountMode),
|
||||
mChannelInterpretation(aChannelInterpretation)
|
||||
{}
|
||||
virtual void Run()
|
||||
virtual void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->
|
||||
SetChannelMixingParametersImpl(mNumberOfChannels, mChannelCountMode,
|
||||
@@ -280,11 +294,13 @@ AudioNodeStream::SetChannelMixingParameters(uint32_t aNumberOfChannels,
|
||||
void
|
||||
AudioNodeStream::SetPassThrough(bool aPassThrough)
|
||||
{
|
||||
class Message : public ControlMessage {
|
||||
class Message final : public ControlMessage
|
||||
{
|
||||
public:
|
||||
Message(AudioNodeStream* aStream, bool aPassThrough)
|
||||
: ControlMessage(aStream), mPassThrough(aPassThrough) {}
|
||||
virtual void Run()
|
||||
: ControlMessage(aStream), mPassThrough(aPassThrough)
|
||||
{}
|
||||
virtual void Run() override
|
||||
{
|
||||
static_cast<AudioNodeStream*>(mStream)->mPassThrough = mPassThrough;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,8 @@ class AudioNodeEngine;
|
||||
* actual audio processing. AudioNodeStream contains the glue code that
|
||||
* integrates audio processing with the MediaStreamGraph.
|
||||
*/
|
||||
class AudioNodeStream : public ProcessedMediaStream {
|
||||
class AudioNodeStream : public ProcessedMediaStream
|
||||
{
|
||||
typedef dom::ChannelCountMode ChannelCountMode;
|
||||
typedef dom::ChannelInterpretation ChannelInterpretation;
|
||||
|
||||
|
||||
@@ -45,11 +45,13 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AudioParam, Release)
|
||||
|
||||
AudioParam::AudioParam(AudioNode* aNode,
|
||||
AudioParam::CallbackType aCallback,
|
||||
float aDefaultValue)
|
||||
float aDefaultValue,
|
||||
const char* aName)
|
||||
: AudioParamTimeline(aDefaultValue)
|
||||
, mNode(aNode)
|
||||
, mCallback(aCallback)
|
||||
, mDefaultValue(aDefaultValue)
|
||||
, mName(aName)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -111,10 +113,11 @@ AudioParam::Stream()
|
||||
mStream = stream.forget();
|
||||
|
||||
// Setup the AudioParam's stream as an input to the owner AudioNode's stream
|
||||
MediaStream* nodeStream = mNode->Stream();
|
||||
MOZ_ASSERT(nodeStream->AsProcessedStream());
|
||||
ProcessedMediaStream* ps = static_cast<ProcessedMediaStream*>(nodeStream);
|
||||
mNodeStreamPort = ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT);
|
||||
AudioNodeStream* nodeStream = mNode->Stream();
|
||||
if (nodeStream) {
|
||||
mNodeStreamPort =
|
||||
nodeStream->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT);
|
||||
}
|
||||
|
||||
// Let the MSG's copy of AudioParamTimeline know about the change in the stream
|
||||
mCallback(mNode);
|
||||
|
||||
@@ -30,7 +30,8 @@ public:
|
||||
|
||||
AudioParam(AudioNode* aNode,
|
||||
CallbackType aCallback,
|
||||
float aDefaultValue);
|
||||
float aDefaultValue,
|
||||
const char* aName);
|
||||
|
||||
NS_IMETHOD_(MozExternalRefCountType) AddRef(void);
|
||||
NS_IMETHOD_(MozExternalRefCountType) Release(void);
|
||||
@@ -121,6 +122,16 @@ public:
|
||||
mCallback(mNode);
|
||||
}
|
||||
|
||||
uint32_t ParentNodeId()
|
||||
{
|
||||
return mNode->Id();
|
||||
}
|
||||
|
||||
void GetName(nsAString& aName)
|
||||
{
|
||||
aName.AssignASCII(mName);
|
||||
}
|
||||
|
||||
float DefaultValue() const
|
||||
{
|
||||
return mDefaultValue;
|
||||
@@ -183,6 +194,7 @@ private:
|
||||
nsTArray<AudioNode::InputNode> mInputNodes;
|
||||
CallbackType mCallback;
|
||||
const float mDefaultValue;
|
||||
const char* mName;
|
||||
// The input port used to connect the AudioParam's stream to its node's stream
|
||||
nsRefPtr<MediaInputPort> mNodeStreamPort;
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AudioProcessingEvent : public Event
|
||||
class AudioProcessingEvent final : public Event
|
||||
{
|
||||
public:
|
||||
AudioProcessingEvent(ScriptProcessorNode* aOwner,
|
||||
|
||||
@@ -73,13 +73,13 @@ SetParamsOnBiquad(WebCore::Biquad& aBiquad,
|
||||
}
|
||||
}
|
||||
|
||||
class BiquadFilterNodeEngine : public AudioNodeEngine
|
||||
class BiquadFilterNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
BiquadFilterNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
|
||||
: AudioNodeEngine(aNode)
|
||||
, mSource(nullptr)
|
||||
, mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
|
||||
, mDestination(aDestination->Stream())
|
||||
// Keep the default values in sync with the default values in
|
||||
// BiquadFilterNode::BiquadFilterNode
|
||||
, mType(BiquadFilterType::Lowpass)
|
||||
@@ -244,14 +244,14 @@ BiquadFilterNode::BiquadFilterNode(AudioContext* aContext)
|
||||
ChannelCountMode::Max,
|
||||
ChannelInterpretation::Speakers)
|
||||
, mType(BiquadFilterType::Lowpass)
|
||||
, mFrequency(new AudioParam(this, SendFrequencyToStream, 350.f))
|
||||
, mDetune(new AudioParam(this, SendDetuneToStream, 0.f))
|
||||
, mQ(new AudioParam(this, SendQToStream, 1.f))
|
||||
, mGain(new AudioParam(this, SendGainToStream, 0.f))
|
||||
, mFrequency(new AudioParam(this, SendFrequencyToStream, 350.f, "frequency"))
|
||||
, mDetune(new AudioParam(this, SendDetuneToStream, 0.f, "detune"))
|
||||
, mQ(new AudioParam(this, SendQToStream, 1.f, "Q"))
|
||||
, mGain(new AudioParam(this, SendGainToStream, 0.f, "gain"))
|
||||
{
|
||||
BiquadFilterNodeEngine* engine = new BiquadFilterNodeEngine(this, aContext->Destination());
|
||||
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
|
||||
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
|
||||
engine->SetSourceStream(mStream);
|
||||
}
|
||||
|
||||
BiquadFilterNode::~BiquadFilterNode()
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
|
||||
class BiquadFilterNode : public AudioNode
|
||||
class BiquadFilterNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
explicit BiquadFilterNode(AudioContext* aContext);
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace mozilla {
|
||||
* This class provides a decoder object which decodes a media file that lives in
|
||||
* a memory buffer.
|
||||
*/
|
||||
class BufferDecoder : public AbstractMediaDecoder
|
||||
class BufferDecoder final : public AbstractMediaDecoder
|
||||
{
|
||||
public:
|
||||
// This class holds a weak pointer to MediaResource. It's the responsibility
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(ChannelMergerNode, AudioNode)
|
||||
|
||||
class ChannelMergerNodeEngine : public AudioNodeEngine
|
||||
class ChannelMergerNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
explicit ChannelMergerNodeEngine(ChannelMergerNode* aNode)
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
|
||||
class ChannelMergerNode : public AudioNode
|
||||
class ChannelMergerNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
ChannelMergerNode(AudioContext* aContext,
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(ChannelSplitterNode, AudioNode)
|
||||
|
||||
class ChannelSplitterNodeEngine : public AudioNodeEngine
|
||||
class ChannelSplitterNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
explicit ChannelSplitterNodeEngine(ChannelSplitterNode* aNode)
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
|
||||
class ChannelSplitterNode : public AudioNode
|
||||
class ChannelSplitterNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
ChannelSplitterNode(AudioContext* aContext,
|
||||
|
||||
@@ -22,7 +22,7 @@ NS_INTERFACE_MAP_END_INHERITING(AudioNode)
|
||||
NS_IMPL_ADDREF_INHERITED(ConvolverNode, AudioNode)
|
||||
NS_IMPL_RELEASE_INHERITED(ConvolverNode, AudioNode)
|
||||
|
||||
class ConvolverNodeEngine : public AudioNodeEngine
|
||||
class ConvolverNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
typedef PlayingRefChangeHandler PlayingRefChanged;
|
||||
public:
|
||||
@@ -241,7 +241,7 @@ ConvolverNode::SetBuffer(JSContext* aCx, AudioBuffer* aBuffer, ErrorResult& aRv)
|
||||
mBuffer = aBuffer;
|
||||
|
||||
// Send the buffer to the stream
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
AudioNodeStream* ns = mStream;
|
||||
MOZ_ASSERT(ns, "Why don't we have a stream here?");
|
||||
if (mBuffer) {
|
||||
uint32_t length = mBuffer->Length();
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class ConvolverNode : public AudioNode
|
||||
class ConvolverNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
explicit ConvolverNode(AudioContext* aContext);
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class DelayBuffer {
|
||||
class DelayBuffer final
|
||||
{
|
||||
typedef dom::ChannelInterpretation ChannelInterpretation;
|
||||
|
||||
public:
|
||||
|
||||
@@ -25,7 +25,7 @@ NS_INTERFACE_MAP_END_INHERITING(AudioNode)
|
||||
NS_IMPL_ADDREF_INHERITED(DelayNode, AudioNode)
|
||||
NS_IMPL_RELEASE_INHERITED(DelayNode, AudioNode)
|
||||
|
||||
class DelayNodeEngine : public AudioNodeEngine
|
||||
class DelayNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
typedef PlayingRefChangeHandler PlayingRefChanged;
|
||||
public:
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
double aMaxDelayTicks)
|
||||
: AudioNodeEngine(aNode)
|
||||
, mSource(nullptr)
|
||||
, mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
|
||||
, mDestination(aDestination->Stream())
|
||||
// Keep the default value in sync with the default value in DelayNode::DelayNode.
|
||||
, mDelay(0.f)
|
||||
// Use a smoothing range of 20ms
|
||||
@@ -190,13 +190,13 @@ DelayNode::DelayNode(AudioContext* aContext, double aMaxDelay)
|
||||
2,
|
||||
ChannelCountMode::Max,
|
||||
ChannelInterpretation::Speakers)
|
||||
, mDelay(new AudioParam(this, SendDelayToStream, 0.0f))
|
||||
, mDelay(new AudioParam(this, SendDelayToStream, 0.0f, "delayTime"))
|
||||
{
|
||||
DelayNodeEngine* engine =
|
||||
new DelayNodeEngine(this, aContext->Destination(),
|
||||
aContext->SampleRate() * aMaxDelay);
|
||||
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
|
||||
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
|
||||
engine->SetSourceStream(mStream);
|
||||
}
|
||||
|
||||
DelayNode::~DelayNode()
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
|
||||
class DelayNode : public AudioNode
|
||||
class DelayNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
DelayNode(AudioContext* aContext, double aMaxDelay);
|
||||
|
||||
@@ -21,7 +21,6 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(DynamicsCompressorNode, AudioNode,
|
||||
mThreshold,
|
||||
mKnee,
|
||||
mRatio,
|
||||
mReduction,
|
||||
mAttack,
|
||||
mRelease)
|
||||
|
||||
@@ -31,14 +30,14 @@ NS_INTERFACE_MAP_END_INHERITING(AudioNode)
|
||||
NS_IMPL_ADDREF_INHERITED(DynamicsCompressorNode, AudioNode)
|
||||
NS_IMPL_RELEASE_INHERITED(DynamicsCompressorNode, AudioNode)
|
||||
|
||||
class DynamicsCompressorNodeEngine : public AudioNodeEngine
|
||||
class DynamicsCompressorNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
explicit DynamicsCompressorNodeEngine(AudioNode* aNode,
|
||||
AudioDestinationNode* aDestination)
|
||||
: AudioNodeEngine(aNode)
|
||||
, mSource(nullptr)
|
||||
, mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
|
||||
, mDestination(aDestination->Stream())
|
||||
// Keep the default value in sync with the default value in
|
||||
// DynamicsCompressorNode::DynamicsCompressorNode.
|
||||
, mThreshold(-24.f)
|
||||
@@ -152,7 +151,7 @@ private:
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
class Command : public nsRunnable
|
||||
class Command final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
Command(AudioNodeStream* aStream, float aReduction)
|
||||
@@ -161,21 +160,13 @@ private:
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP Run()
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
nsRefPtr<DynamicsCompressorNode> node;
|
||||
{
|
||||
// No need to keep holding the lock for the whole duration of this
|
||||
// function, since we're holding a strong reference to it, so if
|
||||
// we can obtain the reference, we will hold the node alive in
|
||||
// this function.
|
||||
MutexAutoLock lock(mStream->Engine()->NodeMutex());
|
||||
node = static_cast<DynamicsCompressorNode*>(mStream->Engine()->Node());
|
||||
}
|
||||
nsRefPtr<DynamicsCompressorNode> node =
|
||||
static_cast<DynamicsCompressorNode*>
|
||||
(mStream->Engine()->NodeMainThread());
|
||||
if (node) {
|
||||
AudioParam* reduction = node->Reduction();
|
||||
reduction->CancelAllEvents();
|
||||
reduction->SetValue(mReduction);
|
||||
node->SetReduction(mReduction);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -204,16 +195,16 @@ DynamicsCompressorNode::DynamicsCompressorNode(AudioContext* aContext)
|
||||
2,
|
||||
ChannelCountMode::Explicit,
|
||||
ChannelInterpretation::Speakers)
|
||||
, mThreshold(new AudioParam(this, SendThresholdToStream, -24.f))
|
||||
, mKnee(new AudioParam(this, SendKneeToStream, 30.f))
|
||||
, mRatio(new AudioParam(this, SendRatioToStream, 12.f))
|
||||
, mReduction(new AudioParam(this, Callback, 0.f))
|
||||
, mAttack(new AudioParam(this, SendAttackToStream, 0.003f))
|
||||
, mRelease(new AudioParam(this, SendReleaseToStream, 0.25f))
|
||||
, mThreshold(new AudioParam(this, SendThresholdToStream, -24.f, "threshold"))
|
||||
, mKnee(new AudioParam(this, SendKneeToStream, 30.f, "knee"))
|
||||
, mRatio(new AudioParam(this, SendRatioToStream, 12.f, "ratio"))
|
||||
, mReduction(0)
|
||||
, mAttack(new AudioParam(this, SendAttackToStream, 0.003f, "attack"))
|
||||
, mRelease(new AudioParam(this, SendReleaseToStream, 0.25f, "release"))
|
||||
{
|
||||
DynamicsCompressorNodeEngine* engine = new DynamicsCompressorNodeEngine(this, aContext->Destination());
|
||||
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
|
||||
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
|
||||
engine->SetSourceStream(mStream);
|
||||
}
|
||||
|
||||
DynamicsCompressorNode::~DynamicsCompressorNode()
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
|
||||
class DynamicsCompressorNode : public AudioNode
|
||||
class DynamicsCompressorNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
explicit DynamicsCompressorNode(AudioContext* aContext);
|
||||
@@ -40,11 +40,6 @@ public:
|
||||
return mRatio;
|
||||
}
|
||||
|
||||
AudioParam* Reduction() const
|
||||
{
|
||||
return mReduction;
|
||||
}
|
||||
|
||||
AudioParam* Attack() const
|
||||
{
|
||||
return mAttack;
|
||||
@@ -56,6 +51,11 @@ public:
|
||||
return mRelease;
|
||||
}
|
||||
|
||||
float Reduction() const
|
||||
{
|
||||
return mReduction;
|
||||
}
|
||||
|
||||
virtual const char* NodeType() const override
|
||||
{
|
||||
return "DynamicsCompressorNode";
|
||||
@@ -64,6 +64,12 @@ public:
|
||||
virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
|
||||
|
||||
void SetReduction(float aReduction)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mReduction = aReduction;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~DynamicsCompressorNode();
|
||||
|
||||
@@ -78,7 +84,7 @@ private:
|
||||
nsRefPtr<AudioParam> mThreshold;
|
||||
nsRefPtr<AudioParam> mKnee;
|
||||
nsRefPtr<AudioParam> mRatio;
|
||||
nsRefPtr<AudioParam> mReduction;
|
||||
float mReduction;
|
||||
nsRefPtr<AudioParam> mAttack;
|
||||
nsRefPtr<AudioParam> mRelease;
|
||||
};
|
||||
|
||||
@@ -16,7 +16,8 @@ namespace mozilla {
|
||||
// This class defines an FFT block, loosely modeled after Blink's FFTFrame
|
||||
// class to make sharing code with Blink easy.
|
||||
// Currently it's implemented on top of KissFFT on all platforms.
|
||||
class FFTBlock {
|
||||
class FFTBlock final
|
||||
{
|
||||
public:
|
||||
explicit FFTBlock(uint32_t aFFTSize)
|
||||
: mFFT(nullptr)
|
||||
|
||||
@@ -23,13 +23,13 @@ NS_INTERFACE_MAP_END_INHERITING(AudioNode)
|
||||
NS_IMPL_ADDREF_INHERITED(GainNode, AudioNode)
|
||||
NS_IMPL_RELEASE_INHERITED(GainNode, AudioNode)
|
||||
|
||||
class GainNodeEngine : public AudioNodeEngine
|
||||
class GainNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
GainNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
|
||||
: AudioNodeEngine(aNode)
|
||||
, mSource(nullptr)
|
||||
, mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
|
||||
, mDestination(aDestination->Stream())
|
||||
// Keep the default value in sync with the default value in GainNode::GainNode.
|
||||
, mGain(1.f)
|
||||
{
|
||||
@@ -125,11 +125,11 @@ GainNode::GainNode(AudioContext* aContext)
|
||||
2,
|
||||
ChannelCountMode::Max,
|
||||
ChannelInterpretation::Speakers)
|
||||
, mGain(new AudioParam(this, SendGainToStream, 1.0f))
|
||||
, mGain(new AudioParam(this, SendGainToStream, 1.0f, "gain"))
|
||||
{
|
||||
GainNodeEngine* engine = new GainNodeEngine(this, aContext->Destination());
|
||||
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
|
||||
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
|
||||
engine->SetSourceStream(mStream);
|
||||
}
|
||||
|
||||
GainNode::~GainNode()
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
|
||||
class GainNode : public AudioNode
|
||||
class GainNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
explicit GainNode(AudioContext* aContext);
|
||||
|
||||
@@ -50,7 +50,7 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebAudioDecodeJob, Release)
|
||||
|
||||
using namespace dom;
|
||||
|
||||
class ReportResultTask : public nsRunnable
|
||||
class ReportResultTask final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
ReportResultTask(WebAudioDecodeJob& aDecodeJob,
|
||||
@@ -82,13 +82,14 @@ private:
|
||||
WebAudioDecodeJob::ErrorCode mErrorCode;
|
||||
};
|
||||
|
||||
enum class PhaseEnum : int {
|
||||
enum class PhaseEnum : int
|
||||
{
|
||||
Decode,
|
||||
AllocateBuffer,
|
||||
Done
|
||||
};
|
||||
|
||||
class MediaDecodeTask : public nsRunnable
|
||||
class MediaDecodeTask final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
MediaDecodeTask(const char* aContentType, uint8_t* aBuffer,
|
||||
@@ -217,7 +218,8 @@ MediaDecodeTask::CreateReader()
|
||||
return true;
|
||||
}
|
||||
|
||||
class AutoResampler {
|
||||
class AutoResampler final
|
||||
{
|
||||
public:
|
||||
AutoResampler()
|
||||
: mResampler(nullptr)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class MediaElementAudioSourceNode : public MediaStreamAudioSourceNode
|
||||
class MediaElementAudioSourceNode final : public MediaStreamAudioSourceNode
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<MediaElementAudioSourceNode>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class MediaStreamAudioDestinationNode : public AudioNode
|
||||
class MediaStreamAudioDestinationNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
explicit MediaStreamAudioDestinationNode(AudioContext* aContext);
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
|
||||
class MediaStreamAudioSourceNodeEngine : public AudioNodeEngine
|
||||
class MediaStreamAudioSourceNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
explicit MediaStreamAudioSourceNodeEngine(AudioNode* aNode)
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
|
||||
class OfflineAudioCompletionEvent : public Event
|
||||
class OfflineAudioCompletionEvent final : public Event
|
||||
{
|
||||
public:
|
||||
OfflineAudioCompletionEvent(AudioContext* aOwner,
|
||||
|
||||
@@ -23,13 +23,13 @@ NS_INTERFACE_MAP_END_INHERITING(AudioNode)
|
||||
NS_IMPL_ADDREF_INHERITED(OscillatorNode, AudioNode)
|
||||
NS_IMPL_RELEASE_INHERITED(OscillatorNode, AudioNode)
|
||||
|
||||
class OscillatorNodeEngine : public AudioNodeEngine
|
||||
class OscillatorNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
OscillatorNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
|
||||
: AudioNodeEngine(aNode)
|
||||
, mSource(nullptr)
|
||||
, mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
|
||||
, mDestination(aDestination->Stream())
|
||||
, mStart(-1)
|
||||
, mStop(STREAM_TIME_MAX)
|
||||
// Keep the default values in sync with OscillatorNode::OscillatorNode.
|
||||
@@ -381,14 +381,13 @@ OscillatorNode::OscillatorNode(AudioContext* aContext)
|
||||
ChannelCountMode::Max,
|
||||
ChannelInterpretation::Speakers)
|
||||
, mType(OscillatorType::Sine)
|
||||
, mFrequency(new AudioParam(this, SendFrequencyToStream, 440.0f))
|
||||
, mDetune(new AudioParam(this, SendDetuneToStream, 0.0f))
|
||||
, mFrequency(new AudioParam(this, SendFrequencyToStream, 440.0f, "frequency"))
|
||||
, mDetune(new AudioParam(this, SendDetuneToStream, 0.0f, "detune"))
|
||||
, mStartCalled(false)
|
||||
, mStopped(false)
|
||||
{
|
||||
OscillatorNodeEngine* engine = new OscillatorNodeEngine(this, aContext->Destination());
|
||||
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::SOURCE_STREAM);
|
||||
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
|
||||
engine->SetSourceStream(mStream);
|
||||
mStream->AddMainThreadListener(this);
|
||||
}
|
||||
|
||||
@@ -422,10 +421,22 @@ OscillatorNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
return OscillatorNodeBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
OscillatorNode::DestroyMediaStream()
|
||||
{
|
||||
if (mStream) {
|
||||
mStream->RemoveMainThreadListener(this);
|
||||
}
|
||||
AudioNode::DestroyMediaStream();
|
||||
}
|
||||
|
||||
void
|
||||
OscillatorNode::SendFrequencyToStream(AudioNode* aNode)
|
||||
{
|
||||
OscillatorNode* This = static_cast<OscillatorNode*>(aNode);
|
||||
if (!This->mStream) {
|
||||
return;
|
||||
}
|
||||
SendTimelineParameterToStream(This, OscillatorNodeEngine::FREQUENCY, *This->mFrequency);
|
||||
}
|
||||
|
||||
@@ -433,12 +444,18 @@ void
|
||||
OscillatorNode::SendDetuneToStream(AudioNode* aNode)
|
||||
{
|
||||
OscillatorNode* This = static_cast<OscillatorNode*>(aNode);
|
||||
if (!This->mStream) {
|
||||
return;
|
||||
}
|
||||
SendTimelineParameterToStream(This, OscillatorNodeEngine::DETUNE, *This->mDetune);
|
||||
}
|
||||
|
||||
void
|
||||
OscillatorNode::SendTypeToStream()
|
||||
{
|
||||
if (!mStream) {
|
||||
return;
|
||||
}
|
||||
if (mType == OscillatorType::Custom) {
|
||||
// The engine assumes we'll send the custom data before updating the type.
|
||||
SendPeriodicWaveToStream();
|
||||
@@ -450,14 +467,13 @@ void OscillatorNode::SendPeriodicWaveToStream()
|
||||
{
|
||||
NS_ASSERTION(mType == OscillatorType::Custom,
|
||||
"Sending custom waveform to engine thread with non-custom type");
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
MOZ_ASSERT(ns, "Missing node stream.");
|
||||
MOZ_ASSERT(mStream, "Missing node stream.");
|
||||
MOZ_ASSERT(mPeriodicWave, "Send called without PeriodicWave object.");
|
||||
SendInt32ParameterToStream(OscillatorNodeEngine::PERIODICWAVE,
|
||||
mPeriodicWave->DataLength());
|
||||
nsRefPtr<ThreadSharedFloatArrayBufferList> data =
|
||||
mPeriodicWave->GetThreadSharedBuffer();
|
||||
ns->SetBuffer(data.forget());
|
||||
mStream->SetBuffer(data.forget());
|
||||
}
|
||||
|
||||
void
|
||||
@@ -474,15 +490,14 @@ OscillatorNode::Start(double aWhen, ErrorResult& aRv)
|
||||
}
|
||||
mStartCalled = true;
|
||||
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
if (!ns) {
|
||||
if (!mStream) {
|
||||
// Nothing to play, or we're already dead for some reason
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Perhaps we need to do more here.
|
||||
ns->SetStreamTimeParameter(OscillatorNodeEngine::START,
|
||||
Context(), aWhen);
|
||||
mStream->SetStreamTimeParameter(OscillatorNodeEngine::START,
|
||||
Context(), aWhen);
|
||||
|
||||
MarkActive();
|
||||
}
|
||||
@@ -500,50 +515,48 @@ OscillatorNode::Stop(double aWhen, ErrorResult& aRv)
|
||||
return;
|
||||
}
|
||||
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
if (!ns || !Context()) {
|
||||
if (!mStream || !Context()) {
|
||||
// We've already stopped and had our stream shut down
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Perhaps we need to do more here.
|
||||
ns->SetStreamTimeParameter(OscillatorNodeEngine::STOP,
|
||||
Context(), std::max(0.0, aWhen));
|
||||
mStream->SetStreamTimeParameter(OscillatorNodeEngine::STOP,
|
||||
Context(), std::max(0.0, aWhen));
|
||||
}
|
||||
|
||||
void
|
||||
OscillatorNode::NotifyMainThreadStateChanged()
|
||||
OscillatorNode::NotifyMainThreadStreamFinished()
|
||||
{
|
||||
if (mStream->IsFinished()) {
|
||||
class EndedEventDispatcher : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit EndedEventDispatcher(OscillatorNode* aNode)
|
||||
: mNode(aNode) {}
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
// If it's not safe to run scripts right now, schedule this to run later
|
||||
if (!nsContentUtils::IsSafeToRunScript()) {
|
||||
nsContentUtils::AddScriptRunner(this);
|
||||
return NS_OK;
|
||||
}
|
||||
MOZ_ASSERT(mStream->IsFinished());
|
||||
|
||||
mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
|
||||
class EndedEventDispatcher final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit EndedEventDispatcher(OscillatorNode* aNode)
|
||||
: mNode(aNode) {}
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
// If it's not safe to run scripts right now, schedule this to run later
|
||||
if (!nsContentUtils::IsSafeToRunScript()) {
|
||||
nsContentUtils::AddScriptRunner(this);
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsRefPtr<OscillatorNode> mNode;
|
||||
};
|
||||
if (!mStopped) {
|
||||
// Only dispatch the ended event once
|
||||
NS_DispatchToMainThread(new EndedEventDispatcher(this));
|
||||
mStopped = true;
|
||||
}
|
||||
|
||||
// Drop the playing reference
|
||||
// Warning: The below line might delete this.
|
||||
MarkInactive();
|
||||
}
|
||||
mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
|
||||
// Release stream resources.
|
||||
mNode->DestroyMediaStream();
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsRefPtr<OscillatorNode> mNode;
|
||||
};
|
||||
|
||||
NS_DispatchToMainThread(new EndedEventDispatcher(this));
|
||||
|
||||
// Drop the playing reference
|
||||
// Warning: The below line might delete this.
|
||||
MarkInactive();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -18,8 +18,8 @@ namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
|
||||
class OscillatorNode : public AudioNode,
|
||||
public MainThreadMediaStreamListener
|
||||
class OscillatorNode final : public AudioNode,
|
||||
public MainThreadMediaStreamListener
|
||||
{
|
||||
public:
|
||||
explicit OscillatorNode(AudioContext* aContext);
|
||||
@@ -29,13 +29,8 @@ public:
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
virtual void DestroyMediaStream() override
|
||||
{
|
||||
if (mStream) {
|
||||
mStream->RemoveMainThreadListener(this);
|
||||
}
|
||||
AudioNode::DestroyMediaStream();
|
||||
}
|
||||
virtual void DestroyMediaStream() override;
|
||||
|
||||
virtual uint16_t NumberOfInputs() const final override
|
||||
{
|
||||
return 0;
|
||||
@@ -78,7 +73,7 @@ public:
|
||||
|
||||
IMPL_EVENT_HANDLER(ended)
|
||||
|
||||
virtual void NotifyMainThreadStateChanged() override;
|
||||
virtual void NotifyMainThreadStreamFinished() override;
|
||||
|
||||
virtual const char* NodeType() const override
|
||||
{
|
||||
@@ -103,7 +98,6 @@ private:
|
||||
nsRefPtr<AudioParam> mFrequency;
|
||||
nsRefPtr<AudioParam> mDetune;
|
||||
bool mStartCalled;
|
||||
bool mStopped;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -39,7 +39,7 @@ NS_INTERFACE_MAP_END_INHERITING(AudioNode)
|
||||
NS_IMPL_ADDREF_INHERITED(PannerNode, AudioNode)
|
||||
NS_IMPL_RELEASE_INHERITED(PannerNode, AudioNode)
|
||||
|
||||
class PannerNodeEngine : public AudioNodeEngine
|
||||
class PannerNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
explicit PannerNodeEngine(AudioNode* aNode)
|
||||
@@ -540,9 +540,10 @@ PannerNode::FindConnectedSources(AudioNode* aNode,
|
||||
// Recurse
|
||||
FindConnectedSources(inputNodes[i].mInputNode, aSources, aNodesSeen);
|
||||
|
||||
// Check if this node is an AudioBufferSourceNode
|
||||
// Check if this node is an AudioBufferSourceNode that still have a stream,
|
||||
// which means it has not finished playing.
|
||||
AudioBufferSourceNode* node = inputNodes[i].mInputNode->AsAudioBufferSourceNode();
|
||||
if (node) {
|
||||
if (node && node->Stream()) {
|
||||
aSources.AppendElement(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ namespace dom {
|
||||
class AudioContext;
|
||||
class AudioBufferSourceNode;
|
||||
|
||||
class PannerNode : public AudioNode,
|
||||
public SupportsWeakPtr<PannerNode>
|
||||
class PannerNode final : public AudioNode,
|
||||
public SupportsWeakPtr<PannerNode>
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(PannerNode)
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class PlayingRefChangeHandler : public nsRunnable
|
||||
class PlayingRefChangeHandler final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
enum ChangeType { ADDREF, RELEASE };
|
||||
@@ -25,15 +25,7 @@ public:
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
nsRefPtr<AudioNode> node;
|
||||
{
|
||||
// No need to keep holding the lock for the whole duration of this
|
||||
// function, since we're holding a strong reference to it, so if
|
||||
// we can obtain the reference, we will hold the node alive in
|
||||
// this function.
|
||||
MutexAutoLock lock(mStream->Engine()->NodeMutex());
|
||||
node = mStream->Engine()->Node();
|
||||
}
|
||||
nsRefPtr<AudioNode> node = mStream->Engine()->NodeMainThread();
|
||||
if (node) {
|
||||
if (mChange == ADDREF) {
|
||||
node->MarkActive();
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ReportDecodeResultTask : public nsRunnable
|
||||
class ReportDecodeResultTask final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
ReportDecodeResultTask(DecodeJob& aDecodeJob,
|
||||
|
||||
@@ -28,10 +28,10 @@ NS_IMPL_ISUPPORTS_INHERITED0(ScriptProcessorNode, AudioNode)
|
||||
|
||||
// This class manages a queue of output buffers shared between
|
||||
// the main thread and the Media Stream Graph thread.
|
||||
class SharedBuffers
|
||||
class SharedBuffers final
|
||||
{
|
||||
private:
|
||||
class OutputQueue
|
||||
class OutputQueue final
|
||||
{
|
||||
public:
|
||||
explicit OutputQueue(const char* aName)
|
||||
@@ -237,7 +237,7 @@ private:
|
||||
bool mDroppingBuffers;
|
||||
};
|
||||
|
||||
class ScriptProcessorNodeEngine : public AudioNodeEngine
|
||||
class ScriptProcessorNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
typedef nsAutoTArray<nsAutoArrayPtr<float>, 2> InputChannels;
|
||||
@@ -249,7 +249,7 @@ public:
|
||||
: AudioNodeEngine(aNode)
|
||||
, mSharedBuffers(aNode->GetSharedBuffers())
|
||||
, mSource(nullptr)
|
||||
, mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
|
||||
, mDestination(aDestination->Stream())
|
||||
, mBufferSize(aBufferSize)
|
||||
, mInputWriteIndex(0)
|
||||
, mSeenNonSilenceInput(false)
|
||||
@@ -268,14 +268,6 @@ public:
|
||||
AudioChunk* aOutput,
|
||||
bool* aFinished) override
|
||||
{
|
||||
MutexAutoLock lock(NodeMutex());
|
||||
|
||||
// If our node is dead, just output silence.
|
||||
if (!Node()) {
|
||||
aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
// This node is not connected to anything. Per spec, we don't fire the
|
||||
// onaudioprocess event. We also want to clear out the input and output
|
||||
// buffer queue, and output a null buffer.
|
||||
@@ -361,7 +353,7 @@ private:
|
||||
double playbackTime =
|
||||
mSource->DestinationTimeFromTicks(mDestination, playbackTick);
|
||||
|
||||
class Command : public nsRunnable
|
||||
class Command final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
Command(AudioNodeStream* aStream,
|
||||
@@ -380,7 +372,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP Run()
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
nsRefPtr<ScriptProcessorNode> node = static_cast<ScriptProcessorNode*>
|
||||
(mStream->Engine()->NodeMainThread());
|
||||
@@ -490,7 +482,7 @@ ScriptProcessorNode::ScriptProcessorNode(AudioContext* aContext,
|
||||
BufferSize(),
|
||||
aNumberOfInputChannels);
|
||||
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
|
||||
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
|
||||
engine->SetSourceStream(mStream);
|
||||
}
|
||||
|
||||
ScriptProcessorNode::~ScriptProcessorNode()
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace dom {
|
||||
class AudioContext;
|
||||
class SharedBuffers;
|
||||
|
||||
class ScriptProcessorNode : public AudioNode
|
||||
class ScriptProcessorNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
ScriptProcessorNode(AudioContext* aContext,
|
||||
|
||||
@@ -27,14 +27,14 @@ NS_INTERFACE_MAP_END_INHERITING(AudioNode)
|
||||
NS_IMPL_ADDREF_INHERITED(StereoPannerNode, AudioNode)
|
||||
NS_IMPL_RELEASE_INHERITED(StereoPannerNode, AudioNode)
|
||||
|
||||
class StereoPannerNodeEngine : public AudioNodeEngine
|
||||
class StereoPannerNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
StereoPannerNodeEngine(AudioNode* aNode,
|
||||
AudioDestinationNode* aDestination)
|
||||
: AudioNodeEngine(aNode)
|
||||
, mSource(nullptr)
|
||||
, mDestination(static_cast<AudioNodeStream*>(aDestination->Stream()))
|
||||
, mDestination(aDestination->Stream())
|
||||
// Keep the default value in sync with the default value in
|
||||
// StereoPannerNode::StereoPannerNode.
|
||||
, mPan(0.f)
|
||||
@@ -176,12 +176,12 @@ StereoPannerNode::StereoPannerNode(AudioContext* aContext)
|
||||
2,
|
||||
ChannelCountMode::Clamped_max,
|
||||
ChannelInterpretation::Speakers)
|
||||
, mPan(new AudioParam(this, SendPanToStream, 0.f))
|
||||
, mPan(new AudioParam(this, SendPanToStream, 0.f, "pan"))
|
||||
{
|
||||
StereoPannerNodeEngine* engine = new StereoPannerNodeEngine(this, aContext->Destination());
|
||||
mStream = aContext->Graph()->CreateAudioNodeStream(engine,
|
||||
MediaStreamGraph::INTERNAL_STREAM);
|
||||
engine->SetSourceStream(static_cast<AudioNodeStream*>(mStream.get()));
|
||||
engine->SetSourceStream(mStream);
|
||||
}
|
||||
|
||||
StereoPannerNode::~StereoPannerNode()
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
|
||||
class StereoPannerNode : public AudioNode
|
||||
class StereoPannerNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(StereoPannerNode)
|
||||
|
||||
@@ -14,7 +14,8 @@ namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
|
||||
struct ThreeDPoint {
|
||||
struct ThreeDPoint final
|
||||
{
|
||||
ThreeDPoint()
|
||||
: x(0.)
|
||||
, y(0.)
|
||||
|
||||
@@ -48,7 +48,7 @@ static uint32_t ValueOf(OverSampleType aType)
|
||||
}
|
||||
}
|
||||
|
||||
class Resampler
|
||||
class Resampler final
|
||||
{
|
||||
public:
|
||||
Resampler()
|
||||
@@ -161,7 +161,7 @@ private:
|
||||
nsTArray<float> mBuffer;
|
||||
};
|
||||
|
||||
class WaveShaperNodeEngine : public AudioNodeEngine
|
||||
class WaveShaperNodeEngine final : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
explicit WaveShaperNodeEngine(AudioNode* aNode)
|
||||
@@ -324,7 +324,7 @@ WaveShaperNode::SetCurve(const Nullable<Float32Array>& aCurve)
|
||||
mCurve = nullptr;
|
||||
}
|
||||
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
AudioNodeStream* ns = mStream;
|
||||
MOZ_ASSERT(ns, "Why don't we have a stream here?");
|
||||
ns->SetRawArrayData(curve);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace dom {
|
||||
|
||||
class AudioContext;
|
||||
|
||||
class WaveShaperNode : public AudioNode
|
||||
class WaveShaperNode final : public AudioNode
|
||||
{
|
||||
public:
|
||||
explicit WaveShaperNode(AudioContext *aContext);
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
skip-if = buildapp == 'b2g'
|
||||
|
||||
[test_AudioNodeDevtoolsAPI.html]
|
||||
[test_AudioParamDevtoolsAPI.html]
|
||||
|
||||
@@ -43,6 +43,7 @@ skip-if = (toolkit == 'android' && (processor == 'x86' || debug)) || os == 'win'
|
||||
[test_audioBufferSourceNodeOffset.html]
|
||||
skip-if = (toolkit == 'gonk') || (toolkit == 'android') || debug #bug 906752
|
||||
[test_audioBufferSourceNodePassThrough.html]
|
||||
[test_audioBufferSourceNodeRate.html]
|
||||
[test_AudioContext.html]
|
||||
[test_audioContextSuspendResumeClose.html]
|
||||
[test_audioDestinationNode.html]
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test the devtool AudioParam API</title>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
Components.utils.import('resource://gre/modules/Services.jsm');
|
||||
|
||||
function checkIdAndName(node, name) {
|
||||
is(node.id, node[name].parentNodeId, "The parent id should be correct");
|
||||
is(node[name].name, name, "The name of the AudioParam should be correct.");
|
||||
}
|
||||
|
||||
var ac = new AudioContext(),
|
||||
gain = ac.createGain(),
|
||||
osc = ac.createOscillator(),
|
||||
del = ac.createDelay(),
|
||||
source = ac.createBufferSource(),
|
||||
stereoPanner = ac.createStereoPanner(),
|
||||
comp = ac.createDynamicsCompressor(),
|
||||
biquad = ac.createBiquadFilter();
|
||||
|
||||
checkIdAndName(gain, "gain");
|
||||
checkIdAndName(osc, "frequency");
|
||||
checkIdAndName(osc, "detune");
|
||||
checkIdAndName(del, "delayTime");
|
||||
checkIdAndName(source, "playbackRate");
|
||||
checkIdAndName(source, "detune");
|
||||
checkIdAndName(stereoPanner, "pan");
|
||||
checkIdAndName(comp, "threshold");
|
||||
checkIdAndName(comp, "knee");
|
||||
checkIdAndName(comp, "ratio");
|
||||
checkIdAndName(comp, "attack");
|
||||
checkIdAndName(comp, "release");
|
||||
checkIdAndName(biquad, "frequency");
|
||||
checkIdAndName(biquad, "detune");
|
||||
checkIdAndName(biquad, "Q");
|
||||
checkIdAndName(biquad, "gain");
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,66 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioBufferSourceNode</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var rate = 44100;
|
||||
var off = new OfflineAudioContext(1, rate, rate);
|
||||
var off2 = new OfflineAudioContext(1, rate, rate);
|
||||
|
||||
var source = off.createBufferSource();
|
||||
var source2 = off2.createBufferSource();
|
||||
|
||||
// a buffer of a 440Hz at half the length. If we detune by -1200 or set the
|
||||
// playbackRate to 0.5, we should get 44100 samples back with a sine at 220Hz.
|
||||
var buf = off.createBuffer(1, rate / 2, rate);
|
||||
var bufarray = buf.getChannelData(0);
|
||||
for (var i = 0; i < bufarray.length; i++) {
|
||||
bufarray[i] = Math.sin(i * 440 * 2 * Math.PI / rate);
|
||||
}
|
||||
|
||||
source.buffer = buf;
|
||||
source.playbackRate.value = 0.5; // 50% slowdown
|
||||
source.connect(off.destination);
|
||||
source.start(0);
|
||||
|
||||
source2.buffer = buf;
|
||||
source2.detune.value = -1200; // one octave -> 50% slowdown
|
||||
source2.connect(off2.destination);
|
||||
source2.start(0);
|
||||
|
||||
var finished = 0;
|
||||
function finish() {
|
||||
finished++;
|
||||
if (finished == 2) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
off.startRendering().then((renderedPlaybackRate) => {
|
||||
// we don't care about comparing the value here, we just want to know whether
|
||||
// the second part is noisy.
|
||||
var rmsValue = rms(renderedPlaybackRate, 0, 22050);
|
||||
ok(rmsValue != 0, "Resampling happened (rms of the second part " + rmsValue + ")");
|
||||
|
||||
off2.startRendering().then((renderedDetune) => {
|
||||
var rmsValue = rms(renderedDetune, 0, 22050);
|
||||
ok(rmsValue != 0, "Resampling happened (rms of the second part " + rmsValue + ")");
|
||||
// The two buffers should be the same: detune of -1200 is a 50% slowdown
|
||||
compareBuffers(renderedPlaybackRate, renderedDetune);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -41,7 +41,7 @@ addLoadEvent(function() {
|
||||
near(threshold.defaultValue, -24, "Correct default value for threshold");
|
||||
near(knee.defaultValue, 30, "Correct default value for knee");
|
||||
near(ratio.defaultValue, 12, "Correct default value for ratio");
|
||||
near(reduction.defaultValue, 0, "Correct default value for reduction");
|
||||
near(reduction, 0, "Correct default value for reduction");
|
||||
near(attack.defaultValue, 0.003, "Correct default value for attack");
|
||||
near(release.defaultValue, 0.25, "Correct default value for release");
|
||||
}
|
||||
|
||||
@@ -105,6 +105,25 @@ function compareBuffers(got, expected) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the root mean square (RMS,
|
||||
* <http://en.wikipedia.org/wiki/Root_mean_square>) of a channel of a slice
|
||||
* (defined by `start` and `end`) of an AudioBuffer.
|
||||
*
|
||||
* This is useful to detect that a buffer is noisy or silent.
|
||||
*/
|
||||
function rms(audiobuffer, channel = 0, start = 0, end = audiobuffer.length) {
|
||||
var buffer= audiobuffer.getChannelData(channel);
|
||||
var rms = 0;
|
||||
for (var i = start; i < end; i++) {
|
||||
rms += buffer[i] * buffer[i];
|
||||
}
|
||||
|
||||
rms /= buffer.length;
|
||||
rms = Math.sqrt(rms);
|
||||
return rms;
|
||||
}
|
||||
|
||||
function getEmptyBuffer(context, length) {
|
||||
return context.createBuffer(gTest.numberOfChannels, length, context.sampleRate);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ interface AudioBufferSourceNode : AudioNode {
|
||||
attribute AudioBuffer? buffer;
|
||||
|
||||
readonly attribute AudioParam playbackRate;
|
||||
readonly attribute AudioParam detune;
|
||||
|
||||
attribute boolean loop;
|
||||
attribute double loopStart;
|
||||
|
||||
@@ -37,3 +37,13 @@ interface AudioParam {
|
||||
void cancelScheduledValues(double startTime);
|
||||
|
||||
};
|
||||
|
||||
// Mozilla extension
|
||||
partial interface AudioParam {
|
||||
// The ID of the AudioNode this AudioParam belongs to.
|
||||
[ChromeOnly]
|
||||
readonly attribute unsigned long parentNodeId;
|
||||
// The name of the AudioParam
|
||||
[ChromeOnly]
|
||||
readonly attribute DOMString name;
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ interface DynamicsCompressorNode : AudioNode {
|
||||
readonly attribute AudioParam threshold; // in Decibels
|
||||
readonly attribute AudioParam knee; // in Decibels
|
||||
readonly attribute AudioParam ratio; // unit-less
|
||||
readonly attribute AudioParam reduction; // in Decibels
|
||||
readonly attribute float reduction; // in Decibels
|
||||
readonly attribute AudioParam attack; // in Seconds
|
||||
readonly attribute AudioParam release; // in Seconds
|
||||
|
||||
|
||||
Reference in New Issue
Block a user