blob: f49c4d5db3d4b1863519ec87a0c2bb866f11eb6c [file] [log] [blame]
// 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 "third_party/blink/renderer/modules/webaudio/stereo_panner_node.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/modules/webaudio/audio_node_input.h"
#include "third_party/blink/renderer/modules/webaudio/audio_node_output.h"
#include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
#include "third_party/blink/renderer/modules/webaudio/stereo_panner_options.h"
#include "third_party/blink/renderer/platform/audio/stereo_panner.h"
#include "third_party/blink/renderer/platform/bindings/exception_messages.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
namespace blink {
StereoPannerHandler::StereoPannerHandler(AudioNode& node,
float sample_rate,
AudioParamHandler& pan)
: AudioHandler(kNodeTypeStereoPanner, node, sample_rate),
pan_(&pan),
sample_accurate_pan_values_(audio_utilities::kRenderQuantumFrames) {
AddInput();
AddOutput(2);
// The node-specific default mixing rules declare that StereoPannerNode
// can handle mono to stereo and stereo to stereo conversion.
channel_count_ = 2;
SetInternalChannelCountMode(kClampedMax);
SetInternalChannelInterpretation(AudioBus::kSpeakers);
Initialize();
}
scoped_refptr<StereoPannerHandler> StereoPannerHandler::Create(
AudioNode& node,
float sample_rate,
AudioParamHandler& pan) {
return base::AdoptRef(new StereoPannerHandler(node, sample_rate, pan));
}
StereoPannerHandler::~StereoPannerHandler() {
Uninitialize();
}
void StereoPannerHandler::Process(uint32_t frames_to_process) {
AudioBus* output_bus = Output(0).Bus();
if (!IsInitialized() || !Input(0).IsConnected() || !stereo_panner_.get()) {
output_bus->Zero();
return;
}
AudioBus* input_bus = Input(0).Bus();
if (!input_bus) {
output_bus->Zero();
return;
}
if (pan_->HasSampleAccurateValues()) {
// Apply sample-accurate panning specified by AudioParam automation.
DCHECK_LE(frames_to_process, sample_accurate_pan_values_.size());
if (frames_to_process <= sample_accurate_pan_values_.size()) {
float* pan_values = sample_accurate_pan_values_.Data();
pan_->CalculateSampleAccurateValues(pan_values, frames_to_process);
stereo_panner_->PanWithSampleAccurateValues(
input_bus, output_bus, pan_values, frames_to_process);
}
} else {
stereo_panner_->PanToTargetValue(input_bus, output_bus, pan_->Value(),
frames_to_process);
}
}
void StereoPannerHandler::ProcessOnlyAudioParams(uint32_t frames_to_process) {
float values[audio_utilities::kRenderQuantumFrames];
DCHECK_LE(frames_to_process, audio_utilities::kRenderQuantumFrames);
pan_->CalculateSampleAccurateValues(values, frames_to_process);
}
void StereoPannerHandler::Initialize() {
if (IsInitialized())
return;
stereo_panner_ = std::make_unique<StereoPanner>(Context()->sampleRate());
AudioHandler::Initialize();
}
void StereoPannerHandler::SetChannelCount(unsigned channel_count,
ExceptionState& exception_state) {
DCHECK(IsMainThread());
BaseAudioContext::GraphAutoLocker locker(Context());
// A PannerNode only supports 1 or 2 channels
if (channel_count > 0 && channel_count <= 2) {
if (channel_count_ != channel_count) {
channel_count_ = channel_count;
if (InternalChannelCountMode() != kMax)
UpdateChannelsForInputs();
}
} else {
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
ExceptionMessages::IndexOutsideRange<unsigned long>(
"channelCount", channel_count, 1,
ExceptionMessages::kInclusiveBound, 2,
ExceptionMessages::kInclusiveBound));
}
}
void StereoPannerHandler::SetChannelCountMode(const String& mode,
ExceptionState& exception_state) {
DCHECK(IsMainThread());
BaseAudioContext::GraphAutoLocker locker(Context());
ChannelCountMode old_mode = InternalChannelCountMode();
if (mode == "clamped-max") {
new_channel_count_mode_ = kClampedMax;
} else if (mode == "explicit") {
new_channel_count_mode_ = kExplicit;
} else if (mode == "max") {
// This is not supported for a StereoPannerNode, which can only handle
// 1 or 2 channels.
exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
"StereoPanner: 'max' is not allowed");
new_channel_count_mode_ = old_mode;
} else {
// Do nothing for other invalid values.
new_channel_count_mode_ = old_mode;
}
if (new_channel_count_mode_ != old_mode)
Context()->GetDeferredTaskHandler().AddChangedChannelCountMode(this);
}
// ----------------------------------------------------------------
StereoPannerNode::StereoPannerNode(BaseAudioContext& context)
: AudioNode(context),
pan_(AudioParam::Create(context,
kParamTypeStereoPannerPan,
0,
AudioParamHandler::AutomationRate::kAudio,
AudioParamHandler::AutomationRateMode::kVariable,
-1,
1)) {
SetHandler(StereoPannerHandler::Create(*this, context.sampleRate(),
pan_->Handler()));
}
StereoPannerNode* StereoPannerNode::Create(BaseAudioContext& context,
ExceptionState& exception_state) {
DCHECK(IsMainThread());
return MakeGarbageCollected<StereoPannerNode>(context);
}
StereoPannerNode* StereoPannerNode::Create(BaseAudioContext* context,
const StereoPannerOptions* options,
ExceptionState& exception_state) {
StereoPannerNode* node = Create(*context, exception_state);
if (!node)
return nullptr;
node->HandleChannelOptions(options, exception_state);
node->pan()->setValue(options->pan());
return node;
}
void StereoPannerNode::Trace(blink::Visitor* visitor) {
visitor->Trace(pan_);
AudioNode::Trace(visitor);
}
AudioParam* StereoPannerNode::pan() const {
return pan_;
}
} // namespace blink