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