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

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

Issue 1006963003: AudioContext.decodeAudioData returns a Promise (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Error callback invoked with error, not null. Created 5 years, 9 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 | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
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
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)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698