2 * Copyright (C) 2010, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #include "modules/webaudio/AudioContext.h"
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"
67 #if DEBUG_AUDIONODE_REFERENCES
71 #include "wtf/ArrayBuffer.h"
72 #include "wtf/Atomics.h"
73 #include "wtf/PassOwnPtr.h"
74 #include "wtf/text/WTFString.h"
76 // FIXME: check the proper way to reference an undefined thread ID
77 const int UndefinedThreadIdentifier = 0xffffffff;
81 bool AudioContext::isSampleRateRangeGood(float sampleRate)
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;
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;
92 PassRefPtrWillBeRawPtr<AudioContext> AudioContext::create(Document& document, ExceptionState& exceptionState)
94 ASSERT(isMainThread());
95 if (s_hardwareContextCount >= MaxHardwareContexts) {
96 exceptionState.throwDOMException(
98 "number of hardware contexts reached maximum (" + String::number(MaxHardwareContexts) + ").");
102 RefPtrWillBeRawPtr<AudioContext> audioContext(adoptRefWillBeThreadSafeRefCountedGarbageCollected(new AudioContext(&document)));
103 audioContext->suspendIfNeeded();
104 return audioContext.release();
107 // Constructor for rendering to the audio hardware.
108 AudioContext::AudioContext(Document* document)
109 : ActiveDOMObject(document)
110 , m_isStopScheduled(false)
112 , m_isInitialized(false)
113 , m_destinationNode(nullptr)
114 , m_isDeletionScheduled(false)
115 , m_automaticPullNodesNeedUpdating(false)
116 , m_connectionCount(0)
118 , m_graphOwnerThread(UndefinedThreadIdentifier)
119 , m_isOfflineContext(false)
121 ScriptWrappable::init(this);
123 m_destinationNode = DefaultAudioDestinationNode::create(this);
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)
133 , m_isInitialized(false)
134 , m_destinationNode(nullptr)
135 , m_isDeletionScheduled(false)
136 , m_automaticPullNodesNeedUpdating(false)
137 , m_connectionCount(0)
139 , m_graphOwnerThread(UndefinedThreadIdentifier)
140 , m_isOfflineContext(true)
142 ScriptWrappable::init(this);
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());
152 AudioContext::~AudioContext()
154 #if DEBUG_AUDIONODE_REFERENCES
155 fprintf(stderr, "%p: AudioContext::~AudioContext()\n", this);
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());
168 void AudioContext::initialize()
173 FFTFrame::initialize();
174 m_listener = AudioListener::create();
176 if (m_destinationNode.get()) {
177 m_destinationNode->initialize();
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;
188 m_isInitialized = true;
192 void AudioContext::clear()
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();
198 // Audio thread is dead. Nobody will schedule node deletion action. Let's do it ourselves.
200 m_nodesToDelete.appendVector(m_nodesMarkedForDeletion);
201 m_nodesMarkedForDeletion.clear();
203 } while (m_nodesToDelete.size());
208 void AudioContext::uninitialize()
210 ASSERT(isMainThread());
212 if (!isInitialized())
215 // This stops the audio thread and all audio rendering.
216 m_destinationNode->uninitialize();
218 if (!isOfflineContext()) {
219 ASSERT(s_hardwareContextCount);
220 --s_hardwareContextCount;
223 // Get rid of the sources which may still be playing.
224 derefUnfinishedSourceNodes();
226 m_isInitialized = false;
229 void AudioContext::stopDispatch(void* userData)
231 AudioContext* context = reinterpret_cast<AudioContext*>(userData);
236 context->uninitialize();
240 void AudioContext::stop()
242 // Usually ExecutionContext calls stop twice.
243 if (m_isStopScheduled)
245 m_isStopScheduled = true;
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);
254 bool AudioContext::hasPendingActivity() const
256 // According to spec AudioContext must die only after page navigates.
260 PassRefPtrWillBeRawPtr<AudioBuffer> AudioContext::createBuffer(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate, ExceptionState& exceptionState)
262 RefPtrWillBeRawPtr<AudioBuffer> audioBuffer = AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate, exceptionState);
267 void AudioContext::decodeAudioData(ArrayBuffer* audioData, PassOwnPtr<AudioBufferCallback> successCallback, PassOwnPtr<AudioBufferCallback> errorCallback, ExceptionState& exceptionState)
270 exceptionState.throwDOMException(
272 "invalid ArrayBuffer for audioData.");
275 m_audioDecoder.decodeAsync(audioData, sampleRate(), successCallback, errorCallback);
278 PassRefPtrWillBeRawPtr<AudioBufferSourceNode> AudioContext::createBufferSource()
280 ASSERT(isMainThread());
281 RefPtrWillBeRawPtr<AudioBufferSourceNode> node = AudioBufferSourceNode::create(this, m_destinationNode->sampleRate());
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().
290 PassRefPtrWillBeRawPtr<MediaElementAudioSourceNode> AudioContext::createMediaElementSource(HTMLMediaElement* mediaElement, ExceptionState& exceptionState)
292 ASSERT(isMainThread());
294 exceptionState.throwDOMException(
296 "invalid HTMLMedialElement.");
300 // First check if this media element already has a source node.
301 if (mediaElement->audioSourceNode()) {
302 exceptionState.throwDOMException(
304 "invalid HTMLMediaElement.");
308 RefPtrWillBeRawPtr<MediaElementAudioSourceNode> node = MediaElementAudioSourceNode::create(this, mediaElement);
310 mediaElement->setAudioSourceNode(node.get());
312 refNode(node.get()); // context keeps reference until node is disconnected
316 PassRefPtrWillBeRawPtr<MediaStreamAudioSourceNode> AudioContext::createMediaStreamSource(MediaStream* mediaStream, ExceptionState& exceptionState)
318 ASSERT(isMainThread());
320 exceptionState.throwDOMException(
322 "invalid MediaStream source");
326 MediaStreamTrackVector audioTracks = mediaStream->getAudioTracks();
327 if (audioTracks.isEmpty()) {
328 exceptionState.throwDOMException(
330 "MediaStream has no audio track");
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());
339 // FIXME: Only stereo streams are supported right now. We should be able to accept multi-channel streams.
340 node->setFormat(2, sampleRate());
342 refNode(node.get()); // context keeps reference until node is disconnected
346 PassRefPtrWillBeRawPtr<MediaStreamAudioDestinationNode> AudioContext::createMediaStreamDestination()
348 // Set number of output channels to stereo by default.
349 return MediaStreamAudioDestinationNode::create(this, 2);
352 PassRefPtrWillBeRawPtr<ScriptProcessorNode> AudioContext::createScriptProcessor(ExceptionState& exceptionState)
354 // Set number of input/output channels to stereo by default.
355 return createScriptProcessor(0, 2, 2, exceptionState);
358 PassRefPtrWillBeRawPtr<ScriptProcessorNode> AudioContext::createScriptProcessor(size_t bufferSize, ExceptionState& exceptionState)
360 // Set number of input/output channels to stereo by default.
361 return createScriptProcessor(bufferSize, 2, 2, exceptionState);
364 PassRefPtrWillBeRawPtr<ScriptProcessorNode> AudioContext::createScriptProcessor(size_t bufferSize, size_t numberOfInputChannels, ExceptionState& exceptionState)
366 // Set number of output channels to stereo by default.
367 return createScriptProcessor(bufferSize, numberOfInputChannels, 2, exceptionState);
370 PassRefPtrWillBeRawPtr<ScriptProcessorNode> AudioContext::createScriptProcessor(size_t bufferSize, size_t numberOfInputChannels, size_t numberOfOutputChannels, ExceptionState& exceptionState)
372 ASSERT(isMainThread());
373 RefPtrWillBeRawPtr<ScriptProcessorNode> node = ScriptProcessorNode::create(this, m_destinationNode->sampleRate(), bufferSize, numberOfInputChannels, numberOfOutputChannels);
376 if (!numberOfInputChannels && !numberOfOutputChannels) {
377 exceptionState.throwDOMException(
379 "number of input channels and output channels cannot both be zero.");
380 } else if (numberOfInputChannels > AudioContext::maxNumberOfChannels()) {
381 exceptionState.throwDOMException(
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(
389 "number of output channels (" + String::number(numberOfInputChannels)
390 + ") exceeds maximum ("
391 + String::number(AudioContext::maxNumberOfChannels()) + ").");
393 exceptionState.throwDOMException(
395 "buffer size (" + String::number(bufferSize)
396 + ") must be a power of two between 256 and 16384.");
401 refNode(node.get()); // context keeps reference until we stop making javascript rendering callbacks
405 PassRefPtrWillBeRawPtr<BiquadFilterNode> AudioContext::createBiquadFilter()
407 ASSERT(isMainThread());
408 return BiquadFilterNode::create(this, m_destinationNode->sampleRate());
411 PassRefPtrWillBeRawPtr<WaveShaperNode> AudioContext::createWaveShaper()
413 ASSERT(isMainThread());
414 return WaveShaperNode::create(this);
417 PassRefPtrWillBeRawPtr<PannerNode> AudioContext::createPanner()
419 ASSERT(isMainThread());
420 return PannerNode::create(this, m_destinationNode->sampleRate());
423 PassRefPtrWillBeRawPtr<ConvolverNode> AudioContext::createConvolver()
425 ASSERT(isMainThread());
426 return ConvolverNode::create(this, m_destinationNode->sampleRate());
429 PassRefPtrWillBeRawPtr<DynamicsCompressorNode> AudioContext::createDynamicsCompressor()
431 ASSERT(isMainThread());
432 return DynamicsCompressorNode::create(this, m_destinationNode->sampleRate());
435 PassRefPtrWillBeRawPtr<AnalyserNode> AudioContext::createAnalyser()
437 ASSERT(isMainThread());
438 return AnalyserNode::create(this, m_destinationNode->sampleRate());
441 PassRefPtrWillBeRawPtr<GainNode> AudioContext::createGain()
443 ASSERT(isMainThread());
444 return GainNode::create(this, m_destinationNode->sampleRate());
447 PassRefPtrWillBeRawPtr<DelayNode> AudioContext::createDelay(ExceptionState& exceptionState)
449 const double defaultMaxDelayTime = 1;
450 return createDelay(defaultMaxDelayTime, exceptionState);
453 PassRefPtrWillBeRawPtr<DelayNode> AudioContext::createDelay(double maxDelayTime, ExceptionState& exceptionState)
455 ASSERT(isMainThread());
456 RefPtrWillBeRawPtr<DelayNode> node = DelayNode::create(this, m_destinationNode->sampleRate(), maxDelayTime, exceptionState);
457 if (exceptionState.hadException())
462 PassRefPtrWillBeRawPtr<ChannelSplitterNode> AudioContext::createChannelSplitter(ExceptionState& exceptionState)
464 const unsigned ChannelSplitterDefaultNumberOfOutputs = 6;
465 return createChannelSplitter(ChannelSplitterDefaultNumberOfOutputs, exceptionState);
468 PassRefPtrWillBeRawPtr<ChannelSplitterNode> AudioContext::createChannelSplitter(size_t numberOfOutputs, ExceptionState& exceptionState)
470 ASSERT(isMainThread());
472 RefPtrWillBeRawPtr<ChannelSplitterNode> node = ChannelSplitterNode::create(this, m_destinationNode->sampleRate(), numberOfOutputs);
475 exceptionState.throwDOMException(
477 "number of outputs (" + String::number(numberOfOutputs)
478 + ") must be between 1 and "
479 + String::number(AudioContext::maxNumberOfChannels()) + ".");
486 PassRefPtrWillBeRawPtr<ChannelMergerNode> AudioContext::createChannelMerger(ExceptionState& exceptionState)
488 const unsigned ChannelMergerDefaultNumberOfInputs = 6;
489 return createChannelMerger(ChannelMergerDefaultNumberOfInputs, exceptionState);
492 PassRefPtrWillBeRawPtr<ChannelMergerNode> AudioContext::createChannelMerger(size_t numberOfInputs, ExceptionState& exceptionState)
494 ASSERT(isMainThread());
496 RefPtrWillBeRawPtr<ChannelMergerNode> node = ChannelMergerNode::create(this, m_destinationNode->sampleRate(), numberOfInputs);
499 exceptionState.throwDOMException(
501 "number of inputs (" + String::number(numberOfInputs)
502 + ") must be between 1 and "
503 + String::number(AudioContext::maxNumberOfChannels()) + ".");
510 PassRefPtrWillBeRawPtr<OscillatorNode> AudioContext::createOscillator()
512 ASSERT(isMainThread());
514 RefPtrWillBeRawPtr<OscillatorNode> node = OscillatorNode::create(this, m_destinationNode->sampleRate());
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().
523 PassRefPtrWillBeRawPtr<PeriodicWave> AudioContext::createPeriodicWave(Float32Array* real, Float32Array* imag, ExceptionState& exceptionState)
525 ASSERT(isMainThread());
528 exceptionState.throwDOMException(
530 "invalid real array");
535 exceptionState.throwDOMException(
537 "invalid imaginary array");
541 if (real->length() != imag->length()) {
542 exceptionState.throwDOMException(
544 "length of real array (" + String::number(real->length())
545 + ") and length of imaginary array (" + String::number(imag->length())
550 if (real->length() > 4096) {
551 exceptionState.throwDOMException(
553 "length of real array (" + String::number(real->length())
554 + ") exceeds allowed maximum of 4096");
558 if (imag->length() > 4096) {
559 exceptionState.throwDOMException(
561 "length of imaginary array (" + String::number(imag->length())
562 + ") exceeds allowed maximum of 4096");
566 return PeriodicWave::create(sampleRate(), real, imag);
569 void AudioContext::notifyNodeFinishedProcessing(AudioNode* node)
571 ASSERT(isAudioThread());
572 m_finishedNodes.append(node);
575 void AudioContext::derefFinishedSourceNodes()
577 ASSERT(isGraphOwner());
578 ASSERT(isAudioThread());
579 for (unsigned i = 0; i < m_finishedNodes.size(); i++)
580 derefNode(m_finishedNodes[i]);
582 m_finishedNodes.clear();
585 void AudioContext::refNode(AudioNode* node)
587 ASSERT(isMainThread());
588 AutoLocker locker(this);
590 node->ref(AudioNode::RefTypeConnection);
591 m_referencedNodes.append(node);
594 void AudioContext::derefNode(AudioNode* node)
596 ASSERT(isGraphOwner());
598 node->deref(AudioNode::RefTypeConnection);
600 for (unsigned i = 0; i < m_referencedNodes.size(); ++i) {
601 if (node == m_referencedNodes[i]) {
602 m_referencedNodes.remove(i);
608 void AudioContext::derefUnfinishedSourceNodes()
610 ASSERT(isMainThread());
611 for (unsigned i = 0; i < m_referencedNodes.size(); ++i)
612 m_referencedNodes[i]->deref(AudioNode::RefTypeConnection);
614 m_referencedNodes.clear();
617 void AudioContext::lock(bool& mustReleaseLock)
619 // Don't allow regular lock in real-time audio thread.
620 ASSERT(isMainThread());
622 ThreadIdentifier thisThread = currentThread();
624 if (thisThread == m_graphOwnerThread) {
625 // We already have the lock.
626 mustReleaseLock = false;
629 m_contextGraphMutex.lock();
630 m_graphOwnerThread = thisThread;
631 mustReleaseLock = true;
635 bool AudioContext::tryLock(bool& mustReleaseLock)
637 ThreadIdentifier thisThread = currentThread();
638 bool isAudioThread = thisThread == audioThread();
640 // Try to catch cases of using try lock on main thread - it should use regular lock.
641 ASSERT(isAudioThread);
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);
651 if (thisThread == m_graphOwnerThread) {
652 // Thread already has the lock.
654 mustReleaseLock = false;
656 // Don't already have the lock - try to acquire it.
657 hasLock = m_contextGraphMutex.tryLock();
660 m_graphOwnerThread = thisThread;
662 mustReleaseLock = hasLock;
668 void AudioContext::unlock()
670 ASSERT(currentThread() == m_graphOwnerThread);
672 m_graphOwnerThread = UndefinedThreadIdentifier;
673 m_contextGraphMutex.unlock();
676 bool AudioContext::isAudioThread() const
678 return currentThread() == m_audioThread;
681 bool AudioContext::isGraphOwner() const
683 return currentThread() == m_graphOwnerThread;
686 void AudioContext::addDeferredFinishDeref(AudioNode* node)
688 ASSERT(isAudioThread());
689 m_deferredFinishDerefList.append(node);
692 void AudioContext::handlePreRenderTasks()
694 ASSERT(isAudioThread());
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();
704 updateAutomaticPullNodes();
711 void AudioContext::handlePostRenderTasks()
713 ASSERT(isAudioThread());
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();
723 // Dynamically clean up nodes which are no longer needed.
724 derefFinishedSourceNodes();
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();
730 // Fixup the state of any dirty AudioSummingJunctions and AudioNodeOutputs.
731 handleDirtyAudioSummingJunctions();
732 handleDirtyAudioNodeOutputs();
734 updateAutomaticPullNodes();
741 void AudioContext::handleDeferredFinishDerefs()
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);
749 m_deferredFinishDerefList.clear();
752 void AudioContext::markForDeletion(AudioNode* node)
754 ASSERT(isGraphOwner());
756 if (!isInitialized())
757 m_nodesToDelete.append(node);
759 m_nodesMarkedForDeletion.append(node);
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);
768 void AudioContext::scheduleNodeDeletion()
770 bool isGood = isInitialized() && isGraphOwner();
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();
780 m_isDeletionScheduled = true;
782 // Don't let ourself get deleted before the callback.
783 // See matching deref() in deleteMarkedNodesDispatch().
785 callOnMainThread(deleteMarkedNodesDispatch, this);
789 void AudioContext::deleteMarkedNodesDispatch(void* userData)
791 AudioContext* context = reinterpret_cast<AudioContext*>(userData);
796 context->deleteMarkedNodes();
800 void AudioContext::deleteMarkedNodes()
802 ASSERT(isMainThread());
804 // Protect this object from being deleted before we release the mutex locked by AutoLocker.
805 RefPtrWillBeRawPtr<AudioContext> protect(this);
807 AutoLocker locker(this);
809 while (size_t n = m_nodesToDelete.size()) {
810 AudioNode* node = m_nodesToDelete[n - 1];
811 m_nodesToDelete.removeLast();
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));
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));
823 // Finally, clear the keep alive handle that keeps this
824 // object from being collected.
825 node->clearKeepAlive();
827 // Finally, delete it.
831 m_isDeletionScheduled = false;
835 void AudioContext::markSummingJunctionDirty(AudioSummingJunction* summingJunction)
837 ASSERT(isGraphOwner());
838 m_dirtySummingJunctions.add(summingJunction);
841 void AudioContext::removeMarkedSummingJunction(AudioSummingJunction* summingJunction)
843 ASSERT(isMainThread());
844 AutoLocker locker(this);
845 m_dirtySummingJunctions.remove(summingJunction);
848 void AudioContext::markAudioNodeOutputDirty(AudioNodeOutput* output)
850 ASSERT(isGraphOwner());
851 m_dirtyAudioNodeOutputs.add(output);
854 void AudioContext::handleDirtyAudioSummingJunctions()
856 ASSERT(isGraphOwner());
858 for (HashSet<AudioSummingJunction* >::iterator i = m_dirtySummingJunctions.begin(); i != m_dirtySummingJunctions.end(); ++i)
859 (*i)->updateRenderingState();
861 m_dirtySummingJunctions.clear();
864 void AudioContext::handleDirtyAudioNodeOutputs()
866 ASSERT(isGraphOwner());
868 for (HashSet<AudioNodeOutput*>::iterator i = m_dirtyAudioNodeOutputs.begin(); i != m_dirtyAudioNodeOutputs.end(); ++i)
869 (*i)->updateRenderingState();
871 m_dirtyAudioNodeOutputs.clear();
874 void AudioContext::addAutomaticPullNode(AudioNode* node)
876 ASSERT(isGraphOwner());
878 if (!m_automaticPullNodes.contains(node)) {
879 m_automaticPullNodes.add(node);
880 m_automaticPullNodesNeedUpdating = true;
884 void AudioContext::removeAutomaticPullNode(AudioNode* node)
886 ASSERT(isGraphOwner());
888 if (m_automaticPullNodes.contains(node)) {
889 m_automaticPullNodes.remove(node);
890 m_automaticPullNodesNeedUpdating = true;
894 void AudioContext::updateAutomaticPullNodes()
896 ASSERT(isGraphOwner());
898 if (m_automaticPullNodesNeedUpdating) {
899 // Copy from m_automaticPullNodes to m_renderingAutomaticPullNodes.
900 m_renderingAutomaticPullNodes.resize(m_automaticPullNodes.size());
903 for (HashSet<AudioNode*>::iterator i = m_automaticPullNodes.begin(); i != m_automaticPullNodes.end(); ++i, ++j) {
904 AudioNode* output = *i;
905 m_renderingAutomaticPullNodes[j] = output;
908 m_automaticPullNodesNeedUpdating = false;
912 void AudioContext::processAutomaticPullNodes(size_t framesToProcess)
914 ASSERT(isAudioThread());
916 for (unsigned i = 0; i < m_renderingAutomaticPullNodes.size(); ++i)
917 m_renderingAutomaticPullNodes[i]->processIfNecessary(framesToProcess);
920 const AtomicString& AudioContext::interfaceName() const
922 return EventTargetNames::AudioContext;
925 ExecutionContext* AudioContext::executionContext() const
927 return m_isStopScheduled ? 0 : ActiveDOMObject::executionContext();
930 void AudioContext::startRendering()
932 destination()->startRendering();
935 void AudioContext::fireCompletionEvent()
937 ASSERT(isMainThread());
941 AudioBuffer* renderedBuffer = m_renderTarget.get();
943 ASSERT(renderedBuffer);
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));
954 void AudioContext::trace(Visitor* visitor)
956 visitor->trace(m_renderTarget);
957 visitor->trace(m_destinationNode);
958 visitor->trace(m_listener);
959 visitor->trace(m_dirtySummingJunctions);
960 EventTargetWithInlineData::trace(visitor);
963 } // namespace WebCore
965 #endif // ENABLE(WEB_AUDIO)