1
1
use std:: any:: Any ;
2
- use std:: sync:: { Mutex , OnceLock } ;
2
+ use std:: sync:: atomic:: Ordering ;
3
+ use std:: sync:: { Arc , Mutex , OnceLock } ;
3
4
4
5
use crate :: buffer:: AudioBuffer ;
5
6
use crate :: context:: { AudioContextRegistration , AudioParamId , BaseAudioContext } ;
6
7
use crate :: param:: { AudioParam , AudioParamDescriptor , AutomationRate } ;
7
8
use crate :: render:: { AudioParamValues , AudioProcessor , AudioRenderQuantum , RenderScope } ;
8
- use crate :: RENDER_QUANTUM_SIZE ;
9
+ use crate :: { AtomicF64 , RENDER_QUANTUM_SIZE } ;
9
10
10
11
use super :: { AudioNode , AudioScheduledSourceNode , ChannelConfig } ;
11
12
@@ -106,6 +107,7 @@ pub struct AudioBufferSourceNode {
106
107
channel_config : ChannelConfig ,
107
108
detune : AudioParam , // has constraints, no a-rate
108
109
playback_rate : AudioParam , // has constraints, no a-rate
110
+ buffer_time : Arc < AtomicF64 > ,
109
111
buffer : OnceLock < AudioBuffer > ,
110
112
inner_state : Mutex < InnerState > ,
111
113
}
@@ -224,6 +226,7 @@ impl AudioBufferSourceNode {
224
226
channel_config : ChannelConfig :: default ( ) ,
225
227
detune : d_param,
226
228
playback_rate : pr_param,
229
+ buffer_time : Arc :: clone ( & renderer. render_state . buffer_time ) ,
227
230
buffer : OnceLock :: new ( ) ,
228
231
inner_state : Mutex :: new ( inner_state) ,
229
232
} ;
@@ -293,6 +296,16 @@ impl AudioBufferSourceNode {
293
296
& self . playback_rate
294
297
}
295
298
299
+ /// Current playhead position in seconds within the [`AudioBuffer`].
300
+ ///
301
+ /// This value is updated at the end of each render quantum.
302
+ ///
303
+ /// Unofficial v2 API extension, not part of the spec yet.
304
+ /// See also: <https://github.com/WebAudio/web-audio-api/issues/2397#issuecomment-709478405>
305
+ pub fn position ( & self ) -> f64 {
306
+ self . buffer_time . load ( Ordering :: Relaxed )
307
+ }
308
+
296
309
/// K-rate [`AudioParam`] that defines a pitch transposition of the file,
297
310
/// expressed in cents
298
311
///
@@ -341,7 +354,7 @@ impl AudioBufferSourceNode {
341
354
}
342
355
343
356
struct AudioBufferRendererState {
344
- buffer_time : f64 ,
357
+ buffer_time : Arc < AtomicF64 > ,
345
358
started : bool ,
346
359
entered_loop : bool ,
347
360
buffer_time_elapsed : f64 ,
@@ -351,7 +364,7 @@ struct AudioBufferRendererState {
351
364
impl Default for AudioBufferRendererState {
352
365
fn default ( ) -> Self {
353
366
Self {
354
- buffer_time : 0. ,
367
+ buffer_time : Arc :: new ( AtomicF64 :: new ( 0. ) ) ,
355
368
started : false ,
356
369
entered_loop : false ,
357
370
buffer_time_elapsed : 0. ,
@@ -463,9 +476,13 @@ impl AudioProcessor for AudioBufferSourceRenderer {
463
476
return false ;
464
477
}
465
478
479
+ // Load the buffer time from the render state.
480
+ // The render state has to be updated before leaving this method!
481
+ let mut buffer_time = self . render_state . buffer_time . load ( Ordering :: Relaxed ) ;
482
+
466
483
// 3. the end of the buffer has been reached.
467
484
if !is_looping {
468
- if computed_playback_rate > 0. && self . render_state . buffer_time >= buffer_duration {
485
+ if computed_playback_rate > 0. && buffer_time >= buffer_duration {
469
486
output. make_silent ( ) ; // also converts to mono
470
487
if !self . ended_triggered {
471
488
scope. send_ended_event ( ) ;
@@ -474,7 +491,7 @@ impl AudioProcessor for AudioBufferSourceRenderer {
474
491
return false ;
475
492
}
476
493
477
- if computed_playback_rate < 0. && self . render_state . buffer_time < 0. {
494
+ if computed_playback_rate < 0. && buffer_time < 0. {
478
495
output. make_silent ( ) ; // also converts to mono
479
496
if !self . ended_triggered {
480
497
scope. send_ended_event ( ) ;
@@ -533,23 +550,21 @@ impl AudioProcessor for AudioBufferSourceRenderer {
533
550
}
534
551
535
552
// check if buffer ends within this block
536
- if self . render_state . buffer_time + block_duration > buffer_duration
537
- || self . render_state . buffer_time + block_duration > self . duration
553
+ if buffer_time + block_duration > buffer_duration
554
+ || buffer_time + block_duration > self . duration
538
555
|| current_time + block_duration > self . stop_time
539
556
{
540
- let buffer_time = self . render_state . buffer_time ;
541
557
let end_index = if current_time + block_duration > self . stop_time
542
- || self . render_state . buffer_time + block_duration > self . duration
558
+ || buffer_time + block_duration > self . duration
543
559
{
544
- let dt = ( self . stop_time - current_time)
545
- . min ( self . duration - self . render_state . buffer_time ) ;
546
- let end_buffer_time = self . render_state . buffer_time + dt;
560
+ let dt = ( self . stop_time - current_time) . min ( self . duration - buffer_time) ;
561
+ let end_buffer_time = buffer_time + dt;
547
562
( end_buffer_time * sample_rate) . round ( ) as usize
548
563
} else {
549
564
buffer. length ( )
550
565
} ;
551
566
// in case of a loop point in the middle of the block, this value
552
- // will be used to recompute `self.render_state. buffer_time` according
567
+ // will be used to recompute `buffer_time` according
553
568
// to the actual loop point.
554
569
let mut loop_point_index: Option < usize > = None ;
555
570
@@ -587,14 +602,13 @@ impl AudioProcessor for AudioBufferSourceRenderer {
587
602
} ) ;
588
603
589
604
if let Some ( loop_point_index) = loop_point_index {
590
- self . render_state . buffer_time =
591
- ( ( RENDER_QUANTUM_SIZE - loop_point_index) as f64 / sample_rate)
592
- % buffer_duration;
605
+ buffer_time = ( ( RENDER_QUANTUM_SIZE - loop_point_index) as f64 / sample_rate)
606
+ % buffer_duration;
593
607
} else {
594
- self . render_state . buffer_time += block_duration;
608
+ buffer_time += block_duration;
595
609
}
596
610
} else {
597
- let start_index = ( self . render_state . buffer_time * sample_rate) . round ( ) as usize ;
611
+ let start_index = ( buffer_time * sample_rate) . round ( ) as usize ;
598
612
let end_index = start_index + RENDER_QUANTUM_SIZE ;
599
613
// we can do memcopy
600
614
buffer
@@ -606,10 +620,13 @@ impl AudioProcessor for AudioBufferSourceRenderer {
606
620
output_channel. copy_from_slice ( & buffer_channel[ start_index..end_index] ) ;
607
621
} ) ;
608
622
609
- self . render_state . buffer_time += block_duration;
623
+ buffer_time += block_duration;
610
624
}
611
625
612
626
// update render state
627
+ self . render_state
628
+ . buffer_time
629
+ . store ( buffer_time, Ordering :: Relaxed ) ;
613
630
self . render_state . buffer_time_elapsed += block_duration;
614
631
615
632
return true ;
@@ -658,44 +675,38 @@ impl AudioProcessor for AudioBufferSourceRenderer {
658
675
self . offset = actual_loop_start;
659
676
}
660
677
661
- self . render_state . buffer_time = self . offset ;
678
+ buffer_time = self . offset ;
662
679
self . render_state . started = true ;
663
680
}
664
681
665
682
if is_looping {
666
683
if !self . render_state . entered_loop {
667
684
// playback began before or within loop, and playhead is now past loop start
668
- if self . offset < actual_loop_end
669
- && self . render_state . buffer_time >= actual_loop_start
670
- {
685
+ if self . offset < actual_loop_end && buffer_time >= actual_loop_start {
671
686
self . render_state . entered_loop = true ;
672
687
}
673
688
674
689
// playback began after loop, and playhead is now prior to the loop end
675
690
// @note - only possible when playback_rate < 0 (?)
676
- if self . offset >= actual_loop_end
677
- && self . render_state . buffer_time < actual_loop_end
678
- {
691
+ if self . offset >= actual_loop_end && buffer_time < actual_loop_end {
679
692
self . render_state . entered_loop = true ;
680
693
}
681
694
}
682
695
683
696
// check loop boundaries
684
697
if self . render_state . entered_loop {
685
- while self . render_state . buffer_time >= actual_loop_end {
686
- self . render_state . buffer_time -= actual_loop_end - actual_loop_start;
698
+ while buffer_time >= actual_loop_end {
699
+ buffer_time -= actual_loop_end - actual_loop_start;
687
700
}
688
701
689
- while self . render_state . buffer_time < actual_loop_start {
690
- self . render_state . buffer_time += actual_loop_end - actual_loop_start;
702
+ while buffer_time < actual_loop_start {
703
+ buffer_time += actual_loop_end - actual_loop_start;
691
704
}
692
705
}
693
706
}
694
707
695
- if self . render_state . buffer_time >= 0.
696
- && self . render_state . buffer_time < buffer_duration
697
- {
698
- let position = self . render_state . buffer_time * sampling_ratio;
708
+ if buffer_time >= 0. && buffer_time < buffer_duration {
709
+ let position = buffer_time * sampling_ratio;
699
710
let playhead = position * sample_rate;
700
711
let playhead_floored = playhead. floor ( ) ;
701
712
let prev_frame_index = playhead_floored as usize ; // can't be < 0.
@@ -710,7 +721,7 @@ impl AudioProcessor for AudioBufferSourceRenderer {
710
721
}
711
722
712
723
let time_incr = dt * computed_playback_rate;
713
- self . render_state . buffer_time += time_incr;
724
+ buffer_time += time_incr;
714
725
self . render_state . buffer_time_elapsed += time_incr;
715
726
current_time += dt;
716
727
}
@@ -746,6 +757,11 @@ impl AudioProcessor for AudioBufferSourceRenderer {
746
757
} ) ;
747
758
} ) ;
748
759
760
+ // update render state
761
+ self . render_state
762
+ . buffer_time
763
+ . store ( buffer_time, Ordering :: Relaxed ) ;
764
+
749
765
true
750
766
}
751
767
0 commit comments