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 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
105 , m_isInitialized(false) | 105 , m_isInitialized(false) |
106 , m_destinationNode(nullptr) | 106 , m_destinationNode(nullptr) |
107 , m_isResolvingResumePromises(false) | 107 , m_isResolvingResumePromises(false) |
108 , m_automaticPullNodesNeedUpdating(false) | 108 , m_automaticPullNodesNeedUpdating(false) |
109 , m_connectionCount(0) | 109 , m_connectionCount(0) |
110 , m_didInitializeContextGraphMutex(false) | 110 , m_didInitializeContextGraphMutex(false) |
111 , m_audioThread(0) | 111 , m_audioThread(0) |
112 , m_isOfflineContext(false) | 112 , m_isOfflineContext(false) |
113 , m_contextState(Suspended) | 113 , m_contextState(Suspended) |
114 , m_cachedSampleFrame(0) | 114 , m_cachedSampleFrame(0) |
115 , m_closedContextSampleRate(-1) | |
tkent
2015/04/07 23:24:47
This member isn't initialized in another construct
| |
115 { | 116 { |
116 m_didInitializeContextGraphMutex = true; | 117 m_didInitializeContextGraphMutex = true; |
117 m_destinationNode = DefaultAudioDestinationNode::create(this); | 118 m_destinationNode = DefaultAudioDestinationNode::create(this); |
118 | 119 |
119 initialize(); | 120 initialize(); |
120 } | 121 } |
121 | 122 |
122 // Constructor for offline (non-realtime) rendering. | 123 // Constructor for offline (non-realtime) rendering. |
123 AudioContext::AudioContext(Document* document, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate) | 124 AudioContext::AudioContext(Document* document, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate) |
124 : ActiveDOMObject(document) | 125 : ActiveDOMObject(document) |
(...skipping 18 matching lines...) Expand all Loading... | |
143 | 144 |
144 initialize(); | 145 initialize(); |
145 } | 146 } |
146 | 147 |
147 AudioContext::~AudioContext() | 148 AudioContext::~AudioContext() |
148 { | 149 { |
149 #if DEBUG_AUDIONODE_REFERENCES | 150 #if DEBUG_AUDIONODE_REFERENCES |
150 fprintf(stderr, "%p: AudioContext::~AudioContext(): %u\n", this, m_contextId ); | 151 fprintf(stderr, "%p: AudioContext::~AudioContext(): %u\n", this, m_contextId ); |
151 #endif | 152 #endif |
152 // 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. | 153 // 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 | |
155 // Context is being destroyed. Reject any decodeAudioData promises that hav en't been fulfilled | |
156 // yet. | |
157 for (auto& resolver : m_audioDecoderResolvers) { | |
haraken
2015/04/07 23:39:14
This is unsafe. You're not allowed to touch other
yhirano
2015/04/08 03:19:27
Rejection is needed when deleting a resolver if
Raymond Toy
2015/04/08 16:47:11
uninitialize should work.
Raymond Toy
2015/04/08 16:47:11
If this is moved to uninitialize(), then the execu
| |
158 resolver->reject(DOMException::create(InvalidStateError, "Audio context is going away")); | |
159 } | |
160 m_audioDecoderResolvers.clear(); | |
161 | |
153 ASSERT(!m_isInitialized); | 162 ASSERT(!m_isInitialized); |
154 ASSERT(!m_referencedNodes.size()); | 163 ASSERT(!m_referencedNodes.size()); |
155 ASSERT(!m_finishedNodes.size()); | 164 ASSERT(!m_finishedNodes.size()); |
156 ASSERT(!m_automaticPullNodes.size()); | 165 ASSERT(!m_automaticPullNodes.size()); |
157 if (m_automaticPullNodesNeedUpdating) | 166 if (m_automaticPullNodesNeedUpdating) |
158 m_renderingAutomaticPullNodes.resize(m_automaticPullNodes.size()); | 167 m_renderingAutomaticPullNodes.resize(m_automaticPullNodes.size()); |
159 ASSERT(!m_renderingAutomaticPullNodes.size()); | 168 ASSERT(!m_renderingAutomaticPullNodes.size()); |
160 ASSERT(!m_suspendResolvers.size()); | 169 ASSERT(!m_suspendResolvers.size()); |
161 ASSERT(!m_isResolvingResumePromises); | 170 ASSERT(!m_isResolvingResumePromises); |
162 ASSERT(!m_resumeResolvers.size()); | 171 ASSERT(!m_resumeResolvers.size()); |
172 ASSERT(!m_audioDecoderResolvers.size()); | |
163 } | 173 } |
164 | 174 |
165 void AudioContext::initialize() | 175 void AudioContext::initialize() |
166 { | 176 { |
167 if (isInitialized()) | 177 if (isInitialized()) |
168 return; | 178 return; |
169 | 179 |
170 FFTFrame::initialize(); | 180 FFTFrame::initialize(); |
171 m_listener = AudioListener::create(); | 181 m_listener = AudioListener::create(); |
172 | 182 |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
265 } | 275 } |
266 | 276 |
267 AudioBuffer* AudioContext::createBuffer(unsigned numberOfChannels, size_t number OfFrames, float sampleRate, ExceptionState& exceptionState) | 277 AudioBuffer* AudioContext::createBuffer(unsigned numberOfChannels, size_t number OfFrames, float sampleRate, ExceptionState& exceptionState) |
268 { | 278 { |
269 // It's ok to call createBuffer, even if the context is closed because the A udioBuffer doesn't | 279 // It's ok to call createBuffer, even if the context is closed because the A udioBuffer doesn't |
270 // really "belong" to any particular context. | 280 // really "belong" to any particular context. |
271 | 281 |
272 return AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate, exc eptionState); | 282 return AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate, exc eptionState); |
273 } | 283 } |
274 | 284 |
275 void AudioContext::decodeAudioData(DOMArrayBuffer* audioData, AudioBufferCallbac k* successCallback, AudioBufferCallback* errorCallback, ExceptionState& exceptio nState) | 285 ScriptPromise AudioContext::decodeAudioData(ScriptState* scriptState, DOMArrayBu ffer* audioData, AudioBufferCallback* successCallback, AudioBufferCallback* erro rCallback, ExceptionState& exceptionState) |
276 { | 286 { |
277 if (isContextClosed()) { | 287 if (!audioData) { |
278 throwExceptionForClosedState(exceptionState); | 288 RefPtr<DOMException> error = DOMException::create( |
279 return; | 289 NotSupportedError, |
290 "invalid ArrayBuffer for audioData."); | |
291 if (errorCallback) { | |
292 errorCallback->handleEvent(error.get()); | |
293 } | |
294 return ScriptPromise::rejectWithDOMException(scriptState, error); | |
280 } | 295 } |
281 | 296 |
282 if (!audioData) { | 297 RefPtrWillBeRawPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver:: create(scriptState); |
283 exceptionState.throwDOMException( | 298 ScriptPromise promise = resolver->promise(); |
284 SyntaxError, | 299 |
285 "invalid ArrayBuffer for audioData."); | 300 m_audioDecoderResolvers.append(resolver); |
286 return; | 301 |
287 } | 302 float rate = isContextClosed() ? m_closedContextSampleRate : sampleRate(); |
288 m_audioDecoder.decodeAsync(audioData, sampleRate(), successCallback, errorCa llback); | 303 |
304 ASSERT(rate > 0); | |
305 | |
306 m_audioDecoder.decodeAsync(audioData, rate, successCallback, errorCallback, resolver.get(), this); | |
yhirano
2015/04/08 03:19:27
Just to confirm: Is this |resolver.get()| safe in
Raymond Toy
2015/04/08 16:47:11
I believe so. As long as uninitialize() has not b
| |
307 | |
308 return promise; | |
289 } | 309 } |
290 | 310 |
291 AudioBufferSourceNode* AudioContext::createBufferSource(ExceptionState& exceptio nState) | 311 AudioBufferSourceNode* AudioContext::createBufferSource(ExceptionState& exceptio nState) |
292 { | 312 { |
293 ASSERT(isMainThread()); | 313 ASSERT(isMainThread()); |
294 | 314 |
295 if (isContextClosed()) { | 315 if (isContextClosed()) { |
296 throwExceptionForClosedState(exceptionState); | 316 throwExceptionForClosedState(exceptionState); |
297 return nullptr; | 317 return nullptr; |
298 } | 318 } |
(...skipping 642 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
941 // Find AudioBufferSourceNodes to see if we can stop playing them. | 961 // Find AudioBufferSourceNodes to see if we can stop playing them. |
942 for (unsigned i = 0; i < m_referencedNodes.size(); ++i) { | 962 for (unsigned i = 0; i < m_referencedNodes.size(); ++i) { |
943 AudioNode* node = m_referencedNodes.at(i).get(); | 963 AudioNode* node = m_referencedNodes.at(i).get(); |
944 | 964 |
945 if (node->nodeType() == AudioNode::NodeTypeAudioBufferSource) { | 965 if (node->nodeType() == AudioNode::NodeTypeAudioBufferSource) { |
946 AudioBufferSourceNode* sourceNode = static_cast<AudioBufferSourceNod e*>(node); | 966 AudioBufferSourceNode* sourceNode = static_cast<AudioBufferSourceNod e*>(node); |
947 sourceNode->handleStoppableSourceNode(); | 967 sourceNode->handleStoppableSourceNode(); |
948 } | 968 } |
949 } | 969 } |
950 } | 970 } |
971 | |
972 void AudioContext::removeAudioDecoderResolver(ScriptPromiseResolver* resolver) | |
973 { | |
974 ASSERT(isMainThread()); | |
975 | |
976 for (size_t k = 0; k < m_audioDecoderResolvers.size(); ++k) { | |
977 if (resolver == m_audioDecoderResolvers.at(k)) { | |
978 m_audioDecoderResolvers.remove(k); | |
979 break; | |
980 } | |
981 } | |
982 } | |
983 | |
951 void AudioContext::handlePreRenderTasks() | 984 void AudioContext::handlePreRenderTasks() |
952 { | 985 { |
953 ASSERT(isAudioThread()); | 986 ASSERT(isAudioThread()); |
954 | 987 |
955 // At the beginning of every render quantum, try to update the internal rend ering graph state (from main thread changes). | 988 // At the beginning of every render quantum, try to update the internal rend ering graph state (from main thread changes). |
956 // It's OK if the tryLock() fails, we'll just take slightly longer to pick u p the changes. | 989 // It's OK if the tryLock() fails, we'll just take slightly longer to pick u p the changes. |
957 if (tryLock()) { | 990 if (tryLock()) { |
958 // Update the channel count mode. | 991 // Update the channel count mode. |
959 updateChangedChannelCountMode(); | 992 updateChangedChannelCountMode(); |
960 | 993 |
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1191 | 1224 |
1192 // Resolve any pending promises created by suspend() | 1225 // Resolve any pending promises created by suspend() |
1193 if (m_suspendResolvers.size() > 0) | 1226 if (m_suspendResolvers.size() > 0) |
1194 Platform::current()->mainThread()->postTask(FROM_HERE, bind(&AudioContex t::resolvePromisesForSuspendOnMainThread, this)); | 1227 Platform::current()->mainThread()->postTask(FROM_HERE, bind(&AudioContex t::resolvePromisesForSuspendOnMainThread, this)); |
1195 } | 1228 } |
1196 | 1229 |
1197 void AudioContext::rejectPendingResolvers() | 1230 void AudioContext::rejectPendingResolvers() |
1198 { | 1231 { |
1199 ASSERT(isMainThread()); | 1232 ASSERT(isMainThread()); |
1200 | 1233 |
1201 // Audio context is closing down so reject any suspend or resume promises th at are still | 1234 // Audio context is closing down so reject any promises that are still pendi ng. |
1202 // pending. | |
1203 | 1235 |
1204 for (auto& resolver : m_suspendResolvers) { | 1236 for (auto& resolver : m_suspendResolvers) { |
1205 resolver->reject(DOMException::create(InvalidStateError, "Audio context is going away")); | 1237 resolver->reject(DOMException::create(InvalidStateError, "Audio context is going away")); |
1206 } | 1238 } |
1207 m_suspendResolvers.clear(); | 1239 m_suspendResolvers.clear(); |
1208 | 1240 |
1209 for (auto& resolver : m_resumeResolvers) { | 1241 for (auto& resolver : m_resumeResolvers) { |
1210 resolver->reject(DOMException::create(InvalidStateError, "Audio context is going away")); | 1242 resolver->reject(DOMException::create(InvalidStateError, "Audio context is going away")); |
1211 } | 1243 } |
1212 m_resumeResolvers.clear(); | 1244 m_resumeResolvers.clear(); |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1266 // Avoid firing the event if the document has already gone away. | 1298 // Avoid firing the event if the document has already gone away. |
1267 if (executionContext()) { | 1299 if (executionContext()) { |
1268 // Call the offline rendering completion event listener and resolve the promise too. | 1300 // Call the offline rendering completion event listener and resolve the promise too. |
1269 dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer)); | 1301 dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer)); |
1270 m_offlineResolver->resolve(renderedBuffer); | 1302 m_offlineResolver->resolve(renderedBuffer); |
1271 } | 1303 } |
1272 } | 1304 } |
1273 | 1305 |
1274 DEFINE_TRACE(AudioContext) | 1306 DEFINE_TRACE(AudioContext) |
1275 { | 1307 { |
1308 visitor->trace(m_audioDecoderResolvers); | |
1276 visitor->trace(m_closeResolver); | 1309 visitor->trace(m_closeResolver); |
1277 visitor->trace(m_offlineResolver); | 1310 visitor->trace(m_offlineResolver); |
1278 visitor->trace(m_renderTarget); | 1311 visitor->trace(m_renderTarget); |
1279 visitor->trace(m_destinationNode); | 1312 visitor->trace(m_destinationNode); |
1280 visitor->trace(m_listener); | 1313 visitor->trace(m_listener); |
1281 // trace() can be called in AudioContext constructor, and | 1314 // trace() can be called in AudioContext constructor, and |
1282 // m_contextGraphMutex might be unavailable. | 1315 // m_contextGraphMutex might be unavailable. |
1283 if (m_didInitializeContextGraphMutex) { | 1316 if (m_didInitializeContextGraphMutex) { |
1284 AutoLocker lock(this); | 1317 AutoLocker lock(this); |
1285 visitor->trace(m_referencedNodes); | 1318 visitor->trace(m_referencedNodes); |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1335 | 1368 |
1336 if (isContextClosed()) { | 1369 if (isContextClosed()) { |
1337 // We've already closed the context previously, but it hasn't yet been r esolved, so just | 1370 // We've already closed the context previously, but it hasn't yet been r esolved, so just |
1338 // create a new promise and reject it. | 1371 // create a new promise and reject it. |
1339 return ScriptPromise::rejectWithDOMException( | 1372 return ScriptPromise::rejectWithDOMException( |
1340 scriptState, | 1373 scriptState, |
1341 DOMException::create(InvalidStateError, | 1374 DOMException::create(InvalidStateError, |
1342 "Cannot close a context that is being closed or has already been closed.")); | 1375 "Cannot close a context that is being closed or has already been closed.")); |
1343 } | 1376 } |
1344 | 1377 |
1378 // Save the current sample rate for any subsequent decodeAudioData calls. | |
1379 m_closedContextSampleRate = sampleRate(); | |
1380 | |
1345 m_closeResolver = ScriptPromiseResolver::create(scriptState); | 1381 m_closeResolver = ScriptPromiseResolver::create(scriptState); |
1346 ScriptPromise promise = m_closeResolver->promise(); | 1382 ScriptPromise promise = m_closeResolver->promise(); |
1347 | 1383 |
1348 // Before closing the context go and disconnect all nodes, allowing them to be collected. This | 1384 // Before closing the context go and disconnect all nodes, allowing them to be collected. This |
1349 // will also break any connections to the destination node. Any unfinished s ourced nodes will | 1385 // will also break any connections to the destination node. Any unfinished s ourced nodes will |
1350 // get stopped when the context is unitialized. | 1386 // get stopped when the context is unitialized. |
1351 for (auto& node : m_liveNodes.keys()) { | 1387 for (auto& node : m_liveNodes.keys()) { |
1352 if (node) { | 1388 if (node) { |
1353 for (unsigned k = 0; k < node->numberOfOutputs(); ++k) | 1389 for (unsigned k = 0; k < node->numberOfOutputs(); ++k) |
1354 node->disconnectWithoutException(k); | 1390 node->disconnectWithoutException(k); |
1355 } | 1391 } |
1356 } | 1392 } |
1357 | 1393 |
1358 // Stop the audio context. This will stop the destination node from pulling audio anymore. And | 1394 // Stop the audio context. This will stop the destination node from pulling audio anymore. And |
1359 // since we have disconnected the destination from the audio graph, and thus has no references, | 1395 // since we have disconnected the destination from the audio graph, and thus has no references, |
1360 // the destination node can GCed if JS has no references. stop() will also r esolve the Promise | 1396 // the destination node can GCed if JS has no references. stop() will also r esolve the Promise |
1361 // created here. | 1397 // created here. |
1362 stop(); | 1398 stop(); |
1363 | 1399 |
1364 return promise; | 1400 return promise; |
1365 } | 1401 } |
1366 | 1402 |
1367 } // namespace blink | 1403 } // namespace blink |
1368 | 1404 |
1369 #endif // ENABLE(WEB_AUDIO) | 1405 #endif // ENABLE(WEB_AUDIO) |
OLD | NEW |