blob: c70238a958a76ff6aee0dac11bfe963062039a36 [file]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modules/webaudio/StereoPannerNode.h"
#include "bindings/core/v8/ExceptionMessages.h"
#include "bindings/core/v8/ExceptionState.h"
#include "core/dom/ExceptionCode.h"
#include "core/dom/ExecutionContext.h"
#include "modules/webaudio/AudioNodeInput.h"
#include "modules/webaudio/AudioNodeOutput.h"
#include "modules/webaudio/BaseAudioContext.h"
#include "modules/webaudio/StereoPannerOptions.h"
#include "platform/audio/StereoPanner.h"
#include "wtf/MathExtras.h"
namespace blink {
StereoPannerHandler::StereoPannerHandler(AudioNode& node,
float sampleRate,
AudioParamHandler& pan)
: AudioHandler(NodeTypeStereoPanner, node, sampleRate),
m_pan(pan),
m_sampleAccuratePanValues(AudioUtilities::kRenderQuantumFrames) {
addInput();
addOutput(2);
// The node-specific default mixing rules declare that StereoPannerNode
// can handle mono to stereo and stereo to stereo conversion.
m_channelCount = 2;
setInternalChannelCountMode(ClampedMax);
setInternalChannelInterpretation(AudioBus::Speakers);
initialize();
}
PassRefPtr<StereoPannerHandler> StereoPannerHandler::create(
AudioNode& node,
float sampleRate,
AudioParamHandler& pan) {
return adoptRef(new StereoPannerHandler(node, sampleRate, pan));
}
StereoPannerHandler::~StereoPannerHandler() {
uninitialize();
}
void StereoPannerHandler::process(size_t framesToProcess) {
AudioBus* outputBus = output(0).bus();
if (!isInitialized() || !input(0).isConnected() || !m_stereoPanner.get()) {
outputBus->zero();
return;
}
AudioBus* inputBus = input(0).bus();
if (!inputBus) {
outputBus->zero();
return;
}
if (m_pan->hasSampleAccurateValues()) {
// Apply sample-accurate panning specified by AudioParam automation.
DCHECK_LE(framesToProcess, m_sampleAccuratePanValues.size());
if (framesToProcess <= m_sampleAccuratePanValues.size()) {
float* panValues = m_sampleAccuratePanValues.data();
m_pan->calculateSampleAccurateValues(panValues, framesToProcess);
m_stereoPanner->panWithSampleAccurateValues(inputBus, outputBus,
panValues, framesToProcess);
}
} else {
m_stereoPanner->panToTargetValue(inputBus, outputBus, m_pan->value(),
framesToProcess);
}
}
void StereoPannerHandler::processOnlyAudioParams(size_t framesToProcess) {
float values[AudioUtilities::kRenderQuantumFrames];
DCHECK_LE(framesToProcess, AudioUtilities::kRenderQuantumFrames);
m_pan->calculateSampleAccurateValues(values, framesToProcess);
}
void StereoPannerHandler::initialize() {
if (isInitialized())
return;
m_stereoPanner = StereoPanner::create(sampleRate());
AudioHandler::initialize();
}
void StereoPannerHandler::setChannelCount(unsigned long channelCount,
ExceptionState& exceptionState) {
DCHECK(isMainThread());
BaseAudioContext::AutoLocker locker(context());
// A PannerNode only supports 1 or 2 channels
if (channelCount > 0 && channelCount <= 2) {
if (m_channelCount != channelCount) {
m_channelCount = channelCount;
if (internalChannelCountMode() != Max)
updateChannelsForInputs();
}
} else {
exceptionState.throwDOMException(
NotSupportedError,
ExceptionMessages::indexOutsideRange<unsigned long>(
"channelCount", channelCount, 1, ExceptionMessages::InclusiveBound,
2, ExceptionMessages::InclusiveBound));
}
}
void StereoPannerHandler::setChannelCountMode(const String& mode,
ExceptionState& exceptionState) {
DCHECK(isMainThread());
BaseAudioContext::AutoLocker locker(context());
ChannelCountMode oldMode = internalChannelCountMode();
if (mode == "clamped-max") {
m_newChannelCountMode = ClampedMax;
} else if (mode == "explicit") {
m_newChannelCountMode = Explicit;
} else if (mode == "max") {
// This is not supported for a StereoPannerNode, which can only handle
// 1 or 2 channels.
exceptionState.throwDOMException(NotSupportedError,
"StereoPanner: 'max' is not allowed");
m_newChannelCountMode = oldMode;
} else {
// Do nothing for other invalid values.
m_newChannelCountMode = oldMode;
}
if (m_newChannelCountMode != oldMode)
context()->deferredTaskHandler().addChangedChannelCountMode(this);
}
// ----------------------------------------------------------------
StereoPannerNode::StereoPannerNode(BaseAudioContext& context)
: AudioNode(context),
m_pan(AudioParam::create(context, ParamTypeStereoPannerPan, 0, -1, 1)) {
setHandler(StereoPannerHandler::create(*this, context.sampleRate(),
m_pan->handler()));
}
StereoPannerNode* StereoPannerNode::create(BaseAudioContext& context,
ExceptionState& exceptionState) {
DCHECK(isMainThread());
if (context.isContextClosed()) {
context.throwExceptionForClosedState(exceptionState);
return nullptr;
}
return new StereoPannerNode(context);
}
StereoPannerNode* StereoPannerNode::create(BaseAudioContext* context,
const StereoPannerOptions& options,
ExceptionState& exceptionState) {
StereoPannerNode* node = create(*context, exceptionState);
if (!node)
return nullptr;
node->handleChannelOptions(options, exceptionState);
if (options.hasPan())
node->pan()->setValue(options.pan());
return node;
}
DEFINE_TRACE(StereoPannerNode) {
visitor->trace(m_pan);
AudioNode::trace(visitor);
}
AudioParam* StereoPannerNode::pan() const {
return m_pan;
}
} // namespace blink