Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
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"
16 namespace mozilla {
17 namespace dom {
19 NS_IMPL_CYCLE_COLLECTION_INHERITED(DelayNode, AudioNode,
20 mDelay)
22 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DelayNode)
23 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
25 NS_IMPL_ADDREF_INHERITED(DelayNode, AudioNode)
26 NS_IMPL_RELEASE_INHERITED(DelayNode, AudioNode)
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 }
50 virtual DelayNodeEngine* AsDelayNodeEngine()
51 {
52 return this;
53 }
55 void SetSourceStream(AudioNodeStream* aSource)
56 {
57 mSource = aSource;
58 }
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 }
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());
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();
102 nsRefPtr<PlayingRefChanged> refchanged =
103 new PlayingRefChanged(aStream, PlayingRefChanged::RELEASE);
104 aStream->Graph()->
105 DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
106 }
107 *aOutput = aInput;
108 return;
109 }
111 mBuffer.Write(aInput);
113 UpdateOutputBlock(aOutput);
114 mBuffer.NextBlock();
115 }
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 }
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 }
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 }
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 }
173 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
174 {
175 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
176 }
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 };
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 }
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 }
212 size_t
213 DelayNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
214 {
215 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
216 }
218 JSObject*
219 DelayNode::WrapObject(JSContext* aCx)
220 {
221 return DelayNodeBinding::Wrap(aCx, this);
222 }
224 void
225 DelayNode::SendDelayToStream(AudioNode* aNode)
226 {
227 DelayNode* This = static_cast<DelayNode*>(aNode);
228 SendTimelineParameterToStream(This, DelayNodeEngine::DELAY, *This->mDelay);
229 }
231 }
232 }