|
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 "DelayNode.h" |
|
8 #include "mozilla/dom/DelayNodeBinding.h" |
|
9 #include "AudioNodeEngine.h" |
|
10 #include "AudioNodeStream.h" |
|
11 #include "AudioDestinationNode.h" |
|
12 #include "WebAudioUtils.h" |
|
13 #include "DelayBuffer.h" |
|
14 #include "PlayingRefChangeHandler.h" |
|
15 |
|
16 namespace mozilla { |
|
17 namespace dom { |
|
18 |
|
19 NS_IMPL_CYCLE_COLLECTION_INHERITED(DelayNode, AudioNode, |
|
20 mDelay) |
|
21 |
|
22 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DelayNode) |
|
23 NS_INTERFACE_MAP_END_INHERITING(AudioNode) |
|
24 |
|
25 NS_IMPL_ADDREF_INHERITED(DelayNode, AudioNode) |
|
26 NS_IMPL_RELEASE_INHERITED(DelayNode, AudioNode) |
|
27 |
|
28 class DelayNodeEngine : public AudioNodeEngine |
|
29 { |
|
30 typedef PlayingRefChangeHandler PlayingRefChanged; |
|
31 public: |
|
32 DelayNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination, |
|
33 double aMaxDelayTicks) |
|
34 : AudioNodeEngine(aNode) |
|
35 , mSource(nullptr) |
|
36 , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream())) |
|
37 // Keep the default value in sync with the default value in DelayNode::DelayNode. |
|
38 , mDelay(0.f) |
|
39 // Use a smoothing range of 20ms |
|
40 , mBuffer(std::max(aMaxDelayTicks, |
|
41 static_cast<double>(WEBAUDIO_BLOCK_SIZE)), |
|
42 WebAudioUtils::ComputeSmoothingRate(0.02, |
|
43 mDestination->SampleRate())) |
|
44 , mMaxDelay(aMaxDelayTicks) |
|
45 , mLastOutputPosition(-1) |
|
46 , mLeftOverData(INT32_MIN) |
|
47 { |
|
48 } |
|
49 |
|
50 virtual DelayNodeEngine* AsDelayNodeEngine() |
|
51 { |
|
52 return this; |
|
53 } |
|
54 |
|
55 void SetSourceStream(AudioNodeStream* aSource) |
|
56 { |
|
57 mSource = aSource; |
|
58 } |
|
59 |
|
60 enum Parameters { |
|
61 DELAY, |
|
62 }; |
|
63 void SetTimelineParameter(uint32_t aIndex, |
|
64 const AudioParamTimeline& aValue, |
|
65 TrackRate aSampleRate) MOZ_OVERRIDE |
|
66 { |
|
67 switch (aIndex) { |
|
68 case DELAY: |
|
69 MOZ_ASSERT(mSource && mDestination); |
|
70 mDelay = aValue; |
|
71 WebAudioUtils::ConvertAudioParamToTicks(mDelay, mSource, mDestination); |
|
72 break; |
|
73 default: |
|
74 NS_ERROR("Bad DelayNodeEngine TimelineParameter"); |
|
75 } |
|
76 } |
|
77 |
|
78 virtual void ProcessBlock(AudioNodeStream* aStream, |
|
79 const AudioChunk& aInput, |
|
80 AudioChunk* aOutput, |
|
81 bool* aFinished) MOZ_OVERRIDE |
|
82 { |
|
83 MOZ_ASSERT(mSource == aStream, "Invalid source stream"); |
|
84 MOZ_ASSERT(aStream->SampleRate() == mDestination->SampleRate()); |
|
85 |
|
86 if (!aInput.IsNull()) { |
|
87 if (mLeftOverData <= 0) { |
|
88 nsRefPtr<PlayingRefChanged> refchanged = |
|
89 new PlayingRefChanged(aStream, PlayingRefChanged::ADDREF); |
|
90 aStream->Graph()-> |
|
91 DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget()); |
|
92 } |
|
93 mLeftOverData = mBuffer.MaxDelayTicks(); |
|
94 } else if (mLeftOverData > 0) { |
|
95 mLeftOverData -= WEBAUDIO_BLOCK_SIZE; |
|
96 } else { |
|
97 if (mLeftOverData != INT32_MIN) { |
|
98 mLeftOverData = INT32_MIN; |
|
99 // Delete our buffered data now we no longer need it |
|
100 mBuffer.Reset(); |
|
101 |
|
102 nsRefPtr<PlayingRefChanged> refchanged = |
|
103 new PlayingRefChanged(aStream, PlayingRefChanged::RELEASE); |
|
104 aStream->Graph()-> |
|
105 DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget()); |
|
106 } |
|
107 *aOutput = aInput; |
|
108 return; |
|
109 } |
|
110 |
|
111 mBuffer.Write(aInput); |
|
112 |
|
113 UpdateOutputBlock(aOutput); |
|
114 mBuffer.NextBlock(); |
|
115 } |
|
116 |
|
117 void UpdateOutputBlock(AudioChunk* aOutput) |
|
118 { |
|
119 TrackTicks tick = mSource->GetCurrentPosition(); |
|
120 if (tick == mLastOutputPosition) { |
|
121 return; // mLastChunks is already set on the stream |
|
122 } |
|
123 |
|
124 mLastOutputPosition = tick; |
|
125 bool inCycle = mSource->AsProcessedStream()->InCycle(); |
|
126 double minDelay = inCycle ? static_cast<double>(WEBAUDIO_BLOCK_SIZE) : 0.0; |
|
127 double maxDelay = mMaxDelay; |
|
128 double sampleRate = mSource->SampleRate(); |
|
129 ChannelInterpretation channelInterpretation = |
|
130 mSource->GetChannelInterpretation(); |
|
131 if (mDelay.HasSimpleValue()) { |
|
132 // If this DelayNode is in a cycle, make sure the delay value is at least |
|
133 // one block, even if that is greater than maxDelay. |
|
134 double delayFrames = mDelay.GetValue() * sampleRate; |
|
135 double delayFramesClamped = |
|
136 std::max(minDelay, std::min(delayFrames, maxDelay)); |
|
137 mBuffer.Read(delayFramesClamped, aOutput, channelInterpretation); |
|
138 } else { |
|
139 // Compute the delay values for the duration of the input AudioChunk |
|
140 // If this DelayNode is in a cycle, make sure the delay value is at least |
|
141 // one block. |
|
142 double computedDelay[WEBAUDIO_BLOCK_SIZE]; |
|
143 for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) { |
|
144 double delayAtTick = mDelay.GetValueAtTime(tick, counter) * sampleRate; |
|
145 double delayAtTickClamped = |
|
146 std::max(minDelay, std::min(delayAtTick, maxDelay)); |
|
147 computedDelay[counter] = delayAtTickClamped; |
|
148 } |
|
149 mBuffer.Read(computedDelay, aOutput, channelInterpretation); |
|
150 } |
|
151 } |
|
152 |
|
153 virtual void ProduceBlockBeforeInput(AudioChunk* aOutput) MOZ_OVERRIDE |
|
154 { |
|
155 if (mLeftOverData <= 0) { |
|
156 aOutput->SetNull(WEBAUDIO_BLOCK_SIZE); |
|
157 } else { |
|
158 UpdateOutputBlock(aOutput); |
|
159 } |
|
160 } |
|
161 |
|
162 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE |
|
163 { |
|
164 size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); |
|
165 // Not owned: |
|
166 // - mSource - probably not owned |
|
167 // - mDestination - probably not owned |
|
168 // - mDelay - shares ref with AudioNode, don't count |
|
169 amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf); |
|
170 return amount; |
|
171 } |
|
172 |
|
173 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE |
|
174 { |
|
175 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
|
176 } |
|
177 |
|
178 AudioNodeStream* mSource; |
|
179 AudioNodeStream* mDestination; |
|
180 AudioParamTimeline mDelay; |
|
181 DelayBuffer mBuffer; |
|
182 double mMaxDelay; |
|
183 TrackTicks mLastOutputPosition; |
|
184 // How much data we have in our buffer which needs to be flushed out when our inputs |
|
185 // finish. |
|
186 int32_t mLeftOverData; |
|
187 }; |
|
188 |
|
189 DelayNode::DelayNode(AudioContext* aContext, double aMaxDelay) |
|
190 : AudioNode(aContext, |
|
191 2, |
|
192 ChannelCountMode::Max, |
|
193 ChannelInterpretation::Speakers) |
|
194 , mDelay(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), |
|
195 SendDelayToStream, 0.0f)) |
|
196 { |
|
197 DelayNodeEngine* engine = |
|
198 new DelayNodeEngine(this, aContext->Destination(), |
|
199 aContext->SampleRate() * aMaxDelay); |
|
200 mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM); |
|
201 engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get())); |
|
202 } |
|
203 |
|
204 size_t |
|
205 DelayNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
|
206 { |
|
207 size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf); |
|
208 amount += mDelay->SizeOfIncludingThis(aMallocSizeOf); |
|
209 return amount; |
|
210 } |
|
211 |
|
212 size_t |
|
213 DelayNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const |
|
214 { |
|
215 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
|
216 } |
|
217 |
|
218 JSObject* |
|
219 DelayNode::WrapObject(JSContext* aCx) |
|
220 { |
|
221 return DelayNodeBinding::Wrap(aCx, this); |
|
222 } |
|
223 |
|
224 void |
|
225 DelayNode::SendDelayToStream(AudioNode* aNode) |
|
226 { |
|
227 DelayNode* This = static_cast<DelayNode*>(aNode); |
|
228 SendTimelineParameterToStream(This, DelayNodeEngine::DELAY, *This->mDelay); |
|
229 } |
|
230 |
|
231 } |
|
232 } |
|
233 |