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 |