1 /* 2 * Copyright (C) 2010, Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY 17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25 #include "config.h" 26 27 #if ENABLE(WEB_AUDIO) 28 29 #include "AudioContext.h" 30 31 #include "ArrayBuffer.h" 32 #include "AudioBuffer.h" 33 #include "AudioBufferSourceNode.h" 34 #include "AudioChannelMerger.h" 35 #include "AudioChannelSplitter.h" 36 #include "AudioGainNode.h" 37 #include "AudioListener.h" 38 #include "AudioNodeInput.h" 39 #include "AudioNodeOutput.h" 40 #include "AudioPannerNode.h" 41 #include "ConvolverNode.h" 42 #include "DefaultAudioDestinationNode.h" 43 #include "DelayNode.h" 44 #include "Document.h" 45 #include "FFTFrame.h" 46 #include "HRTFDatabaseLoader.h" 47 #include "HRTFPanner.h" 48 #include "HighPass2FilterNode.h" 49 #include "JavaScriptAudioNode.h" 50 #include "LowPass2FilterNode.h" 51 #include "OfflineAudioCompletionEvent.h" 52 #include "OfflineAudioDestinationNode.h" 53 #include "PlatformString.h" 54 #include "RealtimeAnalyserNode.h" 55 56 #if DEBUG_AUDIONODE_REFERENCES 57 #include <stdio.h> 58 #endif 59 60 #include <wtf/OwnPtr.h> 61 #include <wtf/PassOwnPtr.h> 62 #include <wtf/RefCounted.h> 63 64 // FIXME: check the proper way to reference an undefined thread ID 65 const int UndefinedThreadIdentifier = 0xffffffff; 66 67 const unsigned MaxNodesToDeletePerQuantum = 10; 68 69 namespace WebCore { 70 71 PassRefPtr<AudioContext> AudioContext::create(Document* document) 72 { 73 return adoptRef(new AudioContext(document)); 74 } 75 76 PassRefPtr<AudioContext> AudioContext::createOfflineContext(Document* document, unsigned numberOfChannels, size_t numberOfFrames, double sampleRate) 77 { 78 return adoptRef(new AudioContext(document, numberOfChannels, numberOfFrames, sampleRate)); 79 } 80 81 // Constructor for rendering to the audio hardware. 82 AudioContext::AudioContext(Document* document) 83 : ActiveDOMObject(document, this) 84 , m_isInitialized(false) 85 , m_isAudioThreadFinished(false) 86 , m_document(document) 87 , m_destinationNode(0) 88 , m_connectionCount(0) 89 , m_audioThread(0) 90 , m_graphOwnerThread(UndefinedThreadIdentifier) 91 , m_isOfflineContext(false) 92 { 93 constructCommon(); 94 95 m_destinationNode = DefaultAudioDestinationNode::create(this); 96 97 // This sets in motion an asynchronous loading mechanism on another thread. 98 // We can check m_hrtfDatabaseLoader->isLoaded() to find out whether or not it has been fully loaded. 99 // It's not that useful to have a callback function for this since the audio thread automatically starts rendering on the graph 100 // when this has finished (see AudioDestinationNode). 101 m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNecessary(sampleRate()); 102 103 // FIXME: for now default AudioContext does not need an explicit startRendering() call. 104 // We may want to consider requiring it for symmetry with OfflineAudioContext 105 m_destinationNode->startRendering(); 106 } 107 108 // Constructor for offline (non-realtime) rendering. 109 AudioContext::AudioContext(Document* document, unsigned numberOfChannels, size_t numberOfFrames, double sampleRate) 110 : ActiveDOMObject(document, this) 111 , m_isInitialized(false) 112 , m_isAudioThreadFinished(false) 113 , m_document(document) 114 , m_destinationNode(0) 115 , m_connectionCount(0) 116 , m_audioThread(0) 117 , m_graphOwnerThread(UndefinedThreadIdentifier) 118 , m_isOfflineContext(true) 119 { 120 constructCommon(); 121 122 // FIXME: the passed in sampleRate MUST match the hardware sample-rate since HRTFDatabaseLoader is a singleton. 123 m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNecessary(sampleRate); 124 125 // Create a new destination for offline rendering. 126 m_renderTarget = AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate); 127 m_destinationNode = OfflineAudioDestinationNode::create(this, m_renderTarget.get()); 128 } 129 130 void AudioContext::constructCommon() 131 { 132 // Note: because adoptRef() won't be called until we leave this constructor, but code in this constructor needs to reference this context, 133 // relax the check. 134 relaxAdoptionRequirement(); 135 136 FFTFrame::initialize(); 137 138 m_listener = AudioListener::create(); 139 m_temporaryMonoBus = adoptPtr(new AudioBus(1, AudioNode::ProcessingSizeInFrames)); 140 m_temporaryStereoBus = adoptPtr(new AudioBus(2, AudioNode::ProcessingSizeInFrames)); 141 } 142 143 AudioContext::~AudioContext() 144 { 145 #if DEBUG_AUDIONODE_REFERENCES 146 printf("%p: AudioContext::~AudioContext()\n", this); 147 #endif 148 // AudioNodes keep a reference to their context, so there should be no way to be in the destructor if there are still AudioNodes around. 149 ASSERT(!m_nodesToDelete.size()); 150 ASSERT(!m_referencedNodes.size()); 151 ASSERT(!m_finishedNodes.size()); 152 } 153 154 void AudioContext::lazyInitialize() 155 { 156 if (!m_isInitialized) { 157 // Don't allow the context to initialize a second time after it's already been explicitly uninitialized. 158 ASSERT(!m_isAudioThreadFinished); 159 if (!m_isAudioThreadFinished) { 160 if (m_destinationNode.get()) { 161 // This starts the audio thread. The destination node's provideInput() method will now be called repeatedly to render audio. 162 // Each time provideInput() is called, a portion of the audio stream is rendered. Let's call this time period a "render quantum". 163 m_destinationNode->initialize(); 164 } 165 m_isInitialized = true; 166 } 167 } 168 } 169 170 void AudioContext::uninitialize() 171 { 172 if (m_isInitialized) { 173 // This stops the audio thread and all audio rendering. 174 m_destinationNode->uninitialize(); 175 176 // Don't allow the context to initialize a second time after it's already been explicitly uninitialized. 177 m_isAudioThreadFinished = true; 178 179 // We have to release our reference to the destination node before the context will ever be deleted since the destination node holds a reference to the context. 180 m_destinationNode.clear(); 181 182 // Get rid of the sources which may still be playing. 183 derefUnfinishedSourceNodes(); 184 185 // Because the AudioBuffers are garbage collected, we can't delete them here. 186 // Instead, at least release the potentially large amount of allocated memory for the audio data. 187 // Note that we do this *after* the context is uninitialized and stops processing audio. 188 for (unsigned i = 0; i < m_allocatedBuffers.size(); ++i) 189 m_allocatedBuffers[i]->releaseMemory(); 190 m_allocatedBuffers.clear(); 191 192 m_isInitialized = false; 193 } 194 } 195 196 bool AudioContext::isInitialized() const 197 { 198 return m_isInitialized; 199 } 200 201 bool AudioContext::isRunnable() const 202 { 203 if (!isInitialized()) 204 return false; 205 206 // Check with the HRTF spatialization system to see if it's finished loading. 207 return m_hrtfDatabaseLoader->isLoaded(); 208 } 209 210 void AudioContext::stop() 211 { 212 m_document = 0; // document is going away 213 uninitialize(); 214 } 215 216 Document* AudioContext::document() const 217 { 218 ASSERT(m_document); 219 return m_document; 220 } 221 222 bool AudioContext::hasDocument() 223 { 224 return m_document; 225 } 226 227 void AudioContext::refBuffer(PassRefPtr<AudioBuffer> buffer) 228 { 229 m_allocatedBuffers.append(buffer); 230 } 231 232 PassRefPtr<AudioBuffer> AudioContext::createBuffer(unsigned numberOfChannels, size_t numberOfFrames, double sampleRate) 233 { 234 return AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate); 235 } 236 237 PassRefPtr<AudioBuffer> AudioContext::createBuffer(ArrayBuffer* arrayBuffer, bool mixToMono) 238 { 239 ASSERT(arrayBuffer); 240 if (!arrayBuffer) 241 return 0; 242 243 return AudioBuffer::createFromAudioFileData(arrayBuffer->data(), arrayBuffer->byteLength(), mixToMono, sampleRate()); 244 } 245 246 PassRefPtr<AudioBufferSourceNode> AudioContext::createBufferSource() 247 { 248 ASSERT(isMainThread()); 249 lazyInitialize(); 250 RefPtr<AudioBufferSourceNode> node = AudioBufferSourceNode::create(this, m_destinationNode->sampleRate()); 251 252 refNode(node.get()); // context keeps reference until source has finished playing 253 return node; 254 } 255 256 PassRefPtr<JavaScriptAudioNode> AudioContext::createJavaScriptNode(size_t bufferSize) 257 { 258 ASSERT(isMainThread()); 259 lazyInitialize(); 260 RefPtr<JavaScriptAudioNode> node = JavaScriptAudioNode::create(this, m_destinationNode->sampleRate(), bufferSize); 261 262 refNode(node.get()); // context keeps reference until we stop making javascript rendering callbacks 263 return node; 264 } 265 266 PassRefPtr<LowPass2FilterNode> AudioContext::createLowPass2Filter() 267 { 268 ASSERT(isMainThread()); 269 lazyInitialize(); 270 return LowPass2FilterNode::create(this, m_destinationNode->sampleRate()); 271 } 272 273 PassRefPtr<HighPass2FilterNode> AudioContext::createHighPass2Filter() 274 { 275 ASSERT(isMainThread()); 276 lazyInitialize(); 277 return HighPass2FilterNode::create(this, m_destinationNode->sampleRate()); 278 } 279 280 PassRefPtr<AudioPannerNode> AudioContext::createPanner() 281 { 282 ASSERT(isMainThread()); 283 lazyInitialize(); 284 return AudioPannerNode::create(this, m_destinationNode->sampleRate()); 285 } 286 287 PassRefPtr<ConvolverNode> AudioContext::createConvolver() 288 { 289 ASSERT(isMainThread()); 290 lazyInitialize(); 291 return ConvolverNode::create(this, m_destinationNode->sampleRate()); 292 } 293 294 PassRefPtr<RealtimeAnalyserNode> AudioContext::createAnalyser() 295 { 296 ASSERT(isMainThread()); 297 lazyInitialize(); 298 return RealtimeAnalyserNode::create(this, m_destinationNode->sampleRate()); 299 } 300 301 PassRefPtr<AudioGainNode> AudioContext::createGainNode() 302 { 303 ASSERT(isMainThread()); 304 lazyInitialize(); 305 return AudioGainNode::create(this, m_destinationNode->sampleRate()); 306 } 307 308 PassRefPtr<DelayNode> AudioContext::createDelayNode() 309 { 310 ASSERT(isMainThread()); 311 lazyInitialize(); 312 return DelayNode::create(this, m_destinationNode->sampleRate()); 313 } 314 315 PassRefPtr<AudioChannelSplitter> AudioContext::createChannelSplitter() 316 { 317 ASSERT(isMainThread()); 318 lazyInitialize(); 319 return AudioChannelSplitter::create(this, m_destinationNode->sampleRate()); 320 } 321 322 PassRefPtr<AudioChannelMerger> AudioContext::createChannelMerger() 323 { 324 ASSERT(isMainThread()); 325 lazyInitialize(); 326 return AudioChannelMerger::create(this, m_destinationNode->sampleRate()); 327 } 328 329 void AudioContext::notifyNodeFinishedProcessing(AudioNode* node) 330 { 331 ASSERT(isAudioThread()); 332 m_finishedNodes.append(node); 333 } 334 335 void AudioContext::derefFinishedSourceNodes() 336 { 337 ASSERT(isGraphOwner()); 338 ASSERT(isAudioThread() || isAudioThreadFinished()); 339 for (unsigned i = 0; i < m_finishedNodes.size(); i++) 340 derefNode(m_finishedNodes[i]); 341 342 m_finishedNodes.clear(); 343 } 344 345 void AudioContext::refNode(AudioNode* node) 346 { 347 ASSERT(isMainThread()); 348 AutoLocker locker(this); 349 350 node->ref(AudioNode::RefTypeConnection); 351 m_referencedNodes.append(node); 352 } 353 354 void AudioContext::derefNode(AudioNode* node) 355 { 356 ASSERT(isGraphOwner()); 357 358 node->deref(AudioNode::RefTypeConnection); 359 360 for (unsigned i = 0; i < m_referencedNodes.size(); ++i) { 361 if (node == m_referencedNodes[i]) { 362 m_referencedNodes.remove(i); 363 break; 364 } 365 } 366 } 367 368 void AudioContext::derefUnfinishedSourceNodes() 369 { 370 ASSERT(isMainThread() && isAudioThreadFinished()); 371 for (unsigned i = 0; i < m_referencedNodes.size(); ++i) 372 m_referencedNodes[i]->deref(AudioNode::RefTypeConnection); 373 374 m_referencedNodes.clear(); 375 } 376 377 void AudioContext::lock(bool& mustReleaseLock) 378 { 379 // Don't allow regular lock in real-time audio thread. 380 ASSERT(isMainThread()); 381 382 ThreadIdentifier thisThread = currentThread(); 383 384 if (thisThread == m_graphOwnerThread) { 385 // We already have the lock. 386 mustReleaseLock = false; 387 } else { 388 // Acquire the lock. 389 m_contextGraphMutex.lock(); 390 m_graphOwnerThread = thisThread; 391 mustReleaseLock = true; 392 } 393 } 394 395 bool AudioContext::tryLock(bool& mustReleaseLock) 396 { 397 ThreadIdentifier thisThread = currentThread(); 398 bool isAudioThread = thisThread == audioThread(); 399 400 // Try to catch cases of using try lock on main thread - it should use regular lock. 401 ASSERT(isAudioThread || isAudioThreadFinished()); 402 403 if (!isAudioThread) { 404 // In release build treat tryLock() as lock() (since above ASSERT(isAudioThread) never fires) - this is the best we can do. 405 lock(mustReleaseLock); 406 return true; 407 } 408 409 bool hasLock; 410 411 if (thisThread == m_graphOwnerThread) { 412 // Thread already has the lock. 413 hasLock = true; 414 mustReleaseLock = false; 415 } else { 416 // Don't already have the lock - try to acquire it. 417 hasLock = m_contextGraphMutex.tryLock(); 418 419 if (hasLock) 420 m_graphOwnerThread = thisThread; 421 422 mustReleaseLock = hasLock; 423 } 424 425 return hasLock; 426 } 427 428 void AudioContext::unlock() 429 { 430 ASSERT(currentThread() == m_graphOwnerThread); 431 432 m_graphOwnerThread = UndefinedThreadIdentifier; 433 m_contextGraphMutex.unlock(); 434 } 435 436 bool AudioContext::isAudioThread() const 437 { 438 return currentThread() == m_audioThread; 439 } 440 441 bool AudioContext::isGraphOwner() const 442 { 443 return currentThread() == m_graphOwnerThread; 444 } 445 446 void AudioContext::addDeferredFinishDeref(AudioNode* node, AudioNode::RefType refType) 447 { 448 ASSERT(isAudioThread()); 449 m_deferredFinishDerefList.append(AudioContext::RefInfo(node, refType)); 450 } 451 452 void AudioContext::handlePreRenderTasks() 453 { 454 ASSERT(isAudioThread()); 455 456 // At the beginning of every render quantum, try to update the internal rendering graph state (from main thread changes). 457 // It's OK if the tryLock() fails, we'll just take slightly longer to pick up the changes. 458 bool mustReleaseLock; 459 if (tryLock(mustReleaseLock)) { 460 // Fixup the state of any dirty AudioNodeInputs and AudioNodeOutputs. 461 handleDirtyAudioNodeInputs(); 462 handleDirtyAudioNodeOutputs(); 463 464 if (mustReleaseLock) 465 unlock(); 466 } 467 } 468 469 void AudioContext::handlePostRenderTasks() 470 { 471 ASSERT(isAudioThread()); 472 473 // Must use a tryLock() here too. Don't worry, the lock will very rarely be contended and this method is called frequently. 474 // The worst that can happen is that there will be some nodes which will take slightly longer than usual to be deleted or removed 475 // from the render graph (in which case they'll render silence). 476 bool mustReleaseLock; 477 if (tryLock(mustReleaseLock)) { 478 // Take care of finishing any derefs where the tryLock() failed previously. 479 handleDeferredFinishDerefs(); 480 481 // Dynamically clean up nodes which are no longer needed. 482 derefFinishedSourceNodes(); 483 484 // Finally actually delete. 485 deleteMarkedNodes(); 486 487 // Fixup the state of any dirty AudioNodeInputs and AudioNodeOutputs. 488 handleDirtyAudioNodeInputs(); 489 handleDirtyAudioNodeOutputs(); 490 491 if (mustReleaseLock) 492 unlock(); 493 } 494 } 495 496 void AudioContext::handleDeferredFinishDerefs() 497 { 498 ASSERT(isAudioThread() && isGraphOwner()); 499 for (unsigned i = 0; i < m_deferredFinishDerefList.size(); ++i) { 500 AudioNode* node = m_deferredFinishDerefList[i].m_node; 501 AudioNode::RefType refType = m_deferredFinishDerefList[i].m_refType; 502 node->finishDeref(refType); 503 } 504 505 m_deferredFinishDerefList.clear(); 506 } 507 508 void AudioContext::markForDeletion(AudioNode* node) 509 { 510 ASSERT(isGraphOwner()); 511 m_nodesToDelete.append(node); 512 } 513 514 void AudioContext::deleteMarkedNodes() 515 { 516 ASSERT(isGraphOwner() || isAudioThreadFinished()); 517 518 // Note: deleting an AudioNode can cause m_nodesToDelete to grow. 519 size_t nodesDeleted = 0; 520 while (size_t n = m_nodesToDelete.size()) { 521 AudioNode* node = m_nodesToDelete[n - 1]; 522 m_nodesToDelete.removeLast(); 523 524 // Before deleting the node, clear out any AudioNodeInputs from m_dirtyAudioNodeInputs. 525 unsigned numberOfInputs = node->numberOfInputs(); 526 for (unsigned i = 0; i < numberOfInputs; ++i) 527 m_dirtyAudioNodeInputs.remove(node->input(i)); 528 529 // Before deleting the node, clear out any AudioNodeOutputs from m_dirtyAudioNodeOutputs. 530 unsigned numberOfOutputs = node->numberOfOutputs(); 531 for (unsigned i = 0; i < numberOfOutputs; ++i) 532 m_dirtyAudioNodeOutputs.remove(node->output(i)); 533 534 // Finally, delete it. 535 delete node; 536 537 // Don't delete too many nodes per render quantum since we don't want to do too much work in the realtime audio thread. 538 if (++nodesDeleted > MaxNodesToDeletePerQuantum) 539 break; 540 } 541 } 542 543 void AudioContext::markAudioNodeInputDirty(AudioNodeInput* input) 544 { 545 ASSERT(isGraphOwner()); 546 m_dirtyAudioNodeInputs.add(input); 547 } 548 549 void AudioContext::markAudioNodeOutputDirty(AudioNodeOutput* output) 550 { 551 ASSERT(isGraphOwner()); 552 m_dirtyAudioNodeOutputs.add(output); 553 } 554 555 void AudioContext::handleDirtyAudioNodeInputs() 556 { 557 ASSERT(isGraphOwner()); 558 559 for (HashSet<AudioNodeInput*>::iterator i = m_dirtyAudioNodeInputs.begin(); i != m_dirtyAudioNodeInputs.end(); ++i) 560 (*i)->updateRenderingState(); 561 562 m_dirtyAudioNodeInputs.clear(); 563 } 564 565 void AudioContext::handleDirtyAudioNodeOutputs() 566 { 567 ASSERT(isGraphOwner()); 568 569 for (HashSet<AudioNodeOutput*>::iterator i = m_dirtyAudioNodeOutputs.begin(); i != m_dirtyAudioNodeOutputs.end(); ++i) 570 (*i)->updateRenderingState(); 571 572 m_dirtyAudioNodeOutputs.clear(); 573 } 574 575 ScriptExecutionContext* AudioContext::scriptExecutionContext() const 576 { 577 return document(); 578 } 579 580 AudioContext* AudioContext::toAudioContext() 581 { 582 return this; 583 } 584 585 void AudioContext::startRendering() 586 { 587 destination()->startRendering(); 588 } 589 590 void AudioContext::fireCompletionEvent() 591 { 592 ASSERT(isMainThread()); 593 if (!isMainThread()) 594 return; 595 596 AudioBuffer* renderedBuffer = m_renderTarget.get(); 597 598 ASSERT(renderedBuffer); 599 if (!renderedBuffer) 600 return; 601 602 // Avoid firing the event if the document has already gone away. 603 if (hasDocument()) { 604 // Call the offline rendering completion event listener. 605 dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer)); 606 } 607 } 608 609 } // namespace WebCore 610 611 #endif // ENABLE(WEB_AUDIO) 612