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 "GainNode.h"
8 #include "mozilla/dom/GainNodeBinding.h"
9 #include "AlignmentUtils.h"
10 #include "AudioNodeEngine.h"
11 #include "AudioNodeStream.h"
12 #include "AudioDestinationNode.h"
13 #include "WebAudioUtils.h"
14
15 namespace mozilla {
16 namespace dom {
17
18 NS_IMPL_CYCLE_COLLECTION_INHERITED(GainNode, AudioNode, mGain)
19
20 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GainNode)
21 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
22
23 NS_IMPL_ADDREF_INHERITED(GainNode, AudioNode)
24 NS_IMPL_RELEASE_INHERITED(GainNode, AudioNode)
25
26 class GainNodeEngine final : public AudioNodeEngine {
27 public:
GainNodeEngine(AudioNode * aNode,AudioDestinationNode * aDestination)28 GainNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
29 : AudioNodeEngine(aNode),
30 mDestination(aDestination->Stream())
31 // Keep the default value in sync with the default value in
32 // GainNode::GainNode.
33 ,
34 mGain(1.f) {}
35
36 enum Parameters { GAIN };
RecvTimelineEvent(uint32_t aIndex,AudioTimelineEvent & aEvent)37 void RecvTimelineEvent(uint32_t aIndex, AudioTimelineEvent& aEvent) override {
38 MOZ_ASSERT(mDestination);
39 WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent, mDestination);
40
41 switch (aIndex) {
42 case GAIN:
43 mGain.InsertEvent<int64_t>(aEvent);
44 break;
45 default:
46 NS_ERROR("Bad GainNodeEngine TimelineParameter");
47 }
48 }
49
ProcessBlock(AudioNodeStream * aStream,GraphTime aFrom,const AudioBlock & aInput,AudioBlock * aOutput,bool * aFinished)50 void ProcessBlock(AudioNodeStream* aStream, GraphTime aFrom,
51 const AudioBlock& aInput, AudioBlock* aOutput,
52 bool* aFinished) override {
53 if (aInput.IsNull()) {
54 // If input is silent, so is the output
55 aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
56 } else if (mGain.HasSimpleValue()) {
57 // Optimize the case where we only have a single value set as the volume
58 float gain = mGain.GetValue();
59 if (gain == 0.0f) {
60 aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
61 } else {
62 *aOutput = aInput;
63 aOutput->mVolume *= gain;
64 }
65 } else {
66 // First, compute a vector of gains for each track tick based on the
67 // timeline at hand, and then for each channel, multiply the values
68 // in the buffer with the gain vector.
69 aOutput->AllocateChannels(aInput.ChannelCount());
70
71 // Compute the gain values for the duration of the input AudioChunk
72 StreamTime tick = mDestination->GraphTimeToStreamTime(aFrom);
73 float computedGain[WEBAUDIO_BLOCK_SIZE + 4];
74 float* alignedComputedGain = ALIGNED16(computedGain);
75 ASSERT_ALIGNED16(alignedComputedGain);
76 mGain.GetValuesAtTime(tick, alignedComputedGain, WEBAUDIO_BLOCK_SIZE);
77
78 for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
79 alignedComputedGain[counter] *= aInput.mVolume;
80 }
81
82 // Apply the gain to the output buffer
83 for (size_t channel = 0; channel < aOutput->ChannelCount(); ++channel) {
84 const float* inputBuffer =
85 static_cast<const float*>(aInput.mChannelData[channel]);
86 float* buffer = aOutput->ChannelFloatsForWrite(channel);
87 AudioBlockCopyChannelWithScale(inputBuffer, alignedComputedGain,
88 buffer);
89 }
90 }
91 }
92
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const93 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override {
94 // Not owned:
95 // - mDestination - MediaStreamGraphImpl::CollectSizesForMemoryReport()
96 // accounts for mDestination.
97 // - mGain - Internal ref owned by AudioNode
98 return AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
99 }
100
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const101 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
102 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
103 }
104
105 RefPtr<AudioNodeStream> mDestination;
106 AudioParamTimeline mGain;
107 };
108
GainNode(AudioContext * aContext)109 GainNode::GainNode(AudioContext* aContext)
110 : AudioNode(aContext, 2, ChannelCountMode::Max,
111 ChannelInterpretation::Speakers),
112 mGain(new AudioParam(this, GainNodeEngine::GAIN, "gain", 1.0f)) {
113 GainNodeEngine* engine = new GainNodeEngine(this, aContext->Destination());
114 mStream = AudioNodeStream::Create(
115 aContext, engine, AudioNodeStream::NO_STREAM_FLAGS, aContext->Graph());
116 }
117
Create(AudioContext & aAudioContext,const GainOptions & aOptions,ErrorResult & aRv)118 /* static */ already_AddRefed<GainNode> GainNode::Create(
119 AudioContext& aAudioContext, const GainOptions& aOptions,
120 ErrorResult& aRv) {
121 if (aAudioContext.CheckClosed(aRv)) {
122 return nullptr;
123 }
124
125 RefPtr<GainNode> audioNode = new GainNode(&aAudioContext);
126
127 audioNode->Initialize(aOptions, aRv);
128 if (NS_WARN_IF(aRv.Failed())) {
129 return nullptr;
130 }
131
132 audioNode->Gain()->SetValue(aOptions.mGain);
133 return audioNode.forget();
134 }
135
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const136 size_t GainNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
137 size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
138 amount += mGain->SizeOfIncludingThis(aMallocSizeOf);
139 return amount;
140 }
141
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const142 size_t GainNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
143 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
144 }
145
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)146 JSObject* GainNode::WrapObject(JSContext* aCx,
147 JS::Handle<JSObject*> aGivenProto) {
148 return GainNodeBinding::Wrap(aCx, this, aGivenProto);
149 }
150
151 } // namespace dom
152 } // namespace mozilla
153