Commit a806d8e4 authored by Raymond Toy's avatar Raymond Toy Committed by Commit Bot

Part 1: Split out ABSN code in preparation for SIMD

Move the core routine that handles the complicated case where the play
back rate is not 1 or interpolation/extrapolation is needed.

Then break out that routine into two new routines for computing the
indices and for computing the output so that we can optimize each
independently.

A slight function difference occurred because we changed the
interpolation formula from using (1-f)*x0+f*x1 to x0+f*(x1-x0).  This
is mathematically equivalent and but not in floating point.  This
shows up in the one test needing the threshold to be updated slightly.

Bug: 1104371
Change-Id: Id299fb9471d01edad8d9c9da73084af9a53533f5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2342136Reviewed-by: default avatarHongchan Choi <hongchan@chromium.org>
Reviewed-by: default avatarRaymond Toy <rtoy@chromium.org>
Commit-Queue: Raymond Toy <rtoy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797065}
parent 51f1660b
...@@ -320,68 +320,154 @@ bool AudioBufferSourceHandler::RenderFromBuffer( ...@@ -320,68 +320,154 @@ bool AudioBufferSourceHandler::RenderFromBuffer(
} }
virtual_read_index = read_index; virtual_read_index = read_index;
} else { } else {
while (frames_to_process--) { virtual_read_index = RenderFromBufferKernel(
unsigned read_index = static_cast<unsigned>(virtual_read_index); write_index, frames_to_process, virtual_read_index, virtual_end_frame,
double interpolation_factor = virtual_read_index - read_index; virtual_delta_frames, computed_playback_rate, buffer_length, bus);
}
// For linear interpolation we need the next sample-frame too.
unsigned read_index2 = read_index + 1; bus->ClearSilentFlag();
if (read_index2 >= buffer_length) {
if (Loop()) { virtual_read_index_ = virtual_read_index;
// Make sure to wrap around at the end of the buffer.
read_index2 = static_cast<unsigned>(virtual_read_index + 1 - return true;
virtual_delta_frames); }
} else {
read_index2 = read_index; std::tuple<double, int, bool> AudioBufferSourceHandler::ComputeIndices(
} const struct InterpolationInfo& interp_info,
const struct IndicesInfo& indices_info,
int frames_to_process,
uint32_t buffer_length,
bool is_looping) const {
int frames_processed = frames_to_process;
bool end_of_buffer_reached = false;
unsigned* read0 = interp_info.read0;
unsigned* read1 = interp_info.read1;
float* interp_factor = interp_info.interp_factor;
auto virtual_read_index = indices_info.virtual_read_index;
auto computed_playback_rate = indices_info.computed_playback_rate;
auto virtual_end_frame = indices_info.virtual_end_frame;
auto virtual_delta_frames = indices_info.virtual_delta_frames;
for (int k = 0; k < frames_to_process; ++k) {
unsigned read_index = static_cast<unsigned>(virtual_read_index);
double interpolation_factor = virtual_read_index - read_index;
// For linear interpolation we need the next sample-frame too.
unsigned read_index2 = read_index + 1;
if (read_index2 >= buffer_length) {
if (is_looping) {
// Make sure to wrap around at the end of the buffer.
read_index2 = static_cast<unsigned>(virtual_read_index + 1 -
virtual_delta_frames);
} else {
read_index2 = read_index;
} }
}
// Final sanity check on buffer access. // Final sanity check on buffer access.
// FIXME: as an optimization, try to get rid of this inner-loop check and // FIXME: as an optimization, try to get rid of this inner-loop check and
// put assertions and guards before the loop. // put assertions and guards before the loop.
if (read_index >= buffer_length || read_index2 >= buffer_length) if (read_index >= buffer_length || read_index2 >= buffer_length)
break;
read0[k] = read_index;
read1[k] = read_index2;
interp_factor[k] = interpolation_factor;
virtual_read_index += computed_playback_rate;
// Wrap-around, retaining sub-sample position since virtualReadIndex is
// floating-point.
if (virtual_read_index >= virtual_end_frame) {
virtual_read_index -= virtual_delta_frames;
if (!is_looping) {
frames_processed = k + 1;
end_of_buffer_reached = true;
break; break;
// Linear interpolation.
for (unsigned i = 0; i < number_of_channels; ++i) {
float* destination = destination_channels[i];
const float* source = source_channels[i];
double sample;
if (read_index == read_index2 && read_index >= 1) {
// We're at the end of the buffer, so just linearly extrapolate from
// the last two samples.
double sample1 = source[read_index - 1];
double sample2 = source[read_index];
sample = sample2 + (sample2 - sample1) * interpolation_factor;
} else {
double sample1 = source[read_index];
double sample2 = source[read_index2];
sample = (1.0 - interpolation_factor) * sample1 +
interpolation_factor * sample2;
}
destination[write_index] = clampTo<float>(sample);
} }
write_index++; }
}
virtual_read_index += computed_playback_rate; return std::make_tuple(virtual_read_index, frames_processed,
end_of_buffer_reached);
}
// Wrap-around, retaining sub-sample position since virtualReadIndex is unsigned AudioBufferSourceHandler::ComputeOutput(
// floating-point. float** destination_channels,
if (virtual_read_index >= virtual_end_frame) { const float** source_channels,
virtual_read_index -= virtual_delta_frames; const struct InterpolationInfo& interp_info,
if (RenderSilenceAndFinishIfNotLooping(bus, write_index, int frames_processed,
frames_to_process)) unsigned write_index,
break; unsigned number_of_channels) const {
unsigned* read0 = interp_info.read0;
unsigned* read1 = interp_info.read1;
float* interp_factor = interp_info.interp_factor;
for (int k = 0; k < frames_processed; ++k) {
// Linear interpolation.
for (unsigned i = 0; i < number_of_channels; ++i) {
float* destination = destination_channels[i];
const float* source = source_channels[i];
float sample;
if (read0[k] == read1[k] && read0[k] >= 1) {
// We're at the end of the buffer, so just linearly extrapolate from
// the last two samples.
float sample1 = source[read0[k] - 1];
float sample2 = source[read0[k]];
sample = sample2 + (sample2 - sample1) * interp_factor[k];
} else {
float sample1 = source[read0[k]];
float sample2 = source[read1[k]];
sample = sample1 + interp_factor[k] * (sample2 - sample1);
} }
destination[write_index] = sample;
} }
write_index++;
} }
bus->ClearSilentFlag(); return write_index;
}
virtual_read_index_ = virtual_read_index; double AudioBufferSourceHandler::RenderFromBufferKernel(
unsigned write_index,
int frames_to_process,
double virtual_read_index,
double virtual_end_frame,
double virtual_delta_frames,
double computed_playback_rate,
uint32_t buffer_length,
AudioBus* bus) {
unsigned number_of_channels = this->NumberOfChannels();
const float** source_channels = source_channels_.get();
float** destination_channels = destination_channels_.get();
return true; unsigned read0[audio_utilities::kRenderQuantumFrames];
unsigned read1[audio_utilities::kRenderQuantumFrames];
float interp_factor[audio_utilities::kRenderQuantumFrames];
int frames_processed;
bool end_of_buffer_reached;
struct InterpolationInfo interp_info = {read0, read1, interp_factor};
struct IndicesInfo indices_info = {virtual_read_index, computed_playback_rate,
virtual_delta_frames, virtual_end_frame};
std::tie<double, int, bool>(virtual_read_index, frames_processed,
end_of_buffer_reached) =
ComputeIndices(interp_info, indices_info, frames_to_process,
buffer_length, Loop());
write_index =
ComputeOutput(destination_channels, source_channels, interp_info,
frames_processed, write_index, number_of_channels);
if (end_of_buffer_reached) {
RenderSilenceAndFinishIfNotLooping(bus, write_index,
frames_to_process - frames_processed);
}
return virtual_read_index;
} }
void AudioBufferSourceHandler::SetBuffer(AudioBuffer* buffer, void AudioBufferSourceHandler::SetBuffer(AudioBuffer* buffer,
......
...@@ -125,6 +125,62 @@ class AudioBufferSourceHandler final : public AudioScheduledSourceHandler { ...@@ -125,6 +125,62 @@ class AudioBufferSourceHandler final : public AudioScheduledSourceHandler {
uint32_t number_of_frames, uint32_t number_of_frames,
double start_time_offset); double start_time_offset);
// Structure for passing in information needed to compute indices for
// interpolation.
struct InterpolationInfo {
unsigned* read0;
unsigned* read1;
float* interp_factor;
};
// Structure for passing information about the virtual read index needed for
// computing indices.
struct IndicesInfo {
double virtual_read_index;
double computed_playback_rate;
double virtual_delta_frames;
double virtual_end_frame;
};
// Handles the general case of rendering from a buffer where the playback rate
// is not 1 or other situations where interpolation is required.
double RenderFromBufferKernel(unsigned write_index,
int frames_to_process,
double virtual_read_index,
double virtual_end_frame,
double virtual_delta_frames,
double computed_playback_rate,
uint32_t buffer_length,
AudioBus* bus);
// Helper routines for RenderFromBufferKernel that don't need access to any
// internals.
// ComputeIndices computes the indices needed for
// interpolation/extrapolation. Returns three values:
//
// 1: the updated virtual_read_index
//
// 2: how many frames were processed (possibly less trhan frames_to_process
// because the buffer end was reached)
// 3: a boolean indicating if we did reach the end of the buffer.
inline std::tuple<double, int, bool> ComputeIndices(
const struct InterpolationInfo& interp_info,
const struct IndicesInfo& indices_info,
int frames_to_process,
uint32_t buffer_length,
bool is_looping) const;
// ComputeOutput takes the indices from ComputeIndices and performs
// interpolates/extrapolates the data to produce the desired output. Returns
// the update write index where the next output should be written to.
inline unsigned ComputeOutput(float** destination_channels,
const float** source_channels,
const struct InterpolationInfo& interp_info,
int frames_processed,
unsigned write_index,
unsigned number_of_channels) const;
// Render silence starting from "index" frame in AudioBus. // Render silence starting from "index" frame in AudioBus.
inline bool RenderSilenceAndFinishIfNotLooping(AudioBus*, inline bool RenderSilenceAndFinishIfNotLooping(AudioBus*,
unsigned index, unsigned index,
......
...@@ -137,7 +137,7 @@ Tests that AudioBufferSourceNode supports loop-points with .loopStart and .loopE ...@@ -137,7 +137,7 @@ Tests that AudioBufferSourceNode supports loop-points with .loopStart and .loopE
// in ULP. This is experimentally determined. Assuming that // in ULP. This is experimentally determined. Assuming that
// the reference file is a 16-bit wav file, the max values in // the reference file is a 16-bit wav file, the max values in
// the wave file are +/- 32768. // the wave file are +/- 32768.
let maxUlp = 0; let maxUlp = 1.9532e-3;
let threshold = maxUlp / 32768; let threshold = maxUlp / 32768;
for (let k = 0; k < renderedAudio.numberOfChannels; ++k) { for (let k = 0; k < renderedAudio.numberOfChannels; ++k) {
......
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