|
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 "AudioNodeEngine.h" |
|
10 #include "AudioNodeStream.h" |
|
11 #include "AudioDestinationNode.h" |
|
12 #include "WebAudioUtils.h" |
|
13 |
|
14 namespace mozilla { |
|
15 namespace dom { |
|
16 |
|
17 NS_IMPL_CYCLE_COLLECTION_INHERITED(GainNode, AudioNode, |
|
18 mGain) |
|
19 |
|
20 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(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 : public AudioNodeEngine |
|
27 { |
|
28 public: |
|
29 GainNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination) |
|
30 : AudioNodeEngine(aNode) |
|
31 , mSource(nullptr) |
|
32 , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream())) |
|
33 // Keep the default value in sync with the default value in GainNode::GainNode. |
|
34 , mGain(1.f) |
|
35 { |
|
36 } |
|
37 |
|
38 void SetSourceStream(AudioNodeStream* aSource) |
|
39 { |
|
40 mSource = aSource; |
|
41 } |
|
42 |
|
43 enum Parameters { |
|
44 GAIN |
|
45 }; |
|
46 void SetTimelineParameter(uint32_t aIndex, |
|
47 const AudioParamTimeline& aValue, |
|
48 TrackRate aSampleRate) MOZ_OVERRIDE |
|
49 { |
|
50 switch (aIndex) { |
|
51 case GAIN: |
|
52 MOZ_ASSERT(mSource && mDestination); |
|
53 mGain = aValue; |
|
54 WebAudioUtils::ConvertAudioParamToTicks(mGain, mSource, mDestination); |
|
55 break; |
|
56 default: |
|
57 NS_ERROR("Bad GainNodeEngine TimelineParameter"); |
|
58 } |
|
59 } |
|
60 |
|
61 virtual void ProcessBlock(AudioNodeStream* aStream, |
|
62 const AudioChunk& aInput, |
|
63 AudioChunk* aOutput, |
|
64 bool* aFinished) |
|
65 { |
|
66 MOZ_ASSERT(mSource == aStream, "Invalid source stream"); |
|
67 |
|
68 if (aInput.IsNull()) { |
|
69 // If input is silent, so is the output |
|
70 aOutput->SetNull(WEBAUDIO_BLOCK_SIZE); |
|
71 } else if (mGain.HasSimpleValue()) { |
|
72 // Optimize the case where we only have a single value set as the volume |
|
73 float gain = mGain.GetValue(); |
|
74 if (gain == 0.0f) { |
|
75 aOutput->SetNull(WEBAUDIO_BLOCK_SIZE); |
|
76 } else { |
|
77 *aOutput = aInput; |
|
78 aOutput->mVolume *= gain; |
|
79 } |
|
80 } else { |
|
81 // First, compute a vector of gains for each track tick based on the |
|
82 // timeline at hand, and then for each channel, multiply the values |
|
83 // in the buffer with the gain vector. |
|
84 AllocateAudioBlock(aInput.mChannelData.Length(), aOutput); |
|
85 |
|
86 // Compute the gain values for the duration of the input AudioChunk |
|
87 // XXX we need to add a method to AudioEventTimeline to compute this buffer directly. |
|
88 float computedGain[WEBAUDIO_BLOCK_SIZE]; |
|
89 for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) { |
|
90 TrackTicks tick = aStream->GetCurrentPosition(); |
|
91 computedGain[counter] = mGain.GetValueAtTime(tick, counter) * aInput.mVolume; |
|
92 } |
|
93 |
|
94 // Apply the gain to the output buffer |
|
95 for (size_t channel = 0; channel < aOutput->mChannelData.Length(); ++channel) { |
|
96 const float* inputBuffer = static_cast<const float*> (aInput.mChannelData[channel]); |
|
97 float* buffer = static_cast<float*> (const_cast<void*> |
|
98 (aOutput->mChannelData[channel])); |
|
99 AudioBlockCopyChannelWithScale(inputBuffer, computedGain, buffer); |
|
100 } |
|
101 } |
|
102 } |
|
103 |
|
104 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE |
|
105 { |
|
106 // Not owned: |
|
107 // - mSource (probably) |
|
108 // - mDestination (probably) |
|
109 // - mGain - Internal ref owned by AudioNode |
|
110 return AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); |
|
111 } |
|
112 |
|
113 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE |
|
114 { |
|
115 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
|
116 } |
|
117 |
|
118 AudioNodeStream* mSource; |
|
119 AudioNodeStream* mDestination; |
|
120 AudioParamTimeline mGain; |
|
121 }; |
|
122 |
|
123 GainNode::GainNode(AudioContext* aContext) |
|
124 : AudioNode(aContext, |
|
125 2, |
|
126 ChannelCountMode::Max, |
|
127 ChannelInterpretation::Speakers) |
|
128 , mGain(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), |
|
129 SendGainToStream, 1.0f)) |
|
130 { |
|
131 GainNodeEngine* engine = new GainNodeEngine(this, aContext->Destination()); |
|
132 mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM); |
|
133 engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get())); |
|
134 } |
|
135 |
|
136 size_t |
|
137 GainNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
|
138 { |
|
139 size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf); |
|
140 amount += mGain->SizeOfIncludingThis(aMallocSizeOf); |
|
141 return amount; |
|
142 } |
|
143 |
|
144 size_t |
|
145 GainNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const |
|
146 { |
|
147 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
|
148 } |
|
149 |
|
150 JSObject* |
|
151 GainNode::WrapObject(JSContext* aCx) |
|
152 { |
|
153 return GainNodeBinding::Wrap(aCx, this); |
|
154 } |
|
155 |
|
156 void |
|
157 GainNode::SendGainToStream(AudioNode* aNode) |
|
158 { |
|
159 GainNode* This = static_cast<GainNode*>(aNode); |
|
160 SendTimelineParameterToStream(This, GainNodeEngine::GAIN, *This->mGain); |
|
161 } |
|
162 |
|
163 } |
|
164 } |
|
165 |