diff -r 000000000000 -r 6474c204b198 content/media/webaudio/DynamicsCompressorNode.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/content/media/webaudio/DynamicsCompressorNode.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,286 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DynamicsCompressorNode.h" +#include "mozilla/dom/DynamicsCompressorNodeBinding.h" +#include "AudioNodeEngine.h" +#include "AudioNodeStream.h" +#include "AudioDestinationNode.h" +#include "WebAudioUtils.h" +#include "blink/DynamicsCompressor.h" + +using WebCore::DynamicsCompressor; + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_INHERITED(DynamicsCompressorNode, AudioNode, + mThreshold, + mKnee, + mRatio, + mReduction, + mAttack, + mRelease) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DynamicsCompressorNode) +NS_INTERFACE_MAP_END_INHERITING(AudioNode) + +NS_IMPL_ADDREF_INHERITED(DynamicsCompressorNode, AudioNode) +NS_IMPL_RELEASE_INHERITED(DynamicsCompressorNode, AudioNode) + +class DynamicsCompressorNodeEngine : public AudioNodeEngine +{ +public: + explicit DynamicsCompressorNodeEngine(AudioNode* aNode, + AudioDestinationNode* aDestination) + : AudioNodeEngine(aNode) + , mSource(nullptr) + , mDestination(static_cast (aDestination->Stream())) + // Keep the default value in sync with the default value in + // DynamicsCompressorNode::DynamicsCompressorNode. + , mThreshold(-24.f) + , mKnee(30.f) + , mRatio(12.f) + , mAttack(0.003f) + , mRelease(0.25f) + , mCompressor(new DynamicsCompressor(mDestination->SampleRate(), 2)) + { + } + + void SetSourceStream(AudioNodeStream* aSource) + { + mSource = aSource; + } + + enum Parameters { + THRESHOLD, + KNEE, + RATIO, + ATTACK, + RELEASE + }; + void SetTimelineParameter(uint32_t aIndex, + const AudioParamTimeline& aValue, + TrackRate aSampleRate) MOZ_OVERRIDE + { + MOZ_ASSERT(mSource && mDestination); + switch (aIndex) { + case THRESHOLD: + mThreshold = aValue; + WebAudioUtils::ConvertAudioParamToTicks(mThreshold, mSource, mDestination); + break; + case KNEE: + mKnee = aValue; + WebAudioUtils::ConvertAudioParamToTicks(mKnee, mSource, mDestination); + break; + case RATIO: + mRatio = aValue; + WebAudioUtils::ConvertAudioParamToTicks(mRatio, mSource, mDestination); + break; + case ATTACK: + mAttack = aValue; + WebAudioUtils::ConvertAudioParamToTicks(mAttack, mSource, mDestination); + break; + case RELEASE: + mRelease = aValue; + WebAudioUtils::ConvertAudioParamToTicks(mRelease, mSource, mDestination); + break; + default: + NS_ERROR("Bad DynamicsCompresssorNodeEngine TimelineParameter"); + } + } + + virtual void ProcessBlock(AudioNodeStream* aStream, + const AudioChunk& aInput, + AudioChunk* aOutput, + bool* aFinished) MOZ_OVERRIDE + { + if (aInput.IsNull()) { + // Just output silence + *aOutput = aInput; + return; + } + + const uint32_t channelCount = aInput.mChannelData.Length(); + if (mCompressor->numberOfChannels() != channelCount) { + // Create a new compressor object with a new channel count + mCompressor = new WebCore::DynamicsCompressor(aStream->SampleRate(), + aInput.mChannelData.Length()); + } + + TrackTicks pos = aStream->GetCurrentPosition(); + mCompressor->setParameterValue(DynamicsCompressor::ParamThreshold, + mThreshold.GetValueAtTime(pos)); + mCompressor->setParameterValue(DynamicsCompressor::ParamKnee, + mKnee.GetValueAtTime(pos)); + mCompressor->setParameterValue(DynamicsCompressor::ParamRatio, + mRatio.GetValueAtTime(pos)); + mCompressor->setParameterValue(DynamicsCompressor::ParamAttack, + mAttack.GetValueAtTime(pos)); + mCompressor->setParameterValue(DynamicsCompressor::ParamRelease, + mRelease.GetValueAtTime(pos)); + + AllocateAudioBlock(channelCount, aOutput); + mCompressor->process(&aInput, aOutput, aInput.GetDuration()); + + SendReductionParamToMainThread(aStream, + mCompressor->parameterValue(DynamicsCompressor::ParamReduction)); + } + + virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE + { + // Not owned: + // - mSource (probably) + // - mDestination (probably) + // - Don't count the AudioParamTimelines, their inner refs are owned by the + // AudioNode. + size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); + amount += mCompressor->sizeOfIncludingThis(aMallocSizeOf); + return amount; + } + + virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE + { + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); + } + +private: + void SendReductionParamToMainThread(AudioNodeStream* aStream, float aReduction) + { + MOZ_ASSERT(!NS_IsMainThread()); + + class Command : public nsRunnable + { + public: + Command(AudioNodeStream* aStream, float aReduction) + : mStream(aStream) + , mReduction(aReduction) + { + } + + NS_IMETHODIMP Run() + { + nsRefPtr node; + { + // No need to keep holding the lock for the whole duration of this + // function, since we're holding a strong reference to it, so if + // we can obtain the reference, we will hold the node alive in + // this function. + MutexAutoLock lock(mStream->Engine()->NodeMutex()); + node = static_cast(mStream->Engine()->Node()); + } + if (node) { + AudioParam* reduction = node->Reduction(); + reduction->CancelAllEvents(); + reduction->SetValue(mReduction); + } + return NS_OK; + } + + private: + nsRefPtr mStream; + float mReduction; + }; + + NS_DispatchToMainThread(new Command(aStream, aReduction)); + } + +private: + AudioNodeStream* mSource; + AudioNodeStream* mDestination; + AudioParamTimeline mThreshold; + AudioParamTimeline mKnee; + AudioParamTimeline mRatio; + AudioParamTimeline mAttack; + AudioParamTimeline mRelease; + nsAutoPtr mCompressor; +}; + +DynamicsCompressorNode::DynamicsCompressorNode(AudioContext* aContext) + : AudioNode(aContext, + 2, + ChannelCountMode::Explicit, + ChannelInterpretation::Speakers) + , mThreshold(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), + SendThresholdToStream, -24.f)) + , mKnee(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), + SendKneeToStream, 30.f)) + , mRatio(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), + SendRatioToStream, 12.f)) + , mReduction(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), + Callback, 0.f)) + , mAttack(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), + SendAttackToStream, 0.003f)) + , mRelease(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), + SendReleaseToStream, 0.25f)) +{ + DynamicsCompressorNodeEngine* engine = new DynamicsCompressorNodeEngine(this, aContext->Destination()); + mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM); + engine->SetSourceStream(static_cast (mStream.get())); +} + +size_t +DynamicsCompressorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const +{ + size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf); + amount += mThreshold->SizeOfIncludingThis(aMallocSizeOf); + amount += mKnee->SizeOfIncludingThis(aMallocSizeOf); + amount += mRatio->SizeOfIncludingThis(aMallocSizeOf); + amount += mAttack->SizeOfIncludingThis(aMallocSizeOf); + amount += mRelease->SizeOfIncludingThis(aMallocSizeOf); + return amount; +} + +size_t +DynamicsCompressorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const +{ + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); +} + +JSObject* +DynamicsCompressorNode::WrapObject(JSContext* aCx) +{ + return DynamicsCompressorNodeBinding::Wrap(aCx, this); +} + +void +DynamicsCompressorNode::SendThresholdToStream(AudioNode* aNode) +{ + DynamicsCompressorNode* This = static_cast(aNode); + SendTimelineParameterToStream(This, DynamicsCompressorNodeEngine::THRESHOLD, *This->mThreshold); +} + +void +DynamicsCompressorNode::SendKneeToStream(AudioNode* aNode) +{ + DynamicsCompressorNode* This = static_cast(aNode); + SendTimelineParameterToStream(This, DynamicsCompressorNodeEngine::KNEE, *This->mKnee); +} + +void +DynamicsCompressorNode::SendRatioToStream(AudioNode* aNode) +{ + DynamicsCompressorNode* This = static_cast(aNode); + SendTimelineParameterToStream(This, DynamicsCompressorNodeEngine::RATIO, *This->mRatio); +} + +void +DynamicsCompressorNode::SendAttackToStream(AudioNode* aNode) +{ + DynamicsCompressorNode* This = static_cast(aNode); + SendTimelineParameterToStream(This, DynamicsCompressorNodeEngine::ATTACK, *This->mAttack); +} + +void +DynamicsCompressorNode::SendReleaseToStream(AudioNode* aNode) +{ + DynamicsCompressorNode* This = static_cast(aNode); + SendTimelineParameterToStream(This, DynamicsCompressorNodeEngine::RELEASE, *This->mRelease); +} + +} +} +