summaryrefslogtreecommitdiffstats
path: root/dom/media/webaudio/DynamicsCompressorNode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webaudio/DynamicsCompressorNode.cpp')
-rw-r--r--dom/media/webaudio/DynamicsCompressorNode.cpp237
1 files changed, 237 insertions, 0 deletions
diff --git a/dom/media/webaudio/DynamicsCompressorNode.cpp b/dom/media/webaudio/DynamicsCompressorNode.cpp
new file mode 100644
index 000000000..3a3dc9849
--- /dev/null
+++ b/dom/media/webaudio/DynamicsCompressorNode.cpp
@@ -0,0 +1,237 @@
+/* -*- 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 "DynamicsCompressorNode.h"
+#include "mozilla/dom/DynamicsCompressorNodeBinding.h"
+#include "nsAutoPtr.h"
+#include "AudioNodeEngine.h"
+#include "AudioNodeStream.h"
+#include "AudioDestinationNode.h"
+#include "WebAudioUtils.h"
+#include "blink/DynamicsCompressor.h"
+
+using WebCore::DynamicsCompressor;
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(DynamicsCompressorNode, AudioNode,
+ mThreshold,
+ mKnee,
+ mRatio,
+ mAttack,
+ mRelease)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DynamicsCompressorNode)
+NS_INTERFACE_MAP_END_INHERITING(AudioNode)
+
+NS_IMPL_ADDREF_INHERITED(DynamicsCompressorNode, AudioNode)
+NS_IMPL_RELEASE_INHERITED(DynamicsCompressorNode, AudioNode)
+
+class DynamicsCompressorNodeEngine final : public AudioNodeEngine
+{
+public:
+ explicit DynamicsCompressorNodeEngine(AudioNode* aNode,
+ AudioDestinationNode* aDestination)
+ : AudioNodeEngine(aNode)
+ , mDestination(aDestination->Stream())
+ // Keep the default value in sync with the default value in
+ // DynamicsCompressorNode::DynamicsCompressorNode.
+ , mThreshold(-24.f)
+ , mKnee(30.f)
+ , mRatio(12.f)
+ , mAttack(0.003f)
+ , mRelease(0.25f)
+ , mCompressor(new DynamicsCompressor(mDestination->SampleRate(), 2))
+ {
+ }
+
+ enum Parameters {
+ THRESHOLD,
+ KNEE,
+ RATIO,
+ ATTACK,
+ RELEASE
+ };
+ void RecvTimelineEvent(uint32_t aIndex,
+ AudioTimelineEvent& aEvent) override
+ {
+ MOZ_ASSERT(mDestination);
+
+ WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent,
+ mDestination);
+
+ switch (aIndex) {
+ case THRESHOLD:
+ mThreshold.InsertEvent<int64_t>(aEvent);
+ break;
+ case KNEE:
+ mKnee.InsertEvent<int64_t>(aEvent);
+ break;
+ case RATIO:
+ mRatio.InsertEvent<int64_t>(aEvent);
+ break;
+ case ATTACK:
+ mAttack.InsertEvent<int64_t>(aEvent);
+ break;
+ case RELEASE:
+ mRelease.InsertEvent<int64_t>(aEvent);
+ break;
+ default:
+ NS_ERROR("Bad DynamicsCompresssorNodeEngine TimelineParameter");
+ }
+ }
+
+ void ProcessBlock(AudioNodeStream* aStream,
+ GraphTime aFrom,
+ const AudioBlock& aInput,
+ AudioBlock* aOutput,
+ bool* aFinished) override
+ {
+ if (aInput.IsNull()) {
+ // Just output silence
+ *aOutput = aInput;
+ return;
+ }
+
+ const uint32_t channelCount = aInput.ChannelCount();
+ if (mCompressor->numberOfChannels() != channelCount) {
+ // Create a new compressor object with a new channel count
+ mCompressor = new WebCore::DynamicsCompressor(aStream->SampleRate(),
+ aInput.ChannelCount());
+ }
+
+ StreamTime pos = mDestination->GraphTimeToStreamTime(aFrom);
+ mCompressor->setParameterValue(DynamicsCompressor::ParamThreshold,
+ mThreshold.GetValueAtTime(pos));
+ mCompressor->setParameterValue(DynamicsCompressor::ParamKnee,
+ mKnee.GetValueAtTime(pos));
+ mCompressor->setParameterValue(DynamicsCompressor::ParamRatio,
+ mRatio.GetValueAtTime(pos));
+ mCompressor->setParameterValue(DynamicsCompressor::ParamAttack,
+ mAttack.GetValueAtTime(pos));
+ mCompressor->setParameterValue(DynamicsCompressor::ParamRelease,
+ mRelease.GetValueAtTime(pos));
+
+ aOutput->AllocateChannels(channelCount);
+ mCompressor->process(&aInput, aOutput, aInput.GetDuration());
+
+ SendReductionParamToMainThread(aStream,
+ mCompressor->parameterValue(DynamicsCompressor::ParamReduction));
+ }
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
+ {
+ // Not owned:
+ // - mDestination (probably)
+ // - Don't count the AudioParamTimelines, their inner refs are owned by the
+ // AudioNode.
+ size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+ amount += mCompressor->sizeOfIncludingThis(aMallocSizeOf);
+ return amount;
+ }
+
+ size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
+ {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+private:
+ void SendReductionParamToMainThread(AudioNodeStream* aStream, float aReduction)
+ {
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ class Command final : public Runnable
+ {
+ public:
+ Command(AudioNodeStream* aStream, float aReduction)
+ : mStream(aStream)
+ , mReduction(aReduction)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ RefPtr<DynamicsCompressorNode> node =
+ static_cast<DynamicsCompressorNode*>
+ (mStream->Engine()->NodeMainThread());
+ if (node) {
+ node->SetReduction(mReduction);
+ }
+ return NS_OK;
+ }
+
+ private:
+ RefPtr<AudioNodeStream> mStream;
+ float mReduction;
+ };
+
+ NS_DispatchToMainThread(new Command(aStream, aReduction));
+ }
+
+private:
+ AudioNodeStream* mDestination;
+ AudioParamTimeline mThreshold;
+ AudioParamTimeline mKnee;
+ AudioParamTimeline mRatio;
+ AudioParamTimeline mAttack;
+ AudioParamTimeline mRelease;
+ nsAutoPtr<DynamicsCompressor> mCompressor;
+};
+
+DynamicsCompressorNode::DynamicsCompressorNode(AudioContext* aContext)
+ : AudioNode(aContext,
+ 2,
+ ChannelCountMode::Explicit,
+ ChannelInterpretation::Speakers)
+ , mThreshold(new AudioParam(this, DynamicsCompressorNodeEngine::THRESHOLD,
+ -24.f, "threshold"))
+ , mKnee(new AudioParam(this, DynamicsCompressorNodeEngine::KNEE,
+ 30.f, "knee"))
+ , mRatio(new AudioParam(this, DynamicsCompressorNodeEngine::RATIO,
+ 12.f, "ratio"))
+ , mReduction(0)
+ , mAttack(new AudioParam(this, DynamicsCompressorNodeEngine::ATTACK,
+ 0.003f, "attack"))
+ , mRelease(new AudioParam(this, DynamicsCompressorNodeEngine::RELEASE,
+ 0.25f, "release"))
+{
+ DynamicsCompressorNodeEngine* engine = new DynamicsCompressorNodeEngine(this, aContext->Destination());
+ mStream = AudioNodeStream::Create(aContext, engine,
+ AudioNodeStream::NO_STREAM_FLAGS,
+ aContext->Graph());
+}
+
+DynamicsCompressorNode::~DynamicsCompressorNode()
+{
+}
+
+size_t
+DynamicsCompressorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+ amount += mThreshold->SizeOfIncludingThis(aMallocSizeOf);
+ amount += mKnee->SizeOfIncludingThis(aMallocSizeOf);
+ amount += mRatio->SizeOfIncludingThis(aMallocSizeOf);
+ amount += mAttack->SizeOfIncludingThis(aMallocSizeOf);
+ amount += mRelease->SizeOfIncludingThis(aMallocSizeOf);
+ return amount;
+}
+
+size_t
+DynamicsCompressorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
+JSObject*
+DynamicsCompressorNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return DynamicsCompressorNodeBinding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace dom
+} // namespace mozilla