gecko-dev/dom/media/webaudio/GainNode.cpp

153 строки
4.7 KiB
C++

/* -*- 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 "GainNode.h"
#include "mozilla/dom/GainNodeBinding.h"
#include "AudioNodeEngine.h"
#include "AudioNodeStream.h"
#include "AudioDestinationNode.h"
#include "WebAudioUtils.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED(GainNode, AudioNode,
mGain)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(GainNode)
NS_INTERFACE_MAP_END_INHERITING(AudioNode)
NS_IMPL_ADDREF_INHERITED(GainNode, AudioNode)
NS_IMPL_RELEASE_INHERITED(GainNode, AudioNode)
class GainNodeEngine final : public AudioNodeEngine
{
public:
GainNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
: AudioNodeEngine(aNode)
, mDestination(aDestination->Stream())
// Keep the default value in sync with the default value in GainNode::GainNode.
, mGain(1.f)
{
}
enum Parameters {
GAIN
};
void RecvTimelineEvent(uint32_t aIndex,
AudioTimelineEvent& aEvent) override
{
MOZ_ASSERT(mDestination);
WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent,
mDestination);
switch (aIndex) {
case GAIN:
mGain.InsertEvent<int64_t>(aEvent);
break;
default:
NS_ERROR("Bad GainNodeEngine TimelineParameter");
}
}
virtual void ProcessBlock(AudioNodeStream* aStream,
GraphTime aFrom,
const AudioBlock& aInput,
AudioBlock* aOutput,
bool* aFinished) override
{
if (aInput.IsNull()) {
// If input is silent, so is the output
aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
} else if (mGain.HasSimpleValue()) {
// Optimize the case where we only have a single value set as the volume
float gain = mGain.GetValue();
if (gain == 0.0f) {
aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
} else {
*aOutput = aInput;
aOutput->mVolume *= gain;
}
} else {
// First, compute a vector of gains for each track tick based on the
// timeline at hand, and then for each channel, multiply the values
// in the buffer with the gain vector.
aOutput->AllocateChannels(aInput.ChannelCount());
// Compute the gain values for the duration of the input AudioChunk
StreamTime tick = mDestination->GraphTimeToStreamTime(aFrom);
float computedGain[WEBAUDIO_BLOCK_SIZE];
mGain.GetValuesAtTime(tick, computedGain, WEBAUDIO_BLOCK_SIZE);
for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
computedGain[counter] *= aInput.mVolume;
}
// Apply the gain to the output buffer
for (size_t channel = 0; channel < aOutput->ChannelCount(); ++channel) {
const float* inputBuffer = static_cast<const float*> (aInput.mChannelData[channel]);
float* buffer = aOutput->ChannelFloatsForWrite(channel);
AudioBlockCopyChannelWithScale(inputBuffer, computedGain, buffer);
}
}
}
virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
{
// Not owned:
// - mDestination (probably)
// - mGain - Internal ref owned by AudioNode
return AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
}
virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
{
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
AudioNodeStream* mDestination;
AudioParamTimeline mGain;
};
GainNode::GainNode(AudioContext* aContext)
: AudioNode(aContext,
2,
ChannelCountMode::Max,
ChannelInterpretation::Speakers)
, mGain(new AudioParam(this, GainNodeEngine::GAIN, 1.0f, "gain"))
{
GainNodeEngine* engine = new GainNodeEngine(this, aContext->Destination());
mStream = AudioNodeStream::Create(aContext, engine,
AudioNodeStream::NO_STREAM_FLAGS);
}
GainNode::~GainNode()
{
}
size_t
GainNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
amount += mGain->SizeOfIncludingThis(aMallocSizeOf);
return amount;
}
size_t
GainNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
JSObject*
GainNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return GainNodeBinding::Wrap(aCx, this, aGivenProto);
}
} // namespace dom
} // namespace mozilla