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