diff --git a/src/node/audio_buffer_source.rs b/src/node/audio_buffer_source.rs index 7354f9bc..7258aae4 100644 --- a/src/node/audio_buffer_source.rs +++ b/src/node/audio_buffer_source.rs @@ -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 @@ -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 = None; @@ -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 { @@ -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; } @@ -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; } @@ -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); @@ -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); @@ -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