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