summaryrefslogtreecommitdiffstats
path: root/dom/media/webaudio/GainNode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webaudio/GainNode.cpp')
-rw-r--r--dom/media/webaudio/GainNode.cpp149
1 files changed, 149 insertions, 0 deletions
diff --git a/dom/media/webaudio/GainNode.cpp b/dom/media/webaudio/GainNode.cpp
new file mode 100644
index 0000000000..c858497353
--- /dev/null
+++ b/dom/media/webaudio/GainNode.cpp
@@ -0,0 +1,149 @@
+/* -*- 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 "GainNode.h"
+#include "mozilla/dom/GainNodeBinding.h"
+#include "AlignmentUtils.h"
+#include "AudioNodeEngine.h"
+#include "AudioNodeTrack.h"
+#include "AudioDestinationNode.h"
+#include "WebAudioUtils.h"
+#include "Tracing.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(GainNode, AudioNode, mGain)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GainNode)
+NS_INTERFACE_MAP_END_INHERITING(AudioNode)
+
+NS_IMPL_ADDREF_INHERITED(GainNode, AudioNode)
+NS_IMPL_RELEASE_INHERITED(GainNode, AudioNode)
+
+class GainNodeEngine final : public AudioNodeEngine {
+ public:
+ GainNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
+ : AudioNodeEngine(aNode),
+ mDestination(aDestination->Track())
+ // Keep the default value in sync with the default value in
+ // GainNode::GainNode.
+ ,
+ mGain(1.f) {}
+
+ enum Parameters { GAIN };
+ void RecvTimelineEvent(uint32_t aIndex, AudioTimelineEvent& aEvent) override {
+ MOZ_ASSERT(mDestination);
+ WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent, mDestination);
+
+ switch (aIndex) {
+ case GAIN:
+ mGain.InsertEvent<int64_t>(aEvent);
+ break;
+ default:
+ NS_ERROR("Bad GainNodeEngine TimelineParameter");
+ }
+ }
+
+ void ProcessBlock(AudioNodeTrack* aTrack, GraphTime aFrom,
+ const AudioBlock& aInput, AudioBlock* aOutput,
+ bool* aFinished) override {
+ TRACE("GainNodeEngine::ProcessBlock");
+ if (aInput.IsNull()) {
+ // If input is silent, so is the output
+ aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
+ } else if (mGain.HasSimpleValue()) {
+ // Optimize the case where we only have a single value set as the volume
+ float gain = mGain.GetValue();
+ if (gain == 0.0f) {
+ aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
+ } else {
+ *aOutput = aInput;
+ aOutput->mVolume *= gain;
+ }
+ } else {
+ // First, compute a vector of gains for each track tick based on the
+ // timeline at hand, and then for each channel, multiply the values
+ // in the buffer with the gain vector.
+ aOutput->AllocateChannels(aInput.ChannelCount());
+
+ // Compute the gain values for the duration of the input AudioChunk
+ TrackTime tick = mDestination->GraphTimeToTrackTime(aFrom);
+ float computedGain[WEBAUDIO_BLOCK_SIZE + 4];
+ float* alignedComputedGain = ALIGNED16(computedGain);
+ ASSERT_ALIGNED16(alignedComputedGain);
+ mGain.GetValuesAtTime(tick, alignedComputedGain, WEBAUDIO_BLOCK_SIZE);
+
+ for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
+ alignedComputedGain[counter] *= aInput.mVolume;
+ }
+
+ // Apply the gain to the output buffer
+ for (size_t channel = 0; channel < aOutput->ChannelCount(); ++channel) {
+ const float* inputBuffer =
+ static_cast<const float*>(aInput.mChannelData[channel]);
+ float* buffer = aOutput->ChannelFloatsForWrite(channel);
+ AudioBlockCopyChannelWithScale(inputBuffer, alignedComputedGain,
+ buffer);
+ }
+ }
+ }
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override {
+ // Not owned:
+ // - mDestination - MediaTrackGraphImpl::CollectSizesForMemoryReport()
+ // accounts for mDestination.
+ // - mGain - Internal ref owned by AudioNode
+ return AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ RefPtr<AudioNodeTrack> mDestination;
+ AudioParamTimeline mGain;
+};
+
+GainNode::GainNode(AudioContext* aContext)
+ : AudioNode(aContext, 2, ChannelCountMode::Max,
+ ChannelInterpretation::Speakers) {
+ mGain = CreateAudioParam(GainNodeEngine::GAIN, u"gain"_ns, 1.0f);
+ GainNodeEngine* engine = new GainNodeEngine(this, aContext->Destination());
+ mTrack = AudioNodeTrack::Create(
+ aContext, engine, AudioNodeTrack::NO_TRACK_FLAGS, aContext->Graph());
+}
+
+/* static */
+already_AddRefed<GainNode> GainNode::Create(AudioContext& aAudioContext,
+ const GainOptions& aOptions,
+ ErrorResult& aRv) {
+ RefPtr<GainNode> audioNode = new GainNode(&aAudioContext);
+
+ audioNode->Initialize(aOptions, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ audioNode->Gain()->SetInitialValue(aOptions.mGain);
+ return audioNode.forget();
+}
+
+size_t GainNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
+ size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+ amount += mGain->SizeOfIncludingThis(aMallocSizeOf);
+ return amount;
+}
+
+size_t GainNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
+JSObject* GainNode::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return GainNode_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace mozilla::dom