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 "mozilla/dom/ChannelSplitterNode.h"
8 #include "mozilla/dom/ChannelSplitterNodeBinding.h"
9 #include "AudioNodeEngine.h"
10 #include "AudioNodeTrack.h"
11 
12 namespace mozilla {
13 namespace dom {
14 
15 class ChannelSplitterNodeEngine final : public AudioNodeEngine {
16  public:
ChannelSplitterNodeEngine(ChannelSplitterNode * aNode)17   explicit ChannelSplitterNodeEngine(ChannelSplitterNode* aNode)
18       : AudioNodeEngine(aNode) {
19     MOZ_ASSERT(NS_IsMainThread());
20   }
21 
ProcessBlocksOnPorts(AudioNodeTrack * aTrack,GraphTime aFrom,Span<const AudioBlock> aInput,Span<AudioBlock> aOutput,bool * aFinished)22   void ProcessBlocksOnPorts(AudioNodeTrack* aTrack, GraphTime aFrom,
23                             Span<const AudioBlock> aInput,
24                             Span<AudioBlock> aOutput,
25                             bool* aFinished) override {
26     MOZ_ASSERT(aInput.Length() == 1, "Should only have one input port");
27     MOZ_ASSERT(aOutput.Length() == OutputCount());
28 
29     for (uint16_t i = 0; i < OutputCount(); ++i) {
30       if (i < aInput[0].ChannelCount()) {
31         // Split out existing channels
32         aOutput[i].AllocateChannels(1);
33         AudioBlockCopyChannelWithScale(
34             static_cast<const float*>(aInput[0].mChannelData[i]),
35             aInput[0].mVolume, aOutput[i].ChannelFloatsForWrite(0));
36       } else {
37         // Pad with silent channels if needed
38         aOutput[i].SetNull(WEBAUDIO_BLOCK_SIZE);
39       }
40     }
41   }
42 
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const43   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
44     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
45   }
46 };
47 
ChannelSplitterNode(AudioContext * aContext,uint16_t aOutputCount)48 ChannelSplitterNode::ChannelSplitterNode(AudioContext* aContext,
49                                          uint16_t aOutputCount)
50     : AudioNode(aContext, aOutputCount, ChannelCountMode::Explicit,
51                 ChannelInterpretation::Discrete),
52       mOutputCount(aOutputCount) {
53   mTrack =
54       AudioNodeTrack::Create(aContext, new ChannelSplitterNodeEngine(this),
55                              AudioNodeTrack::NO_TRACK_FLAGS, aContext->Graph());
56 }
57 
58 /* static */
Create(AudioContext & aAudioContext,const ChannelSplitterOptions & aOptions,ErrorResult & aRv)59 already_AddRefed<ChannelSplitterNode> ChannelSplitterNode::Create(
60     AudioContext& aAudioContext, const ChannelSplitterOptions& aOptions,
61     ErrorResult& aRv) {
62   if (aOptions.mNumberOfOutputs == 0 ||
63       aOptions.mNumberOfOutputs > WebAudioUtils::MaxChannelCount) {
64     aRv.ThrowIndexSizeError(nsPrintfCString(
65         "%u is not a valid number of outputs", aOptions.mNumberOfOutputs));
66     return nullptr;
67   }
68 
69   RefPtr<ChannelSplitterNode> audioNode =
70       new ChannelSplitterNode(&aAudioContext, aOptions.mNumberOfOutputs);
71 
72   // Manually check that the other options are valid, this node has
73   // channelCount, channelCountMode and channelInterpretation constraints: they
74   // cannot be changed from the default.
75   if (aOptions.mChannelCount.WasPassed()) {
76     audioNode->SetChannelCount(aOptions.mChannelCount.Value(), aRv);
77     if (aRv.Failed()) {
78       return nullptr;
79     }
80   }
81   if (aOptions.mChannelInterpretation.WasPassed()) {
82     audioNode->SetChannelInterpretationValue(
83         aOptions.mChannelInterpretation.Value(), aRv);
84     if (aRv.Failed()) {
85       return nullptr;
86     }
87   }
88   if (aOptions.mChannelCountMode.WasPassed()) {
89     audioNode->SetChannelCountModeValue(aOptions.mChannelCountMode.Value(),
90                                         aRv);
91     if (aRv.Failed()) {
92       return nullptr;
93     }
94   }
95 
96   return audioNode.forget();
97 }
98 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)99 JSObject* ChannelSplitterNode::WrapObject(JSContext* aCx,
100                                           JS::Handle<JSObject*> aGivenProto) {
101   return ChannelSplitterNode_Binding::Wrap(aCx, this, aGivenProto);
102 }
103 
104 }  // namespace dom
105 }  // namespace mozilla
106