Skip to content

Commit 2a7ebda

Browse files
committed
AudioBufferSourceNode: Expose current playhead position
1 parent 0f12973 commit 2a7ebda

File tree

1 file changed

+53
-36
lines changed

1 file changed

+53
-36
lines changed

src/node/audio_buffer_source.rs

Lines changed: 53 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use std::any::Any;
2-
use std::sync::{Mutex, OnceLock};
2+
use std::sync::atomic::Ordering;
3+
use std::sync::{Arc, Mutex, OnceLock};
34

45
use crate::buffer::AudioBuffer;
56
use crate::context::{AudioContextRegistration, AudioParamId, BaseAudioContext};
67
use crate::param::{AudioParam, AudioParamDescriptor, AutomationRate};
78
use crate::render::{AudioParamValues, AudioProcessor, AudioRenderQuantum, RenderScope};
8-
use crate::RENDER_QUANTUM_SIZE;
9+
use crate::{AtomicF64, RENDER_QUANTUM_SIZE};
910

1011
use super::{AudioNode, AudioScheduledSourceNode, ChannelConfig};
1112

@@ -106,6 +107,7 @@ pub struct AudioBufferSourceNode {
106107
channel_config: ChannelConfig,
107108
detune: AudioParam, // has constraints, no a-rate
108109
playback_rate: AudioParam, // has constraints, no a-rate
110+
buffer_time: Arc<AtomicF64>,
109111
buffer: OnceLock<AudioBuffer>,
110112
inner_state: Mutex<InnerState>,
111113
}
@@ -224,6 +226,7 @@ impl AudioBufferSourceNode {
224226
channel_config: ChannelConfig::default(),
225227
detune: d_param,
226228
playback_rate: pr_param,
229+
buffer_time: Arc::clone(&renderer.render_state.buffer_time),
227230
buffer: OnceLock::new(),
228231
inner_state: Mutex::new(inner_state),
229232
};
@@ -293,6 +296,16 @@ impl AudioBufferSourceNode {
293296
&self.playback_rate
294297
}
295298

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+
296309
/// K-rate [`AudioParam`] that defines a pitch transposition of the file,
297310
/// expressed in cents
298311
///
@@ -341,7 +354,7 @@ impl AudioBufferSourceNode {
341354
}
342355

343356
struct AudioBufferRendererState {
344-
buffer_time: f64,
357+
buffer_time: Arc<AtomicF64>,
345358
started: bool,
346359
entered_loop: bool,
347360
buffer_time_elapsed: f64,
@@ -351,7 +364,7 @@ struct AudioBufferRendererState {
351364
impl Default for AudioBufferRendererState {
352365
fn default() -> Self {
353366
Self {
354-
buffer_time: 0.,
367+
buffer_time: Arc::new(AtomicF64::new(0.)),
355368
started: false,
356369
entered_loop: false,
357370
buffer_time_elapsed: 0.,
@@ -463,9 +476,13 @@ impl AudioProcessor for AudioBufferSourceRenderer {
463476
return false;
464477
}
465478

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+
466483
// 3. the end of the buffer has been reached.
467484
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 {
469486
output.make_silent(); // also converts to mono
470487
if !self.ended_triggered {
471488
scope.send_ended_event();
@@ -474,7 +491,7 @@ impl AudioProcessor for AudioBufferSourceRenderer {
474491
return false;
475492
}
476493

477-
if computed_playback_rate < 0. && self.render_state.buffer_time < 0. {
494+
if computed_playback_rate < 0. && buffer_time < 0. {
478495
output.make_silent(); // also converts to mono
479496
if !self.ended_triggered {
480497
scope.send_ended_event();
@@ -533,22 +550,21 @@ impl AudioProcessor for AudioBufferSourceRenderer {
533550
}
534551

535552
// 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
538555
|| current_time + block_duration > self.stop_time
539556
{
540557
let end_index = if current_time + block_duration > self.stop_time
541-
|| self.render_state.buffer_time + block_duration > self.duration
558+
|| buffer_time + block_duration > self.duration
542559
{
543-
let dt = (self.stop_time - current_time)
544-
.min(self.duration - self.render_state.buffer_time);
545-
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;
546562
(end_buffer_time * sample_rate).round() as usize
547563
} else {
548564
buffer.length()
549565
};
550566
// in case of a loop point in the middle of the block, this value
551-
// will be used to recompute `self.render_state.buffer_time` according
567+
// will be used to recompute `buffer_time` according
552568
// to the actual loop point.
553569
let mut loop_point_index: Option<usize> = None;
554570

@@ -559,7 +575,7 @@ impl AudioProcessor for AudioBufferSourceRenderer {
559575
.for_each(|(buffer_channel, output_channel)| {
560576
// we need to recompute that for each channel
561577
let buffer_channel = buffer_channel.as_slice();
562-
let mut start_index = (self.render_state.buffer_time * sample_rate).round() as usize;
578+
let mut start_index = (buffer_time * sample_rate).round() as usize;
563579
let mut offset = 0;
564580

565581
for (index, o) in output_channel.iter_mut().enumerate() {
@@ -586,14 +602,13 @@ impl AudioProcessor for AudioBufferSourceRenderer {
586602
});
587603

588604
if let Some(loop_point_index) = loop_point_index {
589-
self.render_state.buffer_time =
590-
((RENDER_QUANTUM_SIZE - loop_point_index) as f64 / sample_rate)
591-
% buffer_duration;
605+
buffer_time = ((RENDER_QUANTUM_SIZE - loop_point_index) as f64 / sample_rate)
606+
% buffer_duration;
592607
} else {
593-
self.render_state.buffer_time += block_duration;
608+
buffer_time += block_duration;
594609
}
595610
} else {
596-
let start_index = (self.render_state.buffer_time * sample_rate).round() as usize;
611+
let start_index = (buffer_time * sample_rate).round() as usize;
597612
let end_index = start_index + RENDER_QUANTUM_SIZE;
598613
// we can do memcopy
599614
buffer
@@ -605,10 +620,13 @@ impl AudioProcessor for AudioBufferSourceRenderer {
605620
output_channel.copy_from_slice(&buffer_channel[start_index..end_index]);
606621
});
607622

608-
self.render_state.buffer_time += block_duration;
623+
buffer_time += block_duration;
609624
}
610625

611626
// update render state
627+
self.render_state
628+
.buffer_time
629+
.store(buffer_time, Ordering::Relaxed);
612630
self.render_state.buffer_time_elapsed += block_duration;
613631

614632
return true;
@@ -657,44 +675,38 @@ impl AudioProcessor for AudioBufferSourceRenderer {
657675
self.offset = actual_loop_start;
658676
}
659677

660-
self.render_state.buffer_time = self.offset;
678+
buffer_time = self.offset;
661679
self.render_state.started = true;
662680
}
663681

664682
if is_looping {
665683
if !self.render_state.entered_loop {
666684
// playback began before or within loop, and playhead is now past loop start
667-
if self.offset < actual_loop_end
668-
&& self.render_state.buffer_time >= actual_loop_start
669-
{
685+
if self.offset < actual_loop_end && buffer_time >= actual_loop_start {
670686
self.render_state.entered_loop = true;
671687
}
672688

673689
// playback began after loop, and playhead is now prior to the loop end
674690
// @note - only possible when playback_rate < 0 (?)
675-
if self.offset >= actual_loop_end
676-
&& self.render_state.buffer_time < actual_loop_end
677-
{
691+
if self.offset >= actual_loop_end && buffer_time < actual_loop_end {
678692
self.render_state.entered_loop = true;
679693
}
680694
}
681695

682696
// check loop boundaries
683697
if self.render_state.entered_loop {
684-
while self.render_state.buffer_time >= actual_loop_end {
685-
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;
686700
}
687701

688-
while self.render_state.buffer_time < actual_loop_start {
689-
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;
690704
}
691705
}
692706
}
693707

694-
if self.render_state.buffer_time >= 0.
695-
&& self.render_state.buffer_time < buffer_duration
696-
{
697-
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;
698710
let playhead = position * sample_rate;
699711
let playhead_floored = playhead.floor();
700712
let prev_frame_index = playhead_floored as usize; // can't be < 0.
@@ -709,7 +721,7 @@ impl AudioProcessor for AudioBufferSourceRenderer {
709721
}
710722

711723
let time_incr = dt * computed_playback_rate;
712-
self.render_state.buffer_time += time_incr;
724+
buffer_time += time_incr;
713725
self.render_state.buffer_time_elapsed += time_incr;
714726
current_time += dt;
715727
}
@@ -745,6 +757,11 @@ impl AudioProcessor for AudioBufferSourceRenderer {
745757
});
746758
});
747759

760+
// update render state
761+
self.render_state
762+
.buffer_time
763+
.store(buffer_time, Ordering::Relaxed);
764+
748765
true
749766
}
750767

0 commit comments

Comments
 (0)