Skip to content

Commit

Permalink
Merge pull request #530 from b-ma/fix/absn-sub-sample-scheduling
Browse files Browse the repository at this point in the history
Fix absn sub sample scheduling
  • Loading branch information
orottier authored Sep 18, 2024
2 parents e78ad1a + bed1bf1 commit 95927ec
Showing 1 changed file with 61 additions and 21 deletions.
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

0 comments on commit 95927ec

Please sign in to comment.