1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/webaudio/DynamicsCompressorNode.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,286 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "DynamicsCompressorNode.h" 1.11 +#include "mozilla/dom/DynamicsCompressorNodeBinding.h" 1.12 +#include "AudioNodeEngine.h" 1.13 +#include "AudioNodeStream.h" 1.14 +#include "AudioDestinationNode.h" 1.15 +#include "WebAudioUtils.h" 1.16 +#include "blink/DynamicsCompressor.h" 1.17 + 1.18 +using WebCore::DynamicsCompressor; 1.19 + 1.20 +namespace mozilla { 1.21 +namespace dom { 1.22 + 1.23 +NS_IMPL_CYCLE_COLLECTION_INHERITED(DynamicsCompressorNode, AudioNode, 1.24 + mThreshold, 1.25 + mKnee, 1.26 + mRatio, 1.27 + mReduction, 1.28 + mAttack, 1.29 + mRelease) 1.30 + 1.31 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DynamicsCompressorNode) 1.32 +NS_INTERFACE_MAP_END_INHERITING(AudioNode) 1.33 + 1.34 +NS_IMPL_ADDREF_INHERITED(DynamicsCompressorNode, AudioNode) 1.35 +NS_IMPL_RELEASE_INHERITED(DynamicsCompressorNode, AudioNode) 1.36 + 1.37 +class DynamicsCompressorNodeEngine : public AudioNodeEngine 1.38 +{ 1.39 +public: 1.40 + explicit DynamicsCompressorNodeEngine(AudioNode* aNode, 1.41 + AudioDestinationNode* aDestination) 1.42 + : AudioNodeEngine(aNode) 1.43 + , mSource(nullptr) 1.44 + , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream())) 1.45 + // Keep the default value in sync with the default value in 1.46 + // DynamicsCompressorNode::DynamicsCompressorNode. 1.47 + , mThreshold(-24.f) 1.48 + , mKnee(30.f) 1.49 + , mRatio(12.f) 1.50 + , mAttack(0.003f) 1.51 + , mRelease(0.25f) 1.52 + , mCompressor(new DynamicsCompressor(mDestination->SampleRate(), 2)) 1.53 + { 1.54 + } 1.55 + 1.56 + void SetSourceStream(AudioNodeStream* aSource) 1.57 + { 1.58 + mSource = aSource; 1.59 + } 1.60 + 1.61 + enum Parameters { 1.62 + THRESHOLD, 1.63 + KNEE, 1.64 + RATIO, 1.65 + ATTACK, 1.66 + RELEASE 1.67 + }; 1.68 + void SetTimelineParameter(uint32_t aIndex, 1.69 + const AudioParamTimeline& aValue, 1.70 + TrackRate aSampleRate) MOZ_OVERRIDE 1.71 + { 1.72 + MOZ_ASSERT(mSource && mDestination); 1.73 + switch (aIndex) { 1.74 + case THRESHOLD: 1.75 + mThreshold = aValue; 1.76 + WebAudioUtils::ConvertAudioParamToTicks(mThreshold, mSource, mDestination); 1.77 + break; 1.78 + case KNEE: 1.79 + mKnee = aValue; 1.80 + WebAudioUtils::ConvertAudioParamToTicks(mKnee, mSource, mDestination); 1.81 + break; 1.82 + case RATIO: 1.83 + mRatio = aValue; 1.84 + WebAudioUtils::ConvertAudioParamToTicks(mRatio, mSource, mDestination); 1.85 + break; 1.86 + case ATTACK: 1.87 + mAttack = aValue; 1.88 + WebAudioUtils::ConvertAudioParamToTicks(mAttack, mSource, mDestination); 1.89 + break; 1.90 + case RELEASE: 1.91 + mRelease = aValue; 1.92 + WebAudioUtils::ConvertAudioParamToTicks(mRelease, mSource, mDestination); 1.93 + break; 1.94 + default: 1.95 + NS_ERROR("Bad DynamicsCompresssorNodeEngine TimelineParameter"); 1.96 + } 1.97 + } 1.98 + 1.99 + virtual void ProcessBlock(AudioNodeStream* aStream, 1.100 + const AudioChunk& aInput, 1.101 + AudioChunk* aOutput, 1.102 + bool* aFinished) MOZ_OVERRIDE 1.103 + { 1.104 + if (aInput.IsNull()) { 1.105 + // Just output silence 1.106 + *aOutput = aInput; 1.107 + return; 1.108 + } 1.109 + 1.110 + const uint32_t channelCount = aInput.mChannelData.Length(); 1.111 + if (mCompressor->numberOfChannels() != channelCount) { 1.112 + // Create a new compressor object with a new channel count 1.113 + mCompressor = new WebCore::DynamicsCompressor(aStream->SampleRate(), 1.114 + aInput.mChannelData.Length()); 1.115 + } 1.116 + 1.117 + TrackTicks pos = aStream->GetCurrentPosition(); 1.118 + mCompressor->setParameterValue(DynamicsCompressor::ParamThreshold, 1.119 + mThreshold.GetValueAtTime(pos)); 1.120 + mCompressor->setParameterValue(DynamicsCompressor::ParamKnee, 1.121 + mKnee.GetValueAtTime(pos)); 1.122 + mCompressor->setParameterValue(DynamicsCompressor::ParamRatio, 1.123 + mRatio.GetValueAtTime(pos)); 1.124 + mCompressor->setParameterValue(DynamicsCompressor::ParamAttack, 1.125 + mAttack.GetValueAtTime(pos)); 1.126 + mCompressor->setParameterValue(DynamicsCompressor::ParamRelease, 1.127 + mRelease.GetValueAtTime(pos)); 1.128 + 1.129 + AllocateAudioBlock(channelCount, aOutput); 1.130 + mCompressor->process(&aInput, aOutput, aInput.GetDuration()); 1.131 + 1.132 + SendReductionParamToMainThread(aStream, 1.133 + mCompressor->parameterValue(DynamicsCompressor::ParamReduction)); 1.134 + } 1.135 + 1.136 + virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE 1.137 + { 1.138 + // Not owned: 1.139 + // - mSource (probably) 1.140 + // - mDestination (probably) 1.141 + // - Don't count the AudioParamTimelines, their inner refs are owned by the 1.142 + // AudioNode. 1.143 + size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); 1.144 + amount += mCompressor->sizeOfIncludingThis(aMallocSizeOf); 1.145 + return amount; 1.146 + } 1.147 + 1.148 + virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE 1.149 + { 1.150 + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1.151 + } 1.152 + 1.153 +private: 1.154 + void SendReductionParamToMainThread(AudioNodeStream* aStream, float aReduction) 1.155 + { 1.156 + MOZ_ASSERT(!NS_IsMainThread()); 1.157 + 1.158 + class Command : public nsRunnable 1.159 + { 1.160 + public: 1.161 + Command(AudioNodeStream* aStream, float aReduction) 1.162 + : mStream(aStream) 1.163 + , mReduction(aReduction) 1.164 + { 1.165 + } 1.166 + 1.167 + NS_IMETHODIMP Run() 1.168 + { 1.169 + nsRefPtr<DynamicsCompressorNode> node; 1.170 + { 1.171 + // No need to keep holding the lock for the whole duration of this 1.172 + // function, since we're holding a strong reference to it, so if 1.173 + // we can obtain the reference, we will hold the node alive in 1.174 + // this function. 1.175 + MutexAutoLock lock(mStream->Engine()->NodeMutex()); 1.176 + node = static_cast<DynamicsCompressorNode*>(mStream->Engine()->Node()); 1.177 + } 1.178 + if (node) { 1.179 + AudioParam* reduction = node->Reduction(); 1.180 + reduction->CancelAllEvents(); 1.181 + reduction->SetValue(mReduction); 1.182 + } 1.183 + return NS_OK; 1.184 + } 1.185 + 1.186 + private: 1.187 + nsRefPtr<AudioNodeStream> mStream; 1.188 + float mReduction; 1.189 + }; 1.190 + 1.191 + NS_DispatchToMainThread(new Command(aStream, aReduction)); 1.192 + } 1.193 + 1.194 +private: 1.195 + AudioNodeStream* mSource; 1.196 + AudioNodeStream* mDestination; 1.197 + AudioParamTimeline mThreshold; 1.198 + AudioParamTimeline mKnee; 1.199 + AudioParamTimeline mRatio; 1.200 + AudioParamTimeline mAttack; 1.201 + AudioParamTimeline mRelease; 1.202 + nsAutoPtr<DynamicsCompressor> mCompressor; 1.203 +}; 1.204 + 1.205 +DynamicsCompressorNode::DynamicsCompressorNode(AudioContext* aContext) 1.206 + : AudioNode(aContext, 1.207 + 2, 1.208 + ChannelCountMode::Explicit, 1.209 + ChannelInterpretation::Speakers) 1.210 + , mThreshold(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), 1.211 + SendThresholdToStream, -24.f)) 1.212 + , mKnee(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), 1.213 + SendKneeToStream, 30.f)) 1.214 + , mRatio(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), 1.215 + SendRatioToStream, 12.f)) 1.216 + , mReduction(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), 1.217 + Callback, 0.f)) 1.218 + , mAttack(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), 1.219 + SendAttackToStream, 0.003f)) 1.220 + , mRelease(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), 1.221 + SendReleaseToStream, 0.25f)) 1.222 +{ 1.223 + DynamicsCompressorNodeEngine* engine = new DynamicsCompressorNodeEngine(this, aContext->Destination()); 1.224 + mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM); 1.225 + engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get())); 1.226 +} 1.227 + 1.228 +size_t 1.229 +DynamicsCompressorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const 1.230 +{ 1.231 + size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf); 1.232 + amount += mThreshold->SizeOfIncludingThis(aMallocSizeOf); 1.233 + amount += mKnee->SizeOfIncludingThis(aMallocSizeOf); 1.234 + amount += mRatio->SizeOfIncludingThis(aMallocSizeOf); 1.235 + amount += mAttack->SizeOfIncludingThis(aMallocSizeOf); 1.236 + amount += mRelease->SizeOfIncludingThis(aMallocSizeOf); 1.237 + return amount; 1.238 +} 1.239 + 1.240 +size_t 1.241 +DynamicsCompressorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const 1.242 +{ 1.243 + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1.244 +} 1.245 + 1.246 +JSObject* 1.247 +DynamicsCompressorNode::WrapObject(JSContext* aCx) 1.248 +{ 1.249 + return DynamicsCompressorNodeBinding::Wrap(aCx, this); 1.250 +} 1.251 + 1.252 +void 1.253 +DynamicsCompressorNode::SendThresholdToStream(AudioNode* aNode) 1.254 +{ 1.255 + DynamicsCompressorNode* This = static_cast<DynamicsCompressorNode*>(aNode); 1.256 + SendTimelineParameterToStream(This, DynamicsCompressorNodeEngine::THRESHOLD, *This->mThreshold); 1.257 +} 1.258 + 1.259 +void 1.260 +DynamicsCompressorNode::SendKneeToStream(AudioNode* aNode) 1.261 +{ 1.262 + DynamicsCompressorNode* This = static_cast<DynamicsCompressorNode*>(aNode); 1.263 + SendTimelineParameterToStream(This, DynamicsCompressorNodeEngine::KNEE, *This->mKnee); 1.264 +} 1.265 + 1.266 +void 1.267 +DynamicsCompressorNode::SendRatioToStream(AudioNode* aNode) 1.268 +{ 1.269 + DynamicsCompressorNode* This = static_cast<DynamicsCompressorNode*>(aNode); 1.270 + SendTimelineParameterToStream(This, DynamicsCompressorNodeEngine::RATIO, *This->mRatio); 1.271 +} 1.272 + 1.273 +void 1.274 +DynamicsCompressorNode::SendAttackToStream(AudioNode* aNode) 1.275 +{ 1.276 + DynamicsCompressorNode* This = static_cast<DynamicsCompressorNode*>(aNode); 1.277 + SendTimelineParameterToStream(This, DynamicsCompressorNodeEngine::ATTACK, *This->mAttack); 1.278 +} 1.279 + 1.280 +void 1.281 +DynamicsCompressorNode::SendReleaseToStream(AudioNode* aNode) 1.282 +{ 1.283 + DynamicsCompressorNode* This = static_cast<DynamicsCompressorNode*>(aNode); 1.284 + SendTimelineParameterToStream(This, DynamicsCompressorNodeEngine::RELEASE, *This->mRelease); 1.285 +} 1.286 + 1.287 +} 1.288 +} 1.289 +