| 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 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 103 audioContext->suspendIfNeeded(); | 103 audioContext->suspendIfNeeded(); |
| 104 return audioContext.release(); | 104 return audioContext.release(); |
| 105 } | 105 } |
| 106 | 106 |
| 107 // Constructor for rendering to the audio hardware. | 107 // Constructor for rendering to the audio hardware. |
| 108 AudioContext::AudioContext(Document* document) | 108 AudioContext::AudioContext(Document* document) |
| 109 : ActiveDOMObject(document) | 109 : ActiveDOMObject(document) |
| 110 , m_isStopScheduled(false) | 110 , m_isStopScheduled(false) |
| 111 , m_isCleared(false) | 111 , m_isCleared(false) |
| 112 , m_isInitialized(false) | 112 , m_isInitialized(false) |
| 113 , m_isAudioThreadFinished(false) | |
| 114 , m_destinationNode(nullptr) | 113 , m_destinationNode(nullptr) |
| 115 , m_isDeletionScheduled(false) | 114 , m_isDeletionScheduled(false) |
| 116 , m_automaticPullNodesNeedUpdating(false) | 115 , m_automaticPullNodesNeedUpdating(false) |
| 117 , m_connectionCount(0) | 116 , m_connectionCount(0) |
| 118 , m_audioThread(0) | 117 , m_audioThread(0) |
| 119 , m_graphOwnerThread(UndefinedThreadIdentifier) | 118 , m_graphOwnerThread(UndefinedThreadIdentifier) |
| 120 , m_isOfflineContext(false) | 119 , m_isOfflineContext(false) |
| 121 { | 120 { |
| 121 ScriptWrappable::init(this); |
| 122 |
| 122 m_destinationNode = DefaultAudioDestinationNode::create(this); | 123 m_destinationNode = DefaultAudioDestinationNode::create(this); |
| 123 | 124 |
| 124 constructCommon(); | 125 initialize(); |
| 125 } | 126 } |
| 126 | 127 |
| 127 // Constructor for offline (non-realtime) rendering. | 128 // Constructor for offline (non-realtime) rendering. |
| 128 AudioContext::AudioContext(Document* document, unsigned numberOfChannels, size_t
numberOfFrames, float sampleRate) | 129 AudioContext::AudioContext(Document* document, unsigned numberOfChannels, size_t
numberOfFrames, float sampleRate) |
| 129 : ActiveDOMObject(document) | 130 : ActiveDOMObject(document) |
| 130 , m_isStopScheduled(false) | 131 , m_isStopScheduled(false) |
| 131 , m_isCleared(false) | 132 , m_isCleared(false) |
| 132 , m_isInitialized(false) | 133 , m_isInitialized(false) |
| 133 , m_isAudioThreadFinished(false) | |
| 134 , m_destinationNode(nullptr) | 134 , m_destinationNode(nullptr) |
| 135 , m_automaticPullNodesNeedUpdating(false) | 135 , m_automaticPullNodesNeedUpdating(false) |
| 136 , m_connectionCount(0) | 136 , m_connectionCount(0) |
| 137 , m_audioThread(0) | 137 , m_audioThread(0) |
| 138 , m_graphOwnerThread(UndefinedThreadIdentifier) | 138 , m_graphOwnerThread(UndefinedThreadIdentifier) |
| 139 , m_isOfflineContext(true) | 139 , m_isOfflineContext(true) |
| 140 { | 140 { |
| 141 ScriptWrappable::init(this); |
| 142 |
| 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 constructCommon(); | |
| 147 } | |
| 148 | |
| 149 void AudioContext::constructCommon() | |
| 150 { | |
| 151 ScriptWrappable::init(this); | |
| 152 | |
| 153 FFTFrame::initialize(); | |
| 154 | |
| 155 m_listener = AudioListener::create(); | |
| 156 | |
| 157 initialize(); | 148 initialize(); |
| 158 } | 149 } |
| 159 | 150 |
| 160 AudioContext::~AudioContext() | 151 AudioContext::~AudioContext() |
| 161 { | 152 { |
| 162 #if DEBUG_AUDIONODE_REFERENCES | 153 #if DEBUG_AUDIONODE_REFERENCES |
| 163 fprintf(stderr, "%p: AudioContext::~AudioContext()\n", this); | 154 fprintf(stderr, "%p: AudioContext::~AudioContext()\n", this); |
| 164 #endif | 155 #endif |
| 165 // 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. |
| 166 ASSERT(!m_isInitialized); | 157 ASSERT(!m_isInitialized); |
| 167 ASSERT(!m_nodesToDelete.size()); | 158 ASSERT(!m_nodesToDelete.size()); |
| 168 ASSERT(!m_referencedNodes.size()); | 159 ASSERT(!m_referencedNodes.size()); |
| 169 ASSERT(!m_finishedNodes.size()); | 160 ASSERT(!m_finishedNodes.size()); |
| 170 ASSERT(!m_automaticPullNodes.size()); | 161 ASSERT(!m_automaticPullNodes.size()); |
| 171 if (m_automaticPullNodesNeedUpdating) | 162 if (m_automaticPullNodesNeedUpdating) |
| 172 m_renderingAutomaticPullNodes.resize(m_automaticPullNodes.size()); | 163 m_renderingAutomaticPullNodes.resize(m_automaticPullNodes.size()); |
| 173 ASSERT(!m_renderingAutomaticPullNodes.size()); | 164 ASSERT(!m_renderingAutomaticPullNodes.size()); |
| 174 } | 165 } |
| 175 | 166 |
| 176 void AudioContext::initialize() | 167 void AudioContext::initialize() |
| 177 { | 168 { |
| 178 if (!m_isInitialized) { | 169 if (isInitialized()) |
| 179 // Don't allow the context to initialize a second time after it's alread
y been explicitly uninitialized. | 170 return; |
| 180 ASSERT(!m_isAudioThreadFinished); | |
| 181 if (!m_isAudioThreadFinished) { | |
| 182 // Creation of a destination node should not start the audio HW. The | |
| 183 // creation of any other AudioNode will initialize the audio HW and
start processing | |
| 184 if (m_destinationNode.get()) { | |
| 185 m_destinationNode->initialize(); | |
| 186 | 171 |
| 187 if (!isOfflineContext()) { | 172 FFTFrame::initialize(); |
| 188 // This starts the audio thread. The destination node's prov
ideInput() method will now be called repeatedly to render audio. | 173 m_listener = AudioListener::create(); |
| 189 // Each time provideInput() is called, a portion of the audi
o stream is rendered. Let's call this time period a "render quantum". | |
| 190 // NOTE: for now default AudioContext does not need an expli
cit startRendering() call from JavaScript. | |
| 191 // We may want to consider requiring it for symmetry with Of
flineAudioContext. | |
| 192 m_destinationNode->startRendering(); | |
| 193 ++s_hardwareContextCount; | |
| 194 } | |
| 195 | 174 |
| 196 m_isInitialized = true; | 175 if (m_destinationNode.get()) { |
| 197 } | 176 m_destinationNode->initialize(); |
| 177 |
| 178 if (!isOfflineContext()) { |
| 179 // This starts the audio thread. The destination node's provideInput
() method will now be called repeatedly to render audio. |
| 180 // Each time provideInput() is called, a portion of the audio stream
is rendered. Let's call this time period a "render quantum". |
| 181 // NOTE: for now default AudioContext does not need an explicit star
tRendering() call from JavaScript. |
| 182 // We may want to consider requiring it for symmetry with OfflineAud
ioContext. |
| 183 m_destinationNode->startRendering(); |
| 184 ++s_hardwareContextCount; |
| 198 } | 185 } |
| 186 |
| 187 m_isInitialized = true; |
| 199 } | 188 } |
| 200 } | 189 } |
| 201 | 190 |
| 202 void AudioContext::clear() | 191 void AudioContext::clear() |
| 203 { | 192 { |
| 204 // We have to release our reference to the destination node before the conte
xt will ever be deleted since the destination node holds a reference to the cont
ext. | 193 // We have to release our reference to the destination node before the conte
xt will ever be deleted since the destination node holds a reference to the cont
ext. |
| 205 if (m_destinationNode) | 194 if (m_destinationNode) |
| 206 m_destinationNode.clear(); | 195 m_destinationNode.clear(); |
| 207 | 196 |
| 208 // Audio thread is dead. Nobody will schedule node deletion action. Let's do
it ourselves. | 197 // Audio thread is dead. Nobody will schedule node deletion action. Let's do
it ourselves. |
| 209 do { | 198 do { |
| 210 m_nodesToDelete.appendVector(m_nodesMarkedForDeletion); | 199 m_nodesToDelete.appendVector(m_nodesMarkedForDeletion); |
| 211 m_nodesMarkedForDeletion.clear(); | 200 m_nodesMarkedForDeletion.clear(); |
| 212 deleteMarkedNodes(); | 201 deleteMarkedNodes(); |
| 213 } while (m_nodesToDelete.size()); | 202 } while (m_nodesToDelete.size()); |
| 214 | 203 |
| 215 m_isCleared = true; | 204 m_isCleared = true; |
| 216 } | 205 } |
| 217 | 206 |
| 218 void AudioContext::uninitialize() | 207 void AudioContext::uninitialize() |
| 219 { | 208 { |
| 220 ASSERT(isMainThread()); | 209 ASSERT(isMainThread()); |
| 221 | 210 |
| 222 if (!m_isInitialized) | 211 if (!isInitialized()) |
| 223 return; | 212 return; |
| 224 | 213 |
| 225 // This stops the audio thread and all audio rendering. | 214 // This stops the audio thread and all audio rendering. |
| 226 m_destinationNode->uninitialize(); | 215 m_destinationNode->uninitialize(); |
| 227 | 216 |
| 228 // Don't allow the context to initialize a second time after it's already be
en explicitly uninitialized. | |
| 229 m_isAudioThreadFinished = true; | |
| 230 | |
| 231 if (!isOfflineContext()) { | 217 if (!isOfflineContext()) { |
| 232 ASSERT(s_hardwareContextCount); | 218 ASSERT(s_hardwareContextCount); |
| 233 --s_hardwareContextCount; | 219 --s_hardwareContextCount; |
| 234 } | 220 } |
| 235 | 221 |
| 236 // Get rid of the sources which may still be playing. | 222 // Get rid of the sources which may still be playing. |
| 237 derefUnfinishedSourceNodes(); | 223 derefUnfinishedSourceNodes(); |
| 238 | 224 |
| 239 m_isInitialized = false; | 225 m_isInitialized = false; |
| 240 } | 226 } |
| 241 | 227 |
| 242 bool AudioContext::isInitialized() const | |
| 243 { | |
| 244 return m_isInitialized; | |
| 245 } | |
| 246 | |
| 247 void AudioContext::stopDispatch(void* userData) | 228 void AudioContext::stopDispatch(void* userData) |
| 248 { | 229 { |
| 249 AudioContext* context = reinterpret_cast<AudioContext*>(userData); | 230 AudioContext* context = reinterpret_cast<AudioContext*>(userData); |
| 250 ASSERT(context); | 231 ASSERT(context); |
| 251 if (!context) | 232 if (!context) |
| 252 return; | 233 return; |
| 253 | 234 |
| 254 context->uninitialize(); | 235 context->uninitialize(); |
| 255 context->clear(); | 236 context->clear(); |
| 256 } | 237 } |
| (...skipping 329 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 586 | 567 |
| 587 void AudioContext::notifyNodeFinishedProcessing(AudioNode* node) | 568 void AudioContext::notifyNodeFinishedProcessing(AudioNode* node) |
| 588 { | 569 { |
| 589 ASSERT(isAudioThread()); | 570 ASSERT(isAudioThread()); |
| 590 m_finishedNodes.append(node); | 571 m_finishedNodes.append(node); |
| 591 } | 572 } |
| 592 | 573 |
| 593 void AudioContext::derefFinishedSourceNodes() | 574 void AudioContext::derefFinishedSourceNodes() |
| 594 { | 575 { |
| 595 ASSERT(isGraphOwner()); | 576 ASSERT(isGraphOwner()); |
| 596 ASSERT(isAudioThread() || isAudioThreadFinished()); | 577 ASSERT(isAudioThread()); |
| 597 for (unsigned i = 0; i < m_finishedNodes.size(); i++) | 578 for (unsigned i = 0; i < m_finishedNodes.size(); i++) |
| 598 derefNode(m_finishedNodes[i]); | 579 derefNode(m_finishedNodes[i]); |
| 599 | 580 |
| 600 m_finishedNodes.clear(); | 581 m_finishedNodes.clear(); |
| 601 } | 582 } |
| 602 | 583 |
| 603 void AudioContext::refNode(AudioNode* node) | 584 void AudioContext::refNode(AudioNode* node) |
| 604 { | 585 { |
| 605 ASSERT(isMainThread()); | 586 ASSERT(isMainThread()); |
| 606 AutoLocker locker(this); | 587 AutoLocker locker(this); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 618 for (unsigned i = 0; i < m_referencedNodes.size(); ++i) { | 599 for (unsigned i = 0; i < m_referencedNodes.size(); ++i) { |
| 619 if (node == m_referencedNodes[i]) { | 600 if (node == m_referencedNodes[i]) { |
| 620 m_referencedNodes.remove(i); | 601 m_referencedNodes.remove(i); |
| 621 break; | 602 break; |
| 622 } | 603 } |
| 623 } | 604 } |
| 624 } | 605 } |
| 625 | 606 |
| 626 void AudioContext::derefUnfinishedSourceNodes() | 607 void AudioContext::derefUnfinishedSourceNodes() |
| 627 { | 608 { |
| 628 ASSERT(isMainThread() && isAudioThreadFinished()); | 609 ASSERT(isMainThread()); |
| 629 for (unsigned i = 0; i < m_referencedNodes.size(); ++i) | 610 for (unsigned i = 0; i < m_referencedNodes.size(); ++i) |
| 630 m_referencedNodes[i]->deref(AudioNode::RefTypeConnection); | 611 m_referencedNodes[i]->deref(AudioNode::RefTypeConnection); |
| 631 | 612 |
| 632 m_referencedNodes.clear(); | 613 m_referencedNodes.clear(); |
| 633 } | 614 } |
| 634 | 615 |
| 635 void AudioContext::lock(bool& mustReleaseLock) | 616 void AudioContext::lock(bool& mustReleaseLock) |
| 636 { | 617 { |
| 637 // Don't allow regular lock in real-time audio thread. | 618 // Don't allow regular lock in real-time audio thread. |
| 638 ASSERT(isMainThread()); | 619 ASSERT(isMainThread()); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 649 mustReleaseLock = true; | 630 mustReleaseLock = true; |
| 650 } | 631 } |
| 651 } | 632 } |
| 652 | 633 |
| 653 bool AudioContext::tryLock(bool& mustReleaseLock) | 634 bool AudioContext::tryLock(bool& mustReleaseLock) |
| 654 { | 635 { |
| 655 ThreadIdentifier thisThread = currentThread(); | 636 ThreadIdentifier thisThread = currentThread(); |
| 656 bool isAudioThread = thisThread == audioThread(); | 637 bool isAudioThread = thisThread == audioThread(); |
| 657 | 638 |
| 658 // Try to catch cases of using try lock on main thread - it should use regul
ar lock. | 639 // Try to catch cases of using try lock on main thread - it should use regul
ar lock. |
| 659 ASSERT(isAudioThread || isAudioThreadFinished()); | 640 ASSERT(isAudioThread); |
| 660 | 641 |
| 661 if (!isAudioThread) { | 642 if (!isAudioThread) { |
| 662 // In release build treat tryLock() as lock() (since above ASSERT(isAudi
oThread) never fires) - this is the best we can do. | 643 // In release build treat tryLock() as lock() (since above ASSERT(isAudi
oThread) never fires) - this is the best we can do. |
| 663 lock(mustReleaseLock); | 644 lock(mustReleaseLock); |
| 664 return true; | 645 return true; |
| 665 } | 646 } |
| 666 | 647 |
| 667 bool hasLock; | 648 bool hasLock; |
| 668 | 649 |
| 669 if (thisThread == m_graphOwnerThread) { | 650 if (thisThread == m_graphOwnerThread) { |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 764 node->finishDeref(AudioNode::RefTypeConnection); | 745 node->finishDeref(AudioNode::RefTypeConnection); |
| 765 } | 746 } |
| 766 | 747 |
| 767 m_deferredFinishDerefList.clear(); | 748 m_deferredFinishDerefList.clear(); |
| 768 } | 749 } |
| 769 | 750 |
| 770 void AudioContext::markForDeletion(AudioNode* node) | 751 void AudioContext::markForDeletion(AudioNode* node) |
| 771 { | 752 { |
| 772 ASSERT(isGraphOwner()); | 753 ASSERT(isGraphOwner()); |
| 773 | 754 |
| 774 if (isAudioThreadFinished()) | 755 if (!isInitialized()) |
| 775 m_nodesToDelete.append(node); | 756 m_nodesToDelete.append(node); |
| 776 else | 757 else |
| 777 m_nodesMarkedForDeletion.append(node); | 758 m_nodesMarkedForDeletion.append(node); |
| 778 | 759 |
| 779 // This is probably the best time for us to remove the node from automatic p
ull list, | 760 // This is probably the best time for us to remove the node from automatic p
ull list, |
| 780 // since all connections are gone and we hold the graph lock. Then when hand
lePostRenderTasks() | 761 // since all connections are gone and we hold the graph lock. Then when hand
lePostRenderTasks() |
| 781 // gets a chance to schedule the deletion work, updateAutomaticPullNodes() a
lso gets a chance to | 762 // gets a chance to schedule the deletion work, updateAutomaticPullNodes() a
lso gets a chance to |
| 782 // modify m_renderingAutomaticPullNodes. | 763 // modify m_renderingAutomaticPullNodes. |
| 783 removeAutomaticPullNode(node); | 764 removeAutomaticPullNode(node); |
| 784 } | 765 } |
| 785 | 766 |
| 786 void AudioContext::scheduleNodeDeletion() | 767 void AudioContext::scheduleNodeDeletion() |
| 787 { | 768 { |
| 788 bool isGood = m_isInitialized && isGraphOwner(); | 769 bool isGood = isInitialized() && isGraphOwner(); |
| 789 ASSERT(isGood); | 770 ASSERT(isGood); |
| 790 if (!isGood) | 771 if (!isGood) |
| 791 return; | 772 return; |
| 792 | 773 |
| 793 // Make sure to call deleteMarkedNodes() on main thread. | 774 // Make sure to call deleteMarkedNodes() on main thread. |
| 794 if (m_nodesMarkedForDeletion.size() && !m_isDeletionScheduled) { | 775 if (m_nodesMarkedForDeletion.size() && !m_isDeletionScheduled) { |
| 795 m_nodesToDelete.appendVector(m_nodesMarkedForDeletion); | 776 m_nodesToDelete.appendVector(m_nodesMarkedForDeletion); |
| 796 m_nodesMarkedForDeletion.clear(); | 777 m_nodesMarkedForDeletion.clear(); |
| 797 | 778 |
| 798 m_isDeletionScheduled = true; | 779 m_isDeletionScheduled = true; |
| (...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 974 visitor->trace(m_renderTarget); | 955 visitor->trace(m_renderTarget); |
| 975 visitor->trace(m_destinationNode); | 956 visitor->trace(m_destinationNode); |
| 976 visitor->trace(m_listener); | 957 visitor->trace(m_listener); |
| 977 visitor->trace(m_dirtySummingJunctions); | 958 visitor->trace(m_dirtySummingJunctions); |
| 978 EventTargetWithInlineData::trace(visitor); | 959 EventTargetWithInlineData::trace(visitor); |
| 979 } | 960 } |
| 980 | 961 |
| 981 } // namespace WebCore | 962 } // namespace WebCore |
| 982 | 963 |
| 983 #endif // ENABLE(WEB_AUDIO) | 964 #endif // ENABLE(WEB_AUDIO) |
| OLD | NEW |