Commit 3edfebea authored by Raymond Toy's avatar Raymond Toy Committed by Commit Bot

Split DelayNode processing into a-rate and k-rate functions

This CL jsut splits the processing for a DelayNode into separate
routines to handle a-rate and k-rate processing.

This is a first step at vectorizing delay node.

No functional changes.

Bug: 1087071
Change-Id: Ia12bad0748e608eb4e20bd86b07b7bd4f1e54c2a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2227335Reviewed-by: default avatarHongchan Choi <hongchan@chromium.org>
Commit-Queue: Raymond Toy <rtoy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#775129}
parent 6fbef2d7
...@@ -81,9 +81,9 @@ double AudioDelayDSPKernel::DelayTime(float sample_rate) { ...@@ -81,9 +81,9 @@ double AudioDelayDSPKernel::DelayTime(float sample_rate) {
return desired_delay_frames_ / sample_rate; return desired_delay_frames_ / sample_rate;
} }
void AudioDelayDSPKernel::Process(const float* source, void AudioDelayDSPKernel::ProcessARate(const float* source,
float* destination, float* destination,
uint32_t frames_to_process) { uint32_t frames_to_process) {
int buffer_length = buffer_.size(); int buffer_length = buffer_.size();
float* buffer = buffer_.Data(); float* buffer = buffer_.Data();
...@@ -96,102 +96,126 @@ void AudioDelayDSPKernel::Process(const float* source, ...@@ -96,102 +96,126 @@ void AudioDelayDSPKernel::Process(const float* source,
float sample_rate = this->SampleRate(); float sample_rate = this->SampleRate();
double max_time = MaxDelayTime(); double max_time = MaxDelayTime();
if (HasSampleAccurateValues() && IsAudioRate()) { float* delay_times = delay_times_.Data();
float* delay_times = delay_times_.Data(); CalculateSampleAccurateValues(delay_times, frames_to_process);
CalculateSampleAccurateValues(delay_times, frames_to_process);
int w_index = write_index_; int w_index = write_index_;
for (unsigned i = 0; i < frames_to_process; ++i) { for (unsigned i = 0; i < frames_to_process; ++i) {
double delay_time = delay_times[i]; double delay_time = delay_times[i];
// TODO(crbug.com/1013345): Don't need this if that bug is fixed // TODO(crbug.com/1013345): Don't need this if that bug is fixed
if (std::isnan(delay_time)) if (std::isnan(delay_time))
delay_time = max_time; delay_time = max_time;
double desired_delay_frames = delay_time * sample_rate; double desired_delay_frames = delay_time * sample_rate;
double read_position = w_index + buffer_length - desired_delay_frames; double read_position = w_index + buffer_length - desired_delay_frames;
if (read_position >= buffer_length) if (read_position >= buffer_length)
read_position -= buffer_length; read_position -= buffer_length;
// Linearly interpolate in-between delay times. // Linearly interpolate in-between delay times.
int read_index1 = static_cast<int>(read_position); int read_index1 = static_cast<int>(read_position);
DCHECK_GE(read_index1, 0); DCHECK_GE(read_index1, 0);
DCHECK_LT(read_index1, buffer_length); DCHECK_LT(read_index1, buffer_length);
int read_index2 = read_index1 + 1; int read_index2 = read_index1 + 1;
if (read_index2 >= buffer_length) if (read_index2 >= buffer_length)
read_index2 -= buffer_length; read_index2 -= buffer_length;
DCHECK_GE(read_index2, 0); DCHECK_GE(read_index2, 0);
DCHECK_LT(read_index2, buffer_length); DCHECK_LT(read_index2, buffer_length);
buffer[w_index] = *source++; buffer[w_index] = *source++;
float interpolation_factor = read_position - read_index1; float interpolation_factor = read_position - read_index1;
float sample1 = buffer[read_index1]; float sample1 = buffer[read_index1];
float sample2 = buffer[read_index2]; float sample2 = buffer[read_index2];
++w_index; ++w_index;
if (w_index >= buffer_length) if (w_index >= buffer_length)
w_index -= buffer_length; w_index -= buffer_length;
*destination++ = sample1 + interpolation_factor * (sample2 - sample1); *destination++ = sample1 + interpolation_factor * (sample2 - sample1);
} }
write_index_ = w_index; write_index_ = w_index;
} else { }
// This is basically the same as above, but optimized for the case where the
// delay time is constant for the current render.
//
// TODO(crbug.com/1012198): There are still some further optimizations that
// could be done. interp_factor could be a float to eliminate several
// conversions between floats and doubles. It might be possible to get rid
// of the wrapping if the buffer were longer. This may aslo allow
// |write_index_| to be different from |read_index1| or |read_index2| which
// simplifies the loop a bit.
double delay_time = this->DelayTime(sample_rate);
// Make sure the delay time is in a valid range.
delay_time = clampTo(delay_time, 0.0, max_time);
double desired_delay_frames = delay_time * sample_rate;
int w_index = write_index_;
double read_position = w_index + buffer_length - desired_delay_frames;
if (read_position >= buffer_length)
read_position -= buffer_length;
// Linearly interpolate in-between delay times. |read_index1| and void AudioDelayDSPKernel::ProcessKRate(const float* source,
// |read_index2| are the indices of the frames to be used for float* destination,
// interpolation. uint32_t frames_to_process) {
int read_index1 = static_cast<int>(read_position); int buffer_length = buffer_.size();
int read_index2 = (read_index1 + 1) % buffer_length; float* buffer = buffer_.Data();
float interp_factor = read_position - read_index1;
DCHECK(buffer_length);
float* w = &buffer[w_index]; DCHECK(source);
float* r1 = &buffer[read_index1]; DCHECK(destination);
float* r2 = &buffer[read_index2]; DCHECK_GE(write_index_, 0);
float* buffer_end = &buffer[buffer_length]; DCHECK_LT(write_index_, buffer_length);
for (unsigned i = 0; i < frames_to_process; ++i) { float sample_rate = this->SampleRate();
// Copy the latest sample into the buffer. Needed because double max_time = MaxDelayTime();
// w_index could be the same as read_index1 or read_index2.
*w++ = *source++; // This is basically the same as above, but optimized for the case where the
float sample1 = *r1++; // delay time is constant for the current render.
float sample2 = *r2++; //
// TODO(crbug.com/1012198): There are still some further optimizations that
// Update the indices and wrap them to the beginning of the buffer if // could be done. interp_factor could be a float to eliminate several
// needed. // conversions between floats and doubles. It might be possible to get rid
if (w >= buffer_end) // of the wrapping if the buffer were longer. This may aslo allow
w = buffer; // |write_index_| to be different from |read_index1| or |read_index2| which
if (r1 >= buffer_end) // simplifies the loop a bit.
r1 = buffer;
if (r2 >= buffer_end) double delay_time = this->DelayTime(sample_rate);
r2 = buffer; // Make sure the delay time is in a valid range.
delay_time = clampTo(delay_time, 0.0, max_time);
// Linearly interpolate between samples. double desired_delay_frames = delay_time * sample_rate;
*destination++ = sample1 + interp_factor * (sample2 - sample1); int w_index = write_index_;
} double read_position = w_index + buffer_length - desired_delay_frames;
if (read_position >= buffer_length)
write_index_ = w - buffer; read_position -= buffer_length;
// Linearly interpolate in-between delay times. |read_index1| and
// |read_index2| are the indices of the frames to be used for
// interpolation.
int read_index1 = static_cast<int>(read_position);
int read_index2 = (read_index1 + 1) % buffer_length;
float interp_factor = read_position - read_index1;
float* w = &buffer[w_index];
float* r1 = &buffer[read_index1];
float* r2 = &buffer[read_index2];
float* buffer_end = &buffer[buffer_length];
for (unsigned i = 0; i < frames_to_process; ++i) {
// Copy the latest sample into the buffer. Needed because
// w_index could be the same as read_index1 or read_index2.
*w++ = *source++;
float sample1 = *r1++;
float sample2 = *r2++;
// Update the indices and wrap them to the beginning of the buffer if
// needed.
if (w >= buffer_end)
w = buffer;
if (r1 >= buffer_end)
r1 = buffer;
if (r2 >= buffer_end)
r2 = buffer;
// Linearly interpolate between samples.
*destination++ = sample1 + interp_factor * (sample2 - sample1);
}
write_index_ = w - buffer;
}
void AudioDelayDSPKernel::Process(const float* source,
float* destination,
uint32_t frames_to_process) {
if (HasSampleAccurateValues() && IsAudioRate()) {
ProcessARate(source, destination, frames_to_process);
} else {
ProcessKRate(source, destination, frames_to_process);
} }
} }
......
...@@ -35,9 +35,22 @@ class PLATFORM_EXPORT AudioDelayDSPKernel : public AudioDSPKernel { ...@@ -35,9 +35,22 @@ class PLATFORM_EXPORT AudioDelayDSPKernel : public AudioDSPKernel {
public: public:
AudioDelayDSPKernel(double max_delay_time, float sample_rate); AudioDelayDSPKernel(double max_delay_time, float sample_rate);
// Process the delay. Basically dispatches to either ProcessKRate or
// ProcessARate.
void Process(const float* source, void Process(const float* source,
float* destination, float* destination,
uint32_t frames_to_process) override; uint32_t frames_to_process) override;
// Handles k-rate processing
void ProcessKRate(const float* source,
float* destination,
uint32_t frames_to_process);
// Handles a-rate processing
void ProcessARate(const float* source,
float* destination,
uint32_t frames_to_process);
void Reset() override; void Reset() override;
float MaxDelayTime() const { return max_delay_time_; } float MaxDelayTime() const { return max_delay_time_; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment