Index: Source/modules/webaudio/OnlineAudioContext.cpp |
diff --git a/Source/modules/webaudio/OnlineAudioContext.cpp b/Source/modules/webaudio/OnlineAudioContext.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7bc59e37621224684fd83ea603b5786f35ed683c |
--- /dev/null |
+++ b/Source/modules/webaudio/OnlineAudioContext.cpp |
@@ -0,0 +1,188 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "config.h" |
+#include "modules/webaudio/OnlineAudioContext.h" |
+ |
+#include "bindings/core/v8/ExceptionMessages.h" |
+#include "bindings/core/v8/ExceptionState.h" |
+#include "bindings/core/v8/ScriptPromiseResolver.h" |
+#include "core/dom/DOMException.h" |
+#include "core/dom/ExceptionCode.h" |
+ |
+#if DEBUG_AUDIONODE_REFERENCES |
+#include <stdio.h> |
+#endif |
+ |
+#if ENABLE(WEB_AUDIO) |
+ |
+namespace blink { |
+ |
+// Don't allow more than this number of simultaneous AudioContexts |
+// talking to hardware. |
+const unsigned MaxHardwareContexts = 6; |
+static unsigned s_hardwareContextCount = 0; |
+static unsigned s_contextId = 0; |
+ |
+AudioContext* OnlineAudioContext::create(Document& document, ExceptionState& exceptionState) |
+{ |
+ ASSERT(isMainThread()); |
+ if (s_hardwareContextCount >= MaxHardwareContexts) { |
+ exceptionState.throwDOMException( |
+ NotSupportedError, |
+ ExceptionMessages::indexExceedsMaximumBound( |
+ "number of hardware contexts", |
+ s_hardwareContextCount, |
+ MaxHardwareContexts)); |
+ return nullptr; |
+ } |
+ |
+ OnlineAudioContext* audioContext = new OnlineAudioContext(document); |
+ audioContext->suspendIfNeeded(); |
+ |
+ // This starts the audio thread. The destination node's |
+ // provideInput() method will now be called repeatedly to render |
+ // audio. Each time provideInput() is called, a portion of the |
+ // audio stream is rendered. Let's call this time period a "render |
+ // quantum". NOTE: for now default AudioContext does not need an |
+ // explicit startRendering() call from JavaScript. We may want to |
+ // consider requiring it for symmetry with OfflineAudioContext. |
+ audioContext->startRendering(); |
+ ++s_hardwareContextCount; |
+#if DEBUG_AUDIONODE_REFERENCES |
+ fprintf(stderr, "%p: OnlineAudioContext::OnlineAudioContext(): %u #%u\n", |
+ audioContext, audioContext->m_contextId, s_hardwareContextCount); |
+#endif |
+ |
+ return audioContext; |
+} |
+ |
+OnlineAudioContext::OnlineAudioContext(Document& document) |
+ : AudioContext(&document) |
+ , m_contextId(s_contextId++) |
+{ |
+} |
+ |
+OnlineAudioContext::~OnlineAudioContext() |
+{ |
+#if DEBUG_AUDIONODE_REFERENCES |
+ fprintf(stderr, "%p: OnlineAudioContext::~OnlineAudioContext(): %u\n", this, m_contextId); |
+#endif |
+} |
+ |
+DEFINE_TRACE(OnlineAudioContext) |
+{ |
+ visitor->trace(m_closeResolver); |
+ AudioContext::trace(visitor); |
+} |
+ |
+ScriptPromise OnlineAudioContext::suspendContext(ScriptState* scriptState) |
+{ |
+ ASSERT(isMainThread()); |
+ AutoLocker locker(this); |
+ |
+ RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState); |
+ ScriptPromise promise = resolver->promise(); |
+ |
+ if (contextState() == Closed) { |
+ resolver->reject( |
+ DOMException::create(InvalidStateError, "Cannot suspend a context that has been closed")); |
+ } else { |
+ // Stop rendering now. |
+ if (destination()) |
+ stopRendering(); |
+ |
+ // Since we don't have any way of knowing when the hardware actually stops, we'll just |
+ // resolve the promise now. |
+ resolver->resolve(); |
+ } |
+ |
+ return promise; |
+} |
+ |
+ScriptPromise OnlineAudioContext::resumeContext(ScriptState* scriptState) |
+{ |
+ ASSERT(isMainThread()); |
+ |
+ if (isContextClosed()) { |
+ return ScriptPromise::rejectWithDOMException( |
+ scriptState, |
+ DOMException::create( |
+ InvalidAccessError, |
+ "cannot resume a closed AudioContext")); |
+ } |
+ |
+ RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState); |
+ ScriptPromise promise = resolver->promise(); |
+ |
+ // Restart the destination node to pull on the audio graph. |
+ if (destination()) |
+ startRendering(); |
+ |
+ // Save the resolver which will get resolved when the destination node starts pulling on the |
+ // graph again. |
+ { |
+ AutoLocker locker(this); |
+ m_resumeResolvers.append(resolver); |
+ } |
+ |
+ return promise; |
+} |
+ |
+ScriptPromise OnlineAudioContext::closeContext(ScriptState* scriptState) |
+{ |
+ if (isContextClosed()) { |
+ // We've already closed the context previously, but it hasn't yet been resolved, so just |
+ // create a new promise and reject it. |
+ return ScriptPromise::rejectWithDOMException( |
+ scriptState, |
+ DOMException::create(InvalidStateError, |
+ "Cannot close a context that is being closed or has already been closed.")); |
+ } |
+ |
+ m_closeResolver = ScriptPromiseResolver::create(scriptState); |
+ ScriptPromise promise = m_closeResolver->promise(); |
+ |
+ // Stop the audio context. This will stop the destination node from pulling audio anymore. And |
+ // since we have disconnected the destination from the audio graph, and thus has no references, |
+ // the destination node can GCed if JS has no references. stop() will also resolve the Promise |
+ // created here. |
+ stop(); |
+ |
+ return promise; |
+} |
+ |
+void OnlineAudioContext::didClose() |
+{ |
+ // This is specific to OnlineAudioContexts. OfflineAudioContexts |
+ // are closed in their completion event. |
+ setContextState(Closed); |
+ |
+ ASSERT(s_hardwareContextCount); |
+ --s_hardwareContextCount; |
+ |
+ if (m_closeResolver) |
+ m_closeResolver->resolve(); |
+} |
+ |
+bool OnlineAudioContext::isContextClosed() const |
+{ |
+ return m_closeResolver || AudioContext::isContextClosed(); |
+} |
+ |
+void OnlineAudioContext::stopRendering() |
+{ |
+ ASSERT(isMainThread()); |
+ ASSERT(destination()); |
+ |
+ if (contextState() == Running) { |
+ destination()->audioDestinationHandler().stopRendering(); |
+ setContextState(Suspended); |
+ deferredTaskHandler().clearHandlersToBeDeleted(); |
+ } |
+} |
+ |
+} // namespace blink |
+ |
+#endif // ENABLE(WEB_AUDIO) |