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