OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2010, Google Inc. All rights reserved. | 2 * Copyright (C) 2010, 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 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
108 , m_isCleared(false) | 108 , m_isCleared(false) |
109 , m_isInitialized(false) | 109 , m_isInitialized(false) |
110 , m_destinationNode(nullptr) | 110 , m_destinationNode(nullptr) |
111 , m_isResolvingResumePromises(false) | 111 , m_isResolvingResumePromises(false) |
112 , m_connectionCount(0) | 112 , m_connectionCount(0) |
113 , m_didInitializeContextGraphMutex(false) | 113 , m_didInitializeContextGraphMutex(false) |
114 , m_deferredTaskHandler(DeferredTaskHandler::create()) | 114 , m_deferredTaskHandler(DeferredTaskHandler::create()) |
115 , m_isOfflineContext(false) | 115 , m_isOfflineContext(false) |
116 , m_contextState(Suspended) | 116 , m_contextState(Suspended) |
117 , m_cachedSampleFrame(0) | 117 , m_cachedSampleFrame(0) |
118 , m_closedContextSampleRate(-1) | |
119 { | 118 { |
120 m_didInitializeContextGraphMutex = true; | 119 m_didInitializeContextGraphMutex = true; |
121 m_destinationNode = DefaultAudioDestinationNode::create(this); | 120 m_destinationNode = DefaultAudioDestinationNode::create(this); |
122 | 121 |
123 initialize(); | 122 initialize(); |
124 } | 123 } |
125 | 124 |
126 // Constructor for offline (non-realtime) rendering. | 125 // Constructor for offline (non-realtime) rendering. |
127 AudioContext::AudioContext(Document* document, unsigned numberOfChannels, size_t
numberOfFrames, float sampleRate) | 126 AudioContext::AudioContext(Document* document, unsigned numberOfChannels, size_t
numberOfFrames, float sampleRate) |
128 : ActiveDOMObject(document) | 127 : ActiveDOMObject(document) |
129 , m_isStopScheduled(false) | 128 , m_isStopScheduled(false) |
130 , m_isCleared(false) | 129 , m_isCleared(false) |
131 , m_isInitialized(false) | 130 , m_isInitialized(false) |
132 , m_destinationNode(nullptr) | 131 , m_destinationNode(nullptr) |
133 , m_isResolvingResumePromises(false) | 132 , m_isResolvingResumePromises(false) |
134 , m_connectionCount(0) | 133 , m_connectionCount(0) |
135 , m_didInitializeContextGraphMutex(false) | 134 , m_didInitializeContextGraphMutex(false) |
136 , m_deferredTaskHandler(DeferredTaskHandler::create()) | 135 , m_deferredTaskHandler(DeferredTaskHandler::create()) |
137 , m_isOfflineContext(true) | 136 , m_isOfflineContext(true) |
138 , m_contextState(Suspended) | 137 , m_contextState(Suspended) |
139 , m_cachedSampleFrame(0) | 138 , m_cachedSampleFrame(0) |
140 , m_closedContextSampleRate(-1) | |
141 { | 139 { |
142 m_didInitializeContextGraphMutex = true; | 140 m_didInitializeContextGraphMutex = true; |
143 // Create a new destination for offline rendering. | 141 // Create a new destination for offline rendering. |
144 m_renderTarget = AudioBuffer::create(numberOfChannels, numberOfFrames, sampl
eRate); | 142 m_renderTarget = AudioBuffer::create(numberOfChannels, numberOfFrames, sampl
eRate); |
145 if (m_renderTarget.get()) | 143 if (m_renderTarget.get()) |
146 m_destinationNode = OfflineAudioDestinationNode::create(this, m_renderTa
rget.get()); | 144 m_destinationNode = OfflineAudioDestinationNode::create(this, m_renderTa
rget.get()); |
147 | 145 |
148 initialize(); | 146 initialize(); |
149 } | 147 } |
150 | 148 |
151 AudioContext::~AudioContext() | 149 AudioContext::~AudioContext() |
152 { | 150 { |
153 #if DEBUG_AUDIONODE_REFERENCES | 151 #if DEBUG_AUDIONODE_REFERENCES |
154 fprintf(stderr, "%p: AudioContext::~AudioContext(): %u\n", this, m_contextId
); | 152 fprintf(stderr, "%p: AudioContext::~AudioContext(): %u\n", this, m_contextId
); |
155 #endif | 153 #endif |
156 // AudioNodes keep a reference to their context, so there should be no way t
o be in the destructor if there are still AudioNodes around. | 154 // AudioNodes keep a reference to their context, so there should be no way t
o be in the destructor if there are still AudioNodes around. |
157 | |
158 ASSERT(!m_isInitialized); | 155 ASSERT(!m_isInitialized); |
159 ASSERT(!m_referencedNodes.size()); | 156 ASSERT(!m_referencedNodes.size()); |
160 ASSERT(!m_finishedNodes.size()); | 157 ASSERT(!m_finishedNodes.size()); |
161 ASSERT(!m_suspendResolvers.size()); | 158 ASSERT(!m_suspendResolvers.size()); |
162 ASSERT(!m_isResolvingResumePromises); | 159 ASSERT(!m_isResolvingResumePromises); |
163 ASSERT(!m_resumeResolvers.size()); | 160 ASSERT(!m_resumeResolvers.size()); |
164 ASSERT(!m_audioDecoderResolvers.size()); | |
165 } | 161 } |
166 | 162 |
167 void AudioContext::initialize() | 163 void AudioContext::initialize() |
168 { | 164 { |
169 if (isInitialized()) | 165 if (isInitialized()) |
170 return; | 166 return; |
171 | 167 |
172 FFTFrame::initialize(); | 168 FFTFrame::initialize(); |
173 m_listener = AudioListener::create(); | 169 m_listener = AudioListener::create(); |
174 | 170 |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
230 if (!isOfflineContext()) | 226 if (!isOfflineContext()) |
231 setContextState(Closed); | 227 setContextState(Closed); |
232 | 228 |
233 // Resolve the promise now, if any | 229 // Resolve the promise now, if any |
234 if (m_closeResolver) | 230 if (m_closeResolver) |
235 m_closeResolver->resolve(); | 231 m_closeResolver->resolve(); |
236 | 232 |
237 ASSERT(m_listener); | 233 ASSERT(m_listener); |
238 m_listener->waitForHRTFDatabaseLoaderThreadCompletion(); | 234 m_listener->waitForHRTFDatabaseLoaderThreadCompletion(); |
239 | 235 |
240 // Reject any decodeAudioData promises that haven't been fulfilled yet. | |
241 for (auto& resolver : m_audioDecoderResolvers) { | |
242 resolver->reject(DOMException::create(InvalidStateError, "Audio context
is going away")); | |
243 } | |
244 m_audioDecoderResolvers.clear(); | |
245 | |
246 // Uninitialization done, so clear flags to indicate that the AudioContext h
as no pending | |
247 // activity anymore. | |
248 clear(); | 236 clear(); |
249 } | 237 } |
250 | 238 |
251 void AudioContext::stop() | 239 void AudioContext::stop() |
252 { | 240 { |
253 // Usually ExecutionContext calls stop twice. | 241 // Usually ExecutionContext calls stop twice. |
254 if (m_isStopScheduled) | 242 if (m_isStopScheduled) |
255 return; | 243 return; |
256 m_isStopScheduled = true; | 244 m_isStopScheduled = true; |
257 | 245 |
(...skipping 16 matching lines...) Expand all Loading... |
274 } | 262 } |
275 | 263 |
276 AudioBuffer* AudioContext::createBuffer(unsigned numberOfChannels, size_t number
OfFrames, float sampleRate, ExceptionState& exceptionState) | 264 AudioBuffer* AudioContext::createBuffer(unsigned numberOfChannels, size_t number
OfFrames, float sampleRate, ExceptionState& exceptionState) |
277 { | 265 { |
278 // It's ok to call createBuffer, even if the context is closed because the A
udioBuffer doesn't | 266 // It's ok to call createBuffer, even if the context is closed because the A
udioBuffer doesn't |
279 // really "belong" to any particular context. | 267 // really "belong" to any particular context. |
280 | 268 |
281 return AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate, exc
eptionState); | 269 return AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate, exc
eptionState); |
282 } | 270 } |
283 | 271 |
284 ScriptPromise AudioContext::decodeAudioData(ScriptState* scriptState, DOMArrayBu
ffer* audioData, AudioBufferCallback* successCallback, AudioBufferCallback* erro
rCallback, ExceptionState& exceptionState) | 272 void AudioContext::decodeAudioData(DOMArrayBuffer* audioData, AudioBufferCallbac
k* successCallback, AudioBufferCallback* errorCallback, ExceptionState& exceptio
nState) |
285 { | 273 { |
286 if (!audioData) { | 274 if (isContextClosed()) { |
287 RefPtrWillBeRawPtr<DOMException> error = DOMException::create( | 275 throwExceptionForClosedState(exceptionState); |
288 NotSupportedError, | 276 return; |
289 "invalid ArrayBuffer for audioData."); | |
290 if (errorCallback) { | |
291 errorCallback->handleEvent(error.get()); | |
292 } | |
293 return ScriptPromise::rejectWithDOMException(scriptState, error); | |
294 } | 277 } |
295 | 278 |
296 RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::
create(scriptState); | 279 if (!audioData) { |
297 ScriptPromise promise = resolver->promise(); | 280 exceptionState.throwDOMException( |
298 | 281 SyntaxError, |
299 m_audioDecoderResolvers.append(resolver); | 282 "invalid ArrayBuffer for audioData."); |
300 | 283 return; |
301 float rate = isContextClosed() ? m_closedContextSampleRate : sampleRate(); | 284 } |
302 | 285 m_audioDecoder.decodeAsync(audioData, sampleRate(), successCallback, errorCa
llback); |
303 ASSERT(rate > 0); | |
304 | |
305 m_audioDecoder.decodeAsync(audioData, rate, successCallback, errorCallback,
resolver.get(), this); | |
306 | |
307 return promise; | |
308 } | 286 } |
309 | 287 |
310 AudioBufferSourceNode* AudioContext::createBufferSource(ExceptionState& exceptio
nState) | 288 AudioBufferSourceNode* AudioContext::createBufferSource(ExceptionState& exceptio
nState) |
311 { | 289 { |
312 ASSERT(isMainThread()); | 290 ASSERT(isMainThread()); |
313 | 291 |
314 if (isContextClosed()) { | 292 if (isContextClosed()) { |
315 throwExceptionForClosedState(exceptionState); | 293 throwExceptionForClosedState(exceptionState); |
316 return nullptr; | 294 return nullptr; |
317 } | 295 } |
(...skipping 609 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
927 // Find AudioBufferSourceNodes to see if we can stop playing them. | 905 // Find AudioBufferSourceNodes to see if we can stop playing them. |
928 for (unsigned i = 0; i < m_referencedNodes.size(); ++i) { | 906 for (unsigned i = 0; i < m_referencedNodes.size(); ++i) { |
929 AudioNode* node = m_referencedNodes.at(i).get(); | 907 AudioNode* node = m_referencedNodes.at(i).get(); |
930 | 908 |
931 if (node->handler().nodeType() == AudioHandler::NodeTypeAudioBufferSourc
e) { | 909 if (node->handler().nodeType() == AudioHandler::NodeTypeAudioBufferSourc
e) { |
932 AudioBufferSourceNode* sourceNode = static_cast<AudioBufferSourceNod
e*>(node); | 910 AudioBufferSourceNode* sourceNode = static_cast<AudioBufferSourceNod
e*>(node); |
933 sourceNode->audioBufferSourceHandler().handleStoppableSourceNode(); | 911 sourceNode->audioBufferSourceHandler().handleStoppableSourceNode(); |
934 } | 912 } |
935 } | 913 } |
936 } | 914 } |
937 | |
938 void AudioContext::removeAudioDecoderResolver(ScriptPromiseResolver* resolver) | |
939 { | |
940 ASSERT(isMainThread()); | |
941 | |
942 for (size_t k = 0; k < m_audioDecoderResolvers.size(); ++k) { | |
943 if (resolver == m_audioDecoderResolvers.at(k)) { | |
944 m_audioDecoderResolvers.remove(k); | |
945 break; | |
946 } | |
947 } | |
948 } | |
949 | |
950 void AudioContext::handlePreRenderTasks() | 915 void AudioContext::handlePreRenderTasks() |
951 { | 916 { |
952 ASSERT(isAudioThread()); | 917 ASSERT(isAudioThread()); |
953 | 918 |
954 // At the beginning of every render quantum, try to update the internal rend
ering graph state (from main thread changes). | 919 // At the beginning of every render quantum, try to update the internal rend
ering graph state (from main thread changes). |
955 // It's OK if the tryLock() fails, we'll just take slightly longer to pick u
p the changes. | 920 // It's OK if the tryLock() fails, we'll just take slightly longer to pick u
p the changes. |
956 if (tryLock()) { | 921 if (tryLock()) { |
957 handler().handleDeferredTasks(); | 922 handler().handleDeferredTasks(); |
958 | 923 |
959 resolvePromisesForResume(); | 924 resolvePromisesForResume(); |
(...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1157 | 1122 |
1158 // Resolve any pending promises created by suspend() | 1123 // Resolve any pending promises created by suspend() |
1159 if (m_suspendResolvers.size() > 0) | 1124 if (m_suspendResolvers.size() > 0) |
1160 Platform::current()->mainThread()->postTask(FROM_HERE, bind(&AudioContex
t::resolvePromisesForSuspendOnMainThread, this)); | 1125 Platform::current()->mainThread()->postTask(FROM_HERE, bind(&AudioContex
t::resolvePromisesForSuspendOnMainThread, this)); |
1161 } | 1126 } |
1162 | 1127 |
1163 void AudioContext::rejectPendingResolvers() | 1128 void AudioContext::rejectPendingResolvers() |
1164 { | 1129 { |
1165 ASSERT(isMainThread()); | 1130 ASSERT(isMainThread()); |
1166 | 1131 |
1167 // Audio context is closing down so reject any promises that are still pendi
ng. | 1132 // Audio context is closing down so reject any suspend or resume promises th
at are still |
| 1133 // pending. |
1168 | 1134 |
1169 for (auto& resolver : m_suspendResolvers) { | 1135 for (auto& resolver : m_suspendResolvers) { |
1170 resolver->reject(DOMException::create(InvalidStateError, "Audio context
is going away")); | 1136 resolver->reject(DOMException::create(InvalidStateError, "Audio context
is going away")); |
1171 } | 1137 } |
1172 m_suspendResolvers.clear(); | 1138 m_suspendResolvers.clear(); |
1173 | 1139 |
1174 for (auto& resolver : m_resumeResolvers) { | 1140 for (auto& resolver : m_resumeResolvers) { |
1175 resolver->reject(DOMException::create(InvalidStateError, "Audio context
is going away")); | 1141 resolver->reject(DOMException::create(InvalidStateError, "Audio context
is going away")); |
1176 } | 1142 } |
1177 m_resumeResolvers.clear(); | 1143 m_resumeResolvers.clear(); |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1231 // Avoid firing the event if the document has already gone away. | 1197 // Avoid firing the event if the document has already gone away. |
1232 if (executionContext()) { | 1198 if (executionContext()) { |
1233 // Call the offline rendering completion event listener and resolve the
promise too. | 1199 // Call the offline rendering completion event listener and resolve the
promise too. |
1234 dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer)); | 1200 dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer)); |
1235 m_offlineResolver->resolve(renderedBuffer); | 1201 m_offlineResolver->resolve(renderedBuffer); |
1236 } | 1202 } |
1237 } | 1203 } |
1238 | 1204 |
1239 DEFINE_TRACE(AudioContext) | 1205 DEFINE_TRACE(AudioContext) |
1240 { | 1206 { |
1241 visitor->trace(m_audioDecoderResolvers); | |
1242 visitor->trace(m_closeResolver); | 1207 visitor->trace(m_closeResolver); |
1243 visitor->trace(m_offlineResolver); | 1208 visitor->trace(m_offlineResolver); |
1244 visitor->trace(m_renderTarget); | 1209 visitor->trace(m_renderTarget); |
1245 visitor->trace(m_destinationNode); | 1210 visitor->trace(m_destinationNode); |
1246 visitor->trace(m_listener); | 1211 visitor->trace(m_listener); |
1247 // trace() can be called in AudioContext constructor, and | 1212 // trace() can be called in AudioContext constructor, and |
1248 // m_contextGraphMutex might be unavailable. | 1213 // m_contextGraphMutex might be unavailable. |
1249 if (m_didInitializeContextGraphMutex) { | 1214 if (m_didInitializeContextGraphMutex) { |
1250 AutoLocker lock(this); | 1215 AutoLocker lock(this); |
1251 visitor->trace(m_referencedNodes); | 1216 visitor->trace(m_referencedNodes); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1299 | 1264 |
1300 if (isContextClosed()) { | 1265 if (isContextClosed()) { |
1301 // We've already closed the context previously, but it hasn't yet been r
esolved, so just | 1266 // We've already closed the context previously, but it hasn't yet been r
esolved, so just |
1302 // create a new promise and reject it. | 1267 // create a new promise and reject it. |
1303 return ScriptPromise::rejectWithDOMException( | 1268 return ScriptPromise::rejectWithDOMException( |
1304 scriptState, | 1269 scriptState, |
1305 DOMException::create(InvalidStateError, | 1270 DOMException::create(InvalidStateError, |
1306 "Cannot close a context that is being closed or has already been
closed.")); | 1271 "Cannot close a context that is being closed or has already been
closed.")); |
1307 } | 1272 } |
1308 | 1273 |
1309 // Save the current sample rate for any subsequent decodeAudioData calls. | |
1310 m_closedContextSampleRate = sampleRate(); | |
1311 | |
1312 m_closeResolver = ScriptPromiseResolver::create(scriptState); | 1274 m_closeResolver = ScriptPromiseResolver::create(scriptState); |
1313 ScriptPromise promise = m_closeResolver->promise(); | 1275 ScriptPromise promise = m_closeResolver->promise(); |
1314 | 1276 |
1315 // Before closing the context go and disconnect all nodes, allowing them to
be collected. This | 1277 // Before closing the context go and disconnect all nodes, allowing them to
be collected. This |
1316 // will also break any connections to the destination node. Any unfinished s
ourced nodes will | 1278 // will also break any connections to the destination node. Any unfinished s
ourced nodes will |
1317 // get stopped when the context is unitialized. | 1279 // get stopped when the context is unitialized. |
1318 for (auto& node : m_liveNodes) { | 1280 for (auto& node : m_liveNodes) { |
1319 if (node) { | 1281 if (node) { |
1320 for (unsigned k = 0; k < node->numberOfOutputs(); ++k) | 1282 for (unsigned k = 0; k < node->numberOfOutputs(); ++k) |
1321 node->disconnectWithoutException(k); | 1283 node->disconnectWithoutException(k); |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1361 DeferredTaskHandler::AutoLocker::AutoLocker(AudioContext* context) | 1323 DeferredTaskHandler::AutoLocker::AutoLocker(AudioContext* context) |
1362 : m_handler(context->handler()) | 1324 : m_handler(context->handler()) |
1363 { | 1325 { |
1364 m_handler.lock(); | 1326 m_handler.lock(); |
1365 } | 1327 } |
1366 | 1328 |
1367 | 1329 |
1368 } // namespace blink | 1330 } // namespace blink |
1369 | 1331 |
1370 #endif // ENABLE(WEB_AUDIO) | 1332 #endif // ENABLE(WEB_AUDIO) |
OLD | NEW |