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
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
23 * DAMAGE.
24 */
25
26 #include "third_party/blink/renderer/modules/webaudio/audio_node.h"
27
28 #include "third_party/blink/renderer/bindings/modules/v8/v8_audio_node_options.h"
29 #include "third_party/blink/renderer/modules/webaudio/audio_graph_tracer.h"
30 #include "third_party/blink/renderer/modules/webaudio/audio_node_input.h"
31 #include "third_party/blink/renderer/modules/webaudio/audio_node_output.h"
32 #include "third_party/blink/renderer/modules/webaudio/audio_node_wiring.h"
33 #include "third_party/blink/renderer/modules/webaudio/audio_param.h"
34 #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
35 #include "third_party/blink/renderer/platform/bindings/exception_messages.h"
36 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
37 #include "third_party/blink/renderer/platform/instrumentation/instance_counters.h"
38
39 #if DEBUG_AUDIONODE_REFERENCES
40 #include <stdio.h>
41 #endif
42
43 namespace blink {
44
AudioHandler(NodeType node_type,AudioNode & node,float sample_rate)45 AudioHandler::AudioHandler(NodeType node_type,
46 AudioNode& node,
47 float sample_rate)
48 : last_processing_time_(-1),
49 last_non_silent_time_(0),
50 is_initialized_(false),
51 node_type_(kNodeTypeUnknown),
52 node_(&node),
53 context_(node.context()),
54 deferred_task_handler_(&context_->GetDeferredTaskHandler()),
55 connection_ref_count_(0),
56 is_disabled_(false),
57 channel_count_(2) {
58 SetNodeType(node_type);
59 SetInternalChannelCountMode(kMax);
60 SetInternalChannelInterpretation(AudioBus::kSpeakers);
61
62 #if DEBUG_AUDIONODE_REFERENCES
63 if (!is_node_count_initialized_) {
64 is_node_count_initialized_ = true;
65 atexit(AudioHandler::PrintNodeCounts);
66 }
67 #endif
68 InstanceCounters::IncrementCounter(InstanceCounters::kAudioHandlerCounter);
69
70 #if DEBUG_AUDIONODE_REFERENCES
71 fprintf(
72 stderr,
73 "[%16p]: %16p: %2d: AudioHandler::AudioHandler() %d [%d] total: %u\n",
74 Context(), this, GetNodeType(), connection_ref_count_,
75 node_count_[GetNodeType()],
76 InstanceCounters::CounterValue(InstanceCounters::kAudioHandlerCounter));
77 #endif
78 node.context()->WarnIfContextClosed(this);
79 }
80
~AudioHandler()81 AudioHandler::~AudioHandler() {
82 DCHECK(IsMainThread());
83 InstanceCounters::DecrementCounter(InstanceCounters::kAudioHandlerCounter);
84 #if DEBUG_AUDIONODE_REFERENCES
85 --node_count_[GetNodeType()];
86 fprintf(
87 stderr,
88 "[%16p]: %16p: %2d: AudioHandler::~AudioHandler() %d [%d] remaining: "
89 "%u\n",
90 Context(), this, GetNodeType(), connection_ref_count_,
91 node_count_[GetNodeType()],
92 InstanceCounters::CounterValue(InstanceCounters::kAudioHandlerCounter));
93 #endif
94 }
95
Initialize()96 void AudioHandler::Initialize() {
97 DCHECK_EQ(new_channel_count_mode_, channel_count_mode_);
98 DCHECK_EQ(new_channel_interpretation_, channel_interpretation_);
99
100 is_initialized_ = true;
101 }
102
Uninitialize()103 void AudioHandler::Uninitialize() {
104 is_initialized_ = false;
105 }
106
Dispose()107 void AudioHandler::Dispose() {
108 DCHECK(IsMainThread());
109 deferred_task_handler_->AssertGraphOwner();
110
111 deferred_task_handler_->RemoveChangedChannelCountMode(this);
112 deferred_task_handler_->RemoveChangedChannelInterpretation(this);
113 deferred_task_handler_->RemoveAutomaticPullNode(this);
114 for (auto& output : outputs_)
115 output->Dispose();
116 }
117
GetNode() const118 AudioNode* AudioHandler::GetNode() const {
119 DCHECK(IsMainThread());
120 return node_;
121 }
122
Context() const123 BaseAudioContext* AudioHandler::Context() const {
124 return context_;
125 }
126
NodeTypeName() const127 String AudioHandler::NodeTypeName() const {
128 switch (node_type_) {
129 case kNodeTypeDestination:
130 return "AudioDestinationNode";
131 case kNodeTypeOscillator:
132 return "OscillatorNode";
133 case kNodeTypeAudioBufferSource:
134 return "AudioBufferSourceNode";
135 case kNodeTypeMediaElementAudioSource:
136 return "MediaElementAudioSourceNode";
137 case kNodeTypeMediaStreamAudioDestination:
138 return "MediaStreamAudioDestinationNode";
139 case kNodeTypeMediaStreamAudioSource:
140 return "MediaStreamAudioSourceNode";
141 case kNodeTypeScriptProcessor:
142 return "ScriptProcessorNode";
143 case kNodeTypeBiquadFilter:
144 return "BiquadFilterNode";
145 case kNodeTypePanner:
146 return "PannerNode";
147 case kNodeTypeStereoPanner:
148 return "StereoPannerNode";
149 case kNodeTypeConvolver:
150 return "ConvolverNode";
151 case kNodeTypeDelay:
152 return "DelayNode";
153 case kNodeTypeGain:
154 return "GainNode";
155 case kNodeTypeChannelSplitter:
156 return "ChannelSplitterNode";
157 case kNodeTypeChannelMerger:
158 return "ChannelMergerNode";
159 case kNodeTypeAnalyser:
160 return "AnalyserNode";
161 case kNodeTypeDynamicsCompressor:
162 return "DynamicsCompressorNode";
163 case kNodeTypeWaveShaper:
164 return "WaveShaperNode";
165 case kNodeTypeIIRFilter:
166 return "IIRFilterNode";
167 case kNodeTypeConstantSource:
168 return "ConstantSourceNode";
169 case kNodeTypeAudioWorklet:
170 return "AudioWorkletNode";
171 case kNodeTypeUnknown:
172 case kNodeTypeEnd:
173 default:
174 NOTREACHED();
175 return "UnknownNode";
176 }
177 }
178
SetNodeType(NodeType type)179 void AudioHandler::SetNodeType(NodeType type) {
180 // Don't allow the node type to be changed to a different node type, after
181 // it's already been set. And the new type can't be unknown or end.
182 DCHECK_EQ(node_type_, kNodeTypeUnknown);
183 DCHECK_NE(type, kNodeTypeUnknown);
184 DCHECK_NE(type, kNodeTypeEnd);
185
186 node_type_ = type;
187
188 #if DEBUG_AUDIONODE_REFERENCES
189 ++node_count_[type];
190 fprintf(stderr, "[%16p]: %16p: %2d: AudioHandler::AudioHandler [%3d]\n",
191 Context(), this, GetNodeType(), node_count_[GetNodeType()]);
192 #endif
193 }
194
AddInput()195 void AudioHandler::AddInput() {
196 inputs_.push_back(std::make_unique<AudioNodeInput>(*this));
197 }
198
AddOutput(unsigned number_of_channels)199 void AudioHandler::AddOutput(unsigned number_of_channels) {
200 DCHECK(IsMainThread());
201 outputs_.push_back(
202 std::make_unique<AudioNodeOutput>(this, number_of_channels));
203 GetNode()->DidAddOutput(NumberOfOutputs());
204 }
205
Input(unsigned i)206 AudioNodeInput& AudioHandler::Input(unsigned i) {
207 return *inputs_[i];
208 }
209
Output(unsigned i)210 AudioNodeOutput& AudioHandler::Output(unsigned i) {
211 return *outputs_[i];
212 }
213
Output(unsigned i) const214 const AudioNodeOutput& AudioHandler::Output(unsigned i) const {
215 return *outputs_[i];
216 }
217
ChannelCount()218 unsigned AudioHandler::ChannelCount() {
219 return channel_count_;
220 }
221
SetInternalChannelCountMode(ChannelCountMode mode)222 void AudioHandler::SetInternalChannelCountMode(ChannelCountMode mode) {
223 channel_count_mode_ = mode;
224 new_channel_count_mode_ = mode;
225 }
226
SetInternalChannelInterpretation(AudioBus::ChannelInterpretation interpretation)227 void AudioHandler::SetInternalChannelInterpretation(
228 AudioBus::ChannelInterpretation interpretation) {
229 channel_interpretation_ = interpretation;
230 new_channel_interpretation_ = interpretation;
231 }
232
SetChannelCount(unsigned channel_count,ExceptionState & exception_state)233 void AudioHandler::SetChannelCount(unsigned channel_count,
234 ExceptionState& exception_state) {
235 DCHECK(IsMainThread());
236 BaseAudioContext::GraphAutoLocker locker(Context());
237
238 if (channel_count > 0 &&
239 channel_count <= BaseAudioContext::MaxNumberOfChannels()) {
240 if (channel_count_ != channel_count) {
241 channel_count_ = channel_count;
242 if (channel_count_mode_ != kMax)
243 UpdateChannelsForInputs();
244 }
245 } else {
246 exception_state.ThrowDOMException(
247 DOMExceptionCode::kNotSupportedError,
248 ExceptionMessages::IndexOutsideRange<uint32_t>(
249 "channel count", channel_count, 1,
250 ExceptionMessages::kInclusiveBound,
251 BaseAudioContext::MaxNumberOfChannels(),
252 ExceptionMessages::kInclusiveBound));
253 }
254 }
255
GetChannelCountMode()256 String AudioHandler::GetChannelCountMode() {
257 // Because we delay the actual setting of the mode to the pre or post
258 // rendering phase, we want to return the value that was set, not the actual
259 // current mode.
260 switch (new_channel_count_mode_) {
261 case kMax:
262 return "max";
263 case kClampedMax:
264 return "clamped-max";
265 case kExplicit:
266 return "explicit";
267 }
268 NOTREACHED();
269 return "";
270 }
271
SetChannelCountMode(const String & mode,ExceptionState & exception_state)272 void AudioHandler::SetChannelCountMode(const String& mode,
273 ExceptionState& exception_state) {
274 DCHECK(IsMainThread());
275 BaseAudioContext::GraphAutoLocker locker(Context());
276
277 ChannelCountMode old_mode = channel_count_mode_;
278
279 if (mode == "max") {
280 new_channel_count_mode_ = kMax;
281 } else if (mode == "clamped-max") {
282 new_channel_count_mode_ = kClampedMax;
283 } else if (mode == "explicit") {
284 new_channel_count_mode_ = kExplicit;
285 } else {
286 NOTREACHED();
287 }
288
289 if (new_channel_count_mode_ != old_mode)
290 Context()->GetDeferredTaskHandler().AddChangedChannelCountMode(this);
291 }
292
ChannelInterpretation()293 String AudioHandler::ChannelInterpretation() {
294 // Because we delay the actual setting of the interpreation to the pre or
295 // post rendering phase, we want to return the value that was set, not the
296 // actual current interpretation.
297 switch (new_channel_interpretation_) {
298 case AudioBus::kSpeakers:
299 return "speakers";
300 case AudioBus::kDiscrete:
301 return "discrete";
302 }
303 NOTREACHED();
304 return "";
305 }
306
SetChannelInterpretation(const String & interpretation,ExceptionState & exception_state)307 void AudioHandler::SetChannelInterpretation(const String& interpretation,
308 ExceptionState& exception_state) {
309 DCHECK(IsMainThread());
310 BaseAudioContext::GraphAutoLocker locker(Context());
311
312 AudioBus::ChannelInterpretation old_mode = channel_interpretation_;
313
314 if (interpretation == "speakers") {
315 new_channel_interpretation_ = AudioBus::kSpeakers;
316 } else if (interpretation == "discrete") {
317 new_channel_interpretation_ = AudioBus::kDiscrete;
318 } else {
319 NOTREACHED();
320 }
321
322 if (new_channel_interpretation_ != old_mode)
323 Context()->GetDeferredTaskHandler().AddChangedChannelInterpretation(this);
324 }
325
UpdateChannelsForInputs()326 void AudioHandler::UpdateChannelsForInputs() {
327 for (auto& input : inputs_)
328 input->ChangedOutputs();
329 }
330
ProcessIfNecessary(uint32_t frames_to_process)331 void AudioHandler::ProcessIfNecessary(uint32_t frames_to_process) {
332 DCHECK(Context()->IsAudioThread());
333
334 if (!IsInitialized())
335 return;
336
337 // Ensure that we only process once per rendering quantum.
338 // This handles the "fanout" problem where an output is connected to multiple
339 // inputs. The first time we're called during this time slice we process, but
340 // after that we don't want to re-process, instead our output(s) will already
341 // have the results cached in their bus;
342 double current_time = Context()->currentTime();
343 if (last_processing_time_ != current_time) {
344 // important to first update this time because of feedback loops in the
345 // rendering graph.
346 last_processing_time_ = current_time;
347
348 PullInputs(frames_to_process);
349
350 bool silent_inputs = InputsAreSilent();
351 if (silent_inputs && PropagatesSilence()) {
352 SilenceOutputs();
353 // AudioParams still need to be processed so that the value can be updated
354 // if there are automations or so that the upstream nodes get pulled if
355 // any are connected to the AudioParam.
356 ProcessOnlyAudioParams(frames_to_process);
357 } else {
358 // Unsilence the outputs first because the processing of the node may
359 // cause the outputs to go silent and we want to propagate that hint to
360 // the downstream nodes. (For example, a Gain node with a gain of 0 will
361 // want to silence its output.)
362 UnsilenceOutputs();
363 Process(frames_to_process);
364 }
365
366 if (!silent_inputs) {
367 // Update |last_non_silent_time| AFTER processing this block.
368 // Doing it before causes |PropagateSilence()| to be one render
369 // quantum longer than necessary.
370 last_non_silent_time_ =
371 (Context()->CurrentSampleFrame() + frames_to_process) /
372 static_cast<double>(Context()->sampleRate());
373 }
374 }
375 }
376
CheckNumberOfChannelsForInput(AudioNodeInput * input)377 void AudioHandler::CheckNumberOfChannelsForInput(AudioNodeInput* input) {
378 DCHECK(Context()->IsAudioThread());
379 deferred_task_handler_->AssertGraphOwner();
380
381 DCHECK(inputs_.Contains(input));
382
383 input->UpdateInternalBus();
384 }
385
PropagatesSilence() const386 bool AudioHandler::PropagatesSilence() const {
387 return last_non_silent_time_ + LatencyTime() + TailTime() <
388 Context()->currentTime();
389 }
390
PullInputs(uint32_t frames_to_process)391 void AudioHandler::PullInputs(uint32_t frames_to_process) {
392 DCHECK(Context()->IsAudioThread());
393
394 // Process all of the AudioNodes connected to our inputs.
395 for (auto& input : inputs_)
396 input->Pull(nullptr, frames_to_process);
397 }
398
InputsAreSilent()399 bool AudioHandler::InputsAreSilent() {
400 for (auto& input : inputs_) {
401 if (!input->Bus()->IsSilent())
402 return false;
403 }
404 return true;
405 }
406
SilenceOutputs()407 void AudioHandler::SilenceOutputs() {
408 for (auto& output : outputs_)
409 output->Bus()->Zero();
410 }
411
UnsilenceOutputs()412 void AudioHandler::UnsilenceOutputs() {
413 for (auto& output : outputs_)
414 output->Bus()->ClearSilentFlag();
415 }
416
EnableOutputsIfNecessary()417 void AudioHandler::EnableOutputsIfNecessary() {
418 DCHECK(IsMainThread());
419 deferred_task_handler_->AssertGraphOwner();
420
421 // We're enabling outputs for this handler. Remove this from the tail
422 // processing list (if it's there) so that we don't inadvertently disable the
423 // outputs later on when the tail processing time has elapsed.
424 Context()->GetDeferredTaskHandler().RemoveTailProcessingHandler(this, false);
425
426 #if DEBUG_AUDIONODE_REFERENCES > 1
427 fprintf(stderr,
428 "[%16p]: %16p: %2d: EnableOutputsIfNecessary: is_disabled %d count "
429 "%d output size %u\n",
430 Context(), this, GetNodeType(), is_disabled_, connection_ref_count_,
431 outputs_.size());
432 #endif
433
434 if (is_disabled_ && connection_ref_count_ > 0) {
435 is_disabled_ = false;
436 for (auto& output : outputs_)
437 output->Enable();
438 }
439 }
440
DisableOutputsIfNecessary()441 void AudioHandler::DisableOutputsIfNecessary() {
442 // This function calls other functions that require graph ownership,
443 // so assert that this needs graph ownership too.
444 deferred_task_handler_->AssertGraphOwner();
445
446 #if DEBUG_AUDIONODE_REFERENCES > 1
447 fprintf(stderr,
448 "[%16p]: %16p: %2d: DisableOutputsIfNecessary is_disabled %d count %d"
449 " tail %d\n",
450 Context(), this, GetNodeType(), is_disabled_, connection_ref_count_,
451 RequiresTailProcessing());
452 #endif
453
454 // Disable outputs if appropriate. We do this if the number of connections is
455 // 0 or 1. The case of 0 is from deref() where there are no connections left.
456 // The case of 1 is from AudioNodeInput::disable() where we want to disable
457 // outputs when there's only one connection left because we're ready to go
458 // away, but can't quite yet.
459 if (connection_ref_count_ <= 1 && !is_disabled_) {
460 // Still may have JavaScript references, but no more "active" connection
461 // references, so put all of our outputs in a "dormant" disabled state.
462 // Garbage collection may take a very long time after this time, so the
463 // "dormant" disabled nodes should not bog down the rendering...
464
465 // As far as JavaScript is concerned, our outputs must still appear to be
466 // connected. But internally our outputs should be disabled from the inputs
467 // they're connected to. disable() can recursively deref connections (and
468 // call disable()) down a whole chain of connected nodes.
469
470 // If a node requires tail processing, we defer the disabling of
471 // the outputs so that the tail for the node can be output.
472 // Otherwise, we can disable the outputs right away.
473 if (RequiresTailProcessing()) {
474 if (deferred_task_handler_->AcceptsTailProcessing())
475 deferred_task_handler_->AddTailProcessingHandler(this);
476 } else {
477 DisableOutputs();
478 }
479 }
480 }
481
DisableOutputs()482 void AudioHandler::DisableOutputs() {
483 is_disabled_ = true;
484 for (auto& output : outputs_)
485 output->Disable();
486 }
487
MakeConnection()488 void AudioHandler::MakeConnection() {
489 deferred_task_handler_->AssertGraphOwner();
490 connection_ref_count_++;
491
492 #if DEBUG_AUDIONODE_REFERENCES
493 fprintf(
494 stderr,
495 "[%16p]: %16p: %2d: AudioHandler::MakeConnection %3d [%3d] @%.15g\n",
496 Context(), this, GetNodeType(), connection_ref_count_,
497 node_count_[GetNodeType()], Context()->currentTime());
498 #endif
499
500 // See the disabling code in disableOutputsIfNecessary(). This handles
501 // the case where a node is being re-connected after being used at least
502 // once and disconnected. In this case, we need to re-enable.
503 EnableOutputsIfNecessary();
504 }
505
BreakConnectionWithLock()506 void AudioHandler::BreakConnectionWithLock() {
507 deferred_task_handler_->AssertGraphOwner();
508 connection_ref_count_--;
509
510 #if DEBUG_AUDIONODE_REFERENCES
511 fprintf(stderr,
512 "[%16p]: %16p: %2d: AudioHandler::BreakConnectionWitLock %3d [%3d] "
513 "@%.15g\n",
514 Context(), this, GetNodeType(), connection_ref_count_,
515 node_count_[GetNodeType()], Context()->currentTime());
516 #endif
517
518 if (!connection_ref_count_)
519 DisableOutputsIfNecessary();
520 }
521
522 #if DEBUG_AUDIONODE_REFERENCES
523
524 bool AudioHandler::is_node_count_initialized_ = false;
525 int AudioHandler::node_count_[kNodeTypeEnd];
526
PrintNodeCounts()527 void AudioHandler::PrintNodeCounts() {
528 fprintf(stderr, "\n\n");
529 fprintf(stderr, "===========================\n");
530 fprintf(stderr, "AudioNode: reference counts\n");
531 fprintf(stderr, "===========================\n");
532
533 for (unsigned i = 0; i < kNodeTypeEnd; ++i)
534 fprintf(stderr, "%2d: %d\n", i, node_count_[i]);
535
536 fprintf(stderr, "===========================\n\n\n");
537 }
538
539 #endif // DEBUG_AUDIONODE_REFERENCES
540
541 #if DEBUG_AUDIONODE_REFERENCES > 1
TailProcessingDebug(const char * note,bool flag)542 void AudioHandler::TailProcessingDebug(const char* note, bool flag) {
543 fprintf(stderr, "[%16p]: %16p: %2d: %s %d @%.15g flag=%d", Context(), this,
544 GetNodeType(), note, connection_ref_count_, Context()->currentTime(),
545 flag);
546
547 // If we're on the audio thread, we can print out the tail and
548 // latency times (because these methods can only be called from the
549 // audio thread.)
550 if (Context()->IsAudioThread()) {
551 fprintf(stderr, ", tail=%.15g + %.15g, last=%.15g\n", TailTime(),
552 LatencyTime(), last_non_silent_time_);
553 }
554
555 fprintf(stderr, "\n");
556 }
557
AddTailProcessingDebug()558 void AudioHandler::AddTailProcessingDebug() {
559 TailProcessingDebug("addTail", false);
560 }
561
RemoveTailProcessingDebug(bool disable_outputs)562 void AudioHandler::RemoveTailProcessingDebug(bool disable_outputs) {
563 TailProcessingDebug("remTail", disable_outputs);
564 }
565 #endif // DEBUG_AUDIONODE_REFERENCES > 1
566
UpdateChannelCountMode()567 void AudioHandler::UpdateChannelCountMode() {
568 channel_count_mode_ = new_channel_count_mode_;
569 UpdateChannelsForInputs();
570 }
571
UpdateChannelInterpretation()572 void AudioHandler::UpdateChannelInterpretation() {
573 channel_interpretation_ = new_channel_interpretation_;
574 }
575
NumberOfOutputChannels() const576 unsigned AudioHandler::NumberOfOutputChannels() const {
577 // This should only be called for ScriptProcessorNodes which are the only
578 // nodes where you can have an output with 0 channels. All other nodes have
579 // have at least one output channel, so there's no reason other nodes should
580 // ever call this function.
581 DCHECK(0) << "numberOfOutputChannels() not valid for node type "
582 << GetNodeType();
583 return 1;
584 }
585 // ----------------------------------------------------------------
586
AudioNode(BaseAudioContext & context)587 AudioNode::AudioNode(BaseAudioContext& context)
588 : InspectorHelperMixin(context.GraphTracer(), context.Uuid()),
589 context_(context),
590 deferred_task_handler_(&context.GetDeferredTaskHandler()),
591 handler_(nullptr) {}
592
~AudioNode()593 AudioNode::~AudioNode() {
594 // The graph lock is required to destroy the handler. And we can't use
595 // |context_| to touch it, since that object may also be a dead heap object.
596 {
597 DeferredTaskHandler::GraphAutoLocker locker(*deferred_task_handler_);
598 handler_ = nullptr;
599 }
600 }
601
Dispose()602 void AudioNode::Dispose() {
603 DCHECK(IsMainThread());
604 #if DEBUG_AUDIONODE_REFERENCES
605 fprintf(stderr, "[%16p]: %16p: %2d: AudioNode::dispose %16p @%g\n", context(),
606 this, Handler().GetNodeType(), handler_.get(),
607 context()->currentTime());
608 #endif
609 BaseAudioContext::GraphAutoLocker locker(context());
610 Handler().Dispose();
611
612 // Add the handler to the orphan list if the context is pulling on the audio
613 // graph. This keeps the handler alive until it can be deleted at a safe
614 // point (in pre/post handler task). If graph isn't being pulled, we can
615 // delete the handler now since nothing on the audio thread will be touching
616 // it.
617 DCHECK(context());
618 if (context()->IsPullingAudioGraph()) {
619 context()->GetDeferredTaskHandler().AddRenderingOrphanHandler(
620 std::move(handler_));
621 }
622
623 // Notify the inspector that this node is going away. The actual clean up
624 // will be done in the subclass implementation.
625 ReportWillBeDestroyed();
626 }
627
SetHandler(scoped_refptr<AudioHandler> handler)628 void AudioNode::SetHandler(scoped_refptr<AudioHandler> handler) {
629 DCHECK(handler);
630 handler_ = std::move(handler);
631
632 // Unless the node is an AudioDestinationNode, notify the inspector that the
633 // construction is completed. The actual report will be done in the subclass
634 // implementation. (A destination node is owned by the context and will be
635 // reported by it.)
636 if (handler_->GetNodeType() != AudioHandler::NodeType::kNodeTypeDestination)
637 ReportDidCreate();
638
639 #if DEBUG_AUDIONODE_REFERENCES
640 fprintf(stderr, "[%16p]: %16p: %2d: AudioNode::AudioNode %16p\n", context(),
641 this, handler_->GetNodeType(), handler_.get());
642 #endif
643 }
644
ContainsHandler() const645 bool AudioNode::ContainsHandler() const {
646 return handler_.get();
647 }
648
Handler() const649 AudioHandler& AudioNode::Handler() const {
650 return *handler_;
651 }
652
Trace(Visitor * visitor) const653 void AudioNode::Trace(Visitor* visitor) const {
654 visitor->Trace(context_);
655 visitor->Trace(connected_nodes_);
656 visitor->Trace(connected_params_);
657 InspectorHelperMixin::Trace(visitor);
658 EventTargetWithInlineData::Trace(visitor);
659 }
660
HandleChannelOptions(const AudioNodeOptions * options,ExceptionState & exception_state)661 void AudioNode::HandleChannelOptions(const AudioNodeOptions* options,
662 ExceptionState& exception_state) {
663 DCHECK(IsMainThread());
664
665 if (options->hasChannelCount())
666 setChannelCount(options->channelCount(), exception_state);
667 if (options->hasChannelCountMode())
668 setChannelCountMode(options->channelCountMode(), exception_state);
669 if (options->hasChannelInterpretation())
670 setChannelInterpretation(options->channelInterpretation(), exception_state);
671 }
672
context() const673 BaseAudioContext* AudioNode::context() const {
674 return context_;
675 }
676
connect(AudioNode * destination,unsigned output_index,unsigned input_index,ExceptionState & exception_state)677 AudioNode* AudioNode::connect(AudioNode* destination,
678 unsigned output_index,
679 unsigned input_index,
680 ExceptionState& exception_state) {
681 DCHECK(IsMainThread());
682 BaseAudioContext::GraphAutoLocker locker(context());
683
684 context()->WarnForConnectionIfContextClosed();
685
686 if (!destination) {
687 exception_state.ThrowDOMException(DOMExceptionCode::kSyntaxError,
688 "invalid destination node.");
689 return nullptr;
690 }
691
692 // Sanity check input and output indices.
693 if (output_index >= numberOfOutputs()) {
694 exception_state.ThrowDOMException(
695 DOMExceptionCode::kIndexSizeError,
696 "output index (" + String::Number(output_index) +
697 ") exceeds number of outputs (" +
698 String::Number(numberOfOutputs()) + ").");
699 return nullptr;
700 }
701
702 if (destination && input_index >= destination->numberOfInputs()) {
703 exception_state.ThrowDOMException(
704 DOMExceptionCode::kIndexSizeError,
705 "input index (" + String::Number(input_index) +
706 ") exceeds number of inputs (" +
707 String::Number(destination->numberOfInputs()) + ").");
708 return nullptr;
709 }
710
711 if (context() != destination->context()) {
712 exception_state.ThrowDOMException(
713 DOMExceptionCode::kInvalidAccessError,
714 "cannot connect to a destination "
715 "belonging to a different audio context.");
716 return nullptr;
717 }
718
719 // ScriptProcessorNodes with 0 output channels can't be connected to any
720 // destination. If there are no output channels, what would the destination
721 // receive? Just disallow this.
722 if (Handler().GetNodeType() == AudioHandler::kNodeTypeScriptProcessor &&
723 Handler().NumberOfOutputChannels() == 0) {
724 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
725 "cannot connect a ScriptProcessorNode "
726 "with 0 output channels to any "
727 "destination node.");
728 return nullptr;
729 }
730
731 AudioNodeWiring::Connect(Handler().Output(output_index),
732 destination->Handler().Input(input_index));
733 if (!connected_nodes_[output_index]) {
734 connected_nodes_[output_index] =
735 MakeGarbageCollected<HeapHashSet<Member<AudioNode>>>();
736 }
737 connected_nodes_[output_index]->insert(destination);
738
739 Handler().UpdatePullStatusIfNeeded();
740
741 GraphTracer().DidConnectNodes(this, destination, output_index, input_index);
742
743 return destination;
744 }
745
connect(AudioParam * param,unsigned output_index,ExceptionState & exception_state)746 void AudioNode::connect(AudioParam* param,
747 unsigned output_index,
748 ExceptionState& exception_state) {
749 DCHECK(IsMainThread());
750 BaseAudioContext::GraphAutoLocker locker(context());
751
752 context()->WarnForConnectionIfContextClosed();
753
754 if (!param) {
755 exception_state.ThrowDOMException(DOMExceptionCode::kSyntaxError,
756 "invalid AudioParam.");
757 return;
758 }
759
760 if (output_index >= numberOfOutputs()) {
761 exception_state.ThrowDOMException(
762 DOMExceptionCode::kIndexSizeError,
763 "output index (" + String::Number(output_index) +
764 ") exceeds number of outputs (" +
765 String::Number(numberOfOutputs()) + ").");
766 return;
767 }
768
769 if (context() != param->Context()) {
770 exception_state.ThrowDOMException(
771 DOMExceptionCode::kSyntaxError,
772 "cannot connect to an AudioParam "
773 "belonging to a different audio context.");
774 return;
775 }
776
777 AudioNodeWiring::Connect(Handler().Output(output_index), param->Handler());
778 if (!connected_params_[output_index]) {
779 connected_params_[output_index] =
780 MakeGarbageCollected<HeapHashSet<Member<AudioParam>>>();
781 }
782 connected_params_[output_index]->insert(param);
783
784 Handler().UpdatePullStatusIfNeeded();
785
786 GraphTracer().DidConnectNodeParam(this, param, output_index);
787 }
788
DisconnectAllFromOutput(unsigned output_index)789 void AudioNode::DisconnectAllFromOutput(unsigned output_index) {
790 Handler().Output(output_index).DisconnectAll();
791 connected_nodes_[output_index] = nullptr;
792 connected_params_[output_index] = nullptr;
793 }
794
DisconnectFromOutputIfConnected(unsigned output_index,AudioNode & destination,unsigned input_index_of_destination)795 bool AudioNode::DisconnectFromOutputIfConnected(
796 unsigned output_index,
797 AudioNode& destination,
798 unsigned input_index_of_destination) {
799 AudioNodeOutput& output = Handler().Output(output_index);
800 AudioNodeInput& input =
801 destination.Handler().Input(input_index_of_destination);
802 if (!AudioNodeWiring::IsConnected(output, input))
803 return false;
804 AudioNodeWiring::Disconnect(output, input);
805 connected_nodes_[output_index]->erase(&destination);
806 return true;
807 }
808
DisconnectFromOutputIfConnected(unsigned output_index,AudioParam & param)809 bool AudioNode::DisconnectFromOutputIfConnected(unsigned output_index,
810 AudioParam& param) {
811 AudioNodeOutput& output = Handler().Output(output_index);
812 if (!AudioNodeWiring::IsConnected(output, param.Handler()))
813 return false;
814 AudioNodeWiring::Disconnect(output, param.Handler());
815 connected_params_[output_index]->erase(¶m);
816 return true;
817 }
818
disconnect()819 void AudioNode::disconnect() {
820 DCHECK(IsMainThread());
821 BaseAudioContext::GraphAutoLocker locker(context());
822
823 // Disconnect all outgoing connections.
824 for (unsigned i = 0; i < numberOfOutputs(); ++i)
825 DisconnectAllFromOutput(i);
826
827 Handler().UpdatePullStatusIfNeeded();
828
829 GraphTracer().DidDisconnectNodes(this);
830 }
831
disconnect(unsigned output_index,ExceptionState & exception_state)832 void AudioNode::disconnect(unsigned output_index,
833 ExceptionState& exception_state) {
834 DCHECK(IsMainThread());
835 BaseAudioContext::GraphAutoLocker locker(context());
836
837 // Sanity check on the output index.
838 if (output_index >= numberOfOutputs()) {
839 exception_state.ThrowDOMException(
840 DOMExceptionCode::kIndexSizeError,
841 ExceptionMessages::IndexOutsideRange(
842 "output index", output_index, 0u,
843 ExceptionMessages::kInclusiveBound, numberOfOutputs() - 1,
844 ExceptionMessages::kInclusiveBound));
845 return;
846 }
847 // Disconnect all outgoing connections from the given output.
848 DisconnectAllFromOutput(output_index);
849
850 Handler().UpdatePullStatusIfNeeded();
851
852 GraphTracer().DidDisconnectNodes(this, nullptr, output_index);
853 }
854
disconnect(AudioNode * destination,ExceptionState & exception_state)855 void AudioNode::disconnect(AudioNode* destination,
856 ExceptionState& exception_state) {
857 DCHECK(IsMainThread());
858 BaseAudioContext::GraphAutoLocker locker(context());
859
860 unsigned number_of_disconnections = 0;
861
862 // FIXME: Can this be optimized? ChannelSplitter and ChannelMerger can have
863 // 32 ports and that requires 1024 iterations to validate entire connections.
864 for (unsigned output_index = 0; output_index < numberOfOutputs();
865 ++output_index) {
866 for (unsigned input_index = 0;
867 input_index < destination->Handler().NumberOfInputs(); ++input_index) {
868 if (DisconnectFromOutputIfConnected(output_index, *destination,
869 input_index))
870 number_of_disconnections++;
871 }
872 }
873
874 // If there is no connection to the destination, throw an exception.
875 if (number_of_disconnections == 0) {
876 exception_state.ThrowDOMException(
877 DOMExceptionCode::kInvalidAccessError,
878 "the given destination is not connected.");
879 return;
880 }
881
882 Handler().UpdatePullStatusIfNeeded();
883
884 GraphTracer().DidDisconnectNodes(this, destination);
885 }
886
disconnect(AudioNode * destination,unsigned output_index,ExceptionState & exception_state)887 void AudioNode::disconnect(AudioNode* destination,
888 unsigned output_index,
889 ExceptionState& exception_state) {
890 DCHECK(IsMainThread());
891 BaseAudioContext::GraphAutoLocker locker(context());
892
893 if (output_index >= numberOfOutputs()) {
894 // The output index is out of range. Throw an exception.
895 exception_state.ThrowDOMException(
896 DOMExceptionCode::kIndexSizeError,
897 ExceptionMessages::IndexOutsideRange(
898 "output index", output_index, 0u,
899 ExceptionMessages::kInclusiveBound, numberOfOutputs() - 1,
900 ExceptionMessages::kInclusiveBound));
901 return;
902 }
903
904 // If the output index is valid, proceed to disconnect.
905 unsigned number_of_disconnections = 0;
906 // Sanity check on destination inputs and disconnect when possible.
907 for (unsigned input_index = 0; input_index < destination->numberOfInputs();
908 ++input_index) {
909 if (DisconnectFromOutputIfConnected(output_index, *destination,
910 input_index))
911 number_of_disconnections++;
912 }
913
914 // If there is no connection to the destination, throw an exception.
915 if (number_of_disconnections == 0) {
916 exception_state.ThrowDOMException(
917 DOMExceptionCode::kInvalidAccessError,
918 "output (" + String::Number(output_index) +
919 ") is not connected to the given destination.");
920 }
921
922 Handler().UpdatePullStatusIfNeeded();
923
924 GraphTracer().DidDisconnectNodes(this, destination, output_index);
925 }
926
disconnect(AudioNode * destination,unsigned output_index,unsigned input_index,ExceptionState & exception_state)927 void AudioNode::disconnect(AudioNode* destination,
928 unsigned output_index,
929 unsigned input_index,
930 ExceptionState& exception_state) {
931 DCHECK(IsMainThread());
932 BaseAudioContext::GraphAutoLocker locker(context());
933
934 if (output_index >= numberOfOutputs()) {
935 exception_state.ThrowDOMException(
936 DOMExceptionCode::kIndexSizeError,
937 ExceptionMessages::IndexOutsideRange(
938 "output index", output_index, 0u,
939 ExceptionMessages::kInclusiveBound, numberOfOutputs() - 1,
940 ExceptionMessages::kInclusiveBound));
941 return;
942 }
943
944 if (input_index >= destination->Handler().NumberOfInputs()) {
945 exception_state.ThrowDOMException(
946 DOMExceptionCode::kIndexSizeError,
947 ExceptionMessages::IndexOutsideRange(
948 "input index", input_index, 0u, ExceptionMessages::kInclusiveBound,
949 destination->numberOfInputs() - 1,
950 ExceptionMessages::kInclusiveBound));
951 return;
952 }
953
954 // If both indices are valid, proceed to disconnect.
955 if (!DisconnectFromOutputIfConnected(output_index, *destination,
956 input_index)) {
957 exception_state.ThrowDOMException(
958 DOMExceptionCode::kInvalidAccessError,
959 "output (" + String::Number(output_index) +
960 ") is not connected to the input (" + String::Number(input_index) +
961 ") of the destination.");
962 return;
963 }
964
965 Handler().UpdatePullStatusIfNeeded();
966
967 GraphTracer().DidDisconnectNodes(
968 this, destination, output_index, input_index);
969 }
970
disconnect(AudioParam * destination_param,ExceptionState & exception_state)971 void AudioNode::disconnect(AudioParam* destination_param,
972 ExceptionState& exception_state) {
973 DCHECK(IsMainThread());
974 BaseAudioContext::GraphAutoLocker locker(context());
975
976 // The number of disconnection made.
977 unsigned number_of_disconnections = 0;
978
979 // Check if the node output is connected the destination AudioParam.
980 // Disconnect if connected and increase |numberOfDisconnectios| by 1.
981 for (unsigned output_index = 0; output_index < Handler().NumberOfOutputs();
982 ++output_index) {
983 if (DisconnectFromOutputIfConnected(output_index, *destination_param))
984 number_of_disconnections++;
985 }
986
987 // Throw an exception when there is no valid connection to the destination.
988 if (number_of_disconnections == 0) {
989 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidAccessError,
990 "the given AudioParam is not connected.");
991 return;
992 }
993
994 Handler().UpdatePullStatusIfNeeded();
995
996 GraphTracer().DidDisconnectNodeParam(this, destination_param);
997 }
998
disconnect(AudioParam * destination_param,unsigned output_index,ExceptionState & exception_state)999 void AudioNode::disconnect(AudioParam* destination_param,
1000 unsigned output_index,
1001 ExceptionState& exception_state) {
1002 DCHECK(IsMainThread());
1003 BaseAudioContext::GraphAutoLocker locker(context());
1004
1005 if (output_index >= Handler().NumberOfOutputs()) {
1006 // The output index is out of range. Throw an exception.
1007 exception_state.ThrowDOMException(
1008 DOMExceptionCode::kIndexSizeError,
1009 ExceptionMessages::IndexOutsideRange(
1010 "output index", output_index, 0u,
1011 ExceptionMessages::kInclusiveBound, numberOfOutputs() - 1,
1012 ExceptionMessages::kInclusiveBound));
1013 return;
1014 }
1015
1016 // If the output index is valid, proceed to disconnect.
1017 if (!DisconnectFromOutputIfConnected(output_index, *destination_param)) {
1018 exception_state.ThrowDOMException(
1019 DOMExceptionCode::kInvalidAccessError,
1020 "specified destination AudioParam and node output (" +
1021 String::Number(output_index) + ") are not connected.");
1022 return;
1023 }
1024
1025 Handler().UpdatePullStatusIfNeeded();
1026
1027 GraphTracer().DidDisconnectNodeParam(this, destination_param, output_index);
1028 }
1029
numberOfInputs() const1030 unsigned AudioNode::numberOfInputs() const {
1031 return Handler().NumberOfInputs();
1032 }
1033
numberOfOutputs() const1034 unsigned AudioNode::numberOfOutputs() const {
1035 return Handler().NumberOfOutputs();
1036 }
1037
channelCount() const1038 unsigned AudioNode::channelCount() const {
1039 return Handler().ChannelCount();
1040 }
1041
setChannelCount(unsigned count,ExceptionState & exception_state)1042 void AudioNode::setChannelCount(unsigned count,
1043 ExceptionState& exception_state) {
1044 Handler().SetChannelCount(count, exception_state);
1045 }
1046
channelCountMode() const1047 String AudioNode::channelCountMode() const {
1048 return Handler().GetChannelCountMode();
1049 }
1050
setChannelCountMode(const String & mode,ExceptionState & exception_state)1051 void AudioNode::setChannelCountMode(const String& mode,
1052 ExceptionState& exception_state) {
1053 Handler().SetChannelCountMode(mode, exception_state);
1054 }
1055
channelInterpretation() const1056 String AudioNode::channelInterpretation() const {
1057 return Handler().ChannelInterpretation();
1058 }
1059
setChannelInterpretation(const String & interpretation,ExceptionState & exception_state)1060 void AudioNode::setChannelInterpretation(const String& interpretation,
1061 ExceptionState& exception_state) {
1062 Handler().SetChannelInterpretation(interpretation, exception_state);
1063 }
1064
InterfaceName() const1065 const AtomicString& AudioNode::InterfaceName() const {
1066 return event_target_names::kAudioNode;
1067 }
1068
GetExecutionContext() const1069 ExecutionContext* AudioNode::GetExecutionContext() const {
1070 return context()->GetExecutionContext();
1071 }
1072
DidAddOutput(unsigned number_of_outputs)1073 void AudioNode::DidAddOutput(unsigned number_of_outputs) {
1074 connected_nodes_.push_back(nullptr);
1075 DCHECK_EQ(number_of_outputs, connected_nodes_.size());
1076 connected_params_.push_back(nullptr);
1077 DCHECK_EQ(number_of_outputs, connected_params_.size());
1078 }
1079
1080 } // namespace blink
1081