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/core/v8/ExceptionMessages.h"
32 #include "bindings/core/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 WTF::ThreadIdentifier UndefinedThreadIdentifier = 0xffffffff;
78
79 namespace blink {
80
81 // Don't allow more than this number of simultaneous AudioContexts talking to hardware.
82 const unsigned MaxHardwareContexts = 6;
83 unsigned AudioContext::s_hardwareContextCount = 0;
84
create(Document & document,ExceptionState & exceptionState)85 AudioContext* AudioContext::create(Document& document, ExceptionState& exceptionState)
86 {
87 ASSERT(isMainThread());
88 if (s_hardwareContextCount >= MaxHardwareContexts) {
89 exceptionState.throwDOMException(
90 SyntaxError,
91 "number of hardware contexts reached maximum (" + String::number(MaxHardwareContexts) + ").");
92 return 0;
93 }
94
95 AudioContext* audioContext = adoptRefCountedGarbageCollectedWillBeNoop(new AudioContext(&document));
96 audioContext->suspendIfNeeded();
97 return audioContext;
98 }
99
100 // Constructor for rendering to the audio hardware.
AudioContext(Document * document)101 AudioContext::AudioContext(Document* document)
102 : ActiveDOMObject(document)
103 , m_isStopScheduled(false)
104 , m_isCleared(false)
105 , m_isInitialized(false)
106 , m_destinationNode(nullptr)
107 , m_automaticPullNodesNeedUpdating(false)
108 , m_connectionCount(0)
109 , m_audioThread(0)
110 , m_graphOwnerThread(UndefinedThreadIdentifier)
111 , m_isOfflineContext(false)
112 {
113 m_destinationNode = DefaultAudioDestinationNode::create(this);
114
115 initialize();
116 #if DEBUG_AUDIONODE_REFERENCES
117 fprintf(stderr, "%p: AudioContext::AudioContext() #%u\n", this, AudioContext::s_hardwareContextCount);
118 #endif
119 }
120
121 // Constructor for offline (non-realtime) rendering.
AudioContext(Document * document,unsigned numberOfChannels,size_t numberOfFrames,float sampleRate)122 AudioContext::AudioContext(Document* document, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate)
123 : ActiveDOMObject(document)
124 , m_isStopScheduled(false)
125 , m_isCleared(false)
126 , m_isInitialized(false)
127 , m_destinationNode(nullptr)
128 , m_automaticPullNodesNeedUpdating(false)
129 , m_connectionCount(0)
130 , m_audioThread(0)
131 , m_graphOwnerThread(UndefinedThreadIdentifier)
132 , m_isOfflineContext(true)
133 {
134 // Create a new destination for offline rendering.
135 m_renderTarget = AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate);
136 if (m_renderTarget.get())
137 m_destinationNode = OfflineAudioDestinationNode::create(this, m_renderTarget.get());
138
139 initialize();
140 }
141
~AudioContext()142 AudioContext::~AudioContext()
143 {
144 #if DEBUG_AUDIONODE_REFERENCES
145 fprintf(stderr, "%p: AudioContext::~AudioContext()\n", this);
146 #endif
147 // AudioNodes keep a reference to their context, so there should be no way to be in the destructor if there are still AudioNodes around.
148 ASSERT(!m_isInitialized);
149 ASSERT(!m_referencedNodes.size());
150 ASSERT(!m_finishedNodes.size());
151 ASSERT(!m_automaticPullNodes.size());
152 if (m_automaticPullNodesNeedUpdating)
153 m_renderingAutomaticPullNodes.resize(m_automaticPullNodes.size());
154 ASSERT(!m_renderingAutomaticPullNodes.size());
155 }
156
initialize()157 void AudioContext::initialize()
158 {
159 if (isInitialized())
160 return;
161
162 FFTFrame::initialize();
163 m_listener = AudioListener::create();
164
165 if (m_destinationNode.get()) {
166 m_destinationNode->initialize();
167
168 if (!isOfflineContext()) {
169 // This starts the audio thread. The destination node's provideInput() method will now be called repeatedly to render audio.
170 // Each time provideInput() is called, a portion of the audio stream is rendered. Let's call this time period a "render quantum".
171 // NOTE: for now default AudioContext does not need an explicit startRendering() call from JavaScript.
172 // We may want to consider requiring it for symmetry with OfflineAudioContext.
173 m_destinationNode->startRendering();
174 ++s_hardwareContextCount;
175 }
176
177 m_isInitialized = true;
178 }
179 }
180
clear()181 void AudioContext::clear()
182 {
183 // We need to run disposers before destructing m_contextGraphMutex.
184 m_liveAudioSummingJunctions.clear();
185 m_liveNodes.clear();
186 m_destinationNode.clear();
187 m_isCleared = true;
188 }
189
uninitialize()190 void AudioContext::uninitialize()
191 {
192 ASSERT(isMainThread());
193
194 if (!isInitialized())
195 return;
196
197 // This stops the audio thread and all audio rendering.
198 m_destinationNode->uninitialize();
199
200 if (!isOfflineContext()) {
201 ASSERT(s_hardwareContextCount);
202 --s_hardwareContextCount;
203 }
204
205 // Get rid of the sources which may still be playing.
206 derefUnfinishedSourceNodes();
207
208 m_isInitialized = false;
209 ASSERT(m_listener);
210 m_listener->waitForHRTFDatabaseLoaderThreadCompletion();
211
212 clear();
213 }
214
stop()215 void AudioContext::stop()
216 {
217 // Usually ExecutionContext calls stop twice.
218 if (m_isStopScheduled)
219 return;
220 m_isStopScheduled = true;
221
222 // Don't call uninitialize() immediately here because the ExecutionContext is in the middle
223 // of dealing with all of its ActiveDOMObjects at this point. uninitialize() can de-reference other
224 // ActiveDOMObjects so let's schedule uninitialize() to be called later.
225 // FIXME: see if there's a more direct way to handle this issue.
226 callOnMainThread(bind(&AudioContext::uninitialize, this));
227 }
228
hasPendingActivity() const229 bool AudioContext::hasPendingActivity() const
230 {
231 // According to spec AudioContext must die only after page navigates.
232 return !m_isCleared;
233 }
234
createBuffer(unsigned numberOfChannels,size_t numberOfFrames,float sampleRate,ExceptionState & exceptionState)235 AudioBuffer* AudioContext::createBuffer(unsigned numberOfChannels, size_t numberOfFrames, float sampleRate, ExceptionState& exceptionState)
236 {
237 return AudioBuffer::create(numberOfChannels, numberOfFrames, sampleRate, exceptionState);
238 }
239
decodeAudioData(ArrayBuffer * audioData,AudioBufferCallback * successCallback,AudioBufferCallback * errorCallback,ExceptionState & exceptionState)240 void AudioContext::decodeAudioData(ArrayBuffer* audioData, AudioBufferCallback* successCallback, AudioBufferCallback* errorCallback, ExceptionState& exceptionState)
241 {
242 if (!audioData) {
243 exceptionState.throwDOMException(
244 SyntaxError,
245 "invalid ArrayBuffer for audioData.");
246 return;
247 }
248 m_audioDecoder.decodeAsync(audioData, sampleRate(), successCallback, errorCallback);
249 }
250
createBufferSource()251 AudioBufferSourceNode* AudioContext::createBufferSource()
252 {
253 ASSERT(isMainThread());
254 AudioBufferSourceNode* node = AudioBufferSourceNode::create(this, m_destinationNode->sampleRate());
255
256 // Because this is an AudioScheduledSourceNode, the context keeps a reference until it has finished playing.
257 // When this happens, AudioScheduledSourceNode::finish() calls AudioContext::notifyNodeFinishedProcessing().
258 refNode(node);
259
260 return node;
261 }
262
createMediaElementSource(HTMLMediaElement * mediaElement,ExceptionState & exceptionState)263 MediaElementAudioSourceNode* AudioContext::createMediaElementSource(HTMLMediaElement* mediaElement, ExceptionState& exceptionState)
264 {
265 ASSERT(isMainThread());
266 if (!mediaElement) {
267 exceptionState.throwDOMException(
268 InvalidStateError,
269 "invalid HTMLMedialElement.");
270 return 0;
271 }
272
273 // First check if this media element already has a source node.
274 if (mediaElement->audioSourceNode()) {
275 exceptionState.throwDOMException(
276 InvalidStateError,
277 "invalid HTMLMediaElement.");
278 return 0;
279 }
280
281 MediaElementAudioSourceNode* node = MediaElementAudioSourceNode::create(this, mediaElement);
282
283 mediaElement->setAudioSourceNode(node);
284
285 refNode(node); // context keeps reference until node is disconnected
286 return node;
287 }
288
createMediaStreamSource(MediaStream * mediaStream,ExceptionState & exceptionState)289 MediaStreamAudioSourceNode* AudioContext::createMediaStreamSource(MediaStream* mediaStream, ExceptionState& exceptionState)
290 {
291 ASSERT(isMainThread());
292 if (!mediaStream) {
293 exceptionState.throwDOMException(
294 InvalidStateError,
295 "invalid MediaStream source");
296 return 0;
297 }
298
299 MediaStreamTrackVector audioTracks = mediaStream->getAudioTracks();
300 if (audioTracks.isEmpty()) {
301 exceptionState.throwDOMException(
302 InvalidStateError,
303 "MediaStream has no audio track");
304 return 0;
305 }
306
307 // Use the first audio track in the media stream.
308 MediaStreamTrack* audioTrack = audioTracks[0];
309 OwnPtr<AudioSourceProvider> provider = audioTrack->createWebAudioSource();
310 MediaStreamAudioSourceNode* node = MediaStreamAudioSourceNode::create(this, mediaStream, audioTrack, provider.release());
311
312 // FIXME: Only stereo streams are supported right now. We should be able to accept multi-channel streams.
313 node->setFormat(2, sampleRate());
314
315 refNode(node); // context keeps reference until node is disconnected
316 return node;
317 }
318
createMediaStreamDestination()319 MediaStreamAudioDestinationNode* AudioContext::createMediaStreamDestination()
320 {
321 // Set number of output channels to stereo by default.
322 return MediaStreamAudioDestinationNode::create(this, 2);
323 }
324
createScriptProcessor(ExceptionState & exceptionState)325 ScriptProcessorNode* AudioContext::createScriptProcessor(ExceptionState& exceptionState)
326 {
327 // Set number of input/output channels to stereo by default.
328 return createScriptProcessor(0, 2, 2, exceptionState);
329 }
330
createScriptProcessor(size_t bufferSize,ExceptionState & exceptionState)331 ScriptProcessorNode* AudioContext::createScriptProcessor(size_t bufferSize, ExceptionState& exceptionState)
332 {
333 // Set number of input/output channels to stereo by default.
334 return createScriptProcessor(bufferSize, 2, 2, exceptionState);
335 }
336
createScriptProcessor(size_t bufferSize,size_t numberOfInputChannels,ExceptionState & exceptionState)337 ScriptProcessorNode* AudioContext::createScriptProcessor(size_t bufferSize, size_t numberOfInputChannels, ExceptionState& exceptionState)
338 {
339 // Set number of output channels to stereo by default.
340 return createScriptProcessor(bufferSize, numberOfInputChannels, 2, exceptionState);
341 }
342
createScriptProcessor(size_t bufferSize,size_t numberOfInputChannels,size_t numberOfOutputChannels,ExceptionState & exceptionState)343 ScriptProcessorNode* AudioContext::createScriptProcessor(size_t bufferSize, size_t numberOfInputChannels, size_t numberOfOutputChannels, ExceptionState& exceptionState)
344 {
345 ASSERT(isMainThread());
346 ScriptProcessorNode* node = ScriptProcessorNode::create(this, m_destinationNode->sampleRate(), bufferSize, numberOfInputChannels, numberOfOutputChannels);
347
348 if (!node) {
349 if (!numberOfInputChannels && !numberOfOutputChannels) {
350 exceptionState.throwDOMException(
351 IndexSizeError,
352 "number of input channels and output channels cannot both be zero.");
353 } else if (numberOfInputChannels > AudioContext::maxNumberOfChannels()) {
354 exceptionState.throwDOMException(
355 IndexSizeError,
356 "number of input channels (" + String::number(numberOfInputChannels)
357 + ") exceeds maximum ("
358 + String::number(AudioContext::maxNumberOfChannels()) + ").");
359 } else if (numberOfOutputChannels > AudioContext::maxNumberOfChannels()) {
360 exceptionState.throwDOMException(
361 IndexSizeError,
362 "number of output channels (" + String::number(numberOfInputChannels)
363 + ") exceeds maximum ("
364 + String::number(AudioContext::maxNumberOfChannels()) + ").");
365 } else {
366 exceptionState.throwDOMException(
367 IndexSizeError,
368 "buffer size (" + String::number(bufferSize)
369 + ") must be a power of two between 256 and 16384.");
370 }
371 return 0;
372 }
373
374 refNode(node); // context keeps reference until we stop making javascript rendering callbacks
375 return node;
376 }
377
createBiquadFilter()378 BiquadFilterNode* AudioContext::createBiquadFilter()
379 {
380 ASSERT(isMainThread());
381 return BiquadFilterNode::create(this, m_destinationNode->sampleRate());
382 }
383
createWaveShaper()384 WaveShaperNode* AudioContext::createWaveShaper()
385 {
386 ASSERT(isMainThread());
387 return WaveShaperNode::create(this);
388 }
389
createPanner()390 PannerNode* AudioContext::createPanner()
391 {
392 ASSERT(isMainThread());
393 return PannerNode::create(this, m_destinationNode->sampleRate());
394 }
395
createConvolver()396 ConvolverNode* AudioContext::createConvolver()
397 {
398 ASSERT(isMainThread());
399 return ConvolverNode::create(this, m_destinationNode->sampleRate());
400 }
401
createDynamicsCompressor()402 DynamicsCompressorNode* AudioContext::createDynamicsCompressor()
403 {
404 ASSERT(isMainThread());
405 return DynamicsCompressorNode::create(this, m_destinationNode->sampleRate());
406 }
407
createAnalyser()408 AnalyserNode* AudioContext::createAnalyser()
409 {
410 ASSERT(isMainThread());
411 return AnalyserNode::create(this, m_destinationNode->sampleRate());
412 }
413
createGain()414 GainNode* AudioContext::createGain()
415 {
416 ASSERT(isMainThread());
417 return GainNode::create(this, m_destinationNode->sampleRate());
418 }
419
createDelay(ExceptionState & exceptionState)420 DelayNode* AudioContext::createDelay(ExceptionState& exceptionState)
421 {
422 const double defaultMaxDelayTime = 1;
423 return createDelay(defaultMaxDelayTime, exceptionState);
424 }
425
createDelay(double maxDelayTime,ExceptionState & exceptionState)426 DelayNode* AudioContext::createDelay(double maxDelayTime, ExceptionState& exceptionState)
427 {
428 ASSERT(isMainThread());
429 DelayNode* node = DelayNode::create(this, m_destinationNode->sampleRate(), maxDelayTime, exceptionState);
430 if (exceptionState.hadException())
431 return 0;
432 return node;
433 }
434
createChannelSplitter(ExceptionState & exceptionState)435 ChannelSplitterNode* AudioContext::createChannelSplitter(ExceptionState& exceptionState)
436 {
437 const unsigned ChannelSplitterDefaultNumberOfOutputs = 6;
438 return createChannelSplitter(ChannelSplitterDefaultNumberOfOutputs, exceptionState);
439 }
440
createChannelSplitter(size_t numberOfOutputs,ExceptionState & exceptionState)441 ChannelSplitterNode* AudioContext::createChannelSplitter(size_t numberOfOutputs, ExceptionState& exceptionState)
442 {
443 ASSERT(isMainThread());
444
445 ChannelSplitterNode* node = ChannelSplitterNode::create(this, m_destinationNode->sampleRate(), numberOfOutputs);
446
447 if (!node) {
448 exceptionState.throwDOMException(
449 IndexSizeError,
450 "number of outputs (" + String::number(numberOfOutputs)
451 + ") must be between 1 and "
452 + String::number(AudioContext::maxNumberOfChannels()) + ".");
453 return 0;
454 }
455
456 return node;
457 }
458
createChannelMerger(ExceptionState & exceptionState)459 ChannelMergerNode* AudioContext::createChannelMerger(ExceptionState& exceptionState)
460 {
461 const unsigned ChannelMergerDefaultNumberOfInputs = 6;
462 return createChannelMerger(ChannelMergerDefaultNumberOfInputs, exceptionState);
463 }
464
createChannelMerger(size_t numberOfInputs,ExceptionState & exceptionState)465 ChannelMergerNode* AudioContext::createChannelMerger(size_t numberOfInputs, ExceptionState& exceptionState)
466 {
467 ASSERT(isMainThread());
468
469 ChannelMergerNode* node = ChannelMergerNode::create(this, m_destinationNode->sampleRate(), numberOfInputs);
470
471 if (!node) {
472 exceptionState.throwDOMException(
473 IndexSizeError,
474 "number of inputs (" + String::number(numberOfInputs)
475 + ") must be between 1 and "
476 + String::number(AudioContext::maxNumberOfChannels()) + ".");
477 return 0;
478 }
479
480 return node;
481 }
482
createOscillator()483 OscillatorNode* AudioContext::createOscillator()
484 {
485 ASSERT(isMainThread());
486
487 OscillatorNode* node = OscillatorNode::create(this, m_destinationNode->sampleRate());
488
489 // Because this is an AudioScheduledSourceNode, the context keeps a reference until it has finished playing.
490 // When this happens, AudioScheduledSourceNode::finish() calls AudioContext::notifyNodeFinishedProcessing().
491 refNode(node);
492
493 return node;
494 }
495
createPeriodicWave(Float32Array * real,Float32Array * imag,ExceptionState & exceptionState)496 PeriodicWave* AudioContext::createPeriodicWave(Float32Array* real, Float32Array* imag, ExceptionState& exceptionState)
497 {
498 ASSERT(isMainThread());
499
500 if (!real) {
501 exceptionState.throwDOMException(
502 SyntaxError,
503 "invalid real array");
504 return 0;
505 }
506
507 if (!imag) {
508 exceptionState.throwDOMException(
509 SyntaxError,
510 "invalid imaginary array");
511 return 0;
512 }
513
514 if (real->length() != imag->length()) {
515 exceptionState.throwDOMException(
516 IndexSizeError,
517 "length of real array (" + String::number(real->length())
518 + ") and length of imaginary array (" + String::number(imag->length())
519 + ") must match.");
520 return 0;
521 }
522
523 if (real->length() > 4096) {
524 exceptionState.throwDOMException(
525 IndexSizeError,
526 "length of real array (" + String::number(real->length())
527 + ") exceeds allowed maximum of 4096");
528 return 0;
529 }
530
531 if (imag->length() > 4096) {
532 exceptionState.throwDOMException(
533 IndexSizeError,
534 "length of imaginary array (" + String::number(imag->length())
535 + ") exceeds allowed maximum of 4096");
536 return 0;
537 }
538
539 return PeriodicWave::create(sampleRate(), real, imag);
540 }
541
notifyNodeFinishedProcessing(AudioNode * node)542 void AudioContext::notifyNodeFinishedProcessing(AudioNode* node)
543 {
544 ASSERT(isAudioThread());
545 m_finishedNodes.append(node);
546 }
547
derefFinishedSourceNodes()548 void AudioContext::derefFinishedSourceNodes()
549 {
550 ASSERT(isGraphOwner());
551 ASSERT(isAudioThread());
552 for (unsigned i = 0; i < m_finishedNodes.size(); i++)
553 derefNode(m_finishedNodes[i]);
554
555 m_finishedNodes.clear();
556 }
557
refNode(AudioNode * node)558 void AudioContext::refNode(AudioNode* node)
559 {
560 ASSERT(isMainThread());
561 AutoLocker locker(this);
562
563 m_referencedNodes.append(node);
564 node->makeConnection();
565 }
566
derefNode(AudioNode * node)567 void AudioContext::derefNode(AudioNode* node)
568 {
569 ASSERT(isGraphOwner());
570
571 for (unsigned i = 0; i < m_referencedNodes.size(); ++i) {
572 if (node == m_referencedNodes[i].get()) {
573 node->breakConnection();
574 m_referencedNodes.remove(i);
575 break;
576 }
577 }
578 }
579
derefUnfinishedSourceNodes()580 void AudioContext::derefUnfinishedSourceNodes()
581 {
582 ASSERT(isMainThread());
583 for (unsigned i = 0; i < m_referencedNodes.size(); ++i)
584 m_referencedNodes[i]->breakConnection();
585
586 m_referencedNodes.clear();
587 }
588
lock(bool & mustReleaseLock)589 void AudioContext::lock(bool& mustReleaseLock)
590 {
591 // Don't allow regular lock in real-time audio thread.
592 ASSERT(isMainThread());
593
594 ThreadIdentifier thisThread = currentThread();
595
596 if (thisThread == m_graphOwnerThread) {
597 // We already have the lock.
598 mustReleaseLock = false;
599 } else {
600 // Acquire the lock.
601 m_contextGraphMutex.lock();
602 m_graphOwnerThread = thisThread;
603 mustReleaseLock = true;
604 }
605 }
606
tryLock(bool & mustReleaseLock)607 bool AudioContext::tryLock(bool& mustReleaseLock)
608 {
609 ThreadIdentifier thisThread = currentThread();
610 bool isAudioThread = thisThread == audioThread();
611
612 // Try to catch cases of using try lock on main thread - it should use regular lock.
613 ASSERT(isAudioThread);
614
615 if (!isAudioThread) {
616 // In release build treat tryLock() as lock() (since above ASSERT(isAudioThread) never fires) - this is the best we can do.
617 lock(mustReleaseLock);
618 return true;
619 }
620
621 bool hasLock;
622
623 if (thisThread == m_graphOwnerThread) {
624 // Thread already has the lock.
625 hasLock = true;
626 mustReleaseLock = false;
627 } else {
628 // Don't already have the lock - try to acquire it.
629 hasLock = m_contextGraphMutex.tryLock();
630
631 if (hasLock)
632 m_graphOwnerThread = thisThread;
633
634 mustReleaseLock = hasLock;
635 }
636
637 return hasLock;
638 }
639
unlock()640 void AudioContext::unlock()
641 {
642 ASSERT(currentThread() == m_graphOwnerThread);
643
644 m_graphOwnerThread = UndefinedThreadIdentifier;
645 m_contextGraphMutex.unlock();
646 }
647
isAudioThread() const648 bool AudioContext::isAudioThread() const
649 {
650 return currentThread() == m_audioThread;
651 }
652
isGraphOwner() const653 bool AudioContext::isGraphOwner() const
654 {
655 return currentThread() == m_graphOwnerThread;
656 }
657
addDeferredBreakConnection(AudioNode & node)658 void AudioContext::addDeferredBreakConnection(AudioNode& node)
659 {
660 ASSERT(isAudioThread());
661 m_deferredBreakConnectionList.append(&node);
662 }
663
handlePreRenderTasks()664 void AudioContext::handlePreRenderTasks()
665 {
666 ASSERT(isAudioThread());
667
668 // At the beginning of every render quantum, try to update the internal rendering graph state (from main thread changes).
669 // It's OK if the tryLock() fails, we'll just take slightly longer to pick up the changes.
670 bool mustReleaseLock;
671 if (tryLock(mustReleaseLock)) {
672 // Update the channel count mode.
673 updateChangedChannelCountMode();
674
675 // Fixup the state of any dirty AudioSummingJunctions and AudioNodeOutputs.
676 handleDirtyAudioSummingJunctions();
677 handleDirtyAudioNodeOutputs();
678
679 updateAutomaticPullNodes();
680
681 if (mustReleaseLock)
682 unlock();
683 }
684 }
685
handlePostRenderTasks()686 void AudioContext::handlePostRenderTasks()
687 {
688 ASSERT(isAudioThread());
689
690 // Must use a tryLock() here too. Don't worry, the lock will very rarely be contended and this method is called frequently.
691 // The worst that can happen is that there will be some nodes which will take slightly longer than usual to be deleted or removed
692 // from the render graph (in which case they'll render silence).
693 bool mustReleaseLock;
694 if (tryLock(mustReleaseLock)) {
695 // Update the channel count mode.
696 updateChangedChannelCountMode();
697
698 // Take care of AudioNode tasks where the tryLock() failed previously.
699 handleDeferredAudioNodeTasks();
700
701 // Dynamically clean up nodes which are no longer needed.
702 derefFinishedSourceNodes();
703
704 // Fixup the state of any dirty AudioSummingJunctions and AudioNodeOutputs.
705 handleDirtyAudioSummingJunctions();
706 handleDirtyAudioNodeOutputs();
707
708 updateAutomaticPullNodes();
709
710 if (mustReleaseLock)
711 unlock();
712 }
713 }
714
handleDeferredAudioNodeTasks()715 void AudioContext::handleDeferredAudioNodeTasks()
716 {
717 ASSERT(isAudioThread() && isGraphOwner());
718
719 for (unsigned i = 0; i < m_deferredBreakConnectionList.size(); ++i)
720 m_deferredBreakConnectionList[i]->breakConnectionWithLock();
721 m_deferredBreakConnectionList.clear();
722 }
723
registerLiveNode(AudioNode & node)724 void AudioContext::registerLiveNode(AudioNode& node)
725 {
726 ASSERT(isMainThread());
727 m_liveNodes.add(&node, adoptPtr(new AudioNodeDisposer(node)));
728 }
729
~AudioNodeDisposer()730 AudioContext::AudioNodeDisposer::~AudioNodeDisposer()
731 {
732 ASSERT(isMainThread());
733 AudioContext::AutoLocker locker(m_node.context());
734 m_node.dispose();
735 }
736
registerLiveAudioSummingJunction(AudioSummingJunction & junction)737 void AudioContext::registerLiveAudioSummingJunction(AudioSummingJunction& junction)
738 {
739 ASSERT(isMainThread());
740 m_liveAudioSummingJunctions.add(&junction, adoptPtr(new AudioSummingJunctionDisposer(junction)));
741 }
742
~AudioSummingJunctionDisposer()743 AudioContext::AudioSummingJunctionDisposer::~AudioSummingJunctionDisposer()
744 {
745 ASSERT(isMainThread());
746 m_junction.dispose();
747 }
748
disposeOutputs(AudioNode & node)749 void AudioContext::disposeOutputs(AudioNode& node)
750 {
751 ASSERT(isGraphOwner());
752 ASSERT(isMainThread());
753 for (unsigned i = 0; i < node.numberOfOutputs(); ++i)
754 node.output(i)->dispose();
755 }
756
markSummingJunctionDirty(AudioSummingJunction * summingJunction)757 void AudioContext::markSummingJunctionDirty(AudioSummingJunction* summingJunction)
758 {
759 ASSERT(isGraphOwner());
760 m_dirtySummingJunctions.add(summingJunction);
761 }
762
removeMarkedSummingJunction(AudioSummingJunction * summingJunction)763 void AudioContext::removeMarkedSummingJunction(AudioSummingJunction* summingJunction)
764 {
765 ASSERT(isMainThread());
766 AutoLocker locker(this);
767 m_dirtySummingJunctions.remove(summingJunction);
768 }
769
markAudioNodeOutputDirty(AudioNodeOutput * output)770 void AudioContext::markAudioNodeOutputDirty(AudioNodeOutput* output)
771 {
772 ASSERT(isGraphOwner());
773 ASSERT(isMainThread());
774 m_dirtyAudioNodeOutputs.add(output);
775 }
776
removeMarkedAudioNodeOutput(AudioNodeOutput * output)777 void AudioContext::removeMarkedAudioNodeOutput(AudioNodeOutput* output)
778 {
779 ASSERT(isGraphOwner());
780 ASSERT(isMainThread());
781 m_dirtyAudioNodeOutputs.remove(output);
782 }
783
handleDirtyAudioSummingJunctions()784 void AudioContext::handleDirtyAudioSummingJunctions()
785 {
786 ASSERT(isGraphOwner());
787
788 for (HashSet<AudioSummingJunction*>::iterator i = m_dirtySummingJunctions.begin(); i != m_dirtySummingJunctions.end(); ++i)
789 (*i)->updateRenderingState();
790
791 m_dirtySummingJunctions.clear();
792 }
793
handleDirtyAudioNodeOutputs()794 void AudioContext::handleDirtyAudioNodeOutputs()
795 {
796 ASSERT(isGraphOwner());
797
798 for (HashSet<AudioNodeOutput*>::iterator i = m_dirtyAudioNodeOutputs.begin(); i != m_dirtyAudioNodeOutputs.end(); ++i)
799 (*i)->updateRenderingState();
800
801 m_dirtyAudioNodeOutputs.clear();
802 }
803
addAutomaticPullNode(AudioNode * node)804 void AudioContext::addAutomaticPullNode(AudioNode* node)
805 {
806 ASSERT(isGraphOwner());
807
808 if (!m_automaticPullNodes.contains(node)) {
809 m_automaticPullNodes.add(node);
810 m_automaticPullNodesNeedUpdating = true;
811 }
812 }
813
removeAutomaticPullNode(AudioNode * node)814 void AudioContext::removeAutomaticPullNode(AudioNode* node)
815 {
816 ASSERT(isGraphOwner());
817
818 if (m_automaticPullNodes.contains(node)) {
819 m_automaticPullNodes.remove(node);
820 m_automaticPullNodesNeedUpdating = true;
821 }
822 }
823
updateAutomaticPullNodes()824 void AudioContext::updateAutomaticPullNodes()
825 {
826 ASSERT(isGraphOwner());
827
828 if (m_automaticPullNodesNeedUpdating) {
829 // Copy from m_automaticPullNodes to m_renderingAutomaticPullNodes.
830 m_renderingAutomaticPullNodes.resize(m_automaticPullNodes.size());
831
832 unsigned j = 0;
833 for (HashSet<AudioNode*>::iterator i = m_automaticPullNodes.begin(); i != m_automaticPullNodes.end(); ++i, ++j) {
834 AudioNode* output = *i;
835 m_renderingAutomaticPullNodes[j] = output;
836 }
837
838 m_automaticPullNodesNeedUpdating = false;
839 }
840 }
841
processAutomaticPullNodes(size_t framesToProcess)842 void AudioContext::processAutomaticPullNodes(size_t framesToProcess)
843 {
844 ASSERT(isAudioThread());
845
846 for (unsigned i = 0; i < m_renderingAutomaticPullNodes.size(); ++i)
847 m_renderingAutomaticPullNodes[i]->processIfNecessary(framesToProcess);
848 }
849
interfaceName() const850 const AtomicString& AudioContext::interfaceName() const
851 {
852 return EventTargetNames::AudioContext;
853 }
854
executionContext() const855 ExecutionContext* AudioContext::executionContext() const
856 {
857 return m_isStopScheduled ? 0 : ActiveDOMObject::executionContext();
858 }
859
startRendering()860 void AudioContext::startRendering()
861 {
862 destination()->startRendering();
863 }
864
fireCompletionEvent()865 void AudioContext::fireCompletionEvent()
866 {
867 ASSERT(isMainThread());
868 if (!isMainThread())
869 return;
870
871 AudioBuffer* renderedBuffer = m_renderTarget.get();
872
873 ASSERT(renderedBuffer);
874 if (!renderedBuffer)
875 return;
876
877 // Avoid firing the event if the document has already gone away.
878 if (executionContext()) {
879 // Call the offline rendering completion event listener.
880 dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer));
881 }
882 }
883
trace(Visitor * visitor)884 void AudioContext::trace(Visitor* visitor)
885 {
886 visitor->trace(m_renderTarget);
887 visitor->trace(m_destinationNode);
888 visitor->trace(m_listener);
889 visitor->trace(m_referencedNodes);
890 visitor->trace(m_liveNodes);
891 visitor->trace(m_liveAudioSummingJunctions);
892 EventTargetWithInlineData::trace(visitor);
893 }
894
addChangedChannelCountMode(AudioNode * node)895 void AudioContext::addChangedChannelCountMode(AudioNode* node)
896 {
897 ASSERT(isGraphOwner());
898 ASSERT(isMainThread());
899 m_deferredCountModeChange.add(node);
900 }
901
removeChangedChannelCountMode(AudioNode * node)902 void AudioContext::removeChangedChannelCountMode(AudioNode* node)
903 {
904 ASSERT(isGraphOwner());
905
906 m_deferredCountModeChange.remove(node);
907 }
908
updateChangedChannelCountMode()909 void AudioContext::updateChangedChannelCountMode()
910 {
911 ASSERT(isGraphOwner());
912
913 for (HashSet<AudioNode*>::iterator k = m_deferredCountModeChange.begin(); k != m_deferredCountModeChange.end(); ++k)
914 (*k)->updateChannelCountMode();
915
916 m_deferredCountModeChange.clear();
917 }
918
919 } // namespace blink
920
921 #endif // ENABLE(WEB_AUDIO)
922