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