Upstream version 9.37.195.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / 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 "modules/webaudio/AudioContext.h"
30
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"
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/PassOwnPtr.h"
74 #include "wtf/text/WTFString.h"
75
76 // FIXME: check the proper way to reference an undefined thread ID
77 const int UndefinedThreadIdentifier = 0xffffffff;
78
79 namespace WebCore {
80
81 bool AudioContext::isSampleRateRangeGood(float sampleRate)
82 {
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;
86 }
87
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;
91
92 PassRefPtrWillBeRawPtr<AudioContext> AudioContext::create(Document& document, ExceptionState& exceptionState)
93 {
94     ASSERT(isMainThread());
95     if (s_hardwareContextCount >= MaxHardwareContexts) {
96         exceptionState.throwDOMException(
97             SyntaxError,
98             "number of hardware contexts reached maximum (" + String::number(MaxHardwareContexts) + ").");
99         return nullptr;
100     }
101
102     RefPtrWillBeRawPtr<AudioContext> audioContext(adoptRefWillBeThreadSafeRefCountedGarbageCollected(new AudioContext(&document)));
103     audioContext->suspendIfNeeded();
104     return audioContext.release();
105 }
106
107 // Constructor for rendering to the audio hardware.
108 AudioContext::AudioContext(Document* document)
109     : ActiveDOMObject(document)
110     , m_isStopScheduled(false)
111     , m_isCleared(false)
112     , m_isInitialized(false)
113     , m_destinationNode(nullptr)
114     , m_isDeletionScheduled(false)
115     , m_automaticPullNodesNeedUpdating(false)
116     , m_connectionCount(0)
117     , m_audioThread(0)
118     , m_graphOwnerThread(UndefinedThreadIdentifier)
119     , m_isOfflineContext(false)
120 {
121     ScriptWrappable::init(this);
122
123     m_destinationNode = DefaultAudioDestinationNode::create(this);
124
125     initialize();
126 }
127
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)
132     , m_isCleared(false)
133     , m_isInitialized(false)
134     , m_destinationNode(nullptr)
135     , m_isDeletionScheduled(false)
136     , m_automaticPullNodesNeedUpdating(false)
137     , m_connectionCount(0)
138     , m_audioThread(0)
139     , m_graphOwnerThread(UndefinedThreadIdentifier)
140     , m_isOfflineContext(true)
141 {
142     ScriptWrappable::init(this);
143
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());
148
149     initialize();
150 }
151
152 AudioContext::~AudioContext()
153 {
154 #if DEBUG_AUDIONODE_REFERENCES
155     fprintf(stderr, "%p: AudioContext::~AudioContext()\n", this);
156 #endif
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());
166 }
167
168 void AudioContext::initialize()
169 {
170     if (isInitialized())
171         return;
172
173     FFTFrame::initialize();
174     m_listener = AudioListener::create();
175
176     if (m_destinationNode.get()) {
177         m_destinationNode->initialize();
178
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;
186         }
187
188         m_isInitialized = true;
189     }
190 }
191
192 void AudioContext::clear()
193 {
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();
197
198     // Audio thread is dead. Nobody will schedule node deletion action. Let's do it ourselves.
199     do {
200         m_nodesToDelete.appendVector(m_nodesMarkedForDeletion);
201         m_nodesMarkedForDeletion.clear();
202         deleteMarkedNodes();
203     } while (m_nodesToDelete.size());
204
205     m_isCleared = true;
206 }
207
208 void AudioContext::uninitialize()
209 {
210     ASSERT(isMainThread());
211
212     if (!isInitialized())
213         return;
214
215     // This stops the audio thread and all audio rendering.
216     m_destinationNode->uninitialize();
217
218     if (!isOfflineContext()) {
219         ASSERT(s_hardwareContextCount);
220         --s_hardwareContextCount;
221     }
222
223     // Get rid of the sources which may still be playing.
224     derefUnfinishedSourceNodes();
225
226     m_isInitialized = false;
227 }
228
229 void AudioContext::stopDispatch(void* userData)
230 {
231     AudioContext* context = reinterpret_cast<AudioContext*>(userData);
232     ASSERT(context);
233     if (!context)
234         return;
235
236     context->uninitialize();
237     context->clear();
238 }
239
240 void AudioContext::stop()
241 {
242     // Usually ExecutionContext calls stop twice.
243     if (m_isStopScheduled)
244         return;
245     m_isStopScheduled = true;
246
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);
252 }
253
254 bool AudioContext::hasPendingActivity() const
255 {
256     // According to spec AudioContext must die only after page navigates.
257     return !m_isCleared;
258 }
259
260 PassRefPtrWillBeRawPtr<AudioBuffer> AudioContext::createBuffer(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate, ExceptionState& exceptionState)
261 {
262     RefPtrWillBeRawPtr<AudioBuffer> audioBuffer = AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate, exceptionState);
263
264     return audioBuffer;
265 }
266
267 void AudioContext::decodeAudioData(ArrayBuffer* audioData, PassOwnPtr<AudioBufferCallback> successCallback, PassOwnPtr<AudioBufferCallback> errorCallback, ExceptionState& exceptionState)
268 {
269     if (!audioData) {
270         exceptionState.throwDOMException(
271             SyntaxError,
272             "invalid ArrayBuffer for audioData.");
273         return;
274     }
275     m_audioDecoder.decodeAsync(audioData, sampleRate(), successCallback, errorCallback);
276 }
277
278 PassRefPtrWillBeRawPtr<AudioBufferSourceNode> AudioContext::createBufferSource()
279 {
280     ASSERT(isMainThread());
281     RefPtrWillBeRawPtr<AudioBufferSourceNode> node = AudioBufferSourceNode::create(this, m_destinationNode->sampleRate());
282
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().
285     refNode(node.get());
286
287     return node;
288 }
289
290 PassRefPtrWillBeRawPtr<MediaElementAudioSourceNode> AudioContext::createMediaElementSource(HTMLMediaElement* mediaElement, ExceptionState& exceptionState)
291 {
292     ASSERT(isMainThread());
293     if (!mediaElement) {
294         exceptionState.throwDOMException(
295             InvalidStateError,
296             "invalid HTMLMedialElement.");
297         return nullptr;
298     }
299
300     // First check if this media element already has a source node.
301     if (mediaElement->audioSourceNode()) {
302         exceptionState.throwDOMException(
303             InvalidStateError,
304             "invalid HTMLMediaElement.");
305         return nullptr;
306     }
307
308     RefPtrWillBeRawPtr<MediaElementAudioSourceNode> node = MediaElementAudioSourceNode::create(this, mediaElement);
309
310     mediaElement->setAudioSourceNode(node.get());
311
312     refNode(node.get()); // context keeps reference until node is disconnected
313     return node;
314 }
315
316 PassRefPtrWillBeRawPtr<MediaStreamAudioSourceNode> AudioContext::createMediaStreamSource(MediaStream* mediaStream, ExceptionState& exceptionState)
317 {
318     ASSERT(isMainThread());
319     if (!mediaStream) {
320         exceptionState.throwDOMException(
321             InvalidStateError,
322             "invalid MediaStream source");
323         return nullptr;
324     }
325
326     MediaStreamTrackVector audioTracks = mediaStream->getAudioTracks();
327     if (audioTracks.isEmpty()) {
328         exceptionState.throwDOMException(
329             InvalidStateError,
330             "MediaStream has no audio track");
331         return nullptr;
332     }
333
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());
338
339     // FIXME: Only stereo streams are supported right now. We should be able to accept multi-channel streams.
340     node->setFormat(2, sampleRate());
341
342     refNode(node.get()); // context keeps reference until node is disconnected
343     return node;
344 }
345
346 PassRefPtrWillBeRawPtr<MediaStreamAudioDestinationNode> AudioContext::createMediaStreamDestination()
347 {
348     // Set number of output channels to stereo by default.
349     return MediaStreamAudioDestinationNode::create(this, 2);
350 }
351
352 PassRefPtrWillBeRawPtr<ScriptProcessorNode> AudioContext::createScriptProcessor(ExceptionState& exceptionState)
353 {
354     // Set number of input/output channels to stereo by default.
355     return createScriptProcessor(0, 2, 2, exceptionState);
356 }
357
358 PassRefPtrWillBeRawPtr<ScriptProcessorNode> AudioContext::createScriptProcessor(size_t bufferSize, ExceptionState& exceptionState)
359 {
360     // Set number of input/output channels to stereo by default.
361     return createScriptProcessor(bufferSize, 2, 2, exceptionState);
362 }
363
364 PassRefPtrWillBeRawPtr<ScriptProcessorNode> AudioContext::createScriptProcessor(size_t bufferSize, size_t numberOfInputChannels, ExceptionState& exceptionState)
365 {
366     // Set number of output channels to stereo by default.
367     return createScriptProcessor(bufferSize, numberOfInputChannels, 2, exceptionState);
368 }
369
370 PassRefPtrWillBeRawPtr<ScriptProcessorNode> AudioContext::createScriptProcessor(size_t bufferSize, size_t numberOfInputChannels, size_t numberOfOutputChannels, ExceptionState& exceptionState)
371 {
372     ASSERT(isMainThread());
373     RefPtrWillBeRawPtr<ScriptProcessorNode> node = ScriptProcessorNode::create(this, m_destinationNode->sampleRate(), bufferSize, numberOfInputChannels, numberOfOutputChannels);
374
375     if (!node.get()) {
376         if (!numberOfInputChannels && !numberOfOutputChannels) {
377             exceptionState.throwDOMException(
378                 IndexSizeError,
379                 "number of input channels and output channels cannot both be zero.");
380         } else if (numberOfInputChannels > AudioContext::maxNumberOfChannels()) {
381             exceptionState.throwDOMException(
382                 IndexSizeError,
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(
388                 IndexSizeError,
389                 "number of output channels (" + String::number(numberOfInputChannels)
390                 + ") exceeds maximum ("
391                 + String::number(AudioContext::maxNumberOfChannels()) + ").");
392         } else {
393             exceptionState.throwDOMException(
394                 IndexSizeError,
395                 "buffer size (" + String::number(bufferSize)
396                 + ") must be a power of two between 256 and 16384.");
397         }
398         return nullptr;
399     }
400
401     refNode(node.get()); // context keeps reference until we stop making javascript rendering callbacks
402     return node;
403 }
404
405 PassRefPtrWillBeRawPtr<BiquadFilterNode> AudioContext::createBiquadFilter()
406 {
407     ASSERT(isMainThread());
408     return BiquadFilterNode::create(this, m_destinationNode->sampleRate());
409 }
410
411 PassRefPtrWillBeRawPtr<WaveShaperNode> AudioContext::createWaveShaper()
412 {
413     ASSERT(isMainThread());
414     return WaveShaperNode::create(this);
415 }
416
417 PassRefPtrWillBeRawPtr<PannerNode> AudioContext::createPanner()
418 {
419     ASSERT(isMainThread());
420     return PannerNode::create(this, m_destinationNode->sampleRate());
421 }
422
423 PassRefPtrWillBeRawPtr<ConvolverNode> AudioContext::createConvolver()
424 {
425     ASSERT(isMainThread());
426     return ConvolverNode::create(this, m_destinationNode->sampleRate());
427 }
428
429 PassRefPtrWillBeRawPtr<DynamicsCompressorNode> AudioContext::createDynamicsCompressor()
430 {
431     ASSERT(isMainThread());
432     return DynamicsCompressorNode::create(this, m_destinationNode->sampleRate());
433 }
434
435 PassRefPtrWillBeRawPtr<AnalyserNode> AudioContext::createAnalyser()
436 {
437     ASSERT(isMainThread());
438     return AnalyserNode::create(this, m_destinationNode->sampleRate());
439 }
440
441 PassRefPtrWillBeRawPtr<GainNode> AudioContext::createGain()
442 {
443     ASSERT(isMainThread());
444     return GainNode::create(this, m_destinationNode->sampleRate());
445 }
446
447 PassRefPtrWillBeRawPtr<DelayNode> AudioContext::createDelay(ExceptionState& exceptionState)
448 {
449     const double defaultMaxDelayTime = 1;
450     return createDelay(defaultMaxDelayTime, exceptionState);
451 }
452
453 PassRefPtrWillBeRawPtr<DelayNode> AudioContext::createDelay(double maxDelayTime, ExceptionState& exceptionState)
454 {
455     ASSERT(isMainThread());
456     RefPtrWillBeRawPtr<DelayNode> node = DelayNode::create(this, m_destinationNode->sampleRate(), maxDelayTime, exceptionState);
457     if (exceptionState.hadException())
458         return nullptr;
459     return node;
460 }
461
462 PassRefPtrWillBeRawPtr<ChannelSplitterNode> AudioContext::createChannelSplitter(ExceptionState& exceptionState)
463 {
464     const unsigned ChannelSplitterDefaultNumberOfOutputs = 6;
465     return createChannelSplitter(ChannelSplitterDefaultNumberOfOutputs, exceptionState);
466 }
467
468 PassRefPtrWillBeRawPtr<ChannelSplitterNode> AudioContext::createChannelSplitter(size_t numberOfOutputs, ExceptionState& exceptionState)
469 {
470     ASSERT(isMainThread());
471
472     RefPtrWillBeRawPtr<ChannelSplitterNode> node = ChannelSplitterNode::create(this, m_destinationNode->sampleRate(), numberOfOutputs);
473
474     if (!node.get()) {
475         exceptionState.throwDOMException(
476             IndexSizeError,
477             "number of outputs (" + String::number(numberOfOutputs)
478             + ") must be between 1 and "
479             + String::number(AudioContext::maxNumberOfChannels()) + ".");
480         return nullptr;
481     }
482
483     return node;
484 }
485
486 PassRefPtrWillBeRawPtr<ChannelMergerNode> AudioContext::createChannelMerger(ExceptionState& exceptionState)
487 {
488     const unsigned ChannelMergerDefaultNumberOfInputs = 6;
489     return createChannelMerger(ChannelMergerDefaultNumberOfInputs, exceptionState);
490 }
491
492 PassRefPtrWillBeRawPtr<ChannelMergerNode> AudioContext::createChannelMerger(size_t numberOfInputs, ExceptionState& exceptionState)
493 {
494     ASSERT(isMainThread());
495
496     RefPtrWillBeRawPtr<ChannelMergerNode> node = ChannelMergerNode::create(this, m_destinationNode->sampleRate(), numberOfInputs);
497
498     if (!node.get()) {
499         exceptionState.throwDOMException(
500             IndexSizeError,
501             "number of inputs (" + String::number(numberOfInputs)
502             + ") must be between 1 and "
503             + String::number(AudioContext::maxNumberOfChannels()) + ".");
504         return nullptr;
505     }
506
507     return node;
508 }
509
510 PassRefPtrWillBeRawPtr<OscillatorNode> AudioContext::createOscillator()
511 {
512     ASSERT(isMainThread());
513
514     RefPtrWillBeRawPtr<OscillatorNode> node = OscillatorNode::create(this, m_destinationNode->sampleRate());
515
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().
518     refNode(node.get());
519
520     return node;
521 }
522
523 PassRefPtrWillBeRawPtr<PeriodicWave> AudioContext::createPeriodicWave(Float32Array* real, Float32Array* imag, ExceptionState& exceptionState)
524 {
525     ASSERT(isMainThread());
526
527     if (!real) {
528         exceptionState.throwDOMException(
529             SyntaxError,
530             "invalid real array");
531         return nullptr;
532     }
533
534     if (!imag) {
535         exceptionState.throwDOMException(
536             SyntaxError,
537             "invalid imaginary array");
538         return nullptr;
539     }
540
541     if (real->length() != imag->length()) {
542         exceptionState.throwDOMException(
543             IndexSizeError,
544             "length of real array (" + String::number(real->length())
545             + ") and length of imaginary array (" +  String::number(imag->length())
546             + ") must match.");
547         return nullptr;
548     }
549
550     if (real->length() > 4096) {
551         exceptionState.throwDOMException(
552             IndexSizeError,
553             "length of real array (" + String::number(real->length())
554             + ") exceeds allowed maximum of 4096");
555         return nullptr;
556     }
557
558     if (imag->length() > 4096) {
559         exceptionState.throwDOMException(
560             IndexSizeError,
561             "length of imaginary array (" + String::number(imag->length())
562             + ") exceeds allowed maximum of 4096");
563         return nullptr;
564     }
565
566     return PeriodicWave::create(sampleRate(), real, imag);
567 }
568
569 void AudioContext::notifyNodeFinishedProcessing(AudioNode* node)
570 {
571     ASSERT(isAudioThread());
572     m_finishedNodes.append(node);
573 }
574
575 void AudioContext::derefFinishedSourceNodes()
576 {
577     ASSERT(isGraphOwner());
578     ASSERT(isAudioThread());
579     for (unsigned i = 0; i < m_finishedNodes.size(); i++)
580         derefNode(m_finishedNodes[i]);
581
582     m_finishedNodes.clear();
583 }
584
585 void AudioContext::refNode(AudioNode* node)
586 {
587     ASSERT(isMainThread());
588     AutoLocker locker(this);
589
590     node->ref(AudioNode::RefTypeConnection);
591     m_referencedNodes.append(node);
592 }
593
594 void AudioContext::derefNode(AudioNode* node)
595 {
596     ASSERT(isGraphOwner());
597
598     node->deref(AudioNode::RefTypeConnection);
599
600     for (unsigned i = 0; i < m_referencedNodes.size(); ++i) {
601         if (node == m_referencedNodes[i]) {
602             m_referencedNodes.remove(i);
603             break;
604         }
605     }
606 }
607
608 void AudioContext::derefUnfinishedSourceNodes()
609 {
610     ASSERT(isMainThread());
611     for (unsigned i = 0; i < m_referencedNodes.size(); ++i)
612         m_referencedNodes[i]->deref(AudioNode::RefTypeConnection);
613
614     m_referencedNodes.clear();
615 }
616
617 void AudioContext::lock(bool& mustReleaseLock)
618 {
619     // Don't allow regular lock in real-time audio thread.
620     ASSERT(isMainThread());
621
622     ThreadIdentifier thisThread = currentThread();
623
624     if (thisThread == m_graphOwnerThread) {
625         // We already have the lock.
626         mustReleaseLock = false;
627     } else {
628         // Acquire the lock.
629         m_contextGraphMutex.lock();
630         m_graphOwnerThread = thisThread;
631         mustReleaseLock = true;
632     }
633 }
634
635 bool AudioContext::tryLock(bool& mustReleaseLock)
636 {
637     ThreadIdentifier thisThread = currentThread();
638     bool isAudioThread = thisThread == audioThread();
639
640     // Try to catch cases of using try lock on main thread - it should use regular lock.
641     ASSERT(isAudioThread);
642
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);
646         return true;
647     }
648
649     bool hasLock;
650
651     if (thisThread == m_graphOwnerThread) {
652         // Thread already has the lock.
653         hasLock = true;
654         mustReleaseLock = false;
655     } else {
656         // Don't already have the lock - try to acquire it.
657         hasLock = m_contextGraphMutex.tryLock();
658
659         if (hasLock)
660             m_graphOwnerThread = thisThread;
661
662         mustReleaseLock = hasLock;
663     }
664
665     return hasLock;
666 }
667
668 void AudioContext::unlock()
669 {
670     ASSERT(currentThread() == m_graphOwnerThread);
671
672     m_graphOwnerThread = UndefinedThreadIdentifier;
673     m_contextGraphMutex.unlock();
674 }
675
676 bool AudioContext::isAudioThread() const
677 {
678     return currentThread() == m_audioThread;
679 }
680
681 bool AudioContext::isGraphOwner() const
682 {
683     return currentThread() == m_graphOwnerThread;
684 }
685
686 void AudioContext::addDeferredFinishDeref(AudioNode* node)
687 {
688     ASSERT(isAudioThread());
689     m_deferredFinishDerefList.append(node);
690 }
691
692 void AudioContext::handlePreRenderTasks()
693 {
694     ASSERT(isAudioThread());
695
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();
703
704         updateAutomaticPullNodes();
705
706         if (mustReleaseLock)
707             unlock();
708     }
709 }
710
711 void AudioContext::handlePostRenderTasks()
712 {
713     ASSERT(isAudioThread());
714
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();
722
723         // Dynamically clean up nodes which are no longer needed.
724         derefFinishedSourceNodes();
725
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();
729
730         // Fixup the state of any dirty AudioSummingJunctions and AudioNodeOutputs.
731         handleDirtyAudioSummingJunctions();
732         handleDirtyAudioNodeOutputs();
733
734         updateAutomaticPullNodes();
735
736         if (mustReleaseLock)
737             unlock();
738     }
739 }
740
741 void AudioContext::handleDeferredFinishDerefs()
742 {
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);
747     }
748
749     m_deferredFinishDerefList.clear();
750 }
751
752 void AudioContext::markForDeletion(AudioNode* node)
753 {
754     ASSERT(isGraphOwner());
755
756     if (!isInitialized())
757         m_nodesToDelete.append(node);
758     else
759         m_nodesMarkedForDeletion.append(node);
760
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);
766 }
767
768 void AudioContext::scheduleNodeDeletion()
769 {
770     bool isGood = isInitialized() && isGraphOwner();
771     ASSERT(isGood);
772     if (!isGood)
773         return;
774
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();
779
780         m_isDeletionScheduled = true;
781
782         // Don't let ourself get deleted before the callback.
783         // See matching deref() in deleteMarkedNodesDispatch().
784         ref();
785         callOnMainThread(deleteMarkedNodesDispatch, this);
786     }
787 }
788
789 void AudioContext::deleteMarkedNodesDispatch(void* userData)
790 {
791     AudioContext* context = reinterpret_cast<AudioContext*>(userData);
792     ASSERT(context);
793     if (!context)
794         return;
795
796     context->deleteMarkedNodes();
797     context->deref();
798 }
799
800 void AudioContext::deleteMarkedNodes()
801 {
802     ASSERT(isMainThread());
803
804     // Protect this object from being deleted before we release the mutex locked by AutoLocker.
805     RefPtrWillBeRawPtr<AudioContext> protect(this);
806     {
807         AutoLocker locker(this);
808
809         while (size_t n = m_nodesToDelete.size()) {
810             AudioNode* node = m_nodesToDelete[n - 1];
811             m_nodesToDelete.removeLast();
812
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));
817
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));
822 #if ENABLE(OILPAN)
823             // Finally, clear the keep alive handle that keeps this
824             // object from being collected.
825             node->clearKeepAlive();
826 #else
827             // Finally, delete it.
828             delete node;
829 #endif
830         }
831         m_isDeletionScheduled = false;
832     }
833 }
834
835 void AudioContext::markSummingJunctionDirty(AudioSummingJunction* summingJunction)
836 {
837     ASSERT(isGraphOwner());
838     m_dirtySummingJunctions.add(summingJunction);
839 }
840
841 void AudioContext::removeMarkedSummingJunction(AudioSummingJunction* summingJunction)
842 {
843     ASSERT(isMainThread());
844     AutoLocker locker(this);
845     m_dirtySummingJunctions.remove(summingJunction);
846 }
847
848 void AudioContext::markAudioNodeOutputDirty(AudioNodeOutput* output)
849 {
850     ASSERT(isGraphOwner());
851     m_dirtyAudioNodeOutputs.add(output);
852 }
853
854 void AudioContext::handleDirtyAudioSummingJunctions()
855 {
856     ASSERT(isGraphOwner());
857
858     for (HashSet<AudioSummingJunction* >::iterator i = m_dirtySummingJunctions.begin(); i != m_dirtySummingJunctions.end(); ++i)
859         (*i)->updateRenderingState();
860
861     m_dirtySummingJunctions.clear();
862 }
863
864 void AudioContext::handleDirtyAudioNodeOutputs()
865 {
866     ASSERT(isGraphOwner());
867
868     for (HashSet<AudioNodeOutput*>::iterator i = m_dirtyAudioNodeOutputs.begin(); i != m_dirtyAudioNodeOutputs.end(); ++i)
869         (*i)->updateRenderingState();
870
871     m_dirtyAudioNodeOutputs.clear();
872 }
873
874 void AudioContext::addAutomaticPullNode(AudioNode* node)
875 {
876     ASSERT(isGraphOwner());
877
878     if (!m_automaticPullNodes.contains(node)) {
879         m_automaticPullNodes.add(node);
880         m_automaticPullNodesNeedUpdating = true;
881     }
882 }
883
884 void AudioContext::removeAutomaticPullNode(AudioNode* node)
885 {
886     ASSERT(isGraphOwner());
887
888     if (m_automaticPullNodes.contains(node)) {
889         m_automaticPullNodes.remove(node);
890         m_automaticPullNodesNeedUpdating = true;
891     }
892 }
893
894 void AudioContext::updateAutomaticPullNodes()
895 {
896     ASSERT(isGraphOwner());
897
898     if (m_automaticPullNodesNeedUpdating) {
899         // Copy from m_automaticPullNodes to m_renderingAutomaticPullNodes.
900         m_renderingAutomaticPullNodes.resize(m_automaticPullNodes.size());
901
902         unsigned j = 0;
903         for (HashSet<AudioNode*>::iterator i = m_automaticPullNodes.begin(); i != m_automaticPullNodes.end(); ++i, ++j) {
904             AudioNode* output = *i;
905             m_renderingAutomaticPullNodes[j] = output;
906         }
907
908         m_automaticPullNodesNeedUpdating = false;
909     }
910 }
911
912 void AudioContext::processAutomaticPullNodes(size_t framesToProcess)
913 {
914     ASSERT(isAudioThread());
915
916     for (unsigned i = 0; i < m_renderingAutomaticPullNodes.size(); ++i)
917         m_renderingAutomaticPullNodes[i]->processIfNecessary(framesToProcess);
918 }
919
920 const AtomicString& AudioContext::interfaceName() const
921 {
922     return EventTargetNames::AudioContext;
923 }
924
925 ExecutionContext* AudioContext::executionContext() const
926 {
927     return m_isStopScheduled ? 0 : ActiveDOMObject::executionContext();
928 }
929
930 void AudioContext::startRendering()
931 {
932     destination()->startRendering();
933 }
934
935 void AudioContext::fireCompletionEvent()
936 {
937     ASSERT(isMainThread());
938     if (!isMainThread())
939         return;
940
941     AudioBuffer* renderedBuffer = m_renderTarget.get();
942
943     ASSERT(renderedBuffer);
944     if (!renderedBuffer)
945         return;
946
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));
951     }
952 }
953
954 void AudioContext::trace(Visitor* visitor)
955 {
956     visitor->trace(m_renderTarget);
957     visitor->trace(m_destinationNode);
958     visitor->trace(m_listener);
959     visitor->trace(m_dirtySummingJunctions);
960     EventTargetWithInlineData::trace(visitor);
961 }
962
963 } // namespace WebCore
964
965 #endif // ENABLE(WEB_AUDIO)