| 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 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 112 return adoptRef(new AudioContext(document, numberOfChannels, numberOfFrames,
sampleRate)); | 112 return adoptRef(new AudioContext(document, numberOfChannels, numberOfFrames,
sampleRate)); |
| 113 } | 113 } |
| 114 | 114 |
| 115 // Constructor for rendering to the audio hardware. | 115 // Constructor for rendering to the audio hardware. |
| 116 AudioContext::AudioContext(Document* document) | 116 AudioContext::AudioContext(Document* document) |
| 117 : ActiveDOMObject(document, this) | 117 : ActiveDOMObject(document, this) |
| 118 , m_isInitialized(false) | 118 , m_isInitialized(false) |
| 119 , m_isAudioThreadFinished(false) | 119 , m_isAudioThreadFinished(false) |
| 120 , m_document(document) | 120 , m_document(document) |
| 121 , m_destinationNode(0) | 121 , m_destinationNode(0) |
| 122 , m_isDeletionScheduled(false) |
| 122 , m_connectionCount(0) | 123 , m_connectionCount(0) |
| 123 , m_audioThread(0) | 124 , m_audioThread(0) |
| 124 , m_graphOwnerThread(UndefinedThreadIdentifier) | 125 , m_graphOwnerThread(UndefinedThreadIdentifier) |
| 125 , m_isOfflineContext(false) | 126 , m_isOfflineContext(false) |
| 126 { | 127 { |
| 127 constructCommon(); | 128 constructCommon(); |
| 128 | 129 |
| 129 m_destinationNode = DefaultAudioDestinationNode::create(this); | 130 m_destinationNode = DefaultAudioDestinationNode::create(this); |
| 130 | 131 |
| 131 // This sets in motion an asynchronous loading mechanism on another thread. | 132 // This sets in motion an asynchronous loading mechanism on another thread. |
| (...skipping 20 matching lines...) Expand all Loading... |
| 152 // FIXME: the passed in sampleRate MUST match the hardware sample-rate since
HRTFDatabaseLoader is a singleton. | 153 // FIXME: the passed in sampleRate MUST match the hardware sample-rate since
HRTFDatabaseLoader is a singleton. |
| 153 m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNece
ssary(sampleRate); | 154 m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNece
ssary(sampleRate); |
| 154 | 155 |
| 155 // Create a new destination for offline rendering. | 156 // Create a new destination for offline rendering. |
| 156 m_renderTarget = AudioBuffer::create(numberOfChannels, numberOfFrames, sampl
eRate); | 157 m_renderTarget = AudioBuffer::create(numberOfChannels, numberOfFrames, sampl
eRate); |
| 157 m_destinationNode = OfflineAudioDestinationNode::create(this, m_renderTarget
.get()); | 158 m_destinationNode = OfflineAudioDestinationNode::create(this, m_renderTarget
.get()); |
| 158 } | 159 } |
| 159 | 160 |
| 160 void AudioContext::constructCommon() | 161 void AudioContext::constructCommon() |
| 161 { | 162 { |
| 162 // Note: because adoptRef() won't be called until we leave this constructor,
but code in this constructor needs to reference this context, | |
| 163 // relax the check. | |
| 164 relaxAdoptionRequirement(); | |
| 165 | |
| 166 FFTFrame::initialize(); | 163 FFTFrame::initialize(); |
| 167 | 164 |
| 168 m_listener = AudioListener::create(); | 165 m_listener = AudioListener::create(); |
| 169 m_temporaryMonoBus = adoptPtr(new AudioBus(1, AudioNode::ProcessingSizeInFra
mes)); | 166 m_temporaryMonoBus = adoptPtr(new AudioBus(1, AudioNode::ProcessingSizeInFra
mes)); |
| 170 m_temporaryStereoBus = adoptPtr(new AudioBus(2, AudioNode::ProcessingSizeInF
rames)); | 167 m_temporaryStereoBus = adoptPtr(new AudioBus(2, AudioNode::ProcessingSizeInF
rames)); |
| 171 } | 168 } |
| 172 | 169 |
| 173 AudioContext::~AudioContext() | 170 AudioContext::~AudioContext() |
| 174 { | 171 { |
| 175 #if DEBUG_AUDIONODE_REFERENCES | 172 #if DEBUG_AUDIONODE_REFERENCES |
| 176 printf("%p: AudioContext::~AudioContext()\n", this); | 173 printf("%p: AudioContext::~AudioContext()\n", this); |
| 177 #endif | 174 #endif |
| 178 // 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. | 175 // 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. |
| 179 ASSERT(!m_nodesToDelete.size()); | 176 ASSERT(!m_nodesToDelete.size()); |
| 180 ASSERT(!m_referencedNodes.size()); | 177 ASSERT(!m_referencedNodes.size()); |
| 181 ASSERT(!m_finishedNodes.size()); | 178 ASSERT(!m_finishedNodes.size()); |
| 182 } | 179 } |
| 183 | 180 |
| 184 void AudioContext::lazyInitialize() | 181 void AudioContext::lazyInitialize() |
| 185 { | 182 { |
| 186 if (!m_isInitialized) { | 183 if (!m_isInitialized) { |
| 187 // Don't allow the context to initialize a second time after it's alread
y been explicitly uninitialized. | 184 // Don't allow the context to initialize a second time after it's alread
y been explicitly uninitialized. |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 219 // We have to release our reference to the destination node before the c
ontext will ever be deleted since the destination node holds a reference to the
context. | 216 // We have to release our reference to the destination node before the c
ontext will ever be deleted since the destination node holds a reference to the
context. |
| 220 m_destinationNode.clear(); | 217 m_destinationNode.clear(); |
| 221 | 218 |
| 222 if (!isOfflineContext()) { | 219 if (!isOfflineContext()) { |
| 223 ASSERT(s_hardwareContextCount); | 220 ASSERT(s_hardwareContextCount); |
| 224 --s_hardwareContextCount; | 221 --s_hardwareContextCount; |
| 225 } | 222 } |
| 226 | 223 |
| 227 // Get rid of the sources which may still be playing. | 224 // Get rid of the sources which may still be playing. |
| 228 derefUnfinishedSourceNodes(); | 225 derefUnfinishedSourceNodes(); |
| 229 | 226 |
| 227 deleteMarkedNodes(); |
| 228 |
| 230 // Because the AudioBuffers are garbage collected, we can't delete them
here. | 229 // Because the AudioBuffers are garbage collected, we can't delete them
here. |
| 231 // Instead, at least release the potentially large amount of allocated m
emory for the audio data. | 230 // Instead, at least release the potentially large amount of allocated m
emory for the audio data. |
| 232 // Note that we do this *after* the context is uninitialized and stops p
rocessing audio. | 231 // Note that we do this *after* the context is uninitialized and stops p
rocessing audio. |
| 233 for (unsigned i = 0; i < m_allocatedBuffers.size(); ++i) | 232 for (unsigned i = 0; i < m_allocatedBuffers.size(); ++i) |
| 234 m_allocatedBuffers[i]->releaseMemory(); | 233 m_allocatedBuffers[i]->releaseMemory(); |
| 235 m_allocatedBuffers.clear(); | 234 m_allocatedBuffers.clear(); |
| 236 | 235 |
| 237 m_isInitialized = false; | 236 m_isInitialized = false; |
| 238 } | 237 } |
| 239 } | 238 } |
| (...skipping 318 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 558 // The worst that can happen is that there will be some nodes which will tak
e slightly longer than usual to be deleted or removed | 557 // The worst that can happen is that there will be some nodes which will tak
e slightly longer than usual to be deleted or removed |
| 559 // from the render graph (in which case they'll render silence). | 558 // from the render graph (in which case they'll render silence). |
| 560 bool mustReleaseLock; | 559 bool mustReleaseLock; |
| 561 if (tryLock(mustReleaseLock)) { | 560 if (tryLock(mustReleaseLock)) { |
| 562 // Take care of finishing any derefs where the tryLock() failed previous
ly. | 561 // Take care of finishing any derefs where the tryLock() failed previous
ly. |
| 563 handleDeferredFinishDerefs(); | 562 handleDeferredFinishDerefs(); |
| 564 | 563 |
| 565 // Dynamically clean up nodes which are no longer needed. | 564 // Dynamically clean up nodes which are no longer needed. |
| 566 derefFinishedSourceNodes(); | 565 derefFinishedSourceNodes(); |
| 567 | 566 |
| 568 // Finally actually delete. | 567 // Don't delete in the real-time thread. Let the main thread do it. |
| 569 deleteMarkedNodes(); | 568 // Ref-counted objects held by certain AudioNodes may not be thread-safe
. |
| 569 scheduleNodeDeletion(); |
| 570 | 570 |
| 571 // Fixup the state of any dirty AudioNodeInputs and AudioNodeOutputs. | 571 // Fixup the state of any dirty AudioNodeInputs and AudioNodeOutputs. |
| 572 handleDirtyAudioNodeInputs(); | 572 handleDirtyAudioNodeInputs(); |
| 573 handleDirtyAudioNodeOutputs(); | 573 handleDirtyAudioNodeOutputs(); |
| 574 | 574 |
| 575 if (mustReleaseLock) | 575 if (mustReleaseLock) |
| 576 unlock(); | 576 unlock(); |
| 577 } | 577 } |
| 578 } | 578 } |
| 579 | 579 |
| 580 void AudioContext::handleDeferredFinishDerefs() | 580 void AudioContext::handleDeferredFinishDerefs() |
| 581 { | 581 { |
| 582 ASSERT(isAudioThread() && isGraphOwner()); | 582 ASSERT(isAudioThread() && isGraphOwner()); |
| 583 for (unsigned i = 0; i < m_deferredFinishDerefList.size(); ++i) { | 583 for (unsigned i = 0; i < m_deferredFinishDerefList.size(); ++i) { |
| 584 AudioNode* node = m_deferredFinishDerefList[i].m_node; | 584 AudioNode* node = m_deferredFinishDerefList[i].m_node; |
| 585 AudioNode::RefType refType = m_deferredFinishDerefList[i].m_refType; | 585 AudioNode::RefType refType = m_deferredFinishDerefList[i].m_refType; |
| 586 node->finishDeref(refType); | 586 node->finishDeref(refType); |
| 587 } | 587 } |
| 588 | 588 |
| 589 m_deferredFinishDerefList.clear(); | 589 m_deferredFinishDerefList.clear(); |
| 590 } | 590 } |
| 591 | 591 |
| 592 void AudioContext::markForDeletion(AudioNode* node) | 592 void AudioContext::markForDeletion(AudioNode* node) |
| 593 { | 593 { |
| 594 ASSERT(isGraphOwner()); | 594 ASSERT(isGraphOwner()); |
| 595 m_nodesToDelete.append(node); | 595 m_nodesToDelete.append(node); |
| 596 } | 596 } |
| 597 | 597 |
| 598 void AudioContext::scheduleNodeDeletion() |
| 599 { |
| 600 bool isGood = m_isInitialized && isGraphOwner(); |
| 601 ASSERT(isGood); |
| 602 if (!isGood) |
| 603 return; |
| 604 |
| 605 // Make sure to call deleteMarkedNodes() on main thread. |
| 606 if (m_nodesToDelete.size() && !m_isDeletionScheduled) { |
| 607 m_isDeletionScheduled = true; |
| 608 |
| 609 // Don't let ourself get deleted before the callback. |
| 610 // See matching deref() in deleteMarkedNodesDispatch(). |
| 611 ref(); |
| 612 callOnMainThread(deleteMarkedNodesDispatch, this); |
| 613 } |
| 614 } |
| 615 |
| 616 void AudioContext::deleteMarkedNodesDispatch(void* userData) |
| 617 { |
| 618 AudioContext* context = reinterpret_cast<AudioContext*>(userData); |
| 619 ASSERT(context); |
| 620 if (!context) |
| 621 return; |
| 622 |
| 623 context->deleteMarkedNodes(); |
| 624 context->deref(); |
| 625 } |
| 626 |
| 598 void AudioContext::deleteMarkedNodes() | 627 void AudioContext::deleteMarkedNodes() |
| 599 { | 628 { |
| 600 ASSERT(isGraphOwner() || isAudioThreadFinished()); | 629 ASSERT(isMainThread()); |
| 601 | 630 |
| 631 AutoLocker locker(this); |
| 632 |
| 602 // Note: deleting an AudioNode can cause m_nodesToDelete to grow. | 633 // Note: deleting an AudioNode can cause m_nodesToDelete to grow. |
| 603 size_t nodesDeleted = 0; | |
| 604 while (size_t n = m_nodesToDelete.size()) { | 634 while (size_t n = m_nodesToDelete.size()) { |
| 605 AudioNode* node = m_nodesToDelete[n - 1]; | 635 AudioNode* node = m_nodesToDelete[n - 1]; |
| 606 m_nodesToDelete.removeLast(); | 636 m_nodesToDelete.removeLast(); |
| 607 | 637 |
| 608 // Before deleting the node, clear out any AudioNodeInputs from m_dirtyA
udioNodeInputs. | 638 // Before deleting the node, clear out any AudioNodeInputs from m_dirtyA
udioNodeInputs. |
| 609 unsigned numberOfInputs = node->numberOfInputs(); | 639 unsigned numberOfInputs = node->numberOfInputs(); |
| 610 for (unsigned i = 0; i < numberOfInputs; ++i) | 640 for (unsigned i = 0; i < numberOfInputs; ++i) |
| 611 m_dirtyAudioNodeInputs.remove(node->input(i)); | 641 m_dirtyAudioNodeInputs.remove(node->input(i)); |
| 612 | 642 |
| 613 // Before deleting the node, clear out any AudioNodeOutputs from m_dirty
AudioNodeOutputs. | 643 // Before deleting the node, clear out any AudioNodeOutputs from m_dirty
AudioNodeOutputs. |
| 614 unsigned numberOfOutputs = node->numberOfOutputs(); | 644 unsigned numberOfOutputs = node->numberOfOutputs(); |
| 615 for (unsigned i = 0; i < numberOfOutputs; ++i) | 645 for (unsigned i = 0; i < numberOfOutputs; ++i) |
| 616 m_dirtyAudioNodeOutputs.remove(node->output(i)); | 646 m_dirtyAudioNodeOutputs.remove(node->output(i)); |
| 617 | 647 |
| 618 // Finally, delete it. | 648 // Finally, delete it. |
| 619 delete node; | 649 delete node; |
| 620 | |
| 621 // Don't delete too many nodes per render quantum since we don't want to
do too much work in the realtime audio thread. | |
| 622 if (++nodesDeleted > MaxNodesToDeletePerQuantum) | |
| 623 break; | |
| 624 } | 650 } |
| 651 |
| 652 m_isDeletionScheduled = false; |
| 625 } | 653 } |
| 626 | 654 |
| 627 void AudioContext::markAudioNodeInputDirty(AudioNodeInput* input) | 655 void AudioContext::markAudioNodeInputDirty(AudioNodeInput* input) |
| 628 { | 656 { |
| 629 ASSERT(isGraphOwner()); | 657 ASSERT(isGraphOwner()); |
| 630 m_dirtyAudioNodeInputs.add(input); | 658 m_dirtyAudioNodeInputs.add(input); |
| 631 } | 659 } |
| 632 | 660 |
| 633 void AudioContext::markAudioNodeOutputDirty(AudioNodeOutput* output) | 661 void AudioContext::markAudioNodeOutputDirty(AudioNodeOutput* output) |
| 634 { | 662 { |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 686 // Avoid firing the event if the document has already gone away. | 714 // Avoid firing the event if the document has already gone away. |
| 687 if (hasDocument()) { | 715 if (hasDocument()) { |
| 688 // Call the offline rendering completion event listener. | 716 // Call the offline rendering completion event listener. |
| 689 dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer)); | 717 dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer)); |
| 690 } | 718 } |
| 691 } | 719 } |
| 692 | 720 |
| 693 } // namespace WebCore | 721 } // namespace WebCore |
| 694 | 722 |
| 695 #endif // ENABLE(WEB_AUDIO) | 723 #endif // ENABLE(WEB_AUDIO) |
| OLD | NEW |