initial import
[vuplus_webkit] / Source / WebCore / webaudio / AudioContext.cpp
1 /*
2  * Copyright (C) 2010, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
23  */
24
25 #include "config.h"
26
27 #if ENABLE(WEB_AUDIO)
28
29 #include "AudioContext.h"
30
31 #include "ArrayBuffer.h"
32 #include "AsyncAudioDecoder.h"
33 #include "AudioBuffer.h"
34 #include "AudioBufferCallback.h"
35 #include "AudioBufferSourceNode.h"
36 #include "AudioChannelMerger.h"
37 #include "AudioChannelSplitter.h"
38 #include "AudioGainNode.h"
39 #include "AudioListener.h"
40 #include "AudioNodeInput.h"
41 #include "AudioNodeOutput.h"
42 #include "AudioPannerNode.h"
43 #include "BiquadFilterNode.h"
44 #include "ConvolverNode.h"
45 #include "DefaultAudioDestinationNode.h"
46 #include "DelayNode.h"
47 #include "Document.h"
48 #include "DynamicsCompressorNode.h"
49 #include "FFTFrame.h"
50 #include "HRTFDatabaseLoader.h"
51 #include "HRTFPanner.h"
52 #include "HighPass2FilterNode.h"
53 #include "JavaScriptAudioNode.h"
54 #include "LowPass2FilterNode.h"
55 #include "OfflineAudioCompletionEvent.h"
56 #include "OfflineAudioDestinationNode.h"
57 #include "PlatformString.h"
58 #include "RealtimeAnalyserNode.h"
59 #include "WaveShaperNode.h"
60 #include "ScriptCallStack.h"
61
62 #if ENABLE(VIDEO)
63 #include "HTMLMediaElement.h"
64 #include "MediaElementAudioSourceNode.h"
65 #endif
66
67 #if DEBUG_AUDIONODE_REFERENCES
68 #include <stdio.h>
69 #endif
70
71 #include <wtf/MainThread.h>
72 #include <wtf/OwnPtr.h>
73 #include <wtf/PassOwnPtr.h>
74 #include <wtf/RefCounted.h>
75
76 // FIXME: check the proper way to reference an undefined thread ID
77 const int UndefinedThreadIdentifier = 0xffffffff;
78
79 const unsigned MaxNodesToDeletePerQuantum = 10;
80
81 namespace WebCore {
82     
83 namespace {
84     
85 bool isSampleRateRangeGood(double sampleRate)
86 {
87     return sampleRate >= 22050 && sampleRate <= 96000;
88 }
89
90 }
91
92 // Don't allow more than this number of simultaneous AudioContexts talking to hardware.
93 const unsigned MaxHardwareContexts = 4;
94 unsigned AudioContext::s_hardwareContextCount = 0;
95     
96 PassRefPtr<AudioContext> AudioContext::create(Document* document)
97 {
98     ASSERT(document);
99     ASSERT(isMainThread());
100     if (s_hardwareContextCount >= MaxHardwareContexts)
101         return 0;
102         
103     return adoptRef(new AudioContext(document));
104 }
105
106 PassRefPtr<AudioContext> AudioContext::createOfflineContext(Document* document, unsigned numberOfChannels, size_t numberOfFrames, double sampleRate, ExceptionCode& ec)
107 {
108     ASSERT(document);
109
110     // FIXME: offline contexts have limitations on supported sample-rates.
111     // Currently all AudioContexts must have the same sample-rate.
112     HRTFDatabaseLoader* loader = HRTFDatabaseLoader::loader();
113     if (numberOfChannels > 10 || !isSampleRateRangeGood(sampleRate) || (loader && loader->databaseSampleRate() != sampleRate)) {
114         ec = SYNTAX_ERR;
115         return 0;
116     }
117
118     return adoptRef(new AudioContext(document, numberOfChannels, numberOfFrames, sampleRate));
119 }
120
121 // Constructor for rendering to the audio hardware.
122 AudioContext::AudioContext(Document* document)
123     : ActiveDOMObject(document, this)
124     , m_isInitialized(false)
125     , m_isAudioThreadFinished(false)
126     , m_document(document)
127     , m_destinationNode(0)
128     , m_isDeletionScheduled(false)
129     , m_connectionCount(0)
130     , m_audioThread(0)
131     , m_graphOwnerThread(UndefinedThreadIdentifier)
132     , m_isOfflineContext(false)
133 {
134     constructCommon();
135
136     m_destinationNode = DefaultAudioDestinationNode::create(this);
137
138     // This sets in motion an asynchronous loading mechanism on another thread.
139     // We can check m_hrtfDatabaseLoader->isLoaded() to find out whether or not it has been fully loaded.
140     // It's not that useful to have a callback function for this since the audio thread automatically starts rendering on the graph
141     // when this has finished (see AudioDestinationNode).
142     m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNecessary(sampleRate());
143 }
144
145 // Constructor for offline (non-realtime) rendering.
146 AudioContext::AudioContext(Document* document, unsigned numberOfChannels, size_t numberOfFrames, double sampleRate)
147     : ActiveDOMObject(document, this)
148     , m_isInitialized(false)
149     , m_isAudioThreadFinished(false)
150     , m_document(document)
151     , m_destinationNode(0)
152     , m_connectionCount(0)
153     , m_audioThread(0)
154     , m_graphOwnerThread(UndefinedThreadIdentifier)
155     , m_isOfflineContext(true)
156 {
157     constructCommon();
158
159     // FIXME: the passed in sampleRate MUST match the hardware sample-rate since HRTFDatabaseLoader is a singleton.
160     m_hrtfDatabaseLoader = HRTFDatabaseLoader::createAndLoadAsynchronouslyIfNecessary(sampleRate);
161
162     // Create a new destination for offline rendering.
163     m_renderTarget = AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate);
164     m_destinationNode = OfflineAudioDestinationNode::create(this, m_renderTarget.get());
165 }
166
167 void AudioContext::constructCommon()
168 {
169     FFTFrame::initialize();
170     
171     m_listener = AudioListener::create();
172     m_temporaryMonoBus = adoptPtr(new AudioBus(1, AudioNode::ProcessingSizeInFrames));
173     m_temporaryStereoBus = adoptPtr(new AudioBus(2, AudioNode::ProcessingSizeInFrames));
174 }
175
176 AudioContext::~AudioContext()
177 {
178 #if DEBUG_AUDIONODE_REFERENCES
179     printf("%p: AudioContext::~AudioContext()\n", this);
180 #endif
181     // AudioNodes keep a reference to their context, so there should be no way to be in the destructor if there are still AudioNodes around.
182     ASSERT(!m_nodesToDelete.size());
183     ASSERT(!m_referencedNodes.size());
184     ASSERT(!m_finishedNodes.size());
185 }
186
187 void AudioContext::lazyInitialize()
188 {
189     if (!m_isInitialized) {
190         // Don't allow the context to initialize a second time after it's already been explicitly uninitialized.
191         ASSERT(!m_isAudioThreadFinished);
192         if (!m_isAudioThreadFinished) {
193             if (m_destinationNode.get()) {
194                 m_destinationNode->initialize();
195
196                 if (!isOfflineContext()) {
197                     // This starts the audio thread. The destination node's provideInput() method will now be called repeatedly to render audio.
198                     // Each time provideInput() is called, a portion of the audio stream is rendered. Let's call this time period a "render quantum".
199                     // NOTE: for now default AudioContext does not need an explicit startRendering() call from JavaScript.
200                     // We may want to consider requiring it for symmetry with OfflineAudioContext.
201                     m_destinationNode->startRendering();                    
202                     ++s_hardwareContextCount;
203                 }
204
205             }
206             m_isInitialized = true;
207         }
208     }
209 }
210
211 void AudioContext::uninitialize()
212 {
213     ASSERT(isMainThread());
214
215     if (m_isInitialized) {    
216         // This stops the audio thread and all audio rendering.
217         m_destinationNode->uninitialize();
218
219         // Don't allow the context to initialize a second time after it's already been explicitly uninitialized.
220         m_isAudioThreadFinished = true;
221
222         // 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.
223         m_destinationNode.clear();
224
225         if (!isOfflineContext()) {
226             ASSERT(s_hardwareContextCount);
227             --s_hardwareContextCount;
228         }
229         
230         // Get rid of the sources which may still be playing.
231         derefUnfinishedSourceNodes();
232
233         deleteMarkedNodes();
234
235         // Because the AudioBuffers are garbage collected, we can't delete them here.
236         // Instead, at least release the potentially large amount of allocated memory for the audio data.
237         // Note that we do this *after* the context is uninitialized and stops processing audio.
238         for (unsigned i = 0; i < m_allocatedBuffers.size(); ++i)
239             m_allocatedBuffers[i]->releaseMemory();
240         m_allocatedBuffers.clear();
241     
242         m_isInitialized = false;
243     }
244 }
245
246 bool AudioContext::isInitialized() const
247 {
248     return m_isInitialized;
249 }
250
251 bool AudioContext::isRunnable() const
252 {
253     if (!isInitialized())
254         return false;
255     
256     // Check with the HRTF spatialization system to see if it's finished loading.
257     return m_hrtfDatabaseLoader->isLoaded();
258 }
259
260 void AudioContext::uninitializeDispatch(void* userData)
261 {
262     AudioContext* context = reinterpret_cast<AudioContext*>(userData);
263     ASSERT(context);
264     if (!context)
265         return;
266
267     context->uninitialize();
268 }
269
270 void AudioContext::stop()
271 {
272     m_document = 0; // document is going away
273
274     // Don't call uninitialize() immediately here because the ScriptExecutionContext is in the middle
275     // of dealing with all of its ActiveDOMObjects at this point. uninitialize() can de-reference other
276     // ActiveDOMObjects so let's schedule uninitialize() to be called later.
277     // FIXME: see if there's a more direct way to handle this issue.
278     callOnMainThread(uninitializeDispatch, this);
279 }
280
281 Document* AudioContext::document() const
282 {
283     ASSERT(m_document);
284     return m_document;
285 }
286
287 bool AudioContext::hasDocument()
288 {
289     return m_document;
290 }
291
292 void AudioContext::refBuffer(PassRefPtr<AudioBuffer> buffer)
293 {
294     m_allocatedBuffers.append(buffer);
295 }
296
297 PassRefPtr<AudioBuffer> AudioContext::createBuffer(unsigned numberOfChannels, size_t numberOfFrames, double sampleRate)
298 {
299     if (!isSampleRateRangeGood(sampleRate) || numberOfChannels > 10 || !numberOfFrames)
300         return 0;
301     
302     return AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate);
303 }
304
305 PassRefPtr<AudioBuffer> AudioContext::createBuffer(ArrayBuffer* arrayBuffer, bool mixToMono)
306 {
307     ASSERT(arrayBuffer);
308     if (!arrayBuffer)
309         return 0;
310     
311     return AudioBuffer::createFromAudioFileData(arrayBuffer->data(), arrayBuffer->byteLength(), mixToMono, sampleRate());
312 }
313
314 void AudioContext::decodeAudioData(ArrayBuffer* audioData, PassRefPtr<AudioBufferCallback> successCallback, PassRefPtr<AudioBufferCallback> errorCallback, ExceptionCode& ec)
315 {
316     if (!audioData) {
317         ec = SYNTAX_ERR;
318         return;
319     }
320     m_audioDecoder.decodeAsync(audioData, sampleRate(), successCallback, errorCallback);
321 }
322
323 PassRefPtr<AudioBufferSourceNode> AudioContext::createBufferSource()
324 {
325     ASSERT(isMainThread());
326     lazyInitialize();
327     RefPtr<AudioBufferSourceNode> node = AudioBufferSourceNode::create(this, m_destinationNode->sampleRate());
328
329     refNode(node.get()); // context keeps reference until source has finished playing
330     return node;
331 }
332
333 #if ENABLE(VIDEO)
334 PassRefPtr<MediaElementAudioSourceNode> AudioContext::createMediaElementSource(HTMLMediaElement* mediaElement, ExceptionCode& ec)
335 {
336     ASSERT(mediaElement);
337     if (!mediaElement) {
338         ec = INVALID_STATE_ERR;
339         return 0;
340     }
341         
342     ASSERT(isMainThread());
343     lazyInitialize();
344     
345     // First check if this media element already has a source node.
346     if (mediaElement->audioSourceNode()) {
347         ec = INVALID_STATE_ERR;
348         return 0;
349     }
350         
351     RefPtr<MediaElementAudioSourceNode> node = MediaElementAudioSourceNode::create(this, mediaElement);
352
353     mediaElement->setAudioSourceNode(node.get());
354
355     refNode(node.get()); // context keeps reference until node is disconnected
356     return node;
357 }
358 #endif
359
360 PassRefPtr<JavaScriptAudioNode> AudioContext::createJavaScriptNode(size_t bufferSize)
361 {
362     ASSERT(isMainThread());
363     lazyInitialize();
364     RefPtr<JavaScriptAudioNode> node = JavaScriptAudioNode::create(this, m_destinationNode->sampleRate(), bufferSize);
365
366     refNode(node.get()); // context keeps reference until we stop making javascript rendering callbacks
367     return node;
368 }
369
370 PassRefPtr<BiquadFilterNode> AudioContext::createBiquadFilter()
371 {
372     ASSERT(isMainThread());
373     lazyInitialize();
374     return BiquadFilterNode::create(this, m_destinationNode->sampleRate());
375 }
376
377 PassRefPtr<WaveShaperNode> AudioContext::createWaveShaper()
378 {
379     ASSERT(isMainThread());
380     lazyInitialize();
381     return WaveShaperNode::create(this);
382 }
383
384 PassRefPtr<LowPass2FilterNode> AudioContext::createLowPass2Filter()
385 {
386     ASSERT(isMainThread());
387     lazyInitialize();
388     if (document())
389         document()->addMessage(JSMessageSource, LogMessageType, WarningMessageLevel, "createLowPass2Filter() is deprecated.  Use createBiquadFilter() instead.", 1, String(), 0);
390         
391     return LowPass2FilterNode::create(this, m_destinationNode->sampleRate());
392 }
393
394 PassRefPtr<HighPass2FilterNode> AudioContext::createHighPass2Filter()
395 {
396     ASSERT(isMainThread());
397     lazyInitialize();
398     if (document())
399         document()->addMessage(JSMessageSource, LogMessageType, WarningMessageLevel, "createHighPass2Filter() is deprecated.  Use createBiquadFilter() instead.", 1, String(), 0);
400
401     return HighPass2FilterNode::create(this, m_destinationNode->sampleRate());
402 }
403
404 PassRefPtr<AudioPannerNode> AudioContext::createPanner()
405 {
406     ASSERT(isMainThread());
407     lazyInitialize();
408     return AudioPannerNode::create(this, m_destinationNode->sampleRate());
409 }
410
411 PassRefPtr<ConvolverNode> AudioContext::createConvolver()
412 {
413     ASSERT(isMainThread());
414     lazyInitialize();
415     return ConvolverNode::create(this, m_destinationNode->sampleRate());
416 }
417
418 PassRefPtr<DynamicsCompressorNode> AudioContext::createDynamicsCompressor()
419 {
420     ASSERT(isMainThread());
421     lazyInitialize();
422     return DynamicsCompressorNode::create(this, m_destinationNode->sampleRate());
423 }
424
425 PassRefPtr<RealtimeAnalyserNode> AudioContext::createAnalyser()
426 {
427     ASSERT(isMainThread());
428     lazyInitialize();
429     return RealtimeAnalyserNode::create(this, m_destinationNode->sampleRate());
430 }
431
432 PassRefPtr<AudioGainNode> AudioContext::createGainNode()
433 {
434     ASSERT(isMainThread());
435     lazyInitialize();
436     return AudioGainNode::create(this, m_destinationNode->sampleRate());
437 }
438
439 PassRefPtr<DelayNode> AudioContext::createDelayNode()
440 {
441     ASSERT(isMainThread());
442     lazyInitialize();
443     return DelayNode::create(this, m_destinationNode->sampleRate());
444 }
445
446 PassRefPtr<AudioChannelSplitter> AudioContext::createChannelSplitter()
447 {
448     ASSERT(isMainThread());
449     lazyInitialize();
450     return AudioChannelSplitter::create(this, m_destinationNode->sampleRate());
451 }
452
453 PassRefPtr<AudioChannelMerger> AudioContext::createChannelMerger()
454 {
455     ASSERT(isMainThread());
456     lazyInitialize();
457     return AudioChannelMerger::create(this, m_destinationNode->sampleRate());
458 }
459
460 void AudioContext::notifyNodeFinishedProcessing(AudioNode* node)
461 {
462     ASSERT(isAudioThread());
463     m_finishedNodes.append(node);
464 }
465
466 void AudioContext::derefFinishedSourceNodes()
467 {
468     ASSERT(isGraphOwner());
469     ASSERT(isAudioThread() || isAudioThreadFinished());
470     for (unsigned i = 0; i < m_finishedNodes.size(); i++)
471         derefNode(m_finishedNodes[i]);
472
473     m_finishedNodes.clear();
474 }
475
476 void AudioContext::refNode(AudioNode* node)
477 {
478     ASSERT(isMainThread());
479     AutoLocker locker(this);
480     
481     node->ref(AudioNode::RefTypeConnection);
482     m_referencedNodes.append(node);
483 }
484
485 void AudioContext::derefNode(AudioNode* node)
486 {
487     ASSERT(isGraphOwner());
488     
489     node->deref(AudioNode::RefTypeConnection);
490
491     for (unsigned i = 0; i < m_referencedNodes.size(); ++i) {
492         if (node == m_referencedNodes[i]) {
493             m_referencedNodes.remove(i);
494             break;
495         }
496     }
497 }
498
499 void AudioContext::derefUnfinishedSourceNodes()
500 {
501     ASSERT(isMainThread() && isAudioThreadFinished());
502     for (unsigned i = 0; i < m_referencedNodes.size(); ++i)
503         m_referencedNodes[i]->deref(AudioNode::RefTypeConnection);
504
505     m_referencedNodes.clear();
506 }
507
508 void AudioContext::lock(bool& mustReleaseLock)
509 {
510     // Don't allow regular lock in real-time audio thread.
511     ASSERT(isMainThread());
512
513     ThreadIdentifier thisThread = currentThread();
514
515     if (thisThread == m_graphOwnerThread) {
516         // We already have the lock.
517         mustReleaseLock = false;
518     } else {
519         // Acquire the lock.
520         m_contextGraphMutex.lock();
521         m_graphOwnerThread = thisThread;
522         mustReleaseLock = true;
523     }
524 }
525
526 bool AudioContext::tryLock(bool& mustReleaseLock)
527 {
528     ThreadIdentifier thisThread = currentThread();
529     bool isAudioThread = thisThread == audioThread();
530
531     // Try to catch cases of using try lock on main thread - it should use regular lock.
532     ASSERT(isAudioThread || isAudioThreadFinished());
533     
534     if (!isAudioThread) {
535         // In release build treat tryLock() as lock() (since above ASSERT(isAudioThread) never fires) - this is the best we can do.
536         lock(mustReleaseLock);
537         return true;
538     }
539     
540     bool hasLock;
541     
542     if (thisThread == m_graphOwnerThread) {
543         // Thread already has the lock.
544         hasLock = true;
545         mustReleaseLock = false;
546     } else {
547         // Don't already have the lock - try to acquire it.
548         hasLock = m_contextGraphMutex.tryLock();
549         
550         if (hasLock)
551             m_graphOwnerThread = thisThread;
552
553         mustReleaseLock = hasLock;
554     }
555     
556     return hasLock;
557 }
558
559 void AudioContext::unlock()
560 {
561     ASSERT(currentThread() == m_graphOwnerThread);
562
563     m_graphOwnerThread = UndefinedThreadIdentifier;
564     m_contextGraphMutex.unlock();
565 }
566
567 bool AudioContext::isAudioThread() const
568 {
569     return currentThread() == m_audioThread;
570 }
571
572 bool AudioContext::isGraphOwner() const
573 {
574     return currentThread() == m_graphOwnerThread;
575 }
576
577 void AudioContext::addDeferredFinishDeref(AudioNode* node, AudioNode::RefType refType)
578 {
579     ASSERT(isAudioThread());
580     m_deferredFinishDerefList.append(AudioContext::RefInfo(node, refType));
581 }
582
583 void AudioContext::handlePreRenderTasks()
584 {
585     ASSERT(isAudioThread());
586  
587     // At the beginning of every render quantum, try to update the internal rendering graph state (from main thread changes).
588     // It's OK if the tryLock() fails, we'll just take slightly longer to pick up the changes.
589     bool mustReleaseLock;
590     if (tryLock(mustReleaseLock)) {
591         // Fixup the state of any dirty AudioNodeInputs and AudioNodeOutputs.
592         handleDirtyAudioNodeInputs();
593         handleDirtyAudioNodeOutputs();
594         
595         if (mustReleaseLock)
596             unlock();
597     }
598 }
599
600 void AudioContext::handlePostRenderTasks()
601 {
602     ASSERT(isAudioThread());
603  
604     // Must use a tryLock() here too.  Don't worry, the lock will very rarely be contended and this method is called frequently.
605     // The worst that can happen is that there will be some nodes which will take slightly longer than usual to be deleted or removed
606     // from the render graph (in which case they'll render silence).
607     bool mustReleaseLock;
608     if (tryLock(mustReleaseLock)) {
609         // Take care of finishing any derefs where the tryLock() failed previously.
610         handleDeferredFinishDerefs();
611
612         // Dynamically clean up nodes which are no longer needed.
613         derefFinishedSourceNodes();
614
615         // Don't delete in the real-time thread. Let the main thread do it.
616         // Ref-counted objects held by certain AudioNodes may not be thread-safe.
617         scheduleNodeDeletion();
618
619         // Fixup the state of any dirty AudioNodeInputs and AudioNodeOutputs.
620         handleDirtyAudioNodeInputs();
621         handleDirtyAudioNodeOutputs();
622         
623         if (mustReleaseLock)
624             unlock();
625     }
626 }
627
628 void AudioContext::handleDeferredFinishDerefs()
629 {
630     ASSERT(isAudioThread() && isGraphOwner());
631     for (unsigned i = 0; i < m_deferredFinishDerefList.size(); ++i) {
632         AudioNode* node = m_deferredFinishDerefList[i].m_node;
633         AudioNode::RefType refType = m_deferredFinishDerefList[i].m_refType;
634         node->finishDeref(refType);
635     }
636     
637     m_deferredFinishDerefList.clear();
638 }
639
640 void AudioContext::markForDeletion(AudioNode* node)
641 {
642     ASSERT(isGraphOwner());
643     m_nodesToDelete.append(node);
644 }
645
646 void AudioContext::scheduleNodeDeletion()
647 {
648     bool isGood = m_isInitialized && isGraphOwner();
649     ASSERT(isGood);
650     if (!isGood)
651         return;
652
653     // Make sure to call deleteMarkedNodes() on main thread.    
654     if (m_nodesToDelete.size() && !m_isDeletionScheduled) {
655         m_isDeletionScheduled = true;
656
657         // Don't let ourself get deleted before the callback.
658         // See matching deref() in deleteMarkedNodesDispatch().
659         ref();
660         callOnMainThread(deleteMarkedNodesDispatch, this);
661     }
662 }
663
664 void AudioContext::deleteMarkedNodesDispatch(void* userData)
665 {
666     AudioContext* context = reinterpret_cast<AudioContext*>(userData);
667     ASSERT(context);
668     if (!context)
669         return;
670
671     context->deleteMarkedNodes();
672     context->deref();
673 }
674
675 void AudioContext::deleteMarkedNodes()
676 {
677     ASSERT(isMainThread());
678
679     AutoLocker locker(this);
680     
681     // Note: deleting an AudioNode can cause m_nodesToDelete to grow.
682     while (size_t n = m_nodesToDelete.size()) {
683         AudioNode* node = m_nodesToDelete[n - 1];
684         m_nodesToDelete.removeLast();
685
686         // Before deleting the node, clear out any AudioNodeInputs from m_dirtyAudioNodeInputs.
687         unsigned numberOfInputs = node->numberOfInputs();
688         for (unsigned i = 0; i < numberOfInputs; ++i)
689             m_dirtyAudioNodeInputs.remove(node->input(i));
690
691         // Before deleting the node, clear out any AudioNodeOutputs from m_dirtyAudioNodeOutputs.
692         unsigned numberOfOutputs = node->numberOfOutputs();
693         for (unsigned i = 0; i < numberOfOutputs; ++i)
694             m_dirtyAudioNodeOutputs.remove(node->output(i));
695
696         // Finally, delete it.
697         delete node;
698     }
699     
700     m_isDeletionScheduled = false;
701 }
702
703 void AudioContext::markAudioNodeInputDirty(AudioNodeInput* input)
704 {
705     ASSERT(isGraphOwner());    
706     m_dirtyAudioNodeInputs.add(input);
707 }
708
709 void AudioContext::markAudioNodeOutputDirty(AudioNodeOutput* output)
710 {
711     ASSERT(isGraphOwner());    
712     m_dirtyAudioNodeOutputs.add(output);
713 }
714
715 void AudioContext::handleDirtyAudioNodeInputs()
716 {
717     ASSERT(isGraphOwner());    
718
719     for (HashSet<AudioNodeInput*>::iterator i = m_dirtyAudioNodeInputs.begin(); i != m_dirtyAudioNodeInputs.end(); ++i)
720         (*i)->updateRenderingState();
721
722     m_dirtyAudioNodeInputs.clear();
723 }
724
725 void AudioContext::handleDirtyAudioNodeOutputs()
726 {
727     ASSERT(isGraphOwner());    
728
729     for (HashSet<AudioNodeOutput*>::iterator i = m_dirtyAudioNodeOutputs.begin(); i != m_dirtyAudioNodeOutputs.end(); ++i)
730         (*i)->updateRenderingState();
731
732     m_dirtyAudioNodeOutputs.clear();
733 }
734
735 ScriptExecutionContext* AudioContext::scriptExecutionContext() const
736 {
737     return document();
738 }
739
740 AudioContext* AudioContext::toAudioContext()
741 {
742     return this;
743 }
744
745 void AudioContext::startRendering()
746 {
747     destination()->startRendering();
748 }
749
750 void AudioContext::fireCompletionEvent()
751 {
752     ASSERT(isMainThread());
753     if (!isMainThread())
754         return;
755         
756     AudioBuffer* renderedBuffer = m_renderTarget.get();
757
758     ASSERT(renderedBuffer);
759     if (!renderedBuffer)
760         return;
761
762     // Avoid firing the event if the document has already gone away.
763     if (hasDocument()) {
764         // Call the offline rendering completion event listener.
765         dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer));
766     }
767 }
768
769 } // namespace WebCore
770
771 #endif // ENABLE(WEB_AUDIO)