OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "config.h" |
| 6 #include "modules/webaudio/OnlineAudioContext.h" |
| 7 |
| 8 #include "bindings/core/v8/ExceptionMessages.h" |
| 9 #include "bindings/core/v8/ExceptionState.h" |
| 10 #include "bindings/core/v8/ScriptPromiseResolver.h" |
| 11 #include "core/dom/DOMException.h" |
| 12 #include "core/dom/ExceptionCode.h" |
| 13 |
| 14 #if DEBUG_AUDIONODE_REFERENCES |
| 15 #include <stdio.h> |
| 16 #endif |
| 17 |
| 18 #if ENABLE(WEB_AUDIO) |
| 19 |
| 20 namespace blink { |
| 21 |
| 22 // Don't allow more than this number of simultaneous AudioContexts |
| 23 // talking to hardware. |
| 24 const unsigned MaxHardwareContexts = 6; |
| 25 static unsigned s_hardwareContextCount = 0; |
| 26 static unsigned s_contextId = 0; |
| 27 |
| 28 AudioContext* OnlineAudioContext::create(Document& document, ExceptionState& exc
eptionState) |
| 29 { |
| 30 ASSERT(isMainThread()); |
| 31 if (s_hardwareContextCount >= MaxHardwareContexts) { |
| 32 exceptionState.throwDOMException( |
| 33 NotSupportedError, |
| 34 ExceptionMessages::indexExceedsMaximumBound( |
| 35 "number of hardware contexts", |
| 36 s_hardwareContextCount, |
| 37 MaxHardwareContexts)); |
| 38 return nullptr; |
| 39 } |
| 40 |
| 41 OnlineAudioContext* audioContext = new OnlineAudioContext(document); |
| 42 audioContext->suspendIfNeeded(); |
| 43 |
| 44 // This starts the audio thread. The destination node's |
| 45 // provideInput() method will now be called repeatedly to render |
| 46 // audio. Each time provideInput() is called, a portion of the |
| 47 // audio stream is rendered. Let's call this time period a "render |
| 48 // quantum". NOTE: for now default AudioContext does not need an |
| 49 // explicit startRendering() call from JavaScript. We may want to |
| 50 // consider requiring it for symmetry with OfflineAudioContext. |
| 51 audioContext->startRendering(); |
| 52 ++s_hardwareContextCount; |
| 53 #if DEBUG_AUDIONODE_REFERENCES |
| 54 fprintf(stderr, "%p: OnlineAudioContext::OnlineAudioContext(): %u #%u\n", |
| 55 audioContext, audioContext->m_contextId, s_hardwareContextCount); |
| 56 #endif |
| 57 |
| 58 return audioContext; |
| 59 } |
| 60 |
| 61 OnlineAudioContext::OnlineAudioContext(Document& document) |
| 62 : AudioContext(&document) |
| 63 , m_contextId(s_contextId++) |
| 64 { |
| 65 } |
| 66 |
| 67 OnlineAudioContext::~OnlineAudioContext() |
| 68 { |
| 69 #if DEBUG_AUDIONODE_REFERENCES |
| 70 fprintf(stderr, "%p: OnlineAudioContext::~OnlineAudioContext(): %u\n", this,
m_contextId); |
| 71 #endif |
| 72 } |
| 73 |
| 74 DEFINE_TRACE(OnlineAudioContext) |
| 75 { |
| 76 visitor->trace(m_closeResolver); |
| 77 AudioContext::trace(visitor); |
| 78 } |
| 79 |
| 80 ScriptPromise OnlineAudioContext::suspendContext(ScriptState* scriptState) |
| 81 { |
| 82 ASSERT(isMainThread()); |
| 83 AutoLocker locker(this); |
| 84 |
| 85 RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::
create(scriptState); |
| 86 ScriptPromise promise = resolver->promise(); |
| 87 |
| 88 if (contextState() == Closed) { |
| 89 resolver->reject( |
| 90 DOMException::create(InvalidStateError, "Cannot suspend a context th
at has been closed")); |
| 91 } else { |
| 92 // Stop rendering now. |
| 93 if (destination()) |
| 94 stopRendering(); |
| 95 |
| 96 // Since we don't have any way of knowing when the hardware actually sto
ps, we'll just |
| 97 // resolve the promise now. |
| 98 resolver->resolve(); |
| 99 } |
| 100 |
| 101 return promise; |
| 102 } |
| 103 |
| 104 ScriptPromise OnlineAudioContext::resumeContext(ScriptState* scriptState) |
| 105 { |
| 106 ASSERT(isMainThread()); |
| 107 |
| 108 if (isContextClosed()) { |
| 109 return ScriptPromise::rejectWithDOMException( |
| 110 scriptState, |
| 111 DOMException::create( |
| 112 InvalidAccessError, |
| 113 "cannot resume a closed AudioContext")); |
| 114 } |
| 115 |
| 116 RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::
create(scriptState); |
| 117 ScriptPromise promise = resolver->promise(); |
| 118 |
| 119 // Restart the destination node to pull on the audio graph. |
| 120 if (destination()) |
| 121 startRendering(); |
| 122 |
| 123 // Save the resolver which will get resolved when the destination node start
s pulling on the |
| 124 // graph again. |
| 125 { |
| 126 AutoLocker locker(this); |
| 127 m_resumeResolvers.append(resolver); |
| 128 } |
| 129 |
| 130 return promise; |
| 131 } |
| 132 |
| 133 ScriptPromise OnlineAudioContext::closeContext(ScriptState* scriptState) |
| 134 { |
| 135 if (isContextClosed()) { |
| 136 // We've already closed the context previously, but it hasn't yet been r
esolved, so just |
| 137 // create a new promise and reject it. |
| 138 return ScriptPromise::rejectWithDOMException( |
| 139 scriptState, |
| 140 DOMException::create(InvalidStateError, |
| 141 "Cannot close a context that is being closed or has already been
closed.")); |
| 142 } |
| 143 |
| 144 m_closeResolver = ScriptPromiseResolver::create(scriptState); |
| 145 ScriptPromise promise = m_closeResolver->promise(); |
| 146 |
| 147 // Stop the audio context. This will stop the destination node from pulling
audio anymore. And |
| 148 // since we have disconnected the destination from the audio graph, and thus
has no references, |
| 149 // the destination node can GCed if JS has no references. stop() will also r
esolve the Promise |
| 150 // created here. |
| 151 stop(); |
| 152 |
| 153 return promise; |
| 154 } |
| 155 |
| 156 void OnlineAudioContext::didClose() |
| 157 { |
| 158 // This is specific to OnlineAudioContexts. OfflineAudioContexts |
| 159 // are closed in their completion event. |
| 160 setContextState(Closed); |
| 161 |
| 162 ASSERT(s_hardwareContextCount); |
| 163 --s_hardwareContextCount; |
| 164 |
| 165 if (m_closeResolver) |
| 166 m_closeResolver->resolve(); |
| 167 } |
| 168 |
| 169 bool OnlineAudioContext::isContextClosed() const |
| 170 { |
| 171 return m_closeResolver || AudioContext::isContextClosed(); |
| 172 } |
| 173 |
| 174 void OnlineAudioContext::stopRendering() |
| 175 { |
| 176 ASSERT(isMainThread()); |
| 177 ASSERT(destination()); |
| 178 |
| 179 if (contextState() == Running) { |
| 180 destination()->audioDestinationHandler().stopRendering(); |
| 181 setContextState(Suspended); |
| 182 deferredTaskHandler().clearHandlersToBeDeleted(); |
| 183 } |
| 184 } |
| 185 |
| 186 } // namespace blink |
| 187 |
| 188 #endif // ENABLE(WEB_AUDIO) |
OLD | NEW |