blob: 2ad8d8b34a63125f7dab1290dfdc261deb45c54e [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/delay_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/modules/webaudio/base_audio_context.h"
#include "third_party/blink/renderer/platform/audio/audio_bus.h"
#include "third_party/blink/renderer/platform/audio/delay.h"
namespace blink {
namespace {
constexpr unsigned kNumberOfOutputs = 1;
constexpr unsigned kDefaultNumberOfChannels = 1;
} // namespace
scoped_refptr<DelayHandler> DelayHandler::Create(AudioNode& node,
float sample_rate,
AudioParamHandler& delay_time,
double max_delay_time) {
return base::AdoptRef(
new DelayHandler(node, sample_rate, delay_time, max_delay_time));
}
DelayHandler::~DelayHandler() {
Uninitialize();
}
DelayHandler::DelayHandler(AudioNode& node,
float sample_rate,
AudioParamHandler& delay_time,
double max_delay_time)
: AudioHandler(NodeType::kNodeTypeDelay, node, sample_rate),
number_of_channels_(kDefaultNumberOfChannels),
sample_rate_(sample_rate),
render_quantum_frames_(node.context()->renderQuantumSize()),
delay_time_(&delay_time),
max_delay_time_(max_delay_time) {
AddInput();
AddOutput(kNumberOfOutputs);
Initialize();
}
void DelayHandler::Process(uint32_t frames_to_process) {
AudioBus* destination_bus = Output(0).Bus();
if (!IsInitialized() || number_of_channels_ != Output(0).NumberOfChannels()) {
destination_bus->Zero();
} else {
scoped_refptr<AudioBus> source_bus = Input(0).Bus();
if (!Input(0).IsConnected()) {
source_bus->Zero();
}
base::AutoTryLock process_try_locker(process_lock_);
base::AutoTryLock rate_try_locker(delay_time_->RateLock());
if (process_try_locker.is_acquired() && rate_try_locker.is_acquired()) {
DCHECK_EQ(source_bus->NumberOfChannels(),
destination_bus->NumberOfChannels());
DCHECK_EQ(source_bus->NumberOfChannels(), kernels_.size());
if (delay_time_->IsAudioRate()) {
for (unsigned i = 0; i < kernels_.size(); ++i) {
// Assumes that the automation rate cannot change in the middle of
// the process function. (See crbug.com/357391257)
CHECK(delay_time_->IsAudioRate());
delay_time_->CalculateSampleAccurateValues(
kernels_[i]->DelayTimes().first(frames_to_process));
kernels_[i]->ProcessARate(source_bus->Channel(i)->Data(),
destination_bus->Channel(i)->MutableData(),
frames_to_process);
}
} else {
for (unsigned i = 0; i < kernels_.size(); ++i) {
CHECK(!delay_time_->IsAudioRate());
kernels_[i]->SetDelayTime(delay_time_->FinalValue());
kernels_[i]->ProcessKRate(source_bus->Channel(i)->Data(),
destination_bus->Channel(i)->MutableData(),
frames_to_process);
}
}
} else {
destination_bus->Zero();
}
}
}
void DelayHandler::ProcessOnlyAudioParams(uint32_t frames_to_process) {
if (!IsInitialized()) {
return;
}
// TODO(crbug.com/40637820): Eventually, the render quantum size will no
// longer be hardcoded as 128. At that point, we'll need to switch from
// stack allocation to heap allocation.
constexpr unsigned render_quantum_frames_expected = 128;
CHECK_EQ(render_quantum_frames_, render_quantum_frames_expected);
DCHECK_LE(frames_to_process, render_quantum_frames_expected);
float values[render_quantum_frames_expected];
delay_time_->CalculateSampleAccurateValues(
base::span(values).first(frames_to_process));
}
void DelayHandler::Initialize() {
if (IsInitialized()) {
return;
}
{
base::AutoLock locker(process_lock_);
DCHECK(!kernels_.size());
// Create processing kernels, one per channel.
for (unsigned i = 0; i < number_of_channels_; ++i) {
kernels_.push_back(std::make_unique<Delay>(max_delay_time_, sample_rate_,
render_quantum_frames_));
}
}
AudioHandler::Initialize();
}
void DelayHandler::Uninitialize() {
if (!IsInitialized()) {
return;
}
{
base::AutoLock locker(process_lock_);
kernels_.clear();
}
AudioHandler::Uninitialize();
}
void DelayHandler::CheckNumberOfChannelsForInput(AudioNodeInput* input) {
DCHECK(Context()->IsAudioThread());
Context()->AssertGraphOwner();
DCHECK_EQ(input, &Input(0));
// 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.
const 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 down
// the chain...
Output(0).SetNumberOfChannels(number_of_channels);
// Re-initialize the processor with the new channel count.
number_of_channels_ = number_of_channels;
Initialize();
}
AudioHandler::CheckNumberOfChannelsForInput(input);
}
bool DelayHandler::RequiresTailProcessing() const {
// Always return true even if the tail time and latency might both be
// zero. This is for simplicity; most interesting delay nodes have non-zero
// delay times anyway. And it's ok to return true. It just means the node
// lives a little longer than strictly necessary.
return true;
}
double DelayHandler::TailTime() const {
// Account for worst case delay.
// Don't try to track actual delay time which can change dynamically.
return max_delay_time_;
}
double DelayHandler::LatencyTime() const {
// A "delay" effect is expected to delay the signal, and this is not
// considered latency.
return 0;
}
void DelayHandler::PullInputs(uint32_t frames_to_process) {
// Render directly into output bus for in-place processing
Input(0).Pull(Output(0).Bus(), frames_to_process);
}
} // namespace blink