1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "GainNode.h"
8 #include "mozilla/dom/GainNodeBinding.h"
9 #include "AlignmentUtils.h"
10 #include "AudioNodeEngine.h"
11 #include "AudioNodeTrack.h"
12 #include "AudioDestinationNode.h"
13 #include "WebAudioUtils.h"
14
15 namespace mozilla::dom {
16
17 NS_IMPL_CYCLE_COLLECTION_INHERITED(GainNode, AudioNode, mGain)
18
19 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GainNode)
20 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
21
22 NS_IMPL_ADDREF_INHERITED(GainNode, AudioNode)
23 NS_IMPL_RELEASE_INHERITED(GainNode, AudioNode)
24
25 class GainNodeEngine final : public AudioNodeEngine {
26 public:
GainNodeEngine(AudioNode * aNode,AudioDestinationNode * aDestination)27 GainNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
28 : AudioNodeEngine(aNode),
29 mDestination(aDestination->Track())
30 // Keep the default value in sync with the default value in
31 // GainNode::GainNode.
32 ,
33 mGain(1.f) {}
34
35 enum Parameters { GAIN };
RecvTimelineEvent(uint32_t aIndex,AudioTimelineEvent & aEvent)36 void RecvTimelineEvent(uint32_t aIndex, AudioTimelineEvent& aEvent) override {
37 MOZ_ASSERT(mDestination);
38 WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent, mDestination);
39
40 switch (aIndex) {
41 case GAIN:
42 mGain.InsertEvent<int64_t>(aEvent);
43 break;
44 default:
45 NS_ERROR("Bad GainNodeEngine TimelineParameter");
46 }
47 }
48
ProcessBlock(AudioNodeTrack * aTrack,GraphTime aFrom,const AudioBlock & aInput,AudioBlock * aOutput,bool * aFinished)49 void ProcessBlock(AudioNodeTrack* aTrack, GraphTime aFrom,
50 const AudioBlock& aInput, AudioBlock* aOutput,
51 bool* aFinished) override {
52 if (aInput.IsNull()) {
53 // If input is silent, so is the output
54 aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
55 } else if (mGain.HasSimpleValue()) {
56 // Optimize the case where we only have a single value set as the volume
57 float gain = mGain.GetValue();
58 if (gain == 0.0f) {
59 aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
60 } else {
61 *aOutput = aInput;
62 aOutput->mVolume *= gain;
63 }
64 } else {
65 // First, compute a vector of gains for each track tick based on the
66 // timeline at hand, and then for each channel, multiply the values
67 // in the buffer with the gain vector.
68 aOutput->AllocateChannels(aInput.ChannelCount());
69
70 // Compute the gain values for the duration of the input AudioChunk
71 TrackTime tick = mDestination->GraphTimeToTrackTime(aFrom);
72 float computedGain[WEBAUDIO_BLOCK_SIZE + 4];
73 float* alignedComputedGain = ALIGNED16(computedGain);
74 ASSERT_ALIGNED16(alignedComputedGain);
75 mGain.GetValuesAtTime(tick, alignedComputedGain, WEBAUDIO_BLOCK_SIZE);
76
77 for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
78 alignedComputedGain[counter] *= aInput.mVolume;
79 }
80
81 // Apply the gain to the output buffer
82 for (size_t channel = 0; channel < aOutput->ChannelCount(); ++channel) {
83 const float* inputBuffer =
84 static_cast<const float*>(aInput.mChannelData[channel]);
85 float* buffer = aOutput->ChannelFloatsForWrite(channel);
86 AudioBlockCopyChannelWithScale(inputBuffer, alignedComputedGain,
87 buffer);
88 }
89 }
90 }
91
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const92 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override {
93 // Not owned:
94 // - mDestination - MediaTrackGraphImpl::CollectSizesForMemoryReport()
95 // accounts for mDestination.
96 // - mGain - Internal ref owned by AudioNode
97 return AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
98 }
99
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const100 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
101 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
102 }
103
104 RefPtr<AudioNodeTrack> mDestination;
105 AudioParamTimeline mGain;
106 };
107
GainNode(AudioContext * aContext)108 GainNode::GainNode(AudioContext* aContext)
109 : AudioNode(aContext, 2, ChannelCountMode::Max,
110 ChannelInterpretation::Speakers) {
111 mGain = CreateAudioParam(GainNodeEngine::GAIN, u"gain"_ns, 1.0f);
112 GainNodeEngine* engine = new GainNodeEngine(this, aContext->Destination());
113 mTrack = AudioNodeTrack::Create(
114 aContext, engine, AudioNodeTrack::NO_TRACK_FLAGS, aContext->Graph());
115 }
116
117 /* static */
Create(AudioContext & aAudioContext,const GainOptions & aOptions,ErrorResult & aRv)118 already_AddRefed<GainNode> GainNode::Create(AudioContext& aAudioContext,
119 const GainOptions& aOptions,
120 ErrorResult& aRv) {
121 RefPtr<GainNode> audioNode = new GainNode(&aAudioContext);
122
123 audioNode->Initialize(aOptions, aRv);
124 if (NS_WARN_IF(aRv.Failed())) {
125 return nullptr;
126 }
127
128 audioNode->Gain()->SetInitialValue(aOptions.mGain);
129 return audioNode.forget();
130 }
131
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const132 size_t GainNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
133 size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
134 amount += mGain->SizeOfIncludingThis(aMallocSizeOf);
135 return amount;
136 }
137
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const138 size_t GainNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
139 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
140 }
141
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)142 JSObject* GainNode::WrapObject(JSContext* aCx,
143 JS::Handle<JSObject*> aGivenProto) {
144 return GainNode_Binding::Wrap(aCx, this, aGivenProto);
145 }
146
147 } // namespace mozilla::dom
148