/src/mozilla-central/dom/media/webaudio/ConstantSourceNode.cpp
Line | Count | Source (jump to first uncovered line) |
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 "ConstantSourceNode.h" |
8 | | |
9 | | #include "AudioDestinationNode.h" |
10 | | #include "nsContentUtils.h" |
11 | | #include "AudioNodeEngine.h" |
12 | | #include "AudioNodeStream.h" |
13 | | |
14 | | namespace mozilla { |
15 | | namespace dom { |
16 | | |
17 | | NS_IMPL_CYCLE_COLLECTION_INHERITED(ConstantSourceNode, AudioScheduledSourceNode, |
18 | | mOffset) |
19 | | |
20 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ConstantSourceNode) |
21 | 0 | NS_INTERFACE_MAP_END_INHERITING(AudioScheduledSourceNode) |
22 | | |
23 | | NS_IMPL_ADDREF_INHERITED(ConstantSourceNode, AudioScheduledSourceNode) |
24 | | NS_IMPL_RELEASE_INHERITED(ConstantSourceNode, AudioScheduledSourceNode) |
25 | | |
26 | | class ConstantSourceNodeEngine final : public AudioNodeEngine |
27 | | { |
28 | | public: |
29 | | ConstantSourceNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination) |
30 | | : AudioNodeEngine(aNode) |
31 | | , mSource(nullptr) |
32 | | , mDestination(aDestination->Stream()) |
33 | | , mStart(-1) |
34 | | , mStop(STREAM_TIME_MAX) |
35 | | // Keep the default values in sync with ConstantSourceNode::ConstantSourceNode. |
36 | | , mOffset(1.0f) |
37 | 0 | { |
38 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
39 | 0 | } |
40 | | |
41 | | void SetSourceStream(AudioNodeStream* aSource) |
42 | 0 | { |
43 | 0 | mSource = aSource; |
44 | 0 | } |
45 | | |
46 | | enum Parameters { |
47 | | OFFSET, |
48 | | START, |
49 | | STOP, |
50 | | }; |
51 | | void RecvTimelineEvent(uint32_t aIndex, |
52 | | AudioTimelineEvent& aEvent) override |
53 | 0 | { |
54 | 0 | MOZ_ASSERT(mDestination); |
55 | 0 |
|
56 | 0 | WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent, |
57 | 0 | mDestination); |
58 | 0 |
|
59 | 0 | switch (aIndex) { |
60 | 0 | case OFFSET: |
61 | 0 | mOffset.InsertEvent<int64_t>(aEvent); |
62 | 0 | break; |
63 | 0 | default: |
64 | 0 | NS_ERROR("Bad ConstantSourceNodeEngine TimelineParameter"); |
65 | 0 | } |
66 | 0 | } |
67 | | |
68 | | void SetStreamTimeParameter(uint32_t aIndex, StreamTime aParam) override |
69 | 0 | { |
70 | 0 | switch (aIndex) { |
71 | 0 | case START: |
72 | 0 | mStart = aParam; |
73 | 0 | mSource->SetActive(); |
74 | 0 | break; |
75 | 0 | case STOP: mStop = aParam; break; |
76 | 0 | default: |
77 | 0 | NS_ERROR("Bad ConstantSourceNodeEngine StreamTimeParameter"); |
78 | 0 | } |
79 | 0 | } |
80 | | |
81 | | void ProcessBlock(AudioNodeStream* aStream, |
82 | | GraphTime aFrom, |
83 | | const AudioBlock& aInput, |
84 | | AudioBlock* aOutput, |
85 | | bool* aFinished) override |
86 | 0 | { |
87 | 0 | MOZ_ASSERT(mSource == aStream, "Invalid source stream"); |
88 | 0 |
|
89 | 0 | StreamTime ticks = mDestination->GraphTimeToStreamTime(aFrom); |
90 | 0 | if (mStart == -1) { |
91 | 0 | aOutput->SetNull(WEBAUDIO_BLOCK_SIZE); |
92 | 0 | return; |
93 | 0 | } |
94 | 0 | |
95 | 0 | if (ticks + WEBAUDIO_BLOCK_SIZE <= mStart || |
96 | 0 | ticks >= mStop || |
97 | 0 | mStop <= mStart) { |
98 | 0 | aOutput->SetNull(WEBAUDIO_BLOCK_SIZE); |
99 | 0 | } else { |
100 | 0 | aOutput->AllocateChannels(1); |
101 | 0 | float* output = aOutput->ChannelFloatsForWrite(0); |
102 | 0 | uint32_t writeOffset = 0; |
103 | 0 |
|
104 | 0 | if (ticks < mStart) { |
105 | 0 | MOZ_ASSERT(mStart - ticks <= WEBAUDIO_BLOCK_SIZE); |
106 | 0 | uint32_t count = mStart - ticks; |
107 | 0 | std::fill_n(output, count, 0.0f); |
108 | 0 | writeOffset += count; |
109 | 0 | } |
110 | 0 |
|
111 | 0 | MOZ_ASSERT(ticks + writeOffset >= mStart); |
112 | 0 | MOZ_ASSERT(mStop - ticks >= writeOffset); |
113 | 0 | uint32_t count = |
114 | 0 | std::min<StreamTime>(WEBAUDIO_BLOCK_SIZE, mStop - ticks) - writeOffset; |
115 | 0 |
|
116 | 0 | if (mOffset.HasSimpleValue()) { |
117 | 0 | float value = mOffset.GetValueAtTime(ticks); |
118 | 0 | std::fill_n(output + writeOffset, count, value); |
119 | 0 | } else { |
120 | 0 | mOffset.GetValuesAtTime(ticks + writeOffset, output + writeOffset, count); |
121 | 0 | } |
122 | 0 |
|
123 | 0 | writeOffset += count; |
124 | 0 |
|
125 | 0 | std::fill_n(output + writeOffset, WEBAUDIO_BLOCK_SIZE - writeOffset, 0.0f); |
126 | 0 | } |
127 | 0 |
|
128 | 0 | if (ticks + WEBAUDIO_BLOCK_SIZE >= mStop) { |
129 | 0 | // We've finished playing. |
130 | 0 | *aFinished = true; |
131 | 0 | } |
132 | 0 | } |
133 | | |
134 | | bool IsActive() const override |
135 | 0 | { |
136 | 0 | // start() has been called. |
137 | 0 | return mStart != -1; |
138 | 0 | } |
139 | | |
140 | | size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override |
141 | 0 | { |
142 | 0 | size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); |
143 | 0 |
|
144 | 0 | // Not owned: |
145 | 0 | // - mSource |
146 | 0 | // - mDestination |
147 | 0 | // - mOffset (internal ref owned by node) |
148 | 0 |
|
149 | 0 | return amount; |
150 | 0 | } |
151 | | |
152 | | size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override |
153 | 0 | { |
154 | 0 | return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
155 | 0 | } |
156 | | |
157 | | // mSource deletes the engine in its destructor. |
158 | | AudioNodeStream* MOZ_NON_OWNING_REF mSource; |
159 | | RefPtr<AudioNodeStream> mDestination; |
160 | | StreamTime mStart; |
161 | | StreamTime mStop; |
162 | | AudioParamTimeline mOffset; |
163 | | }; |
164 | | |
165 | | ConstantSourceNode::ConstantSourceNode(AudioContext* aContext) |
166 | | : AudioScheduledSourceNode(aContext, |
167 | | 2, |
168 | | ChannelCountMode::Max, |
169 | | ChannelInterpretation::Speakers) |
170 | | , mOffset(new AudioParam(this, ConstantSourceNodeEngine::OFFSET, |
171 | | "offset", 1.0f)) |
172 | | , mStartCalled(false) |
173 | 0 | { |
174 | 0 | ConstantSourceNodeEngine* engine = new ConstantSourceNodeEngine(this, aContext->Destination()); |
175 | 0 | mStream = AudioNodeStream::Create(aContext, engine, |
176 | 0 | AudioNodeStream::NEED_MAIN_THREAD_FINISHED, |
177 | 0 | aContext->Graph()); |
178 | 0 | engine->SetSourceStream(mStream); |
179 | 0 | mStream->AddMainThreadListener(this); |
180 | 0 | } |
181 | | |
182 | | ConstantSourceNode::~ConstantSourceNode() |
183 | 0 | { |
184 | 0 | } |
185 | | |
186 | | size_t |
187 | | ConstantSourceNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
188 | 0 | { |
189 | 0 | size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf); |
190 | 0 |
|
191 | 0 | amount += mOffset->SizeOfIncludingThis(aMallocSizeOf); |
192 | 0 | return amount; |
193 | 0 | } |
194 | | |
195 | | size_t |
196 | | ConstantSourceNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const |
197 | 0 | { |
198 | 0 | return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
199 | 0 | } |
200 | | |
201 | | JSObject* |
202 | | ConstantSourceNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) |
203 | 0 | { |
204 | 0 | return ConstantSourceNode_Binding::Wrap(aCx, this, aGivenProto); |
205 | 0 | } |
206 | | |
207 | | already_AddRefed<ConstantSourceNode> |
208 | | ConstantSourceNode::Constructor(const GlobalObject& aGlobal, |
209 | | AudioContext& aContext, |
210 | | const ConstantSourceOptions& aOptions, |
211 | | ErrorResult& aRv) |
212 | 0 | { |
213 | 0 | RefPtr<ConstantSourceNode> object = new ConstantSourceNode(&aContext); |
214 | 0 | object->mOffset->SetValue(aOptions.mOffset); |
215 | 0 | return object.forget(); |
216 | 0 | } |
217 | | |
218 | | void |
219 | | ConstantSourceNode::DestroyMediaStream() |
220 | 0 | { |
221 | 0 | if (mStream) { |
222 | 0 | mStream->RemoveMainThreadListener(this); |
223 | 0 | } |
224 | 0 | AudioNode::DestroyMediaStream(); |
225 | 0 | } |
226 | | |
227 | | void |
228 | | ConstantSourceNode::Start(double aWhen, ErrorResult& aRv) |
229 | 0 | { |
230 | 0 | if (!WebAudioUtils::IsTimeValid(aWhen)) { |
231 | 0 | aRv.ThrowRangeError<MSG_VALUE_OUT_OF_RANGE>(NS_LITERAL_STRING("start time")); |
232 | 0 | return; |
233 | 0 | } |
234 | 0 |
|
235 | 0 | if (mStartCalled) { |
236 | 0 | aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); |
237 | 0 | return; |
238 | 0 | } |
239 | 0 | mStartCalled = true; |
240 | 0 |
|
241 | 0 | if (!mStream) { |
242 | 0 | return; |
243 | 0 | } |
244 | 0 | |
245 | 0 | mStream->SetStreamTimeParameter(ConstantSourceNodeEngine::START, |
246 | 0 | Context(), aWhen); |
247 | 0 |
|
248 | 0 | MarkActive(); |
249 | 0 | } |
250 | | |
251 | | void |
252 | | ConstantSourceNode::Stop(double aWhen, ErrorResult& aRv) |
253 | 0 | { |
254 | 0 | if (!WebAudioUtils::IsTimeValid(aWhen)) { |
255 | 0 | aRv.ThrowRangeError<MSG_VALUE_OUT_OF_RANGE>(NS_LITERAL_STRING("stop time")); |
256 | 0 | return; |
257 | 0 | } |
258 | 0 |
|
259 | 0 | if (!mStartCalled) { |
260 | 0 | aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); |
261 | 0 | return; |
262 | 0 | } |
263 | 0 | |
264 | 0 | if (!mStream || !Context()) { |
265 | 0 | return; |
266 | 0 | } |
267 | 0 | |
268 | 0 | mStream->SetStreamTimeParameter(ConstantSourceNodeEngine::STOP, |
269 | 0 | Context(), std::max(0.0, aWhen)); |
270 | 0 | } |
271 | | |
272 | | void |
273 | | ConstantSourceNode::NotifyMainThreadStreamFinished() |
274 | 0 | { |
275 | 0 | MOZ_ASSERT(mStream->IsFinished()); |
276 | 0 |
|
277 | 0 | class EndedEventDispatcher final : public Runnable |
278 | 0 | { |
279 | 0 | public: |
280 | 0 | explicit EndedEventDispatcher(ConstantSourceNode* aNode) |
281 | 0 | : mozilla::Runnable("EndedEventDispatcher") |
282 | 0 | , mNode(aNode) |
283 | 0 | { |
284 | 0 | } |
285 | 0 | NS_IMETHOD Run() override |
286 | 0 | { |
287 | 0 | // If it's not safe to run scripts right now, schedule this to run later |
288 | 0 | if (!nsContentUtils::IsSafeToRunScript()) { |
289 | 0 | nsContentUtils::AddScriptRunner(this); |
290 | 0 | return NS_OK; |
291 | 0 | } |
292 | 0 | |
293 | 0 | mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended")); |
294 | 0 | // Release stream resources. |
295 | 0 | mNode->DestroyMediaStream(); |
296 | 0 | return NS_OK; |
297 | 0 | } |
298 | 0 | private: |
299 | 0 | RefPtr<ConstantSourceNode> mNode; |
300 | 0 | }; |
301 | 0 |
|
302 | 0 | Context()->Dispatch(do_AddRef(new EndedEventDispatcher(this))); |
303 | 0 |
|
304 | 0 | // Drop the playing reference |
305 | 0 | // Warning: The below line might delete this. |
306 | 0 | MarkInactive(); |
307 | 0 | } |
308 | | |
309 | | } // namespace dom |
310 | | } // namespace mozilla |