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 "modules/webaudio/AudioContext.h" 30 31 #include "bindings/v8/ExceptionMessages.h" 32 #include "bindings/v8/ExceptionState.h" 33 #include "core/dom/Document.h" 34 #include "core/dom/ExceptionCode.h" 35 #include "core/html/HTMLMediaElement.h" 36 #include "core/inspector/ScriptCallStack.h" 37 #include "platform/audio/FFTFrame.h" 38 #include "platform/audio/HRTFPanner.h" 39 #include "modules/mediastream/MediaStream.h" 40 #include "modules/webaudio/AnalyserNode.h" 41 #include "modules/webaudio/AudioBuffer.h" 42 #include "modules/webaudio/AudioBufferCallback.h" 43 #include "modules/webaudio/AudioBufferSourceNode.h" 44 #include "modules/webaudio/AudioListener.h" 45 #include "modules/webaudio/AudioNodeInput.h" 46 #include "modules/webaudio/AudioNodeOutput.h" 47 #include "modules/webaudio/BiquadFilterNode.h" 48 #include "modules/webaudio/ChannelMergerNode.h" 49 #include "modules/webaudio/ChannelSplitterNode.h" 50 #include "modules/webaudio/ConvolverNode.h" 51 #include "modules/webaudio/DefaultAudioDestinationNode.h" 52 #include "modules/webaudio/DelayNode.h" 53 #include "modules/webaudio/DynamicsCompressorNode.h" 54 #include "modules/webaudio/GainNode.h" 55 #include "modules/webaudio/MediaElementAudioSourceNode.h" 56 #include "modules/webaudio/MediaStreamAudioDestinationNode.h" 57 #include "modules/webaudio/MediaStreamAudioSourceNode.h" 58 #include "modules/webaudio/OfflineAudioCompletionEvent.h" 59 #include "modules/webaudio/OfflineAudioContext.h" 60 #include "modules/webaudio/OfflineAudioDestinationNode.h" 61 #include "modules/webaudio/OscillatorNode.h" 62 #include "modules/webaudio/PannerNode.h" 63 #include "modules/webaudio/PeriodicWave.h" 64 #include "modules/webaudio/ScriptProcessorNode.h" 65 #include "modules/webaudio/WaveShaperNode.h" 66 67 #if DEBUG_AUDIONODE_REFERENCES 68 #include <stdio.h> 69 #endif 70 71 #include "wtf/ArrayBuffer.h" 72 #include "wtf/Atomics.h" 73 #include "wtf/PassOwnPtr.h" 74 #include "wtf/text/WTFString.h" 75 76 // FIXME: check the proper way to reference an undefined thread ID 77 const int UndefinedThreadIdentifier = 0xffffffff; 78 79 namespace WebCore { 80 81 bool AudioContext::isSampleRateRangeGood(float sampleRate) 82 { 83 // FIXME: It would be nice if the minimum sample-rate could be less than 44.1KHz, 84 // but that will require some fixes in HRTFPanner::fftSizeForSampleRate(), and some testing there. 85 return sampleRate >= 44100 && sampleRate <= 96000; 86 } 87 88 // Don't allow more than this number of simultaneous AudioContexts talking to hardware. 89 const unsigned MaxHardwareContexts = 6; 90 unsigned AudioContext::s_hardwareContextCount = 0; 91 92 PassRefPtrWillBeRawPtr<AudioContext> AudioContext::create(Document& document, ExceptionState& exceptionState) 93 { 94 ASSERT(isMainThread()); 95 if (s_hardwareContextCount >= MaxHardwareContexts) { 96 exceptionState.throwDOMException( 97 SyntaxError, 98 "number of hardware contexts reached maximum (" + String::number(MaxHardwareContexts) + ")."); 99 return nullptr; 100 } 101 102 RefPtrWillBeRawPtr<AudioContext> audioContext(adoptRefWillBeThreadSafeRefCountedGarbageCollected(new AudioContext(&document))); 103 audioContext->suspendIfNeeded(); 104 return audioContext.release(); 105 } 106 107 // Constructor for rendering to the audio hardware. 108 AudioContext::AudioContext(Document* document) 109 : ActiveDOMObject(document) 110 , m_isStopScheduled(false) 111 , m_isCleared(false) 112 , m_isInitialized(false) 113 , m_destinationNode(nullptr) 114 , m_isDeletionScheduled(false) 115 , m_automaticPullNodesNeedUpdating(false) 116 , m_connectionCount(0) 117 , m_audioThread(0) 118 , m_graphOwnerThread(UndefinedThreadIdentifier) 119 , m_isOfflineContext(false) 120 { 121 ScriptWrappable::init(this); 122 123 m_destinationNode = DefaultAudioDestinationNode::create(this); 124 125 initialize(); 126 } 127 128 // Constructor for offline (non-realtime) rendering. 129 AudioContext::AudioContext(Document* document, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate) 130 : ActiveDOMObject(document) 131 , m_isStopScheduled(false) 132 , m_isCleared(false) 133 , m_isInitialized(false) 134 , m_destinationNode(nullptr) 135 , m_isDeletionScheduled(false) 136 , m_automaticPullNodesNeedUpdating(false) 137 , m_connectionCount(0) 138 , m_audioThread(0) 139 , m_graphOwnerThread(UndefinedThreadIdentifier) 140 , m_isOfflineContext(true) 141 { 142 ScriptWrappable::init(this); 143 144 // Create a new destination for offline rendering. 145 m_renderTarget = AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate); 146 if (m_renderTarget.get()) 147 m_destinationNode = OfflineAudioDestinationNode::create(this, m_renderTarget.get()); 148 149 initialize(); 150 } 151 152 AudioContext::~AudioContext() 153 { 154 #if DEBUG_AUDIONODE_REFERENCES 155 fprintf(stderr, "%p: AudioContext::~AudioContext()\n", this); 156 #endif 157 // AudioNodes keep a reference to their context, so there should be no way to be in the destructor if there are still AudioNodes around. 158 ASSERT(!m_isInitialized); 159 ASSERT(!m_nodesToDelete.size()); 160 ASSERT(!m_referencedNodes.size()); 161 ASSERT(!m_finishedNodes.size()); 162 ASSERT(!m_automaticPullNodes.size()); 163 if (m_automaticPullNodesNeedUpdating) 164 m_renderingAutomaticPullNodes.resize(m_automaticPullNodes.size()); 165 ASSERT(!m_renderingAutomaticPullNodes.size()); 166 } 167 168 void AudioContext::initialize() 169 { 170 if (isInitialized()) 171 return; 172 173 FFTFrame::initialize(); 174 m_listener = AudioListener::create(); 175 176 if (m_destinationNode.get()) { 177 m_destinationNode->initialize(); 178 179 if (!isOfflineContext()) { 180 // This starts the audio thread. The destination node's provideInput() method will now be called repeatedly to render audio. 181 // Each time provideInput() is called, a portion of the audio stream is rendered. Let's call this time period a "render quantum". 182 // NOTE: for now default AudioContext does not need an explicit startRendering() call from JavaScript. 183 // We may want to consider requiring it for symmetry with OfflineAudioContext. 184 m_destinationNode->startRendering(); 185 ++s_hardwareContextCount; 186 } 187 188 m_isInitialized = true; 189 } 190 } 191 192 void AudioContext::clear() 193 { 194 // 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. 195 if (m_destinationNode) 196 m_destinationNode.clear(); 197 198 // Audio thread is dead. Nobody will schedule node deletion action. Let's do it ourselves. 199 do { 200 m_nodesToDelete.appendVector(m_nodesMarkedForDeletion); 201 m_nodesMarkedForDeletion.clear(); 202 deleteMarkedNodes(); 203 } while (m_nodesToDelete.size()); 204 205 m_isCleared = true; 206 } 207 208 void AudioContext::uninitialize() 209 { 210 ASSERT(isMainThread()); 211 212 if (!isInitialized()) 213 return; 214 215 // This stops the audio thread and all audio rendering. 216 m_destinationNode->uninitialize(); 217 218 if (!isOfflineContext()) { 219 ASSERT(s_hardwareContextCount); 220 --s_hardwareContextCount; 221 } 222 223 // Get rid of the sources which may still be playing. 224 derefUnfinishedSourceNodes(); 225 226 m_isInitialized = false; 227 } 228 229 void AudioContext::stopDispatch(void* userData) 230 { 231 AudioContext* context = reinterpret_cast<AudioContext*>(userData); 232 ASSERT(context); 233 if (!context) 234 return; 235 236 context->uninitialize(); 237 context->clear(); 238 } 239 240 void AudioContext::stop() 241 { 242 // Usually ExecutionContext calls stop twice. 243 if (m_isStopScheduled) 244 return; 245 m_isStopScheduled = true; 246 247 // Don't call uninitialize() immediately here because the ExecutionContext is in the middle 248 // of dealing with all of its ActiveDOMObjects at this point. uninitialize() can de-reference other 249 // ActiveDOMObjects so let's schedule uninitialize() to be called later. 250 // FIXME: see if there's a more direct way to handle this issue. 251 callOnMainThread(stopDispatch, this); 252 } 253 254 bool AudioContext::hasPendingActivity() const 255 { 256 // According to spec AudioContext must die only after page navigates. 257 return !m_isCleared; 258 } 259 260 PassRefPtrWillBeRawPtr<AudioBuffer> AudioContext::createBuffer(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate, ExceptionState& exceptionState) 261 { 262 RefPtrWillBeRawPtr<AudioBuffer> audioBuffer = AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate, exceptionState); 263 264 return audioBuffer; 265 } 266 267 void AudioContext::decodeAudioData(ArrayBuffer* audioData, PassOwnPtr<AudioBufferCallback> successCallback, PassOwnPtr<AudioBufferCallback> errorCallback, ExceptionState& exceptionState) 268 { 269 if (!audioData) { 270 exceptionState.throwDOMException( 271 SyntaxError, 272 "invalid ArrayBuffer for audioData."); 273 return; 274 } 275 m_audioDecoder.decodeAsync(audioData, sampleRate(), successCallback, errorCallback); 276 } 277 278 PassRefPtrWillBeRawPtr<AudioBufferSourceNode> AudioContext::createBufferSource() 279 { 280 ASSERT(isMainThread()); 281 RefPtrWillBeRawPtr<AudioBufferSourceNode> node = AudioBufferSourceNode::create(this, m_destinationNode->sampleRate()); 282 283 // Because this is an AudioScheduledSourceNode, the context keeps a reference until it has finished playing. 284 // When this happens, AudioScheduledSourceNode::finish() calls AudioContext::notifyNodeFinishedProcessing(). 285 refNode(node.get()); 286 287 return node; 288 } 289 290 PassRefPtrWillBeRawPtr<MediaElementAudioSourceNode> AudioContext::createMediaElementSource(HTMLMediaElement* mediaElement, ExceptionState& exceptionState) 291 { 292 ASSERT(isMainThread()); 293 if (!mediaElement) { 294 exceptionState.throwDOMException( 295 InvalidStateError, 296 "invalid HTMLMedialElement."); 297 return nullptr; 298 } 299 300 // First check if this media element already has a source node. 301 if (mediaElement->audioSourceNode()) { 302 exceptionState.throwDOMException( 303 InvalidStateError, 304 "invalid HTMLMediaElement."); 305 return nullptr; 306 } 307 308 RefPtrWillBeRawPtr<MediaElementAudioSourceNode> node = MediaElementAudioSourceNode::create(this, mediaElement); 309 310 mediaElement->setAudioSourceNode(node.get()); 311 312 refNode(node.get()); // context keeps reference until node is disconnected 313 return node; 314 } 315 316 PassRefPtrWillBeRawPtr<MediaStreamAudioSourceNode> AudioContext::createMediaStreamSource(MediaStream* mediaStream, ExceptionState& exceptionState) 317 { 318 ASSERT(isMainThread()); 319 if (!mediaStream) { 320 exceptionState.throwDOMException( 321 InvalidStateError, 322 "invalid MediaStream source"); 323 return nullptr; 324 } 325 326 MediaStreamTrackVector audioTracks = mediaStream->getAudioTracks(); 327 if (audioTracks.isEmpty()) { 328 exceptionState.throwDOMException( 329 InvalidStateError, 330 "MediaStream has no audio track"); 331 return nullptr; 332 } 333 334 // Use the first audio track in the media stream. 335 RefPtrWillBeRawPtr<MediaStreamTrack> audioTrack = audioTracks[0]; 336 OwnPtr<AudioSourceProvider> provider = audioTrack->createWebAudioSource(); 337 RefPtrWillBeRawPtr<MediaStreamAudioSourceNode> node = MediaStreamAudioSourceNode::create(this, mediaStream, audioTrack.get(), provider.release()); 338 339 // FIXME: Only stereo streams are supported right now. We should be able to accept multi-channel streams. 340 node->setFormat(2, sampleRate()); 341 342 refNode(node.get()); // context keeps reference until node is disconnected 343 return node; 344 } 345 346 PassRefPtrWillBeRawPtr<MediaStreamAudioDestinationNode> AudioContext::createMediaStreamDestination() 347 { 348 // Set number of output channels to stereo by default. 349 return MediaStreamAudioDestinationNode::create(this, 2); 350 } 351 352 PassRefPtrWillBeRawPtr<ScriptProcessorNode> AudioContext::createScriptProcessor(ExceptionState& exceptionState) 353 { 354 // Set number of input/output channels to stereo by default. 355 return createScriptProcessor(0, 2, 2, exceptionState); 356 } 357 358 PassRefPtrWillBeRawPtr<ScriptProcessorNode> AudioContext::createScriptProcessor(size_t bufferSize, ExceptionState& exceptionState) 359 { 360 // Set number of input/output channels to stereo by default. 361 return createScriptProcessor(bufferSize, 2, 2, exceptionState); 362 } 363 364 PassRefPtrWillBeRawPtr<ScriptProcessorNode> AudioContext::createScriptProcessor(size_t bufferSize, size_t numberOfInputChannels, ExceptionState& exceptionState) 365 { 366 // Set number of output channels to stereo by default. 367 return createScriptProcessor(bufferSize, numberOfInputChannels, 2, exceptionState); 368 } 369 370 PassRefPtrWillBeRawPtr<ScriptProcessorNode> AudioContext::createScriptProcessor(size_t bufferSize, size_t numberOfInputChannels, size_t numberOfOutputChannels, ExceptionState& exceptionState) 371 { 372 ASSERT(isMainThread()); 373 RefPtrWillBeRawPtr<ScriptProcessorNode> node = ScriptProcessorNode::create(this, m_destinationNode->sampleRate(), bufferSize, numberOfInputChannels, numberOfOutputChannels); 374 375 if (!node.get()) { 376 if (!numberOfInputChannels && !numberOfOutputChannels) { 377 exceptionState.throwDOMException( 378 IndexSizeError, 379 "number of input channels and output channels cannot both be zero."); 380 } else if (numberOfInputChannels > AudioContext::maxNumberOfChannels()) { 381 exceptionState.throwDOMException( 382 IndexSizeError, 383 "number of input channels (" + String::number(numberOfInputChannels) 384 + ") exceeds maximum (" 385 + String::number(AudioContext::maxNumberOfChannels()) + ")."); 386 } else if (numberOfOutputChannels > AudioContext::maxNumberOfChannels()) { 387 exceptionState.throwDOMException( 388 IndexSizeError, 389 "number of output channels (" + String::number(numberOfInputChannels) 390 + ") exceeds maximum (" 391 + String::number(AudioContext::maxNumberOfChannels()) + ")."); 392 } else { 393 exceptionState.throwDOMException( 394 IndexSizeError, 395 "buffer size (" + String::number(bufferSize) 396 + ") must be a power of two between 256 and 16384."); 397 } 398 return nullptr; 399 } 400 401 refNode(node.get()); // context keeps reference until we stop making javascript rendering callbacks 402 return node; 403 } 404 405 PassRefPtrWillBeRawPtr<BiquadFilterNode> AudioContext::createBiquadFilter() 406 { 407 ASSERT(isMainThread()); 408 return BiquadFilterNode::create(this, m_destinationNode->sampleRate()); 409 } 410 411 PassRefPtrWillBeRawPtr<WaveShaperNode> AudioContext::createWaveShaper() 412 { 413 ASSERT(isMainThread()); 414 return WaveShaperNode::create(this); 415 } 416 417 PassRefPtrWillBeRawPtr<PannerNode> AudioContext::createPanner() 418 { 419 ASSERT(isMainThread()); 420 return PannerNode::create(this, m_destinationNode->sampleRate()); 421 } 422 423 PassRefPtrWillBeRawPtr<ConvolverNode> AudioContext::createConvolver() 424 { 425 ASSERT(isMainThread()); 426 return ConvolverNode::create(this, m_destinationNode->sampleRate()); 427 } 428 429 PassRefPtrWillBeRawPtr<DynamicsCompressorNode> AudioContext::createDynamicsCompressor() 430 { 431 ASSERT(isMainThread()); 432 return DynamicsCompressorNode::create(this, m_destinationNode->sampleRate()); 433 } 434 435 PassRefPtrWillBeRawPtr<AnalyserNode> AudioContext::createAnalyser() 436 { 437 ASSERT(isMainThread()); 438 return AnalyserNode::create(this, m_destinationNode->sampleRate()); 439 } 440 441 PassRefPtrWillBeRawPtr<GainNode> AudioContext::createGain() 442 { 443 ASSERT(isMainThread()); 444 return GainNode::create(this, m_destinationNode->sampleRate()); 445 } 446 447 PassRefPtrWillBeRawPtr<DelayNode> AudioContext::createDelay(ExceptionState& exceptionState) 448 { 449 const double defaultMaxDelayTime = 1; 450 return createDelay(defaultMaxDelayTime, exceptionState); 451 } 452 453 PassRefPtrWillBeRawPtr<DelayNode> AudioContext::createDelay(double maxDelayTime, ExceptionState& exceptionState) 454 { 455 ASSERT(isMainThread()); 456 RefPtrWillBeRawPtr<DelayNode> node = DelayNode::create(this, m_destinationNode->sampleRate(), maxDelayTime, exceptionState); 457 if (exceptionState.hadException()) 458 return nullptr; 459 return node; 460 } 461 462 PassRefPtrWillBeRawPtr<ChannelSplitterNode> AudioContext::createChannelSplitter(ExceptionState& exceptionState) 463 { 464 const unsigned ChannelSplitterDefaultNumberOfOutputs = 6; 465 return createChannelSplitter(ChannelSplitterDefaultNumberOfOutputs, exceptionState); 466 } 467 468 PassRefPtrWillBeRawPtr<ChannelSplitterNode> AudioContext::createChannelSplitter(size_t numberOfOutputs, ExceptionState& exceptionState) 469 { 470 ASSERT(isMainThread()); 471 472 RefPtrWillBeRawPtr<ChannelSplitterNode> node = ChannelSplitterNode::create(this, m_destinationNode->sampleRate(), numberOfOutputs); 473 474 if (!node.get()) { 475 exceptionState.throwDOMException( 476 IndexSizeError, 477 "number of outputs (" + String::number(numberOfOutputs) 478 + ") must be between 1 and " 479 + String::number(AudioContext::maxNumberOfChannels()) + "."); 480 return nullptr; 481 } 482 483 return node; 484 } 485 486 PassRefPtrWillBeRawPtr<ChannelMergerNode> AudioContext::createChannelMerger(ExceptionState& exceptionState) 487 { 488 const unsigned ChannelMergerDefaultNumberOfInputs = 6; 489 return createChannelMerger(ChannelMergerDefaultNumberOfInputs, exceptionState); 490 } 491 492 PassRefPtrWillBeRawPtr<ChannelMergerNode> AudioContext::createChannelMerger(size_t numberOfInputs, ExceptionState& exceptionState) 493 { 494 ASSERT(isMainThread()); 495 496 RefPtrWillBeRawPtr<ChannelMergerNode> node = ChannelMergerNode::create(this, m_destinationNode->sampleRate(), numberOfInputs); 497 498 if (!node.get()) { 499 exceptionState.throwDOMException( 500 IndexSizeError, 501 "number of inputs (" + String::number(numberOfInputs) 502 + ") must be between 1 and " 503 + String::number(AudioContext::maxNumberOfChannels()) + "."); 504 return nullptr; 505 } 506 507 return node; 508 } 509 510 PassRefPtrWillBeRawPtr<OscillatorNode> AudioContext::createOscillator() 511 { 512 ASSERT(isMainThread()); 513 514 RefPtrWillBeRawPtr<OscillatorNode> node = OscillatorNode::create(this, m_destinationNode->sampleRate()); 515 516 // Because this is an AudioScheduledSourceNode, the context keeps a reference until it has finished playing. 517 // When this happens, AudioScheduledSourceNode::finish() calls AudioContext::notifyNodeFinishedProcessing(). 518 refNode(node.get()); 519 520 return node; 521 } 522 523 PassRefPtrWillBeRawPtr<PeriodicWave> AudioContext::createPeriodicWave(Float32Array* real, Float32Array* imag, ExceptionState& exceptionState) 524 { 525 ASSERT(isMainThread()); 526 527 if (!real) { 528 exceptionState.throwDOMException( 529 SyntaxError, 530 "invalid real array"); 531 return nullptr; 532 } 533 534 if (!imag) { 535 exceptionState.throwDOMException( 536 SyntaxError, 537 "invalid imaginary array"); 538 return nullptr; 539 } 540 541 if (real->length() != imag->length()) { 542 exceptionState.throwDOMException( 543 IndexSizeError, 544 "length of real array (" + String::number(real->length()) 545 + ") and length of imaginary array (" + String::number(imag->length()) 546 + ") must match."); 547 return nullptr; 548 } 549 550 if (real->length() > 4096) { 551 exceptionState.throwDOMException( 552 IndexSizeError, 553 "length of real array (" + String::number(real->length()) 554 + ") exceeds allowed maximum of 4096"); 555 return nullptr; 556 } 557 558 if (imag->length() > 4096) { 559 exceptionState.throwDOMException( 560 IndexSizeError, 561 "length of imaginary array (" + String::number(imag->length()) 562 + ") exceeds allowed maximum of 4096"); 563 return nullptr; 564 } 565 566 return PeriodicWave::create(sampleRate(), real, imag); 567 } 568 569 void AudioContext::notifyNodeFinishedProcessing(AudioNode* node) 570 { 571 ASSERT(isAudioThread()); 572 m_finishedNodes.append(node); 573 } 574 575 void AudioContext::derefFinishedSourceNodes() 576 { 577 ASSERT(isGraphOwner()); 578 ASSERT(isAudioThread()); 579 for (unsigned i = 0; i < m_finishedNodes.size(); i++) 580 derefNode(m_finishedNodes[i]); 581 582 m_finishedNodes.clear(); 583 } 584 585 void AudioContext::refNode(AudioNode* node) 586 { 587 ASSERT(isMainThread()); 588 AutoLocker locker(this); 589 590 node->ref(AudioNode::RefTypeConnection); 591 m_referencedNodes.append(node); 592 } 593 594 void AudioContext::derefNode(AudioNode* node) 595 { 596 ASSERT(isGraphOwner()); 597 598 node->deref(AudioNode::RefTypeConnection); 599 600 for (unsigned i = 0; i < m_referencedNodes.size(); ++i) { 601 if (node == m_referencedNodes[i]) { 602 m_referencedNodes.remove(i); 603 break; 604 } 605 } 606 } 607 608 void AudioContext::derefUnfinishedSourceNodes() 609 { 610 ASSERT(isMainThread()); 611 for (unsigned i = 0; i < m_referencedNodes.size(); ++i) 612 m_referencedNodes[i]->deref(AudioNode::RefTypeConnection); 613 614 m_referencedNodes.clear(); 615 } 616 617 void AudioContext::lock(bool& mustReleaseLock) 618 { 619 // Don't allow regular lock in real-time audio thread. 620 ASSERT(isMainThread()); 621 622 ThreadIdentifier thisThread = currentThread(); 623 624 if (thisThread == m_graphOwnerThread) { 625 // We already have the lock. 626 mustReleaseLock = false; 627 } else { 628 // Acquire the lock. 629 m_contextGraphMutex.lock(); 630 m_graphOwnerThread = thisThread; 631 mustReleaseLock = true; 632 } 633 } 634 635 bool AudioContext::tryLock(bool& mustReleaseLock) 636 { 637 ThreadIdentifier thisThread = currentThread(); 638 bool isAudioThread = thisThread == audioThread(); 639 640 // Try to catch cases of using try lock on main thread - it should use regular lock. 641 ASSERT(isAudioThread); 642 643 if (!isAudioThread) { 644 // In release build treat tryLock() as lock() (since above ASSERT(isAudioThread) never fires) - this is the best we can do. 645 lock(mustReleaseLock); 646 return true; 647 } 648 649 bool hasLock; 650 651 if (thisThread == m_graphOwnerThread) { 652 // Thread already has the lock. 653 hasLock = true; 654 mustReleaseLock = false; 655 } else { 656 // Don't already have the lock - try to acquire it. 657 hasLock = m_contextGraphMutex.tryLock(); 658 659 if (hasLock) 660 m_graphOwnerThread = thisThread; 661 662 mustReleaseLock = hasLock; 663 } 664 665 return hasLock; 666 } 667 668 void AudioContext::unlock() 669 { 670 ASSERT(currentThread() == m_graphOwnerThread); 671 672 m_graphOwnerThread = UndefinedThreadIdentifier; 673 m_contextGraphMutex.unlock(); 674 } 675 676 bool AudioContext::isAudioThread() const 677 { 678 return currentThread() == m_audioThread; 679 } 680 681 bool AudioContext::isGraphOwner() const 682 { 683 return currentThread() == m_graphOwnerThread; 684 } 685 686 void AudioContext::addDeferredFinishDeref(AudioNode* node) 687 { 688 ASSERT(isAudioThread()); 689 m_deferredFinishDerefList.append(node); 690 } 691 692 void AudioContext::handlePreRenderTasks() 693 { 694 ASSERT(isAudioThread()); 695 696 // At the beginning of every render quantum, try to update the internal rendering graph state (from main thread changes). 697 // It's OK if the tryLock() fails, we'll just take slightly longer to pick up the changes. 698 bool mustReleaseLock; 699 if (tryLock(mustReleaseLock)) { 700 // Fixup the state of any dirty AudioSummingJunctions and AudioNodeOutputs. 701 handleDirtyAudioSummingJunctions(); 702 handleDirtyAudioNodeOutputs(); 703 704 updateAutomaticPullNodes(); 705 706 if (mustReleaseLock) 707 unlock(); 708 } 709 } 710 711 void AudioContext::handlePostRenderTasks() 712 { 713 ASSERT(isAudioThread()); 714 715 // Must use a tryLock() here too. Don't worry, the lock will very rarely be contended and this method is called frequently. 716 // The worst that can happen is that there will be some nodes which will take slightly longer than usual to be deleted or removed 717 // from the render graph (in which case they'll render silence). 718 bool mustReleaseLock; 719 if (tryLock(mustReleaseLock)) { 720 // Take care of finishing any derefs where the tryLock() failed previously. 721 handleDeferredFinishDerefs(); 722 723 // Dynamically clean up nodes which are no longer needed. 724 derefFinishedSourceNodes(); 725 726 // Don't delete in the real-time thread. Let the main thread do it. 727 // Ref-counted objects held by certain AudioNodes may not be thread-safe. 728 scheduleNodeDeletion(); 729 730 // Fixup the state of any dirty AudioSummingJunctions and AudioNodeOutputs. 731 handleDirtyAudioSummingJunctions(); 732 handleDirtyAudioNodeOutputs(); 733 734 updateAutomaticPullNodes(); 735 736 if (mustReleaseLock) 737 unlock(); 738 } 739 } 740 741 void AudioContext::handleDeferredFinishDerefs() 742 { 743 ASSERT(isAudioThread() && isGraphOwner()); 744 for (unsigned i = 0; i < m_deferredFinishDerefList.size(); ++i) { 745 AudioNode* node = m_deferredFinishDerefList[i]; 746 node->finishDeref(AudioNode::RefTypeConnection); 747 } 748 749 m_deferredFinishDerefList.clear(); 750 } 751 752 void AudioContext::markForDeletion(AudioNode* node) 753 { 754 ASSERT(isGraphOwner()); 755 756 if (!isInitialized()) 757 m_nodesToDelete.append(node); 758 else 759 m_nodesMarkedForDeletion.append(node); 760 761 // This is probably the best time for us to remove the node from automatic pull list, 762 // since all connections are gone and we hold the graph lock. Then when handlePostRenderTasks() 763 // gets a chance to schedule the deletion work, updateAutomaticPullNodes() also gets a chance to 764 // modify m_renderingAutomaticPullNodes. 765 removeAutomaticPullNode(node); 766 } 767 768 void AudioContext::scheduleNodeDeletion() 769 { 770 bool isGood = isInitialized() && isGraphOwner(); 771 ASSERT(isGood); 772 if (!isGood) 773 return; 774 775 // Make sure to call deleteMarkedNodes() on main thread. 776 if (m_nodesMarkedForDeletion.size() && !m_isDeletionScheduled) { 777 m_nodesToDelete.appendVector(m_nodesMarkedForDeletion); 778 m_nodesMarkedForDeletion.clear(); 779 780 m_isDeletionScheduled = true; 781 782 // Don't let ourself get deleted before the callback. 783 // See matching deref() in deleteMarkedNodesDispatch(). 784 ref(); 785 callOnMainThread(deleteMarkedNodesDispatch, this); 786 } 787 } 788 789 void AudioContext::deleteMarkedNodesDispatch(void* userData) 790 { 791 AudioContext* context = reinterpret_cast<AudioContext*>(userData); 792 ASSERT(context); 793 if (!context) 794 return; 795 796 context->deleteMarkedNodes(); 797 context->deref(); 798 } 799 800 void AudioContext::deleteMarkedNodes() 801 { 802 ASSERT(isMainThread()); 803 804 // Protect this object from being deleted before we release the mutex locked by AutoLocker. 805 RefPtrWillBeRawPtr<AudioContext> protect(this); 806 { 807 AutoLocker locker(this); 808 809 while (size_t n = m_nodesToDelete.size()) { 810 AudioNode* node = m_nodesToDelete[n - 1]; 811 m_nodesToDelete.removeLast(); 812 813 // Before deleting the node, clear out any AudioNodeInputs from m_dirtySummingJunctions. 814 unsigned numberOfInputs = node->numberOfInputs(); 815 for (unsigned i = 0; i < numberOfInputs; ++i) 816 m_dirtySummingJunctions.remove(node->input(i)); 817 818 // Before deleting the node, clear out any AudioNodeOutputs from m_dirtyAudioNodeOutputs. 819 unsigned numberOfOutputs = node->numberOfOutputs(); 820 for (unsigned i = 0; i < numberOfOutputs; ++i) 821 m_dirtyAudioNodeOutputs.remove(node->output(i)); 822 #if ENABLE(OILPAN) 823 // Finally, clear the keep alive handle that keeps this 824 // object from being collected. 825 node->clearKeepAlive(); 826 #else 827 // Finally, delete it. 828 delete node; 829 #endif 830 } 831 m_isDeletionScheduled = false; 832 } 833 } 834 835 void AudioContext::markSummingJunctionDirty(AudioSummingJunction* summingJunction) 836 { 837 ASSERT(isGraphOwner()); 838 m_dirtySummingJunctions.add(summingJunction); 839 } 840 841 void AudioContext::removeMarkedSummingJunction(AudioSummingJunction* summingJunction) 842 { 843 ASSERT(isMainThread()); 844 AutoLocker locker(this); 845 m_dirtySummingJunctions.remove(summingJunction); 846 } 847 848 void AudioContext::markAudioNodeOutputDirty(AudioNodeOutput* output) 849 { 850 ASSERT(isGraphOwner()); 851 m_dirtyAudioNodeOutputs.add(output); 852 } 853 854 void AudioContext::handleDirtyAudioSummingJunctions() 855 { 856 ASSERT(isGraphOwner()); 857 858 for (HashSet<AudioSummingJunction* >::iterator i = m_dirtySummingJunctions.begin(); i != m_dirtySummingJunctions.end(); ++i) 859 (*i)->updateRenderingState(); 860 861 m_dirtySummingJunctions.clear(); 862 } 863 864 void AudioContext::handleDirtyAudioNodeOutputs() 865 { 866 ASSERT(isGraphOwner()); 867 868 for (HashSet<AudioNodeOutput*>::iterator i = m_dirtyAudioNodeOutputs.begin(); i != m_dirtyAudioNodeOutputs.end(); ++i) 869 (*i)->updateRenderingState(); 870 871 m_dirtyAudioNodeOutputs.clear(); 872 } 873 874 void AudioContext::addAutomaticPullNode(AudioNode* node) 875 { 876 ASSERT(isGraphOwner()); 877 878 if (!m_automaticPullNodes.contains(node)) { 879 m_automaticPullNodes.add(node); 880 m_automaticPullNodesNeedUpdating = true; 881 } 882 } 883 884 void AudioContext::removeAutomaticPullNode(AudioNode* node) 885 { 886 ASSERT(isGraphOwner()); 887 888 if (m_automaticPullNodes.contains(node)) { 889 m_automaticPullNodes.remove(node); 890 m_automaticPullNodesNeedUpdating = true; 891 } 892 } 893 894 void AudioContext::updateAutomaticPullNodes() 895 { 896 ASSERT(isGraphOwner()); 897 898 if (m_automaticPullNodesNeedUpdating) { 899 // Copy from m_automaticPullNodes to m_renderingAutomaticPullNodes. 900 m_renderingAutomaticPullNodes.resize(m_automaticPullNodes.size()); 901 902 unsigned j = 0; 903 for (HashSet<AudioNode*>::iterator i = m_automaticPullNodes.begin(); i != m_automaticPullNodes.end(); ++i, ++j) { 904 AudioNode* output = *i; 905 m_renderingAutomaticPullNodes[j] = output; 906 } 907 908 m_automaticPullNodesNeedUpdating = false; 909 } 910 } 911 912 void AudioContext::processAutomaticPullNodes(size_t framesToProcess) 913 { 914 ASSERT(isAudioThread()); 915 916 for (unsigned i = 0; i < m_renderingAutomaticPullNodes.size(); ++i) 917 m_renderingAutomaticPullNodes[i]->processIfNecessary(framesToProcess); 918 } 919 920 const AtomicString& AudioContext::interfaceName() const 921 { 922 return EventTargetNames::AudioContext; 923 } 924 925 ExecutionContext* AudioContext::executionContext() const 926 { 927 return m_isStopScheduled ? 0 : ActiveDOMObject::executionContext(); 928 } 929 930 void AudioContext::startRendering() 931 { 932 destination()->startRendering(); 933 } 934 935 void AudioContext::fireCompletionEvent() 936 { 937 ASSERT(isMainThread()); 938 if (!isMainThread()) 939 return; 940 941 AudioBuffer* renderedBuffer = m_renderTarget.get(); 942 943 ASSERT(renderedBuffer); 944 if (!renderedBuffer) 945 return; 946 947 // Avoid firing the event if the document has already gone away. 948 if (executionContext()) { 949 // Call the offline rendering completion event listener. 950 dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer)); 951 } 952 } 953 954 void AudioContext::trace(Visitor* visitor) 955 { 956 visitor->trace(m_renderTarget); 957 visitor->trace(m_destinationNode); 958 visitor->trace(m_listener); 959 visitor->trace(m_dirtySummingJunctions); 960 EventTargetWithInlineData::trace(visitor); 961 } 962 963 } // namespace WebCore 964 965 #endif // ENABLE(WEB_AUDIO) 966