Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 61 additions & 21 deletions src/node/audio_buffer_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,15 @@ impl AudioProcessor for AudioBufferSourceRenderer {
self.render_state.is_aligned = false;
}

// If some user defined end of rendering, i.e. explicit stop_time or duration,
// is within this render quantum force slow track as well. It might imply
// resampling e.g. if stop_time is between 2 samples
if buffer_time + block_duration > self.duration
|| block_time + block_duration > self.stop_time
{
self.render_state.is_aligned = false;
}

if self.render_state.is_aligned {
// ---------------------------------------------------------------
// Fast track
Expand All @@ -537,22 +546,9 @@ impl AudioProcessor for AudioBufferSourceRenderer {
self.render_state.started = true;
}

// check if buffer ends within this block
if buffer_time + block_duration > buffer_duration
|| buffer_time + block_duration > self.duration
|| block_time + block_duration > self.stop_time
{
let end_index = if block_time + block_duration > self.stop_time
|| buffer_time + block_duration > self.duration
{
let dt = (self.stop_time - block_time).min(self.duration - buffer_time);
let end_buffer_time = buffer_time + dt;
let end_index = (end_buffer_time * sample_rate).round() as usize;
end_index.min(buffer.length())
} else {
buffer.length()
};

// buffer ends within this block
if buffer_time + block_duration > buffer_duration {
let end_index = buffer.length();
// In case of a loop point in the middle of the block, this value will
// be used to recompute `buffer_time` according to the actual loop point.
let mut loop_point_index: Option<usize> = None;
Expand Down Expand Up @@ -651,8 +647,9 @@ impl AudioProcessor for AudioBufferSourceRenderer {

// we have now reached start time
if !self.render_state.started {
let delta = current_time - self.start_time;
// handle that start time may be between last sample and this one
self.offset += current_time - self.start_time;
self.offset += delta;

if is_looping && computed_playback_rate >= 0. && self.offset >= actual_loop_end
{
Expand All @@ -664,7 +661,8 @@ impl AudioProcessor for AudioBufferSourceRenderer {
self.offset = actual_loop_start;
}

buffer_time = self.offset;
buffer_time = self.offset * computed_playback_rate;
self.render_state.buffer_time_elapsed = delta * computed_playback_rate;
self.render_state.started = true;
}

Expand All @@ -676,7 +674,6 @@ impl AudioProcessor for AudioBufferSourceRenderer {
}

// playback began after loop, and playhead is now prior to the loop end
// @note - only possible when playback_rate < 0 (?)
if self.offset >= actual_loop_end && buffer_time < actual_loop_end {
self.render_state.entered_loop = true;
}
Expand Down Expand Up @@ -1292,7 +1289,7 @@ mod tests {
}

#[test]
fn test_with_duration_fast_track() {
fn test_with_duration_0() {
let sample_rate = 48_000.;
let mut context = OfflineAudioContext::new(1, RENDER_QUANTUM_SIZE, sample_rate);

Expand All @@ -1315,7 +1312,7 @@ mod tests {
}

#[test]
fn test_with_duration_slow_track() {
fn test_with_duration_1() {
let sample_rate = 48_000.;
let mut context = OfflineAudioContext::new(1, RENDER_QUANTUM_SIZE, sample_rate);

Expand Down Expand Up @@ -1343,6 +1340,49 @@ mod tests {
assert_float_eq!(channel[..], expected[..], abs_all <= 0.);
}

#[test]
// port from wpt - sub-sample-scheduling.html / sub-sample-grain
fn test_with_duration_2() {
let sample_rate = 32_768.;
let mut context = OfflineAudioContext::new(1, RENDER_QUANTUM_SIZE, sample_rate);

let mut buffer = context.create_buffer(1, RENDER_QUANTUM_SIZE, sample_rate);
buffer.copy_to_channel(&[1.; RENDER_QUANTUM_SIZE], 0);

let start_grain_index = 3.1;
let end_grain_index = 37.2;

let mut src = context.create_buffer_source();
src.connect(&context.destination());
src.set_buffer(buffer);

src.start_at_with_offset_and_duration(
start_grain_index / sample_rate as f64,
0.,
(end_grain_index - start_grain_index) / sample_rate as f64,
);

let result = context.start_rendering_sync();
let channel = result.get_channel_data(0);

let mut expected = [1.; RENDER_QUANTUM_SIZE];
for s in expected
.iter_mut()
.take(start_grain_index.floor() as usize + 1)
{
*s = 0.;
}
for s in expected
.iter_mut()
.take(RENDER_QUANTUM_SIZE)
.skip(end_grain_index.ceil() as usize)
{
*s = 0.;
}

assert_float_eq!(channel[..], expected[..], abs_all <= 0.);
}

#[test]
fn test_with_offset() {
// offset always bypass slow track
Expand Down