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