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