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 |