@@ -633,12 +633,20 @@ impl AudioProcessor for AudioBufferSourceRenderer {
633633 for ( i, playback_info) in playback_infos. iter_mut ( ) . enumerate ( ) {
634634 let current_time = block_time + i as f64 * dt;
635635
636+ // handle floating point errors due to start time computation
637+ // cf. test_subsample_buffer_stitching
638+ if !self . render_state . started {
639+ if almost:: equal ( current_time, self . start_time ) {
640+ self . start_time = current_time;
641+ }
642+ }
643+
636644 // Handle following cases:
637645 // - we are before start time
638646 // - we are after stop time
639647 // - explicit duration (in buffer time reference) has been given and we have reached it
640648 // Note that checking against buffer duration is done below to handle looping
641- if current_time < self . start_time
649+ if ( current_time < self . start_time )
642650 || current_time >= self . stop_time
643651 || self . render_state . buffer_time_elapsed >= self . duration
644652 {
@@ -647,6 +655,7 @@ impl AudioProcessor for AudioBufferSourceRenderer {
647655
648656 // we have now reached start time
649657 if !self . render_state . started {
658+ // println!("start, {}, {}, {}", current_time, self.start_time, (current_time - self.start_time).abs());
650659 let delta = current_time - self . start_time ;
651660 // handle that start time may be between last sample and this one
652661 self . offset += delta;
@@ -835,6 +844,7 @@ mod tests {
835844 use std:: sync:: { Arc , Mutex } ;
836845
837846 use crate :: context:: { BaseAudioContext , OfflineAudioContext } ;
847+ use crate :: AudioBufferOptions ;
838848 use crate :: RENDER_QUANTUM_SIZE ;
839849
840850 use super :: * ;
@@ -1817,6 +1827,49 @@ mod tests {
18171827 assert_float_eq ! ( channel[ ..] , expected[ ..] , abs_all <= 0. ) ;
18181828 }
18191829
1830+ #[ test]
1831+ // ported from wpt: the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html
1832+ fn test_subsample_buffer_stitching ( ) {
1833+ let sample_rate = 44_100. ;
1834+ let buffer_rate = 44_100. ;
1835+ let buffer_length = 30 ;
1836+ let frequency = 440. ;
1837+
1838+ let length = buffer_length * 15 ;
1839+ let mut context = OfflineAudioContext :: new ( 1 , length, sample_rate) ;
1840+
1841+ let mut wave_signal = vec ! [ 0. ; context. length( ) ] ;
1842+ let omega = 2. * PI / buffer_rate * frequency;
1843+
1844+ wave_signal. iter_mut ( ) . enumerate ( ) . for_each ( |( i, s) | {
1845+ * s = ( omega * i as f32 ) . sin ( ) ;
1846+ } ) ;
1847+
1848+ // Slice the sine wave into many little buffers to be assigned to ABSNs
1849+ // that are started at the appropriate times to produce a final sine
1850+ // wave.
1851+ for k in ( 0 ..context. length ( ) ) . step_by ( buffer_length) {
1852+ let mut buffer = AudioBuffer :: new ( AudioBufferOptions {
1853+ number_of_channels : 1 ,
1854+ length : buffer_length,
1855+ sample_rate,
1856+ } ) ;
1857+ buffer. copy_to_channel ( & wave_signal[ k..k + buffer_length] , 0 ) ;
1858+
1859+ let mut src = AudioBufferSourceNode :: new ( & context, AudioBufferSourceOptions {
1860+ buffer : Some ( buffer) ,
1861+ ..Default :: default ( )
1862+ } ) ;
1863+ src. connect ( & context. destination ( ) ) ;
1864+ src. start_at ( k as f64 / buffer_rate as f64 ) ;
1865+ }
1866+
1867+ let result = context. start_rendering_sync ( ) ;
1868+ let channel = result. get_channel_data ( 0 ) ;
1869+
1870+ assert_float_eq ! ( channel[ ..] , wave_signal[ ..] , abs_all <= 1e-9 ) ;
1871+ }
1872+
18201873 #[ test]
18211874 fn test_onended_before_drop ( ) {
18221875 let sample_rate = 48_000. ;
0 commit comments