michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "GainNode.h" michael@0: #include "mozilla/dom/GainNodeBinding.h" michael@0: #include "AudioNodeEngine.h" michael@0: #include "AudioNodeStream.h" michael@0: #include "AudioDestinationNode.h" michael@0: #include "WebAudioUtils.h" michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_INHERITED(GainNode, AudioNode, michael@0: mGain) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(GainNode) michael@0: NS_INTERFACE_MAP_END_INHERITING(AudioNode) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(GainNode, AudioNode) michael@0: NS_IMPL_RELEASE_INHERITED(GainNode, AudioNode) michael@0: michael@0: class GainNodeEngine : public AudioNodeEngine michael@0: { michael@0: public: michael@0: GainNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination) michael@0: : AudioNodeEngine(aNode) michael@0: , mSource(nullptr) michael@0: , mDestination(static_cast (aDestination->Stream())) michael@0: // Keep the default value in sync with the default value in GainNode::GainNode. michael@0: , mGain(1.f) michael@0: { michael@0: } michael@0: michael@0: void SetSourceStream(AudioNodeStream* aSource) michael@0: { michael@0: mSource = aSource; michael@0: } michael@0: michael@0: enum Parameters { michael@0: GAIN michael@0: }; michael@0: void SetTimelineParameter(uint32_t aIndex, michael@0: const AudioParamTimeline& aValue, michael@0: TrackRate aSampleRate) MOZ_OVERRIDE michael@0: { michael@0: switch (aIndex) { michael@0: case GAIN: michael@0: MOZ_ASSERT(mSource && mDestination); michael@0: mGain = aValue; michael@0: WebAudioUtils::ConvertAudioParamToTicks(mGain, mSource, mDestination); michael@0: break; michael@0: default: michael@0: NS_ERROR("Bad GainNodeEngine TimelineParameter"); michael@0: } michael@0: } michael@0: michael@0: virtual void ProcessBlock(AudioNodeStream* aStream, michael@0: const AudioChunk& aInput, michael@0: AudioChunk* aOutput, michael@0: bool* aFinished) michael@0: { michael@0: MOZ_ASSERT(mSource == aStream, "Invalid source stream"); michael@0: michael@0: if (aInput.IsNull()) { michael@0: // If input is silent, so is the output michael@0: aOutput->SetNull(WEBAUDIO_BLOCK_SIZE); michael@0: } else if (mGain.HasSimpleValue()) { michael@0: // Optimize the case where we only have a single value set as the volume michael@0: float gain = mGain.GetValue(); michael@0: if (gain == 0.0f) { michael@0: aOutput->SetNull(WEBAUDIO_BLOCK_SIZE); michael@0: } else { michael@0: *aOutput = aInput; michael@0: aOutput->mVolume *= gain; michael@0: } michael@0: } else { michael@0: // First, compute a vector of gains for each track tick based on the michael@0: // timeline at hand, and then for each channel, multiply the values michael@0: // in the buffer with the gain vector. michael@0: AllocateAudioBlock(aInput.mChannelData.Length(), aOutput); michael@0: michael@0: // Compute the gain values for the duration of the input AudioChunk michael@0: // XXX we need to add a method to AudioEventTimeline to compute this buffer directly. michael@0: float computedGain[WEBAUDIO_BLOCK_SIZE]; michael@0: for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) { michael@0: TrackTicks tick = aStream->GetCurrentPosition(); michael@0: computedGain[counter] = mGain.GetValueAtTime(tick, counter) * aInput.mVolume; michael@0: } michael@0: michael@0: // Apply the gain to the output buffer michael@0: for (size_t channel = 0; channel < aOutput->mChannelData.Length(); ++channel) { michael@0: const float* inputBuffer = static_cast (aInput.mChannelData[channel]); michael@0: float* buffer = static_cast (const_cast michael@0: (aOutput->mChannelData[channel])); michael@0: AudioBlockCopyChannelWithScale(inputBuffer, computedGain, buffer); michael@0: } michael@0: } michael@0: } michael@0: michael@0: virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE michael@0: { michael@0: // Not owned: michael@0: // - mSource (probably) michael@0: // - mDestination (probably) michael@0: // - mGain - Internal ref owned by AudioNode michael@0: return AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE michael@0: { michael@0: return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: AudioNodeStream* mSource; michael@0: AudioNodeStream* mDestination; michael@0: AudioParamTimeline mGain; michael@0: }; michael@0: michael@0: GainNode::GainNode(AudioContext* aContext) michael@0: : AudioNode(aContext, michael@0: 2, michael@0: ChannelCountMode::Max, michael@0: ChannelInterpretation::Speakers) michael@0: , mGain(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), michael@0: SendGainToStream, 1.0f)) michael@0: { michael@0: GainNodeEngine* engine = new GainNodeEngine(this, aContext->Destination()); michael@0: mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM); michael@0: engine->SetSourceStream(static_cast (mStream.get())); michael@0: } michael@0: michael@0: size_t michael@0: GainNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf); michael@0: amount += mGain->SizeOfIncludingThis(aMallocSizeOf); michael@0: return amount; michael@0: } michael@0: michael@0: size_t michael@0: GainNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: JSObject* michael@0: GainNode::WrapObject(JSContext* aCx) michael@0: { michael@0: return GainNodeBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: void michael@0: GainNode::SendGainToStream(AudioNode* aNode) michael@0: { michael@0: GainNode* This = static_cast(aNode); michael@0: SendTimelineParameterToStream(This, GainNodeEngine::GAIN, *This->mGain); michael@0: } michael@0: michael@0: } michael@0: } michael@0: