Skip to content

Commit

Permalink
Refactor DelayHandler
Browse files Browse the repository at this point in the history
This CL makes DelayHandler inherit directly from AudioHandler instead
of AudioBasicProcessorHandler.

This CL should cause no functional change.

This CL is in preparation for solving a spec compliance problem, but
does *not* solve the spec compliance problem.  It is just a refactor.

The spec problem is as follows: the WebAudio spec requires that when the
channel count changes, we keep the audio data in the delay lines and
upmix or downmix it:
https://webaudio.github.io/web-audio-api/#DelayNode
However, the DelayHandler class inherits from
AudioBasicProcessorHandler, which will always destroy and recreate
the processing kernels whenever the channel count changes.  This
discards the delay line state.

Bug: 1137007
Change-Id: I527b25e1785539475aee02f07549e93b94967569
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4789439
Commit-Queue: Michael Wilson <mjwilson@chromium.org>
Reviewed-by: Hongchan Choi <hongchan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1185476}
  • Loading branch information
Michael Wilson authored and Chromium LUCI CQ committed Aug 19, 2023
1 parent 33adb97 commit 31f15dc
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 225 deletions.
2 changes: 0 additions & 2 deletions third_party/blink/renderer/modules/webaudio/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,6 @@ blink_modules_sources("webaudio") {
"delay_handler.h",
"delay_node.cc",
"delay_node.h",
"delay_processor.cc",
"delay_processor.h",
"dynamics_compressor_handler.cc",
"dynamics_compressor_handler.h",
"dynamics_compressor_node.cc",
Expand Down
182 changes: 161 additions & 21 deletions third_party/blink/renderer/modules/webaudio/delay_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,183 @@

#include "third_party/blink/renderer/modules/webaudio/delay_handler.h"

#include <memory>

#include "third_party/blink/renderer/modules/webaudio/audio_node.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/delay_processor.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 kNumberOfChannels = 1;
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)
: AudioBasicProcessorHandler(
kNodeTypeDelay,
node,
sample_rate,
std::make_unique<DelayProcessor>(
sample_rate,
kNumberOfChannels,
node.context()->GetDeferredTaskHandler().RenderQuantumFrames(),
delay_time,
max_delay_time)) {
: AudioHandler(kNodeTypeDelay, node, sample_rate),
number_of_channels_(kDefaultNumberOfChannels),
sample_rate_(sample_rate),
render_quantum_frames_(
node.context()->GetDeferredTaskHandler().RenderQuantumFrames()),
delay_time_(&delay_time),
max_delay_time_(max_delay_time) {
AddInput();
AddOutput(kNumberOfOutputs);
Initialize();
}

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));
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 try_locker(process_lock_);
if (try_locker.is_acquired()) {
DCHECK_EQ(source_bus->NumberOfChannels(),
destination_bus->NumberOfChannels());
DCHECK_EQ(source_bus->NumberOfChannels(), kernels_.size());

for (unsigned i = 0; i < kernels_.size(); ++i) {
if (delay_time_->HasSampleAccurateValues() &&
delay_time_->IsAudioRate()) {
delay_time_->CalculateSampleAccurateValues(kernels_[i]->DelayTimes(),
frames_to_process);
kernels_[i]->ProcessARate(source_bus->Channel(i)->Data(),
destination_bus->Channel(i)->MutableData(),
frames_to_process);
} else {
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;
}

DCHECK_LE(frames_to_process, render_quantum_frames_);
float values[render_quantum_frames_];
delay_time_->CalculateSampleAccurateValues(values, 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
36 changes: 33 additions & 3 deletions third_party/blink/renderer/modules/webaudio/delay_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,56 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_DELAY_HANDLER_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_DELAY_HANDLER_H_

#include <memory>

#include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/modules/webaudio/audio_basic_processor_handler.h"
#include "third_party/blink/renderer/modules/webaudio/audio_handler.h"
#include "third_party/blink/renderer/modules/webaudio/audio_node.h"

namespace blink {

class AudioNode;
class AudioParamHandler;
class AudioNodeInput;
class Delay;

class DelayHandler : public AudioBasicProcessorHandler {
class DelayHandler final : public AudioHandler {
public:
static scoped_refptr<DelayHandler> Create(AudioNode&,
float sample_rate,
AudioParamHandler& delay_time,
double max_delay_time);

~DelayHandler() override;

private:
DelayHandler(AudioNode&,
float sample_rate,
AudioParamHandler& delay_time,
double max_delay_time);

void Process(uint32_t frames_to_process) override;
void ProcessOnlyAudioParams(uint32_t frames_to_process) override;

void Initialize() override;
void Uninitialize() override;

void CheckNumberOfChannelsForInput(AudioNodeInput*) override;

bool RequiresTailProcessing() const override;
double TailTime() const override;
double LatencyTime() const override;

void PullInputs(uint32_t frames_to_process) override;

unsigned number_of_channels_;
float sample_rate_;
unsigned render_quantum_frames_;

Vector<std::unique_ptr<Delay>> kernels_ GUARDED_BY(process_lock_);
mutable base::Lock process_lock_;

scoped_refptr<AudioParamHandler> delay_time_;
double max_delay_time_;
};

} // namespace blink
Expand Down

0 comments on commit 31f15dc

Please sign in to comment.