@@ -633,12 +633,10 @@ 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
636+ // Sticky behavior to handle floating point errors due to start time computation
637637 // 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- }
638+ if !self . render_state . started && almost:: equal ( current_time, self . start_time ) {
639+ self . start_time = current_time;
642640 }
643641
644642 // Handle following cases:
@@ -655,7 +653,6 @@ impl AudioProcessor for AudioBufferSourceRenderer {
655653
656654 // we have now reached start time
657655 if !self . render_state . started {
658- // println!("start, {}, {}, {}", current_time, self.start_time, (current_time - self.start_time).abs());
659656 let delta = current_time - self . start_time ;
660657 // handle that start time may be between last sample and this one
661658 self . offset += delta;
@@ -745,8 +742,9 @@ impl AudioProcessor for AudioBufferSourceRenderer {
745742 let next_sample = match buffer_channel. get ( prev_frame_index + 1 )
746743 {
747744 Some ( val) => * val as f64 ,
745+ // End of buffer
748746 None => {
749- let sample = if is_looping {
747+ if is_looping {
750748 if playback_rate >= 0. {
751749 let start_playhead =
752750 actual_loop_start * sample_rate;
@@ -758,18 +756,31 @@ impl AudioProcessor for AudioBufferSourceRenderer {
758756 start_playhead as usize + 1
759757 } ;
760758
761- buffer_channel[ start_index]
759+ buffer_channel[ start_index] as f64
762760 } else {
763761 let end_playhead =
764762 actual_loop_end * sample_rate;
765763 let end_index = end_playhead as usize ;
766- buffer_channel[ end_index]
764+ buffer_channel[ end_index] as f64
767765 }
768766 } else {
769- 0.
770- } ;
771-
772- sample as f64
767+ // Handle 2 edge cases:
768+ // 1. We are in a case where buffer time is below buffer
769+ // duration due to floating point errors, but where
770+ // prev_frame_index is last index and k is near 1. We can't
771+ // filter this case before, because it might break
772+ // loops logic.
773+ // 2. Buffer contains only one sample
774+ if almost:: equal ( * k, 1. ) || * prev_frame_index == 0 {
775+ 0.
776+ } else {
777+ // Extrapolate next sample using the last two known samples
778+ // cf. https://github.com/WebAudio/web-audio-api/issues/2032
779+ let prev_prev_sample =
780+ buffer_channel[ * prev_frame_index - 1 ] ;
781+ 2. * prev_sample - prev_prev_sample as f64
782+ }
783+ }
773784 }
774785 } ;
775786
@@ -1130,44 +1141,47 @@ mod tests {
11301141
11311142 #[ test]
11321143 fn test_audio_buffer_resampling ( ) {
1133- [ 22_500 , 38_000 , 48_000 , 96_000 ] . iter ( ) . for_each ( |sr| {
1134- let base_sr = 44_100 ;
1135- let mut context = OfflineAudioContext :: new ( 1 , base_sr, base_sr as f32 ) ;
1136-
1137- // 1Hz sine at different sample rates
1138- let buf_sr = * sr;
1139- // safe cast for sample rate, see discussion at #113
1140- let sample_rate = buf_sr as f32 ;
1141- let mut buffer = context. create_buffer ( 1 , buf_sr, sample_rate) ;
1142- let mut sine = vec ! [ ] ;
1143-
1144- for i in 0 ..buf_sr {
1145- let phase = i as f32 / buf_sr as f32 * 2. * PI ;
1146- let sample = phase. sin ( ) ;
1147- sine. push ( sample) ;
1148- }
1144+ [ 22_500 , 38_000 , 43_800 , 48_000 , 96_000 ]
1145+ . iter ( )
1146+ . for_each ( |sr| {
1147+ let freq = 1. ;
1148+ let base_sr = 44_100 ;
1149+ let mut context = OfflineAudioContext :: new ( 1 , base_sr, base_sr as f32 ) ;
1150+
1151+ // 1Hz sine at different sample rates
1152+ let buf_sr = * sr;
1153+ // safe cast for sample rate, see discussion at #113
1154+ let sample_rate = buf_sr as f32 ;
1155+ let mut buffer = context. create_buffer ( 1 , buf_sr, sample_rate) ;
1156+ let mut sine = vec ! [ ] ;
1157+
1158+ for i in 0 ..buf_sr {
1159+ let phase = freq * i as f32 / buf_sr as f32 * 2. * PI ;
1160+ let sample = phase. sin ( ) ;
1161+ sine. push ( sample) ;
1162+ }
11491163
1150- buffer. copy_to_channel ( & sine[ ..] , 0 ) ;
1164+ buffer. copy_to_channel ( & sine[ ..] , 0 ) ;
11511165
1152- let mut src = context. create_buffer_source ( ) ;
1153- src. connect ( & context. destination ( ) ) ;
1154- src. set_buffer ( buffer) ;
1155- src. start_at ( 0. / sample_rate as f64 ) ;
1166+ let mut src = context. create_buffer_source ( ) ;
1167+ src. connect ( & context. destination ( ) ) ;
1168+ src. set_buffer ( buffer) ;
1169+ src. start_at ( 0. / sample_rate as f64 ) ;
11561170
1157- let result = context. start_rendering_sync ( ) ;
1158- let channel = result. get_channel_data ( 0 ) ;
1171+ let result = context. start_rendering_sync ( ) ;
1172+ let channel = result. get_channel_data ( 0 ) ;
11591173
1160- // 1Hz sine at audio context sample rate
1161- let mut expected = vec ! [ ] ;
1174+ // 1Hz sine at audio context sample rate
1175+ let mut expected = vec ! [ ] ;
11621176
1163- for i in 0 ..base_sr {
1164- let phase = i as f32 / base_sr as f32 * 2. * PI ;
1165- let sample = phase. sin ( ) ;
1166- expected. push ( sample) ;
1167- }
1177+ for i in 0 ..base_sr {
1178+ let phase = freq * i as f32 / base_sr as f32 * 2. * PI ;
1179+ let sample = phase. sin ( ) ;
1180+ expected. push ( sample) ;
1181+ }
11681182
1169- assert_float_eq ! ( channel[ ..] , expected[ ..] , abs_all <= 1e-6 ) ;
1170- } ) ;
1183+ assert_float_eq ! ( channel[ ..] , expected[ ..] , abs_all <= 1e-6 ) ;
1184+ } ) ;
11711185 }
11721186
11731187 #[ test]
@@ -1273,7 +1287,7 @@ mod tests {
12731287 }
12741288
12751289 #[ test]
1276- fn test_end_of_file_slow_track ( ) {
1290+ fn test_end_of_file_slow_track_1 ( ) {
12771291 let sample_rate = 48_000. ;
12781292 let mut context = OfflineAudioContext :: new ( 1 , RENDER_QUANTUM_SIZE * 2 , sample_rate) ;
12791293
@@ -1828,46 +1842,61 @@ mod tests {
18281842 }
18291843
18301844 #[ test]
1831- // ported from wpt: the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html
1845+ // Ported from wpt: the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html
1846+ // Note that in wpt, results are tested against an oscillator node, which fails
1847+ // in the (44_100., 43_800., 3.8986e-3) condition for some (yet) unknown reason
18321848 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. ;
1849+ [ ( 44_100. , 44_100. , 9.0957e-5 ) , ( 44_100. , 43_800. , 3.8986e-3 ) ]
1850+ . iter ( )
1851+ . for_each ( |( sample_rate, buffer_rate, error_threshold) | {
1852+ let sample_rate = * sample_rate;
1853+ let buffer_rate = * buffer_rate;
1854+ let buffer_length = 30 ;
1855+ let frequency = 440. ;
1856+
1857+ // let length = sample_rate as usize;
1858+ let length = buffer_length * 15 ;
1859+ let mut context = OfflineAudioContext :: new ( 2 , length, sample_rate) ;
1860+
1861+ let mut wave_signal = vec ! [ 0. ; context. length( ) ] ;
1862+ let omega = 2. * PI / buffer_rate * frequency;
1863+ wave_signal. iter_mut ( ) . enumerate ( ) . for_each ( |( i, s) | {
1864+ * s = ( omega * i as f32 ) . sin ( ) ;
1865+ } ) ;
18371866
1838- let length = buffer_length * 15 ;
1839- let mut context = OfflineAudioContext :: new ( 1 , length, sample_rate) ;
1867+ // Slice the sine wave into many little buffers to be assigned to ABSNs
1868+ // that are started at the appropriate times to produce a final sine
1869+ // wave.
1870+ for k in ( 0 ..context. length ( ) ) . step_by ( buffer_length) {
1871+ let mut buffer = AudioBuffer :: new ( AudioBufferOptions {
1872+ number_of_channels : 1 ,
1873+ length : buffer_length,
1874+ sample_rate : buffer_rate,
1875+ } ) ;
1876+ buffer. copy_to_channel ( & wave_signal[ k..k + buffer_length] , 0 ) ;
1877+
1878+ let mut src = AudioBufferSourceNode :: new (
1879+ & context,
1880+ AudioBufferSourceOptions {
1881+ buffer : Some ( buffer) ,
1882+ ..Default :: default ( )
1883+ } ,
1884+ ) ;
1885+ src. connect ( & context. destination ( ) ) ;
1886+ src. start_at ( k as f64 / buffer_rate as f64 ) ;
1887+ }
18401888
1841- let mut wave_signal = vec ! [ 0. ; context. length( ) ] ;
1842- let omega = 2. * PI / buffer_rate * frequency;
1889+ let mut expected = vec ! [ 0. ; context. length( ) ] ;
1890+ let omega = 2. * PI / sample_rate * frequency;
1891+ expected. iter_mut ( ) . enumerate ( ) . for_each ( |( i, s) | {
1892+ * s = ( omega * i as f32 ) . sin ( ) ;
1893+ } ) ;
18431894
1844- wave_signal. iter_mut ( ) . enumerate ( ) . for_each ( |( i, s) | {
1845- * s = ( omega * i as f32 ) . sin ( ) ;
1846- } ) ;
1895+ let result = context. start_rendering_sync ( ) ;
1896+ let actual = result. get_channel_data ( 0 ) ;
18471897
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,
1898+ assert_float_eq ! ( actual[ ..] , expected[ ..] , abs_all <= error_threshold) ;
18561899 } ) ;
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 ) ;
18711900 }
18721901
18731902 #[ test]
0 commit comments