1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "AudioNode.h"
8 #include "mozilla/ErrorResult.h"
9 #include "AudioNodeTrack.h"
10 #include "AudioNodeEngine.h"
11 #include "mozilla/dom/AudioParam.h"
12 #include "mozilla/Services.h"
13 #include "nsIObserverService.h"
14
15 namespace mozilla::dom {
16
17 static const uint32_t INVALID_PORT = 0xffffffff;
18 static uint32_t gId = 0;
19
20 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioNode)
21
22 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AudioNode, DOMEventTargetHelper)
23 tmp->DisconnectFromGraph();
24 if (tmp->mContext) {
25 tmp->mContext->UnregisterNode(tmp);
26 }
27 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParams)28 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParams)
29 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputNodes)
30 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputParams)
31 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
32 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
33 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AudioNode,
34 DOMEventTargetHelper)
35 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
36 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParams)
37 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputNodes)
38 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputParams)
39 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
40
41 NS_IMPL_ADDREF_INHERITED(AudioNode, DOMEventTargetHelper)
42 NS_IMPL_RELEASE_INHERITED(AudioNode, DOMEventTargetHelper)
43
44 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioNode)
45 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
46 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
47
48 AudioNode::AudioNode(AudioContext* aContext, uint32_t aChannelCount,
49 ChannelCountMode aChannelCountMode,
50 ChannelInterpretation aChannelInterpretation)
51 : DOMEventTargetHelper(aContext->GetParentObject()),
52 mContext(aContext),
53 mChannelCount(aChannelCount),
54 mChannelCountMode(aChannelCountMode),
55 mChannelInterpretation(aChannelInterpretation),
56 mId(gId++),
57 mPassThrough(false),
58 mAbstractMainThread(
59 aContext->GetOwnerGlobal()
60 ? aContext->GetOwnerGlobal()->AbstractMainThreadFor(
61 TaskCategory::Other)
62 : nullptr) {
63 MOZ_ASSERT(aContext);
64 aContext->RegisterNode(this);
65 }
66
~AudioNode()67 AudioNode::~AudioNode() {
68 MOZ_ASSERT(mInputNodes.IsEmpty());
69 MOZ_ASSERT(mOutputNodes.IsEmpty());
70 MOZ_ASSERT(mOutputParams.IsEmpty());
71 MOZ_ASSERT(!mTrack,
72 "The webaudio-node-demise notification must have been sent");
73 if (mContext) {
74 mContext->UnregisterNode(this);
75 }
76 }
77
Initialize(const AudioNodeOptions & aOptions,ErrorResult & aRv)78 void AudioNode::Initialize(const AudioNodeOptions& aOptions, ErrorResult& aRv) {
79 if (aOptions.mChannelCount.WasPassed()) {
80 SetChannelCount(aOptions.mChannelCount.Value(), aRv);
81 if (NS_WARN_IF(aRv.Failed())) {
82 return;
83 }
84 }
85
86 if (aOptions.mChannelCountMode.WasPassed()) {
87 SetChannelCountModeValue(aOptions.mChannelCountMode.Value(), aRv);
88 if (NS_WARN_IF(aRv.Failed())) {
89 return;
90 }
91 }
92
93 if (aOptions.mChannelInterpretation.WasPassed()) {
94 SetChannelInterpretationValue(aOptions.mChannelInterpretation.Value(), aRv);
95 if (NS_WARN_IF(aRv.Failed())) {
96 return;
97 }
98 }
99 }
100
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const101 size_t AudioNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
102 // Not owned:
103 // - mContext
104 // - mTrack
105 size_t amount = 0;
106
107 amount += mInputNodes.ShallowSizeOfExcludingThis(aMallocSizeOf);
108 for (size_t i = 0; i < mInputNodes.Length(); i++) {
109 amount += mInputNodes[i].SizeOfExcludingThis(aMallocSizeOf);
110 }
111
112 // Just measure the array. The entire audio node graph is measured via the
113 // MediaTrackGraph's tracks, so we don't want to double-count the elements.
114 amount += mOutputNodes.ShallowSizeOfExcludingThis(aMallocSizeOf);
115
116 amount += mOutputParams.ShallowSizeOfExcludingThis(aMallocSizeOf);
117 for (size_t i = 0; i < mOutputParams.Length(); i++) {
118 amount += mOutputParams[i]->SizeOfIncludingThis(aMallocSizeOf);
119 }
120
121 return amount;
122 }
123
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const124 size_t AudioNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
125 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
126 }
127
128 template <class InputNode>
FindIndexOfNode(const nsTArray<InputNode> & aInputNodes,const AudioNode * aNode)129 static size_t FindIndexOfNode(const nsTArray<InputNode>& aInputNodes,
130 const AudioNode* aNode) {
131 for (size_t i = 0; i < aInputNodes.Length(); ++i) {
132 if (aInputNodes[i].mInputNode == aNode) {
133 return i;
134 }
135 }
136 return nsTArray<InputNode>::NoIndex;
137 }
138
139 template <class InputNode>
FindIndexOfNodeWithPorts(const nsTArray<InputNode> & aInputNodes,const AudioNode * aNode,uint32_t aInputPort,uint32_t aOutputPort)140 static size_t FindIndexOfNodeWithPorts(const nsTArray<InputNode>& aInputNodes,
141 const AudioNode* aNode,
142 uint32_t aInputPort,
143 uint32_t aOutputPort) {
144 for (size_t i = 0; i < aInputNodes.Length(); ++i) {
145 if (aInputNodes[i].mInputNode == aNode &&
146 aInputNodes[i].mInputPort == aInputPort &&
147 aInputNodes[i].mOutputPort == aOutputPort) {
148 return i;
149 }
150 }
151 return nsTArray<InputNode>::NoIndex;
152 }
153
DisconnectFromGraph()154 void AudioNode::DisconnectFromGraph() {
155 MOZ_ASSERT(mRefCnt.get() > mInputNodes.Length(),
156 "Caller should be holding a reference");
157
158 // The idea here is that we remove connections one by one, and at each step
159 // the graph is in a valid state.
160
161 // Disconnect inputs. We don't need them anymore.
162 while (!mInputNodes.IsEmpty()) {
163 InputNode inputNode = mInputNodes.PopLastElement();
164 inputNode.mInputNode->mOutputNodes.RemoveElement(this);
165 }
166
167 while (!mOutputNodes.IsEmpty()) {
168 RefPtr<AudioNode> output = mOutputNodes.PopLastElement();
169 size_t inputIndex = FindIndexOfNode(output->mInputNodes, this);
170 // It doesn't matter which one we remove, since we're going to remove all
171 // entries for this node anyway.
172 output->mInputNodes.RemoveElementAt(inputIndex);
173 // This effects of this connection will remain.
174 output->NotifyHasPhantomInput();
175 }
176
177 while (!mOutputParams.IsEmpty()) {
178 RefPtr<AudioParam> output = mOutputParams.PopLastElement();
179 size_t inputIndex = FindIndexOfNode(output->InputNodes(), this);
180 // It doesn't matter which one we remove, since we're going to remove all
181 // entries for this node anyway.
182 output->RemoveInputNode(inputIndex);
183 }
184
185 DestroyMediaTrack();
186 }
187
Connect(AudioNode & aDestination,uint32_t aOutput,uint32_t aInput,ErrorResult & aRv)188 AudioNode* AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput,
189 uint32_t aInput, ErrorResult& aRv) {
190 if (aOutput >= NumberOfOutputs()) {
191 aRv.ThrowIndexSizeError(
192 nsPrintfCString("Output index %u is out of bounds", aOutput));
193 return nullptr;
194 }
195
196 if (aInput >= aDestination.NumberOfInputs()) {
197 aRv.ThrowIndexSizeError(
198 nsPrintfCString("Input index %u is out of bounds", aInput));
199 return nullptr;
200 }
201
202 if (Context() != aDestination.Context()) {
203 aRv.ThrowInvalidAccessError(
204 "Can't connect nodes from different AudioContexts");
205 return nullptr;
206 }
207
208 if (FindIndexOfNodeWithPorts(aDestination.mInputNodes, this, aInput,
209 aOutput) !=
210 nsTArray<AudioNode::InputNode>::NoIndex) {
211 // connection already exists.
212 return &aDestination;
213 }
214
215 WEB_AUDIO_API_LOG("%f: %s %u Connect() to %s %u", Context()->CurrentTime(),
216 NodeType(), Id(), aDestination.NodeType(),
217 aDestination.Id());
218
219 // The MediaTrackGraph will handle cycle detection. We don't need to do it
220 // here.
221
222 mOutputNodes.AppendElement(&aDestination);
223 InputNode* input = aDestination.mInputNodes.AppendElement();
224 input->mInputNode = this;
225 input->mInputPort = aInput;
226 input->mOutputPort = aOutput;
227 AudioNodeTrack* destinationTrack = aDestination.mTrack;
228 if (mTrack && destinationTrack) {
229 // Connect tracks in the MediaTrackGraph
230 MOZ_ASSERT(aInput <= UINT16_MAX, "Unexpected large input port number");
231 MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number");
232 input->mTrackPort = destinationTrack->AllocateInputPort(
233 mTrack, static_cast<uint16_t>(aInput), static_cast<uint16_t>(aOutput));
234 }
235 aDestination.NotifyInputsChanged();
236
237 return &aDestination;
238 }
239
Connect(AudioParam & aDestination,uint32_t aOutput,ErrorResult & aRv)240 void AudioNode::Connect(AudioParam& aDestination, uint32_t aOutput,
241 ErrorResult& aRv) {
242 if (aOutput >= NumberOfOutputs()) {
243 aRv.ThrowIndexSizeError(
244 nsPrintfCString("Output index %u is out of bounds", aOutput));
245 return;
246 }
247
248 if (Context() != aDestination.GetParentObject()) {
249 aRv.ThrowInvalidAccessError(
250 "Can't connect a node to an AudioParam from a different AudioContext");
251 return;
252 }
253
254 if (FindIndexOfNodeWithPorts(aDestination.InputNodes(), this, INVALID_PORT,
255 aOutput) !=
256 nsTArray<AudioNode::InputNode>::NoIndex) {
257 // connection already exists.
258 return;
259 }
260
261 mOutputParams.AppendElement(&aDestination);
262 InputNode* input = aDestination.AppendInputNode();
263 input->mInputNode = this;
264 input->mInputPort = INVALID_PORT;
265 input->mOutputPort = aOutput;
266
267 mozilla::MediaTrack* track = aDestination.Track();
268 MOZ_ASSERT(track->AsProcessedTrack());
269 ProcessedMediaTrack* ps = static_cast<ProcessedMediaTrack*>(track);
270 if (mTrack) {
271 // Setup our track as an input to the AudioParam's track
272 MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number");
273 input->mTrackPort =
274 ps->AllocateInputPort(mTrack, 0, static_cast<uint16_t>(aOutput));
275 }
276 }
277
SendDoubleParameterToTrack(uint32_t aIndex,double aValue)278 void AudioNode::SendDoubleParameterToTrack(uint32_t aIndex, double aValue) {
279 MOZ_ASSERT(mTrack, "How come we don't have a track here?");
280 mTrack->SetDoubleParameter(aIndex, aValue);
281 }
282
SendInt32ParameterToTrack(uint32_t aIndex,int32_t aValue)283 void AudioNode::SendInt32ParameterToTrack(uint32_t aIndex, int32_t aValue) {
284 MOZ_ASSERT(mTrack, "How come we don't have a track here?");
285 mTrack->SetInt32Parameter(aIndex, aValue);
286 }
287
SendChannelMixingParametersToTrack()288 void AudioNode::SendChannelMixingParametersToTrack() {
289 if (mTrack) {
290 mTrack->SetChannelMixingParameters(mChannelCount, mChannelCountMode,
291 mChannelInterpretation);
292 }
293 }
294
295 template <>
DisconnectFromOutputIfConnected(uint32_t aOutputNodeIndex,uint32_t aInputIndex)296 bool AudioNode::DisconnectFromOutputIfConnected<AudioNode>(
297 uint32_t aOutputNodeIndex, uint32_t aInputIndex) {
298 WEB_AUDIO_API_LOG("%f: %s %u Disconnect()", Context()->CurrentTime(),
299 NodeType(), Id());
300
301 AudioNode* destination = mOutputNodes[aOutputNodeIndex];
302
303 MOZ_ASSERT(aOutputNodeIndex < mOutputNodes.Length());
304 MOZ_ASSERT(aInputIndex < destination->InputNodes().Length());
305
306 // An upstream node may be starting to play on the graph thread, and the
307 // engine for a downstream node may be sending a PlayingRefChangeHandler
308 // ADDREF message to this (main) thread. Wait for a round trip before
309 // releasing nodes, to give engines receiving sound now time to keep their
310 // nodes alive.
311 class RunnableRelease final : public Runnable {
312 public:
313 explicit RunnableRelease(already_AddRefed<AudioNode> aNode)
314 : mozilla::Runnable("RunnableRelease"), mNode(aNode) {}
315
316 NS_IMETHOD Run() override {
317 mNode = nullptr;
318 return NS_OK;
319 }
320
321 private:
322 RefPtr<AudioNode> mNode;
323 };
324
325 InputNode& input = destination->mInputNodes[aInputIndex];
326 if (input.mInputNode != this) {
327 return false;
328 }
329
330 // Remove one instance of 'dest' from mOutputNodes. There could be
331 // others, and it's not correct to remove them all since some of them
332 // could be for different output ports.
333 RefPtr<AudioNode> output = std::move(mOutputNodes[aOutputNodeIndex]);
334 mOutputNodes.RemoveElementAt(aOutputNodeIndex);
335 // Destroying the InputNode here sends a message to the graph thread
336 // to disconnect the tracks, which should be sent before the
337 // RunAfterPendingUpdates() call below.
338 destination->mInputNodes.RemoveElementAt(aInputIndex);
339 output->NotifyInputsChanged();
340 if (mTrack) {
341 nsCOMPtr<nsIRunnable> runnable = new RunnableRelease(output.forget());
342 mTrack->RunAfterPendingUpdates(runnable.forget());
343 }
344 return true;
345 }
346
347 template <>
DisconnectFromOutputIfConnected(uint32_t aOutputParamIndex,uint32_t aInputIndex)348 bool AudioNode::DisconnectFromOutputIfConnected<AudioParam>(
349 uint32_t aOutputParamIndex, uint32_t aInputIndex) {
350 MOZ_ASSERT(aOutputParamIndex < mOutputParams.Length());
351
352 AudioParam* destination = mOutputParams[aOutputParamIndex];
353
354 MOZ_ASSERT(aInputIndex < destination->InputNodes().Length());
355
356 const InputNode& input = destination->InputNodes()[aInputIndex];
357 if (input.mInputNode != this) {
358 return false;
359 }
360 destination->RemoveInputNode(aInputIndex);
361 // Remove one instance of 'dest' from mOutputParams. There could be
362 // others, and it's not correct to remove them all since some of them
363 // could be for different output ports.
364 mOutputParams.RemoveElementAt(aOutputParamIndex);
365 return true;
366 }
367
368 template <>
369 const nsTArray<AudioNode::InputNode>&
InputsForDestination(uint32_t aOutputNodeIndex) const370 AudioNode::InputsForDestination<AudioNode>(uint32_t aOutputNodeIndex) const {
371 return mOutputNodes[aOutputNodeIndex]->InputNodes();
372 }
373
374 template <>
375 const nsTArray<AudioNode::InputNode>&
InputsForDestination(uint32_t aOutputNodeIndex) const376 AudioNode::InputsForDestination<AudioParam>(uint32_t aOutputNodeIndex) const {
377 return mOutputParams[aOutputNodeIndex]->InputNodes();
378 }
379
380 template <typename DestinationType, typename Predicate>
DisconnectMatchingDestinationInputs(uint32_t aDestinationIndex,Predicate aPredicate)381 bool AudioNode::DisconnectMatchingDestinationInputs(uint32_t aDestinationIndex,
382 Predicate aPredicate) {
383 bool wasConnected = false;
384 uint32_t inputCount =
385 InputsForDestination<DestinationType>(aDestinationIndex).Length();
386
387 for (int32_t inputIndex = inputCount - 1; inputIndex >= 0; --inputIndex) {
388 const InputNode& input =
389 InputsForDestination<DestinationType>(aDestinationIndex)[inputIndex];
390 if (aPredicate(input)) {
391 if (DisconnectFromOutputIfConnected<DestinationType>(aDestinationIndex,
392 inputIndex)) {
393 wasConnected = true;
394 break;
395 }
396 }
397 }
398 return wasConnected;
399 }
400
Disconnect(ErrorResult & aRv)401 void AudioNode::Disconnect(ErrorResult& aRv) {
402 for (int32_t outputIndex = mOutputNodes.Length() - 1; outputIndex >= 0;
403 --outputIndex) {
404 DisconnectMatchingDestinationInputs<AudioNode>(
405 outputIndex, [](const InputNode&) { return true; });
406 }
407
408 for (int32_t outputIndex = mOutputParams.Length() - 1; outputIndex >= 0;
409 --outputIndex) {
410 DisconnectMatchingDestinationInputs<AudioParam>(
411 outputIndex, [](const InputNode&) { return true; });
412 }
413 }
414
Disconnect(uint32_t aOutput,ErrorResult & aRv)415 void AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv) {
416 if (aOutput >= NumberOfOutputs()) {
417 aRv.ThrowIndexSizeError(
418 nsPrintfCString("Output index %u is out of bounds", aOutput));
419 return;
420 }
421
422 for (int32_t outputIndex = mOutputNodes.Length() - 1; outputIndex >= 0;
423 --outputIndex) {
424 DisconnectMatchingDestinationInputs<AudioNode>(
425 outputIndex, [aOutput](const InputNode& aInputNode) {
426 return aInputNode.mOutputPort == aOutput;
427 });
428 }
429
430 for (int32_t outputIndex = mOutputParams.Length() - 1; outputIndex >= 0;
431 --outputIndex) {
432 DisconnectMatchingDestinationInputs<AudioParam>(
433 outputIndex, [aOutput](const InputNode& aInputNode) {
434 return aInputNode.mOutputPort == aOutput;
435 });
436 }
437 }
438
Disconnect(AudioNode & aDestination,ErrorResult & aRv)439 void AudioNode::Disconnect(AudioNode& aDestination, ErrorResult& aRv) {
440 bool wasConnected = false;
441
442 for (int32_t outputIndex = mOutputNodes.Length() - 1; outputIndex >= 0;
443 --outputIndex) {
444 if (mOutputNodes[outputIndex] != &aDestination) {
445 continue;
446 }
447 wasConnected |= DisconnectMatchingDestinationInputs<AudioNode>(
448 outputIndex, [](const InputNode&) { return true; });
449 }
450
451 if (!wasConnected) {
452 aRv.ThrowInvalidAccessError(
453 "Trying to disconnect from a node we're not connected to");
454 return;
455 }
456 }
457
Disconnect(AudioNode & aDestination,uint32_t aOutput,ErrorResult & aRv)458 void AudioNode::Disconnect(AudioNode& aDestination, uint32_t aOutput,
459 ErrorResult& aRv) {
460 if (aOutput >= NumberOfOutputs()) {
461 aRv.ThrowIndexSizeError(
462 nsPrintfCString("Output index %u is out of bounds", aOutput));
463 return;
464 }
465
466 bool wasConnected = false;
467
468 for (int32_t outputIndex = mOutputNodes.Length() - 1; outputIndex >= 0;
469 --outputIndex) {
470 if (mOutputNodes[outputIndex] != &aDestination) {
471 continue;
472 }
473 wasConnected |= DisconnectMatchingDestinationInputs<AudioNode>(
474 outputIndex, [aOutput](const InputNode& aInputNode) {
475 return aInputNode.mOutputPort == aOutput;
476 });
477 }
478
479 if (!wasConnected) {
480 aRv.ThrowInvalidAccessError(
481 "Trying to disconnect from a node we're not connected to");
482 return;
483 }
484 }
485
Disconnect(AudioNode & aDestination,uint32_t aOutput,uint32_t aInput,ErrorResult & aRv)486 void AudioNode::Disconnect(AudioNode& aDestination, uint32_t aOutput,
487 uint32_t aInput, ErrorResult& aRv) {
488 if (aOutput >= NumberOfOutputs()) {
489 aRv.ThrowIndexSizeError(
490 nsPrintfCString("Output index %u is out of bounds", aOutput));
491 return;
492 }
493
494 if (aInput >= aDestination.NumberOfInputs()) {
495 aRv.ThrowIndexSizeError(
496 nsPrintfCString("Input index %u is out of bounds", aInput));
497 return;
498 }
499
500 bool wasConnected = false;
501
502 for (int32_t outputIndex = mOutputNodes.Length() - 1; outputIndex >= 0;
503 --outputIndex) {
504 if (mOutputNodes[outputIndex] != &aDestination) {
505 continue;
506 }
507 wasConnected |= DisconnectMatchingDestinationInputs<AudioNode>(
508 outputIndex, [aOutput, aInput](const InputNode& aInputNode) {
509 return aInputNode.mOutputPort == aOutput &&
510 aInputNode.mInputPort == aInput;
511 });
512 }
513
514 if (!wasConnected) {
515 aRv.ThrowInvalidAccessError(
516 "Trying to disconnect from a node we're not connected to");
517 return;
518 }
519 }
520
Disconnect(AudioParam & aDestination,ErrorResult & aRv)521 void AudioNode::Disconnect(AudioParam& aDestination, ErrorResult& aRv) {
522 bool wasConnected = false;
523
524 for (int32_t outputIndex = mOutputParams.Length() - 1; outputIndex >= 0;
525 --outputIndex) {
526 if (mOutputParams[outputIndex] != &aDestination) {
527 continue;
528 }
529 wasConnected |= DisconnectMatchingDestinationInputs<AudioParam>(
530 outputIndex, [](const InputNode&) { return true; });
531 }
532
533 if (!wasConnected) {
534 aRv.ThrowInvalidAccessError(
535 "Trying to disconnect from an AudioParam we're not connected to");
536 return;
537 }
538 }
539
Disconnect(AudioParam & aDestination,uint32_t aOutput,ErrorResult & aRv)540 void AudioNode::Disconnect(AudioParam& aDestination, uint32_t aOutput,
541 ErrorResult& aRv) {
542 if (aOutput >= NumberOfOutputs()) {
543 aRv.ThrowIndexSizeError(
544 nsPrintfCString("Output index %u is out of bounds", aOutput));
545 return;
546 }
547
548 bool wasConnected = false;
549
550 for (int32_t outputIndex = mOutputParams.Length() - 1; outputIndex >= 0;
551 --outputIndex) {
552 if (mOutputParams[outputIndex] != &aDestination) {
553 continue;
554 }
555 wasConnected |= DisconnectMatchingDestinationInputs<AudioParam>(
556 outputIndex, [aOutput](const InputNode& aInputNode) {
557 return aInputNode.mOutputPort == aOutput;
558 });
559 }
560
561 if (!wasConnected) {
562 aRv.ThrowInvalidAccessError(
563 "Trying to disconnect from an AudioParam we're not connected to");
564 return;
565 }
566 }
567
DestroyMediaTrack()568 void AudioNode::DestroyMediaTrack() {
569 if (mTrack) {
570 // Remove the node pointer on the engine.
571 AudioNodeTrack* ns = mTrack;
572 MOZ_ASSERT(ns, "How come we don't have a track here?");
573 MOZ_ASSERT(ns->Engine()->NodeMainThread() == this,
574 "Invalid node reference");
575 ns->Engine()->ClearNode();
576
577 mTrack->Destroy();
578 mTrack = nullptr;
579
580 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
581 if (obs) {
582 nsAutoString id;
583 id.AppendPrintf("%u", mId);
584 obs->NotifyObservers(nullptr, "webaudio-node-demise", id.get());
585 }
586 }
587 }
588
RemoveOutputParam(AudioParam * aParam)589 void AudioNode::RemoveOutputParam(AudioParam* aParam) {
590 mOutputParams.RemoveElement(aParam);
591 }
592
PassThrough() const593 bool AudioNode::PassThrough() const {
594 MOZ_ASSERT(NumberOfInputs() <= 1 && NumberOfOutputs() == 1);
595 return mPassThrough;
596 }
597
SetPassThrough(bool aPassThrough)598 void AudioNode::SetPassThrough(bool aPassThrough) {
599 MOZ_ASSERT(NumberOfInputs() <= 1 && NumberOfOutputs() == 1);
600 mPassThrough = aPassThrough;
601 if (mTrack) {
602 mTrack->SetPassThrough(mPassThrough);
603 }
604 }
605
CreateAudioParam(uint32_t aIndex,const nsAString & aName,float aDefaultValue,float aMinValue,float aMaxValue)606 AudioParam* AudioNode::CreateAudioParam(uint32_t aIndex, const nsAString& aName,
607 float aDefaultValue, float aMinValue,
608 float aMaxValue) {
609 return *mParams.AppendElement(
610 new AudioParam(this, aIndex, aName, aDefaultValue, aMinValue, aMaxValue));
611 }
612
613 } // namespace mozilla::dom
614