Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(537)

Side by Side Diff: Source/modules/webaudio/OfflineAudioContext.cpp

Issue 1140723003: Implement suspend() and resume() for OfflineAudioContext (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Revering oilpan-compatible changes + HashMap Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2012, Google Inc. All rights reserved. 2 * Copyright (C) 2012, Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 13 matching lines...) Expand all
24 24
25 #include "config.h" 25 #include "config.h"
26 #if ENABLE(WEB_AUDIO) 26 #if ENABLE(WEB_AUDIO)
27 #include "modules/webaudio/OfflineAudioContext.h" 27 #include "modules/webaudio/OfflineAudioContext.h"
28 28
29 #include "bindings/core/v8/ExceptionMessages.h" 29 #include "bindings/core/v8/ExceptionMessages.h"
30 #include "bindings/core/v8/ExceptionState.h" 30 #include "bindings/core/v8/ExceptionState.h"
31 #include "core/dom/Document.h" 31 #include "core/dom/Document.h"
32 #include "core/dom/ExceptionCode.h" 32 #include "core/dom/ExceptionCode.h"
33 #include "core/dom/ExecutionContext.h" 33 #include "core/dom/ExecutionContext.h"
34 #include "modules/webaudio/OfflineAudioCompletionEvent.h"
35 #include "modules/webaudio/OfflineAudioDestinationNode.h"
36 #include "platform/Task.h"
37 #include "platform/ThreadSafeFunctional.h"
34 #include "platform/audio/AudioUtilities.h" 38 #include "platform/audio/AudioUtilities.h"
39 #include "public/platform/Platform.h"
40
35 41
36 namespace blink { 42 namespace blink {
37 43
38 OfflineAudioContext* OfflineAudioContext::create(ExecutionContext* context, unsi gned numberOfChannels, size_t numberOfFrames, float sampleRate, ExceptionState& exceptionState) 44 OfflineAudioContext* OfflineAudioContext::create(ExecutionContext* context, unsi gned numberOfChannels, size_t numberOfFrames, float sampleRate, ExceptionState& exceptionState)
39 { 45 {
40 // FIXME: add support for workers. 46 // FIXME: add support for workers.
41 if (!context || !context->isDocument()) { 47 if (!context || !context->isDocument()) {
42 exceptionState.throwDOMException( 48 exceptionState.throwDOMException(
43 NotSupportedError, 49 NotSupportedError,
44 "Workers are not supported."); 50 "Workers are not supported.");
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
85 + ", " + String::number(sampleRate) 91 + ", " + String::number(sampleRate)
86 + ")"); 92 + ")");
87 } 93 }
88 94
89 audioContext->suspendIfNeeded(); 95 audioContext->suspendIfNeeded();
90 return audioContext; 96 return audioContext;
91 } 97 }
92 98
93 OfflineAudioContext::OfflineAudioContext(Document* document, unsigned numberOfCh annels, size_t numberOfFrames, float sampleRate) 99 OfflineAudioContext::OfflineAudioContext(Document* document, unsigned numberOfCh annels, size_t numberOfFrames, float sampleRate)
94 : AudioContext(document, numberOfChannels, numberOfFrames, sampleRate) 100 : AudioContext(document, numberOfChannels, numberOfFrames, sampleRate)
101 , m_isRenderingStarted(false)
102 , m_isResolvingSuspendPromise(false)
103 , m_totalRenderFrames(numberOfFrames)
95 { 104 {
96 } 105 }
97 106
98 OfflineAudioContext::~OfflineAudioContext() 107 OfflineAudioContext::~OfflineAudioContext()
99 { 108 {
100 } 109 }
101 110
111 DEFINE_TRACE(OfflineAudioContext)
112 {
113 visitor->trace(m_scheduledSuspends);
hongchan 2015/06/24 21:14:12 This gives me a compile error: ./../third_party/W
haraken 2015/06/24 23:25:57 m_scheduledSuspends is not a HashMap of Oilpan. Yo
hongchan 2015/06/25 23:34:13 Done.
114 visitor->trace(m_completeResolver);
115 AudioContext::trace(visitor);
116 }
117
118 bool OfflineAudioContext::shouldSuspendNow()
119 {
120 ASSERT(!isMainThread());
121
122 fprintf(stderr, "shouldSuspendNow\n");
123
124 // If there is any scheduled suspend at |currentSampleFrame|, the context
125 // should be suspended.
126 if (m_scheduledSuspends.contains(currentSampleFrame()))
127 return true;
128
129 return false;
130 }
131
132 void OfflineAudioContext::resolvePendingSuspendPromises()
133 {
134 ASSERT(!isMainThread());
135
136 fprintf(stderr, "resolvePendingSuspendPromises\n");
137
138 if (!m_isResolvingSuspendPromise || m_scheduledSuspends.isEmpty())
139 return;
140
141 size_t nowFrame = currentSampleFrame();
142
143 SuspendContainerMap::iterator it = m_scheduledSuspends.find(nowFrame);
144 if (it == m_scheduledSuspends.end())
145 return;
146
147 RefPtrWillBeRawPtr<ScheduledSuspendContainer> suspendContainer = it->value;
yhirano 2015/06/25 04:00:53 This should be just RefPtr.
hongchan 2015/06/25 23:34:13 Done.
148 Platform::current()->mainThread()->postTask(FROM_HERE,
149 threadSafeBind(&OfflineAudioContext::resolvePendingSuspendPromisesOnMain Thread, this, suspendContainer));
yhirano 2015/06/25 04:00:53 suspendContainer.release()
hongchan 2015/06/25 23:34:13 Done.
150 m_scheduledSuspends.remove(nowFrame);
yhirano 2015/06/25 04:00:52 Please move this statement to L148.
yhirano 2015/06/25 04:00:53 [optional] remove(it) may be more straightforward?
hongchan 2015/06/25 23:34:13 Done and done.
151 }
152
153 void OfflineAudioContext::fireCompletionEvent()
154 {
155 ASSERT(isMainThread());
156
157 // We set the state to closed here so that the oncomplete event handler sees
158 // that the context has been closed.
159 setContextState(Closed);
160
161 AudioBuffer* renderedBuffer = renderTarget();
162
163 ASSERT(renderedBuffer);
164 if (!renderedBuffer)
165 return;
166
167 // Avoid firing the event if the document has already gone away.
168 if (executionContext()) {
169 // Call the offline rendering completion event listener and resolve the
170 // promise too.
171 dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer));
172 m_completeResolver->resolve(renderedBuffer);
173 }
174 }
175
102 ScriptPromise OfflineAudioContext::startOfflineRendering(ScriptState* scriptStat e) 176 ScriptPromise OfflineAudioContext::startOfflineRendering(ScriptState* scriptStat e)
103 { 177 {
178 ASSERT(isMainThread());
179
104 // Calling close() on an OfflineAudioContext is not supported/allowed, 180 // Calling close() on an OfflineAudioContext is not supported/allowed,
105 // but it might well have been stopped by its execution context. 181 // but it might well have been stopped by its execution context.
106 if (isContextClosed()) { 182 if (isContextClosed()) {
107 return ScriptPromise::rejectWithDOMException( 183 return ScriptPromise::rejectWithDOMException(
108 scriptState, 184 scriptState,
109 DOMException::create( 185 DOMException::create(
110 InvalidStateError, 186 InvalidStateError,
111 "cannot call startRendering on an OfflineAudioContext in a stopp ed state.")); 187 "cannot call startRendering on an OfflineAudioContext in a stopp ed state."));
112 } 188 }
113 189
114 if (m_offlineResolver) { 190 if (m_completeResolver) {
115 // Can't call startRendering more than once. Return a rejected promise now. 191 // Can't call startRendering more than once. Return a rejected promise now.
116 return ScriptPromise::rejectWithDOMException( 192 return ScriptPromise::rejectWithDOMException(
117 scriptState, 193 scriptState,
118 DOMException::create( 194 DOMException::create(
119 InvalidStateError, 195 InvalidStateError,
120 "cannot call startRendering more than once")); 196 "cannot call startRendering more than once"));
121 } 197 }
122 198
123 m_offlineResolver = ScriptPromiseResolver::create(scriptState); 199 // If the context is not in the suspended state, reject the promise.
124 startRendering(); 200 if (contextState() != AudioContextState::Suspended) {
125 return m_offlineResolver->promise(); 201 return ScriptPromise::rejectWithDOMException(
202 scriptState,
203 DOMException::create(
204 InvalidStateError,
205 "cannot startRendering when an OfflineAudioContext is not in a s uspended state"));
206 }
207
208 m_completeResolver = ScriptPromiseResolver::create(scriptState);
209
210 // Start rendering and return the promise.
211 m_isRenderingStarted = true;
212 setContextState(Running);
213 destination()->audioDestinationHandler().startRendering();
214
215 return m_completeResolver->promise();
216 }
217
218 ScriptPromise OfflineAudioContext::suspendOfflineRendering(ScriptState* scriptSt ate, double when)
219 {
220 ASSERT(isMainThread());
221 ASSERT(destination()->audioDestinationHandler().offlineRenderThread());
222
223 RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver:: create(scriptState);
224 ScriptPromise promise = resolver->promise();
225
226 // The specified suspend time is negative, reject the promise.
227 if (when < 0) {
228 resolver->reject(DOMException::create(InvalidStateError,
229 "negative suspend time (" + String::number(when) + ") is not allowed "));
230 return promise;
231 }
232
233 // Quantize the suspend time to the rendering block boundary.
234 size_t quantizedFrame = destination()->audioDestinationHandler().quantizeTim eToRenderQuantum(when);
235
236 // The specified suspend time is in the past, reject the promise.
237 if (quantizedFrame < currentSampleFrame()) {
238 resolver->reject(DOMException::create(InvalidStateError,
239 "cannot schedule a suspend at frame " + String::number(quantizedFram e) +
240 " (" + String::number(when) + " seconds) because it is earlier than the current frame of " +
241 String::number(currentSampleFrame())));
242 return promise;
243 }
244
245 // The suspend time should be earlier than the total render frame. If the
246 // requested suspension time is equal to the total render frame, the promise
247 // will be rejected.
248 if (m_totalRenderFrames <= quantizedFrame) {
249 resolver->reject(DOMException::create(InvalidStateError,
250 "cannot schedule a suspend at frame " + String::number(quantizedFram e) +
251 " (" + String::number(when) + " seconds) because it is greater than or equal to the total render duration of " +
252 String::number(m_totalRenderFrames) + " frames"));
253 return promise;
254 }
255
256 ScheduledSuspendContainer* suspendContainer = ScheduledSuspendContainer::cre ate(when, quantizedFrame, resolver);
yhirano 2015/06/25 04:00:52 RefPtr<ScheduledSuspendContainer> suspendContainer
hongchan 2015/06/25 23:34:13 Done.
257
258 // Validate the suspend and append if necessary on the render thread.
259 destination()->audioDestinationHandler().offlineRenderThread()->postTask(FRO M_HERE,
260 threadSafeBind(&OfflineAudioContext::checkDuplicateSuspend, this, suspen dContainer));
261
262 return promise;
263 }
264
265 ScriptPromise OfflineAudioContext::resumeOfflineRendering(ScriptState* scriptSta te)
266 {
267 ASSERT(isMainThread());
268
269 RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver:: create(scriptState);
270 ScriptPromise promise = resolver->promise();
271
272 // If the context is not in a suspended state, reject the promise.
273 if (contextState() != AudioContextState::Suspended) {
274 resolver->reject(DOMException::create(InvalidStateError,
275 "cannot resume a context that is not suspended"));
276 return promise;
277 }
278
279 // If the rendering has not started, reject the promise.
280 if (!m_isRenderingStarted) {
281 resolver->reject(DOMException::create(InvalidStateError,
282 "cannot resume a context that has not started"));
283 return promise;
284 }
285
286 // If the context is suspended, resume rendering by setting the state to
287 // "Running." and calling startRendering(). Note that resuming is possible
288 // only after the rendering started.
289 setContextState(Running);
290 destination()->audioDestinationHandler().startRendering();
291
292 // Resolve the promise immediately.
293 resolver->resolve();
294
295 return promise;
296 }
297
298 void OfflineAudioContext::checkDuplicateSuspend(ScheduledSuspendContainer* suspe ndContainer)
yhirano 2015/06/25 04:00:53 PassRefPtr<ScheduledSuspendContainer>
hongchan 2015/06/25 23:34:13 Done.
299 {
300 ASSERT(!isMainThread());
301
302 fprintf(stderr, "checkDuplicateSuspend\n");
303
304 // If there is a duplicate suspension at the same quantize frame, reject the
305 // promise. The rejection of promise should happen in the main thread, so we
306 // post a task to it.
307 if (m_scheduledSuspends.contains(suspendContainer->suspendFrame())) {
308 Platform::current()->mainThread()->postTask(FROM_HERE,
309 threadSafeBind(&OfflineAudioContext::rejectSuspendPromiseOnMainThrea d, this, suspendContainer));
310 }
311
312 // If duplicate check passes, we can add the container safely here in the
313 // render thread.
314 RefPtr<ScheduledSuspendContainer> adoptedContainer = adoptRef(suspendContain er);
yhirano 2015/06/25 04:00:53 You should not use adoptRef here.
hongchan 2015/06/25 23:34:13 Done.
315 m_scheduledSuspends.set(suspendContainer->suspendFrame(), adoptedContainer);
hongchan 2015/06/24 21:14:12 This might be the problematic part. Since the |sus
316 }
317
318 void OfflineAudioContext::rejectSuspendPromiseOnMainThread(ScheduledSuspendConta iner* suspendContainer)
yhirano 2015/06/25 04:00:52 PassRefPtr<ScheduledSuspendContainer>
hongchan 2015/06/25 23:34:13 Done.
319 {
320 ASSERT(isMainThread());
321
322 suspendContainer->resolver()->reject(DOMException::create(InvalidStateError,
323 "cannot schedule more than one suspend at frame " +
324 String::number(suspendContainer->suspendFrame()) + " (" +
325 String::number(suspendContainer->suspendTime()) + " seconds)"));
326 }
327
328 void OfflineAudioContext::resolvePendingSuspendPromisesOnMainThread(ScheduledSus pendContainer* suspendContainer)
yhirano 2015/06/25 04:00:53 PassRefPtr<ScheduledSuspendContainer>
hongchan 2015/06/25 23:34:13 Done.
329 {
330 ASSERT(isMainThread());
331 AutoLocker locker(this);
332
333 // Suspend the context first. This will fire onstatechange event.
334 setContextState(Suspended);
335
336 suspendContainer->resolver()->resolve();
337 m_isResolvingSuspendPromise = false;
338 }
339
340 ScheduledSuspendContainer::ScheduledSuspendContainer(
341 double suspendTime, size_t suspendFrame, PassRefPtrWillBeRawPtr<ScriptPromis eResolver> resolver)
342 : m_suspendTime(suspendTime)
343 , m_suspendFrame(suspendFrame)
344 , m_resolver(resolver)
345 , m_isPending(false)
346 {
yhirano 2015/06/25 04:00:53 ASSERT(isMainThread);
hongchan 2015/06/25 23:34:13 Done.
347 }
348
349 ScheduledSuspendContainer::~ScheduledSuspendContainer()
350 {
yhirano 2015/06/25 04:00:53 ASSERT(isMainThread);
hongchan 2015/06/25 23:34:13 Done.
351 }
352
353 DEFINE_TRACE(ScheduledSuspendContainer)
yhirano 2015/06/25 04:00:53 No trace is needed.
hongchan 2015/06/25 23:34:13 Done.
354 {
355 visitor->trace(m_resolver);
356 }
357
358 ScheduledSuspendContainer* ScheduledSuspendContainer::create(
359 double suspendTime, size_t suspendFrame, PassRefPtrWillBeRawPtr<ScriptPromis eResolver> resolver)
360 {
361 return new ScheduledSuspendContainer(suspendTime, suspendFrame, resolver);
362 }
363
364 bool ScheduledSuspendContainer::shouldSuspendAtFrame(size_t whenFrame) const
365 {
366 if (m_suspendFrame != whenFrame)
367 return false;
368
369 return true;
370 }
371
372 bool ScheduledSuspendContainer::isPending() const
373 {
374 return m_isPending;
375 }
376
377 void ScheduledSuspendContainer::markAsPending()
378 {
379 m_isPending = true;
126 } 380 }
127 381
128 } // namespace blink 382 } // namespace blink
129 383
130 #endif // ENABLE(WEB_AUDIO) 384 #endif // ENABLE(WEB_AUDIO)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698