DynamicsCompressorNode.cpp (8271B)
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 "DynamicsCompressorNode.h" 8 9 #include "AudioDestinationNode.h" 10 #include "AudioNodeEngine.h" 11 #include "AudioNodeTrack.h" 12 #include "Tracing.h" 13 #include "WebAudioUtils.h" 14 #include "blink/DynamicsCompressor.h" 15 #include "mozilla/dom/DynamicsCompressorNodeBinding.h" 16 17 using WebCore::DynamicsCompressor; 18 19 namespace mozilla::dom { 20 21 NS_IMPL_CYCLE_COLLECTION_INHERITED(DynamicsCompressorNode, AudioNode, 22 mThreshold, mKnee, mRatio, mAttack, mRelease) 23 24 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DynamicsCompressorNode) 25 NS_INTERFACE_MAP_END_INHERITING(AudioNode) 26 27 NS_IMPL_ADDREF_INHERITED(DynamicsCompressorNode, AudioNode) 28 NS_IMPL_RELEASE_INHERITED(DynamicsCompressorNode, AudioNode) 29 30 class DynamicsCompressorNodeEngine final : public AudioNodeEngine { 31 public: 32 explicit DynamicsCompressorNodeEngine(AudioNode* aNode, 33 AudioDestinationNode* aDestination) 34 : AudioNodeEngine(aNode), 35 mDestination(aDestination->Track()) 36 // Keep the default value in sync with the default value in 37 // DynamicsCompressorNode::DynamicsCompressorNode. 38 , 39 mThreshold(-24.f), 40 mKnee(30.f), 41 mRatio(12.f), 42 mAttack(0.003f), 43 mRelease(0.25f), 44 mCompressor(new DynamicsCompressor(mDestination->mSampleRate, 2)) {} 45 46 enum Parameters { THRESHOLD, KNEE, RATIO, ATTACK, RELEASE }; 47 void RecvTimelineEvent(uint32_t aIndex, AudioParamEvent& aEvent) override { 48 MOZ_ASSERT(mDestination); 49 50 aEvent.ConvertToTicks(mDestination); 51 52 switch (aIndex) { 53 case THRESHOLD: 54 mThreshold.InsertEvent<int64_t>(aEvent); 55 break; 56 case KNEE: 57 mKnee.InsertEvent<int64_t>(aEvent); 58 break; 59 case RATIO: 60 mRatio.InsertEvent<int64_t>(aEvent); 61 break; 62 case ATTACK: 63 mAttack.InsertEvent<int64_t>(aEvent); 64 break; 65 case RELEASE: 66 mRelease.InsertEvent<int64_t>(aEvent); 67 break; 68 default: 69 NS_ERROR("Bad DynamicsCompresssorNodeEngine TimelineParameter"); 70 } 71 } 72 73 void ProcessBlock(AudioNodeTrack* aTrack, GraphTime aFrom, 74 const AudioBlock& aInput, AudioBlock* aOutput, 75 bool* aFinished) override { 76 TRACE("DynamicsCompressorNodeEngine::ProcessBlock"); 77 if (aInput.IsNull()) { 78 // Just output silence 79 *aOutput = aInput; 80 return; 81 } 82 83 const uint32_t channelCount = aInput.ChannelCount(); 84 if (mCompressor->numberOfChannels() != channelCount) { 85 // Create a new compressor object with a new channel count 86 mCompressor = MakeUnique<WebCore::DynamicsCompressor>( 87 aTrack->mSampleRate, aInput.ChannelCount()); 88 } 89 90 TrackTime pos = mDestination->GraphTimeToTrackTime(aFrom); 91 mCompressor->setParameterValue(DynamicsCompressor::ParamThreshold, 92 mThreshold.GetValueAtTime(pos)); 93 mCompressor->setParameterValue(DynamicsCompressor::ParamKnee, 94 mKnee.GetValueAtTime(pos)); 95 mCompressor->setParameterValue(DynamicsCompressor::ParamRatio, 96 mRatio.GetValueAtTime(pos)); 97 mCompressor->setParameterValue(DynamicsCompressor::ParamAttack, 98 mAttack.GetValueAtTime(pos)); 99 mCompressor->setParameterValue(DynamicsCompressor::ParamRelease, 100 mRelease.GetValueAtTime(pos)); 101 102 aOutput->AllocateChannels(channelCount); 103 mCompressor->process(&aInput, aOutput, aInput.GetDuration()); 104 105 SendReductionParamToMainThread( 106 aTrack, 107 mCompressor->parameterValue(DynamicsCompressor::ParamReduction)); 108 } 109 110 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override { 111 // Not owned: 112 // - mDestination (probably) 113 // - Don't count the AudioParamTimelines, their inner refs are owned by the 114 // AudioNode. 115 size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); 116 amount += mCompressor->sizeOfIncludingThis(aMallocSizeOf); 117 return amount; 118 } 119 120 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override { 121 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 122 } 123 124 private: 125 void SendReductionParamToMainThread(AudioNodeTrack* aTrack, 126 float aReduction) { 127 MOZ_ASSERT(!NS_IsMainThread()); 128 129 class Command final : public Runnable { 130 public: 131 Command(AudioNodeTrack* aTrack, float aReduction) 132 : mozilla::Runnable("Command"), 133 mTrack(aTrack), 134 mReduction(aReduction) {} 135 136 NS_IMETHOD Run() override { 137 RefPtr<DynamicsCompressorNode> node = 138 static_cast<DynamicsCompressorNode*>( 139 mTrack->Engine()->NodeMainThread()); 140 if (node) { 141 node->SetReduction(mReduction); 142 } 143 return NS_OK; 144 } 145 146 private: 147 RefPtr<AudioNodeTrack> mTrack; 148 float mReduction; 149 }; 150 151 AbstractThread::MainThread()->Dispatch( 152 do_AddRef(new Command(aTrack, aReduction))); 153 } 154 155 private: 156 RefPtr<AudioNodeTrack> mDestination; 157 AudioParamTimeline mThreshold; 158 AudioParamTimeline mKnee; 159 AudioParamTimeline mRatio; 160 AudioParamTimeline mAttack; 161 AudioParamTimeline mRelease; 162 UniquePtr<DynamicsCompressor> mCompressor; 163 }; 164 165 DynamicsCompressorNode::DynamicsCompressorNode(AudioContext* aContext) 166 : AudioNode(aContext, 2, ChannelCountMode::Clamped_max, 167 ChannelInterpretation::Speakers), 168 mReduction(0) { 169 mThreshold = CreateAudioParam(DynamicsCompressorNodeEngine::THRESHOLD, 170 u"threshold"_ns, -24.f, -100.f, 0.f); 171 mKnee = CreateAudioParam(DynamicsCompressorNodeEngine::KNEE, u"knee"_ns, 30.f, 172 0.f, 40.f); 173 mRatio = CreateAudioParam(DynamicsCompressorNodeEngine::RATIO, u"ratio"_ns, 174 12.f, 1.f, 20.f); 175 mAttack = CreateAudioParam(DynamicsCompressorNodeEngine::ATTACK, u"attack"_ns, 176 0.003f, 0.f, 1.f); 177 mRelease = CreateAudioParam(DynamicsCompressorNodeEngine::RELEASE, 178 u"release"_ns, 0.25f, 0.f, 1.f); 179 DynamicsCompressorNodeEngine* engine = 180 new DynamicsCompressorNodeEngine(this, aContext->Destination()); 181 mTrack = AudioNodeTrack::Create( 182 aContext, engine, AudioNodeTrack::NO_TRACK_FLAGS, aContext->Graph()); 183 } 184 185 /* static */ 186 already_AddRefed<DynamicsCompressorNode> DynamicsCompressorNode::Create( 187 AudioContext& aAudioContext, const DynamicsCompressorOptions& aOptions, 188 ErrorResult& aRv) { 189 RefPtr<DynamicsCompressorNode> audioNode = 190 new DynamicsCompressorNode(&aAudioContext); 191 192 audioNode->Initialize(aOptions, aRv); 193 if (NS_WARN_IF(aRv.Failed())) { 194 return nullptr; 195 } 196 197 audioNode->Attack()->SetInitialValue(aOptions.mAttack); 198 audioNode->Knee()->SetInitialValue(aOptions.mKnee); 199 audioNode->Ratio()->SetInitialValue(aOptions.mRatio); 200 audioNode->GetRelease()->SetInitialValue(aOptions.mRelease); 201 audioNode->Threshold()->SetInitialValue(aOptions.mThreshold); 202 203 return audioNode.forget(); 204 } 205 206 size_t DynamicsCompressorNode::SizeOfExcludingThis( 207 MallocSizeOf aMallocSizeOf) const { 208 size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf); 209 amount += mThreshold->SizeOfIncludingThis(aMallocSizeOf); 210 amount += mKnee->SizeOfIncludingThis(aMallocSizeOf); 211 amount += mRatio->SizeOfIncludingThis(aMallocSizeOf); 212 amount += mAttack->SizeOfIncludingThis(aMallocSizeOf); 213 amount += mRelease->SizeOfIncludingThis(aMallocSizeOf); 214 return amount; 215 } 216 217 size_t DynamicsCompressorNode::SizeOfIncludingThis( 218 MallocSizeOf aMallocSizeOf) const { 219 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 220 } 221 222 JSObject* DynamicsCompressorNode::WrapObject( 223 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { 224 return DynamicsCompressorNode_Binding::Wrap(aCx, this, aGivenProto); 225 } 226 227 } // namespace mozilla::dom