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 "AudioNodeStream.h"
12 #include "AudioDestinationNode.h"
13 #include "WebAudioUtils.h"
14 
15 namespace mozilla {
16 namespace dom {
17 
18 NS_IMPL_CYCLE_COLLECTION_INHERITED(GainNode, AudioNode, mGain)
19 
20 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GainNode)
21 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
22 
23 NS_IMPL_ADDREF_INHERITED(GainNode, AudioNode)
24 NS_IMPL_RELEASE_INHERITED(GainNode, AudioNode)
25 
26 class GainNodeEngine final : public AudioNodeEngine {
27  public:
GainNodeEngine(AudioNode * aNode,AudioDestinationNode * aDestination)28   GainNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
29       : AudioNodeEngine(aNode),
30         mDestination(aDestination->Stream())
31         // Keep the default value in sync with the default value in
32         // GainNode::GainNode.
33         ,
34         mGain(1.f) {}
35 
36   enum Parameters { GAIN };
RecvTimelineEvent(uint32_t aIndex,AudioTimelineEvent & aEvent)37   void RecvTimelineEvent(uint32_t aIndex, AudioTimelineEvent& aEvent) override {
38     MOZ_ASSERT(mDestination);
39     WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent, mDestination);
40 
41     switch (aIndex) {
42       case GAIN:
43         mGain.InsertEvent<int64_t>(aEvent);
44         break;
45       default:
46         NS_ERROR("Bad GainNodeEngine TimelineParameter");
47     }
48   }
49 
ProcessBlock(AudioNodeStream * aStream,GraphTime aFrom,const AudioBlock & aInput,AudioBlock * aOutput,bool * aFinished)50   void ProcessBlock(AudioNodeStream* aStream, GraphTime aFrom,
51                     const AudioBlock& aInput, AudioBlock* aOutput,
52                     bool* aFinished) override {
53     if (aInput.IsNull()) {
54       // If input is silent, so is the output
55       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
56     } else if (mGain.HasSimpleValue()) {
57       // Optimize the case where we only have a single value set as the volume
58       float gain = mGain.GetValue();
59       if (gain == 0.0f) {
60         aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
61       } else {
62         *aOutput = aInput;
63         aOutput->mVolume *= gain;
64       }
65     } else {
66       // First, compute a vector of gains for each track tick based on the
67       // timeline at hand, and then for each channel, multiply the values
68       // in the buffer with the gain vector.
69       aOutput->AllocateChannels(aInput.ChannelCount());
70 
71       // Compute the gain values for the duration of the input AudioChunk
72       StreamTime tick = mDestination->GraphTimeToStreamTime(aFrom);
73       float computedGain[WEBAUDIO_BLOCK_SIZE + 4];
74       float* alignedComputedGain = ALIGNED16(computedGain);
75       ASSERT_ALIGNED16(alignedComputedGain);
76       mGain.GetValuesAtTime(tick, alignedComputedGain, WEBAUDIO_BLOCK_SIZE);
77 
78       for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
79         alignedComputedGain[counter] *= aInput.mVolume;
80       }
81 
82       // Apply the gain to the output buffer
83       for (size_t channel = 0; channel < aOutput->ChannelCount(); ++channel) {
84         const float* inputBuffer =
85             static_cast<const float*>(aInput.mChannelData[channel]);
86         float* buffer = aOutput->ChannelFloatsForWrite(channel);
87         AudioBlockCopyChannelWithScale(inputBuffer, alignedComputedGain,
88                                        buffer);
89       }
90     }
91   }
92 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const93   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override {
94     // Not owned:
95     // - mDestination - MediaStreamGraphImpl::CollectSizesForMemoryReport()
96     // accounts for mDestination.
97     // - mGain - Internal ref owned by AudioNode
98     return AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
99   }
100 
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const101   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
102     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
103   }
104 
105   RefPtr<AudioNodeStream> mDestination;
106   AudioParamTimeline mGain;
107 };
108 
GainNode(AudioContext * aContext)109 GainNode::GainNode(AudioContext* aContext)
110     : AudioNode(aContext, 2, ChannelCountMode::Max,
111                 ChannelInterpretation::Speakers),
112       mGain(new AudioParam(this, GainNodeEngine::GAIN, "gain", 1.0f)) {
113   GainNodeEngine* engine = new GainNodeEngine(this, aContext->Destination());
114   mStream = AudioNodeStream::Create(
115       aContext, engine, AudioNodeStream::NO_STREAM_FLAGS, aContext->Graph());
116 }
117 
Create(AudioContext & aAudioContext,const GainOptions & aOptions,ErrorResult & aRv)118 /* static */ already_AddRefed<GainNode> GainNode::Create(
119     AudioContext& aAudioContext, const GainOptions& aOptions,
120     ErrorResult& aRv) {
121   if (aAudioContext.CheckClosed(aRv)) {
122     return nullptr;
123   }
124 
125   RefPtr<GainNode> audioNode = new GainNode(&aAudioContext);
126 
127   audioNode->Initialize(aOptions, aRv);
128   if (NS_WARN_IF(aRv.Failed())) {
129     return nullptr;
130   }
131 
132   audioNode->Gain()->SetValue(aOptions.mGain);
133   return audioNode.forget();
134 }
135 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const136 size_t GainNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
137   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
138   amount += mGain->SizeOfIncludingThis(aMallocSizeOf);
139   return amount;
140 }
141 
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const142 size_t GainNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
143   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
144 }
145 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)146 JSObject* GainNode::WrapObject(JSContext* aCx,
147                                JS::Handle<JSObject*> aGivenProto) {
148   return GainNodeBinding::Wrap(aCx, this, aGivenProto);
149 }
150 
151 }  // namespace dom
152 }  // namespace mozilla
153