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