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/AudioNode.h"
30
31 #include "bindings/core/v8/ExceptionState.h"
32 #include "core/dom/ExceptionCode.h"
33 #include "modules/webaudio/AudioContext.h"
34 #include "modules/webaudio/AudioNodeInput.h"
35 #include "modules/webaudio/AudioNodeOutput.h"
36 #include "modules/webaudio/AudioParam.h"
37 #include "wtf/Atomics.h"
38 #include "wtf/MainThread.h"
39
40 #if DEBUG_AUDIONODE_REFERENCES
41 #include <stdio.h>
42 #endif
43
44 namespace blink {
45
46 unsigned AudioNode::s_instanceCount = 0;
47
AudioNode(AudioContext * context,float sampleRate)48 AudioNode::AudioNode(AudioContext* context, float sampleRate)
49 : m_isInitialized(false)
50 , m_nodeType(NodeTypeUnknown)
51 , m_context(context)
52 , m_sampleRate(sampleRate)
53 , m_lastProcessingTime(-1)
54 , m_lastNonSilentTime(-1)
55 , m_connectionRefCount(0)
56 , m_isDisabled(false)
57 , m_newChannelCountMode(Max)
58 , m_channelCount(2)
59 , m_channelCountMode(Max)
60 , m_channelInterpretation(AudioBus::Speakers)
61 {
62 m_context->registerLiveNode(*this);
63 #if DEBUG_AUDIONODE_REFERENCES
64 if (!s_isNodeCountInitialized) {
65 s_isNodeCountInitialized = true;
66 atexit(AudioNode::printNodeCounts);
67 }
68 #endif
69 ++s_instanceCount;
70 }
71
~AudioNode()72 AudioNode::~AudioNode()
73 {
74 --s_instanceCount;
75 #if DEBUG_AUDIONODE_REFERENCES
76 --s_nodeCount[nodeType()];
77 fprintf(stderr, "%p: %2d: AudioNode::~AudioNode() %d [%d]\n",
78 this, nodeType(), m_connectionRefCount, s_nodeCount[nodeType()]);
79 #endif
80 }
81
initialize()82 void AudioNode::initialize()
83 {
84 m_isInitialized = true;
85 }
86
uninitialize()87 void AudioNode::uninitialize()
88 {
89 m_isInitialized = false;
90 }
91
dispose()92 void AudioNode::dispose()
93 {
94 ASSERT(isMainThread());
95 ASSERT(context()->isGraphOwner());
96
97 context()->removeChangedChannelCountMode(this);
98 context()->removeAutomaticPullNode(this);
99 context()->disposeOutputs(*this);
100 for (unsigned i = 0; i < m_outputs.size(); ++i)
101 output(i)->disconnectAll();
102 }
103
nodeTypeName() const104 String AudioNode::nodeTypeName() const
105 {
106 switch (m_nodeType) {
107 case NodeTypeDestination:
108 return "AudioDestinationNode";
109 case NodeTypeOscillator:
110 return "OscillatorNode";
111 case NodeTypeAudioBufferSource:
112 return "AudioBufferSourceNode";
113 case NodeTypeMediaElementAudioSource:
114 return "MediaElementAudioSourceNode";
115 case NodeTypeMediaStreamAudioDestination:
116 return "MediaStreamAudioDestinationNode";
117 case NodeTypeMediaStreamAudioSource:
118 return "MediaStreamAudioSourceNode";
119 case NodeTypeJavaScript:
120 return "ScriptProcessorNode";
121 case NodeTypeBiquadFilter:
122 return "BiquadFilterNode";
123 case NodeTypePanner:
124 return "PannerNode";
125 case NodeTypeConvolver:
126 return "ConvolverNode";
127 case NodeTypeDelay:
128 return "DelayNode";
129 case NodeTypeGain:
130 return "GainNode";
131 case NodeTypeChannelSplitter:
132 return "ChannelSplitterNode";
133 case NodeTypeChannelMerger:
134 return "ChannelMergerNode";
135 case NodeTypeAnalyser:
136 return "AnalyserNode";
137 case NodeTypeDynamicsCompressor:
138 return "DynamicsCompressorNode";
139 case NodeTypeWaveShaper:
140 return "WaveShaperNode";
141 case NodeTypeUnknown:
142 case NodeTypeEnd:
143 default:
144 ASSERT_NOT_REACHED();
145 return "UnknownNode";
146 }
147 }
148
setNodeType(NodeType type)149 void AudioNode::setNodeType(NodeType type)
150 {
151 m_nodeType = type;
152
153 #if DEBUG_AUDIONODE_REFERENCES
154 ++s_nodeCount[type];
155 fprintf(stderr, "%p: %2d: AudioNode::AudioNode [%3d]\n", this, nodeType(), s_nodeCount[nodeType()]);
156 #endif
157 }
158
addInput()159 void AudioNode::addInput()
160 {
161 m_inputs.append(AudioNodeInput::create(*this));
162 }
163
addOutput(AudioNodeOutput * output)164 void AudioNode::addOutput(AudioNodeOutput* output)
165 {
166 m_outputs.append(output);
167 }
168
input(unsigned i)169 AudioNodeInput* AudioNode::input(unsigned i)
170 {
171 if (i < m_inputs.size())
172 return m_inputs[i].get();
173 return 0;
174 }
175
output(unsigned i)176 AudioNodeOutput* AudioNode::output(unsigned i)
177 {
178 if (i < m_outputs.size())
179 return m_outputs[i].get();
180 return 0;
181 }
182
connect(AudioNode * destination,unsigned outputIndex,unsigned inputIndex,ExceptionState & exceptionState)183 void AudioNode::connect(AudioNode* destination, unsigned outputIndex, unsigned inputIndex, ExceptionState& exceptionState)
184 {
185 ASSERT(isMainThread());
186 AudioContext::AutoLocker locker(context());
187
188 if (!destination) {
189 exceptionState.throwDOMException(
190 SyntaxError,
191 "invalid destination node.");
192 return;
193 }
194
195 // Sanity check input and output indices.
196 if (outputIndex >= numberOfOutputs()) {
197 exceptionState.throwDOMException(
198 IndexSizeError,
199 "output index (" + String::number(outputIndex) + ") exceeds number of outputs (" + String::number(numberOfOutputs()) + ").");
200 return;
201 }
202
203 if (destination && inputIndex >= destination->numberOfInputs()) {
204 exceptionState.throwDOMException(
205 IndexSizeError,
206 "input index (" + String::number(inputIndex) + ") exceeds number of inputs (" + String::number(destination->numberOfInputs()) + ").");
207 return;
208 }
209
210 if (context() != destination->context()) {
211 exceptionState.throwDOMException(
212 SyntaxError,
213 "cannot connect to a destination belonging to a different audio context.");
214 return;
215 }
216
217 AudioNodeInput* input = destination->input(inputIndex);
218 input->connect(*output(outputIndex));
219
220 // Let context know that a connection has been made.
221 context()->incrementConnectionCount();
222 }
223
connect(AudioParam * param,unsigned outputIndex,ExceptionState & exceptionState)224 void AudioNode::connect(AudioParam* param, unsigned outputIndex, ExceptionState& exceptionState)
225 {
226 ASSERT(isMainThread());
227 AudioContext::AutoLocker locker(context());
228
229 if (!param) {
230 exceptionState.throwDOMException(
231 SyntaxError,
232 "invalid AudioParam.");
233 return;
234 }
235
236 if (outputIndex >= numberOfOutputs()) {
237 exceptionState.throwDOMException(
238 IndexSizeError,
239 "output index (" + String::number(outputIndex) + ") exceeds number of outputs (" + String::number(numberOfOutputs()) + ").");
240 return;
241 }
242
243 if (context() != param->context()) {
244 exceptionState.throwDOMException(
245 SyntaxError,
246 "cannot connect to an AudioParam belonging to a different audio context.");
247 return;
248 }
249
250 param->connect(*output(outputIndex));
251 }
252
disconnect(unsigned outputIndex,ExceptionState & exceptionState)253 void AudioNode::disconnect(unsigned outputIndex, ExceptionState& exceptionState)
254 {
255 ASSERT(isMainThread());
256 AudioContext::AutoLocker locker(context());
257
258 // Sanity check input and output indices.
259 if (outputIndex >= numberOfOutputs()) {
260 exceptionState.throwDOMException(
261 IndexSizeError,
262 "output index (" + String::number(outputIndex) + ") exceeds number of outputs (" + String::number(numberOfOutputs()) + ").");
263 return;
264 }
265
266 AudioNodeOutput* output = this->output(outputIndex);
267 output->disconnectAll();
268 }
269
channelCount()270 unsigned long AudioNode::channelCount()
271 {
272 return m_channelCount;
273 }
274
setChannelCount(unsigned long channelCount,ExceptionState & exceptionState)275 void AudioNode::setChannelCount(unsigned long channelCount, ExceptionState& exceptionState)
276 {
277 ASSERT(isMainThread());
278 AudioContext::AutoLocker locker(context());
279
280 if (channelCount > 0 && channelCount <= AudioContext::maxNumberOfChannels()) {
281 if (m_channelCount != channelCount) {
282 m_channelCount = channelCount;
283 if (m_channelCountMode != Max)
284 updateChannelsForInputs();
285 }
286 } else {
287 exceptionState.throwDOMException(
288 NotSupportedError,
289 "channel count (" + String::number(channelCount) + ") must be between 1 and " + String::number(AudioContext::maxNumberOfChannels()) + ".");
290 }
291 }
292
channelCountMode()293 String AudioNode::channelCountMode()
294 {
295 switch (m_channelCountMode) {
296 case Max:
297 return "max";
298 case ClampedMax:
299 return "clamped-max";
300 case Explicit:
301 return "explicit";
302 }
303 ASSERT_NOT_REACHED();
304 return "";
305 }
306
setChannelCountMode(const String & mode,ExceptionState & exceptionState)307 void AudioNode::setChannelCountMode(const String& mode, ExceptionState& exceptionState)
308 {
309 ASSERT(isMainThread());
310 AudioContext::AutoLocker locker(context());
311
312 ChannelCountMode oldMode = m_channelCountMode;
313
314 if (mode == "max") {
315 m_newChannelCountMode = Max;
316 } else if (mode == "clamped-max") {
317 m_newChannelCountMode = ClampedMax;
318 } else if (mode == "explicit") {
319 m_newChannelCountMode = Explicit;
320 } else {
321 ASSERT_NOT_REACHED();
322 }
323
324 if (m_newChannelCountMode != oldMode)
325 context()->addChangedChannelCountMode(this);
326 }
327
channelInterpretation()328 String AudioNode::channelInterpretation()
329 {
330 switch (m_channelInterpretation) {
331 case AudioBus::Speakers:
332 return "speakers";
333 case AudioBus::Discrete:
334 return "discrete";
335 }
336 ASSERT_NOT_REACHED();
337 return "";
338 }
339
setChannelInterpretation(const String & interpretation,ExceptionState & exceptionState)340 void AudioNode::setChannelInterpretation(const String& interpretation, ExceptionState& exceptionState)
341 {
342 ASSERT(isMainThread());
343 AudioContext::AutoLocker locker(context());
344
345 if (interpretation == "speakers") {
346 m_channelInterpretation = AudioBus::Speakers;
347 } else if (interpretation == "discrete") {
348 m_channelInterpretation = AudioBus::Discrete;
349 } else {
350 ASSERT_NOT_REACHED();
351 }
352 }
353
updateChannelsForInputs()354 void AudioNode::updateChannelsForInputs()
355 {
356 for (unsigned i = 0; i < m_inputs.size(); ++i)
357 input(i)->changedOutputs();
358 }
359
interfaceName() const360 const AtomicString& AudioNode::interfaceName() const
361 {
362 return EventTargetNames::AudioNode;
363 }
364
executionContext() const365 ExecutionContext* AudioNode::executionContext() const
366 {
367 return const_cast<AudioNode*>(this)->context()->executionContext();
368 }
369
processIfNecessary(size_t framesToProcess)370 void AudioNode::processIfNecessary(size_t framesToProcess)
371 {
372 ASSERT(context()->isAudioThread());
373
374 if (!isInitialized())
375 return;
376
377 // Ensure that we only process once per rendering quantum.
378 // This handles the "fanout" problem where an output is connected to multiple inputs.
379 // The first time we're called during this time slice we process, but after that we don't want to re-process,
380 // instead our output(s) will already have the results cached in their bus;
381 double currentTime = context()->currentTime();
382 if (m_lastProcessingTime != currentTime) {
383 m_lastProcessingTime = currentTime; // important to first update this time because of feedback loops in the rendering graph
384
385 pullInputs(framesToProcess);
386
387 bool silentInputs = inputsAreSilent();
388 if (!silentInputs)
389 m_lastNonSilentTime = (context()->currentSampleFrame() + framesToProcess) / static_cast<double>(m_sampleRate);
390
391 if (silentInputs && propagatesSilence())
392 silenceOutputs();
393 else {
394 process(framesToProcess);
395 unsilenceOutputs();
396 }
397 }
398 }
399
checkNumberOfChannelsForInput(AudioNodeInput * input)400 void AudioNode::checkNumberOfChannelsForInput(AudioNodeInput* input)
401 {
402 ASSERT(context()->isAudioThread() && context()->isGraphOwner());
403
404 ASSERT(m_inputs.contains(input));
405 if (!m_inputs.contains(input))
406 return;
407
408 input->updateInternalBus();
409 }
410
propagatesSilence() const411 bool AudioNode::propagatesSilence() const
412 {
413 return m_lastNonSilentTime + latencyTime() + tailTime() < context()->currentTime();
414 }
415
pullInputs(size_t framesToProcess)416 void AudioNode::pullInputs(size_t framesToProcess)
417 {
418 ASSERT(context()->isAudioThread());
419
420 // Process all of the AudioNodes connected to our inputs.
421 for (unsigned i = 0; i < m_inputs.size(); ++i)
422 input(i)->pull(0, framesToProcess);
423 }
424
inputsAreSilent()425 bool AudioNode::inputsAreSilent()
426 {
427 for (unsigned i = 0; i < m_inputs.size(); ++i) {
428 if (!input(i)->bus()->isSilent())
429 return false;
430 }
431 return true;
432 }
433
silenceOutputs()434 void AudioNode::silenceOutputs()
435 {
436 for (unsigned i = 0; i < m_outputs.size(); ++i)
437 output(i)->bus()->zero();
438 }
439
unsilenceOutputs()440 void AudioNode::unsilenceOutputs()
441 {
442 for (unsigned i = 0; i < m_outputs.size(); ++i)
443 output(i)->bus()->clearSilentFlag();
444 }
445
enableOutputsIfNecessary()446 void AudioNode::enableOutputsIfNecessary()
447 {
448 if (m_isDisabled && m_connectionRefCount > 0) {
449 ASSERT(isMainThread());
450 AudioContext::AutoLocker locker(context());
451
452 m_isDisabled = false;
453 for (unsigned i = 0; i < m_outputs.size(); ++i)
454 output(i)->enable();
455 }
456 }
457
disableOutputsIfNecessary()458 void AudioNode::disableOutputsIfNecessary()
459 {
460 // Disable outputs if appropriate. We do this if the number of connections is 0 or 1. The case
461 // of 0 is from deref() where there are no connections left. The case of 1 is from
462 // AudioNodeInput::disable() where we want to disable outputs when there's only one connection
463 // left because we're ready to go away, but can't quite yet.
464 if (m_connectionRefCount <= 1 && !m_isDisabled) {
465 // Still may have JavaScript references, but no more "active" connection references, so put all of our outputs in a "dormant" disabled state.
466 // Garbage collection may take a very long time after this time, so the "dormant" disabled nodes should not bog down the rendering...
467
468 // As far as JavaScript is concerned, our outputs must still appear to be connected.
469 // But internally our outputs should be disabled from the inputs they're connected to.
470 // disable() can recursively deref connections (and call disable()) down a whole chain of connected nodes.
471
472 // FIXME: we special case the convolver and delay since they have a significant tail-time and shouldn't be disconnected simply
473 // because they no longer have any input connections. This needs to be handled more generally where AudioNodes have
474 // a tailTime attribute. Then the AudioNode only needs to remain "active" for tailTime seconds after there are no
475 // longer any active connections.
476 if (nodeType() != NodeTypeConvolver && nodeType() != NodeTypeDelay) {
477 m_isDisabled = true;
478 for (unsigned i = 0; i < m_outputs.size(); ++i)
479 output(i)->disable();
480 }
481 }
482 }
483
makeConnection()484 void AudioNode::makeConnection()
485 {
486 atomicIncrement(&m_connectionRefCount);
487
488 #if DEBUG_AUDIONODE_REFERENCES
489 fprintf(stderr, "%p: %2d: AudioNode::ref %3d [%3d]\n",
490 this, nodeType(), m_connectionRefCount, s_nodeCount[nodeType()]);
491 #endif
492 // See the disabling code in disableOutputsIfNecessary(). This handles
493 // the case where a node is being re-connected after being used at least
494 // once and disconnected. In this case, we need to re-enable.
495 enableOutputsIfNecessary();
496 }
497
breakConnection()498 void AudioNode::breakConnection()
499 {
500 // The actual work for deref happens completely within the audio context's
501 // graph lock. In the case of the audio thread, we must use a tryLock to
502 // avoid glitches.
503 bool hasLock = false;
504 bool mustReleaseLock = false;
505
506 if (context()->isAudioThread()) {
507 // Real-time audio thread must not contend lock (to avoid glitches).
508 hasLock = context()->tryLock(mustReleaseLock);
509 } else {
510 context()->lock(mustReleaseLock);
511 hasLock = true;
512 }
513
514 if (hasLock) {
515 breakConnectionWithLock();
516
517 if (mustReleaseLock)
518 context()->unlock();
519 } else {
520 // We were unable to get the lock, so put this in a list to finish up
521 // later.
522 ASSERT(context()->isAudioThread());
523 context()->addDeferredBreakConnection(*this);
524 }
525 }
526
breakConnectionWithLock()527 void AudioNode::breakConnectionWithLock()
528 {
529 atomicDecrement(&m_connectionRefCount);
530
531 #if DEBUG_AUDIONODE_REFERENCES
532 fprintf(stderr, "%p: %2d: AudioNode::deref %3d [%3d]\n",
533 this, nodeType(), m_connectionRefCount, s_nodeCount[nodeType()]);
534 #endif
535
536 if (!m_connectionRefCount)
537 disableOutputsIfNecessary();
538 }
539
540 #if DEBUG_AUDIONODE_REFERENCES
541
542 bool AudioNode::s_isNodeCountInitialized = false;
543 int AudioNode::s_nodeCount[NodeTypeEnd];
544
printNodeCounts()545 void AudioNode::printNodeCounts()
546 {
547 fprintf(stderr, "\n\n");
548 fprintf(stderr, "===========================\n");
549 fprintf(stderr, "AudioNode: reference counts\n");
550 fprintf(stderr, "===========================\n");
551
552 for (unsigned i = 0; i < NodeTypeEnd; ++i)
553 fprintf(stderr, "%2d: %d\n", i, s_nodeCount[i]);
554
555 fprintf(stderr, "===========================\n\n\n");
556 }
557
558 #endif // DEBUG_AUDIONODE_REFERENCES
559
trace(Visitor * visitor)560 void AudioNode::trace(Visitor* visitor)
561 {
562 visitor->trace(m_context);
563 visitor->trace(m_inputs);
564 visitor->trace(m_outputs);
565 EventTargetWithInlineData::trace(visitor);
566 }
567
updateChannelCountMode()568 void AudioNode::updateChannelCountMode()
569 {
570 m_channelCountMode = m_newChannelCountMode;
571 updateChannelsForInputs();
572 }
573
574 } // namespace blink
575
576 #endif // ENABLE(WEB_AUDIO)
577