blob: e1d0230960633c987f0d7c63c09b9f20f119080e [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// 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/gain_handler.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/platform/audio/audio_bus.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
namespace blink {
namespace {
constexpr unsigned kNumberOfOutputChannels = 1;
} // namespace
GainHandler::GainHandler(AudioNode& node,
float sample_rate,
AudioParamHandler& gain)
: AudioHandler(kNodeTypeGain, node, sample_rate),
gain_(&gain),
sample_accurate_gain_values_(
GetDeferredTaskHandler().RenderQuantumFrames()) {
AddInput();
AddOutput(kNumberOfOutputChannels);
Initialize();
}
scoped_refptr<GainHandler> GainHandler::Create(AudioNode& node,
float sample_rate,
AudioParamHandler& gain) {
return base::AdoptRef(new GainHandler(node, sample_rate, gain));
}
void GainHandler::Process(uint32_t frames_to_process) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("webaudio.audionode"),
"GainHandler::Process");
AudioBus* output_bus = Output(0).Bus();
DCHECK(output_bus);
if (!IsInitialized() || !Input(0).IsConnected()) {
output_bus->Zero();
} else {
scoped_refptr<AudioBus> input_bus = Input(0).Bus();
bool is_sample_accurate = gain_->HasSampleAccurateValues();
if (is_sample_accurate && gain_->IsAudioRate()) {
// Apply sample-accurate gain scaling for precise envelopes, grain
// windows, etc.
DCHECK_LE(frames_to_process, sample_accurate_gain_values_.size());
float* gain_values = sample_accurate_gain_values_.Data();
gain_->CalculateSampleAccurateValues(gain_values, frames_to_process);
output_bus->CopyWithSampleAccurateGainValuesFrom(*input_bus, gain_values,
frames_to_process);
return;
}
// The gain is not sample-accurate or not a-rate. In this case, we have a
// fixed gain for the render and just need to incorporate any inputs to the
// gain, if any.
float gain = is_sample_accurate ? gain_->FinalValue() : gain_->Value();
if (gain == 0) {
output_bus->Zero();
} else {
output_bus->CopyWithGainFrom(*input_bus, gain);
}
}
}
void GainHandler::ProcessOnlyAudioParams(uint32_t frames_to_process) {
DCHECK(Context()->IsAudioThread());
DCHECK_LE(frames_to_process, GetDeferredTaskHandler().RenderQuantumFrames());
float values[GetDeferredTaskHandler().RenderQuantumFrames()];
gain_->CalculateSampleAccurateValues(values, frames_to_process);
}
// As soon as we know the channel count of our input, we can lazily initialize.
// Sometimes this may be called more than once with different channel counts, in
// which case we must safely uninitialize and then re-initialize with the new
// channel count.
void GainHandler::CheckNumberOfChannelsForInput(AudioNodeInput* input) {
DCHECK(Context()->IsAudioThread());
Context()->AssertGraphOwner();
DCHECK(input);
DCHECK_EQ(input, &Input(0));
unsigned number_of_channels = input->NumberOfChannels();
if (IsInitialized() && number_of_channels != Output(0).NumberOfChannels()) {
// We're already initialized but the channel count has changed.
Uninitialize();
}
if (!IsInitialized()) {
// This will propagate the channel count to any nodes connected further
// downstream in the graph.
Output(0).SetNumberOfChannels(number_of_channels);
Initialize();
}
AudioHandler::CheckNumberOfChannelsForInput(input);
}
} // namespace blink