summaryrefslogtreecommitdiffstats
path: root/dom/media/webaudio/ConstantSourceNode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webaudio/ConstantSourceNode.cpp')
-rw-r--r--dom/media/webaudio/ConstantSourceNode.cpp286
1 files changed, 286 insertions, 0 deletions
diff --git a/dom/media/webaudio/ConstantSourceNode.cpp b/dom/media/webaudio/ConstantSourceNode.cpp
new file mode 100644
index 000000000..b6884105c
--- /dev/null
+++ b/dom/media/webaudio/ConstantSourceNode.cpp
@@ -0,0 +1,286 @@
+/* -*- 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 "ConstantSourceNode.h"
+
+#include "AudioDestinationNode.h"
+#include "nsContentUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(ConstantSourceNode, AudioNode,
+ mOffset)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ConstantSourceNode)
+NS_INTERFACE_MAP_END_INHERITING(AudioNode)
+
+NS_IMPL_ADDREF_INHERITED(ConstantSourceNode, AudioNode)
+NS_IMPL_RELEASE_INHERITED(ConstantSourceNode, AudioNode)
+
+class ConstantSourceNodeEngine final : public AudioNodeEngine
+{
+public:
+ ConstantSourceNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
+ : AudioNodeEngine(aNode)
+ , mSource(nullptr)
+ , mDestination(aDestination->Stream())
+ , mStart(-1)
+ , mStop(STREAM_TIME_MAX)
+ // Keep the default values in sync with ConstantSourceNode::ConstantSourceNode.
+ , mOffset(1.0f)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ void SetSourceStream(AudioNodeStream* aSource)
+ {
+ mSource = aSource;
+ }
+
+ enum Parameters {
+ OFFSET,
+ START,
+ STOP,
+ };
+ void RecvTimelineEvent(uint32_t aIndex,
+ AudioTimelineEvent& aEvent) override
+ {
+ MOZ_ASSERT(mDestination);
+
+ WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent,
+ mDestination);
+
+ switch (aIndex) {
+ case OFFSET:
+ mOffset.InsertEvent<int64_t>(aEvent);
+ break;
+ default:
+ NS_ERROR("Bad ConstantSourceNodeEngine TimelineParameter");
+ }
+ }
+
+ void SetStreamTimeParameter(uint32_t aIndex, StreamTime aParam) override
+ {
+ switch (aIndex) {
+ case START:
+ mStart = aParam;
+ mSource->SetActive();
+ break;
+ case STOP: mStop = aParam; break;
+ default:
+ NS_ERROR("Bad ConstantSourceNodeEngine StreamTimeParameter");
+ }
+ }
+
+ void ProcessBlock(AudioNodeStream* aStream,
+ GraphTime aFrom,
+ const AudioBlock& aInput,
+ AudioBlock* aOutput,
+ bool* aFinished) override
+ {
+ MOZ_ASSERT(mSource == aStream, "Invalid source stream");
+
+ StreamTime ticks = mDestination->GraphTimeToStreamTime(aFrom);
+ if (mStart == -1) {
+ aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
+ return;
+ }
+
+ if (ticks + WEBAUDIO_BLOCK_SIZE <= mStart || ticks >= mStop) {
+ aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
+ } else {
+ aOutput->AllocateChannels(1);
+ float* output = aOutput->ChannelFloatsForWrite(0);
+
+ if (mOffset.HasSimpleValue()) {
+ for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
+ output[i] = mOffset.GetValueAtTime(aFrom, 0);
+ }
+ } else {
+ mOffset.GetValuesAtTime(ticks, output, WEBAUDIO_BLOCK_SIZE);
+ }
+ }
+
+ if (ticks + WEBAUDIO_BLOCK_SIZE >= mStop) {
+ // We've finished playing.
+ *aFinished = true;
+ }
+ }
+
+ bool IsActive() const override
+ {
+ // start() has been called.
+ return mStart != -1;
+ }
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
+ {
+ size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+
+ // Not owned:
+ // - mSource
+ // - mDestination
+ // - mOffset (internal ref owned by node)
+
+ return amount;
+ }
+
+ size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
+ {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ AudioNodeStream* mSource;
+ AudioNodeStream* mDestination;
+ StreamTime mStart;
+ StreamTime mStop;
+ AudioParamTimeline mOffset;
+};
+
+ConstantSourceNode::ConstantSourceNode(AudioContext* aContext)
+ : AudioNode(aContext,
+ 1,
+ ChannelCountMode::Max,
+ ChannelInterpretation::Speakers)
+ , mOffset(new AudioParam(this, ConstantSourceNodeEngine::OFFSET,
+ 1.0, "offset"))
+ , mStartCalled(false)
+{
+ ConstantSourceNodeEngine* engine = new ConstantSourceNodeEngine(this, aContext->Destination());
+ mStream = AudioNodeStream::Create(aContext, engine,
+ AudioNodeStream::NEED_MAIN_THREAD_FINISHED,
+ aContext->Graph());
+ engine->SetSourceStream(mStream);
+ mStream->AddMainThreadListener(this);
+}
+
+ConstantSourceNode::~ConstantSourceNode()
+{
+}
+
+size_t
+ConstantSourceNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+
+ amount += mOffset->SizeOfIncludingThis(aMallocSizeOf);
+ return amount;
+}
+
+size_t
+ConstantSourceNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
+JSObject*
+ConstantSourceNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return ConstantSourceNodeBinding::Wrap(aCx, this, aGivenProto);
+}
+
+already_AddRefed<ConstantSourceNode>
+ConstantSourceNode::Constructor(const GlobalObject& aGlobal,
+ AudioContext& aContext,
+ const ConstantSourceOptions& aOptions,
+ ErrorResult& aRv)
+{
+ RefPtr<ConstantSourceNode> object = new ConstantSourceNode(&aContext);
+ object->mOffset->SetValue(aOptions.mOffset);
+ return object.forget();
+}
+
+void
+ConstantSourceNode::DestroyMediaStream()
+{
+ if (mStream) {
+ mStream->RemoveMainThreadListener(this);
+ }
+ AudioNode::DestroyMediaStream();
+}
+
+void
+ConstantSourceNode::Start(double aWhen, ErrorResult& aRv)
+{
+ if (!WebAudioUtils::IsTimeValid(aWhen)) {
+ aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return;
+ }
+
+ if (mStartCalled) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+ mStartCalled = true;
+
+ if (!mStream) {
+ return;
+ }
+
+ mStream->SetStreamTimeParameter(ConstantSourceNodeEngine::START,
+ Context(), aWhen);
+
+ MarkActive();
+}
+
+void
+ConstantSourceNode::Stop(double aWhen, ErrorResult& aRv)
+{
+ if (!WebAudioUtils::IsTimeValid(aWhen)) {
+ aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return;
+ }
+
+ if (!mStartCalled) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ if (!mStream || !Context()) {
+ return;
+ }
+
+ mStream->SetStreamTimeParameter(ConstantSourceNodeEngine::STOP,
+ Context(), std::max(0.0, aWhen));
+}
+
+void
+ConstantSourceNode::NotifyMainThreadStreamFinished()
+{
+ MOZ_ASSERT(mStream->IsFinished());
+
+ class EndedEventDispatcher final : public Runnable
+ {
+ public:
+ explicit EndedEventDispatcher(ConstantSourceNode* 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;
+ }
+
+ mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
+ // Release stream resources.
+ mNode->DestroyMediaStream();
+ return NS_OK;
+ }
+ private:
+ RefPtr<ConstantSourceNode> mNode;
+ };
+
+ NS_DispatchToMainThread(new EndedEventDispatcher(this));
+
+ // Drop the playing reference
+ // Warning: The below line might delete this.
+ MarkInactive();
+}
+
+} // namespace dom
+} // namespace mozilla