Skip to content

Commit b97cc68

Browse files
authored
Merge pull request #342 from uklotzde/loopstate
Use local loop state in both render and control thread
2 parents 76a8eb4 + 12657fa commit b97cc68

File tree

1 file changed

+62
-94
lines changed

1 file changed

+62
-94
lines changed

src/node/audio_buffer_source.rs

Lines changed: 62 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
use std::any::Any;
2-
use std::cell::OnceCell;
3-
use std::sync::atomic::{AtomicBool, Ordering};
2+
use std::cell::{OnceCell, RefCell};
43

54
use crate::buffer::AudioBuffer;
65
use crate::context::{AudioContextRegistration, AudioParamId, BaseAudioContext};
76
use crate::param::{AudioParam, AudioParamDescriptor, AutomationRate};
87
use crate::render::{AudioParamValues, AudioProcessor, AudioRenderQuantum, RenderScope};
9-
use crate::AtomicF64;
108
use crate::RENDER_QUANTUM_SIZE;
119

1210
use super::{AudioNode, AudioScheduledSourceNode, ChannelConfig};
@@ -49,54 +47,15 @@ struct PlaybackInfo {
4947
k: f32,
5048
}
5149

52-
// The strategy for loop parameters is as follow: store the given values
53-
// in the `loop_control` thread safe instance which only lives in control
54-
// thread(s) and send a message to the render thread which stores the raw values.
55-
// Values between control and render side might be desynchronised for little while
56-
// but the developer experience will appear more logical, i.e.
57-
// ```no_run
58-
// node.set_loop(true);
59-
// println!("{:?}", node.loop_());
60-
// > true // is guaranteed
61-
// ```
62-
// Note that this seems to be the strategy used by Firefox
63-
#[derive(Debug)]
64-
struct LoopControl {
65-
loop_: AtomicBool,
66-
loop_start: AtomicF64,
67-
loop_end: AtomicF64,
68-
}
69-
70-
// Uses the canonical ordering for handover of values,
71-
// i.e. `Acquire` on load and `Release` on store.
72-
impl LoopControl {
73-
fn loop_(&self) -> bool {
74-
self.loop_.load(Ordering::Acquire)
75-
}
76-
77-
fn set_loop(&self, loop_: bool) {
78-
self.loop_.store(loop_, Ordering::Release);
79-
}
80-
81-
fn loop_start(&self) -> f64 {
82-
self.loop_start.load(Ordering::Acquire)
83-
}
84-
85-
fn set_loop_start(&self, loop_start: f64) {
86-
self.loop_start.store(loop_start, Ordering::Release);
87-
}
88-
89-
fn loop_end(&self) -> f64 {
90-
self.loop_end.load(Ordering::Acquire)
91-
}
92-
93-
fn set_loop_end(&self, loop_end: f64) {
94-
self.loop_end.store(loop_end, Ordering::Release);
95-
}
50+
#[derive(Debug, Clone)]
51+
struct LoopState {
52+
pub is_looping: bool,
53+
pub start: f64,
54+
pub end: f64,
9655
}
9756

9857
/// Instructions to start or stop processing
99-
#[derive(Debug, Copy, Clone)]
58+
#[derive(Debug, Clone)]
10059
enum ControlMessage {
10160
StartWithOffsetAndDuration(f64, f64, f64),
10261
Stop(f64),
@@ -139,12 +98,17 @@ enum ControlMessage {
13998
///
14099
pub struct AudioBufferSourceNode {
141100
registration: AudioContextRegistration,
142-
loop_control: LoopControl,
143101
channel_config: ChannelConfig,
144102
detune: AudioParam, // has constraints, no a-rate
145103
playback_rate: AudioParam, // has constraints, no a-rate
146104
buffer: OnceCell<AudioBuffer>,
147-
source_started: AtomicBool,
105+
inner_state: RefCell<InnerState>,
106+
}
107+
108+
#[derive(Debug, Clone)]
109+
struct InnerState {
110+
loop_state: LoopState,
111+
source_started: bool,
148112
}
149113

150114
impl AudioNode for AudioBufferSourceNode {
@@ -181,9 +145,10 @@ impl AudioScheduledSourceNode for AudioBufferSourceNode {
181145
}
182146

183147
fn stop_at(&self, when: f64) {
184-
if !self.source_started.load(Ordering::SeqCst) {
185-
panic!("InvalidStateError cannot stop before start");
186-
}
148+
assert!(
149+
self.inner_state.borrow().source_started,
150+
"InvalidStateError cannot stop before start"
151+
);
187152

188153
self.registration.post_message(ControlMessage::Stop(when));
189154
}
@@ -226,35 +191,36 @@ impl AudioBufferSourceNode {
226191
pr_param.set_automation_rate_constrained(true);
227192
pr_param.set_value(playback_rate);
228193

229-
let loop_control = LoopControl {
230-
loop_: AtomicBool::new(loop_),
231-
loop_start: AtomicF64::new(loop_start),
232-
loop_end: AtomicF64::new(loop_end),
194+
let loop_state = LoopState {
195+
is_looping: loop_,
196+
start: loop_start,
197+
end: loop_end,
233198
};
234199

235200
let renderer = AudioBufferSourceRenderer {
236201
start_time: f64::MAX,
237202
stop_time: f64::MAX,
238203
duration: f64::MAX,
239204
offset: 0.,
240-
loop_,
241-
loop_start,
242-
loop_end,
243205
buffer: None,
244206
detune: d_proc,
245207
playback_rate: pr_proc,
208+
loop_state: loop_state.clone(),
246209
render_state: AudioBufferRendererState::default(),
247210
ended_triggered: false,
248211
};
249212

213+
let inner_state = InnerState {
214+
loop_state,
215+
source_started: false,
216+
};
250217
let node = Self {
251218
registration,
252-
loop_control,
253219
channel_config: ChannelConfig::default(),
254220
detune: d_param,
255221
playback_rate: pr_param,
256222
buffer: OnceCell::new(),
257-
source_started: AtomicBool::new(false),
223+
inner_state: RefCell::new(inner_state),
258224
};
259225

260226
if let Some(buf) = buffer {
@@ -280,9 +246,12 @@ impl AudioBufferSourceNode {
280246
///
281247
/// Panics if the source was already started
282248
pub fn start_at_with_offset_and_duration(&self, start: f64, offset: f64, duration: f64) {
283-
if self.source_started.swap(true, Ordering::SeqCst) {
284-
panic!("InvalidStateError: Cannot call `start` twice");
285-
}
249+
let source_started = &mut self.inner_state.borrow_mut().source_started;
250+
assert!(
251+
!*source_started,
252+
"InvalidStateError: Cannot call `start` twice"
253+
);
254+
*source_started = true;
286255

287256
let control = ControlMessage::StartWithOffsetAndDuration(start, offset, duration);
288257
self.registration.post_message(control);
@@ -329,32 +298,32 @@ impl AudioBufferSourceNode {
329298

330299
/// Defines if the playback the [`AudioBuffer`] should be looped
331300
pub fn loop_(&self) -> bool {
332-
self.loop_control.loop_()
301+
self.inner_state.borrow().loop_state.is_looping
333302
}
334303

335304
pub fn set_loop(&self, value: bool) {
336-
self.loop_control.set_loop(value);
305+
self.inner_state.borrow_mut().loop_state.is_looping = value;
337306
self.registration.post_message(ControlMessage::Loop(value));
338307
}
339308

340309
/// Defines the loop start point, in the time reference of the [`AudioBuffer`]
341310
pub fn loop_start(&self) -> f64 {
342-
self.loop_control.loop_start()
311+
self.inner_state.borrow().loop_state.start
343312
}
344313

345314
pub fn set_loop_start(&self, value: f64) {
346-
self.loop_control.set_loop_start(value);
315+
self.inner_state.borrow_mut().loop_state.start = value;
347316
self.registration
348317
.post_message(ControlMessage::LoopStart(value));
349318
}
350319

351320
/// Defines the loop end point, in the time reference of the [`AudioBuffer`]
352321
pub fn loop_end(&self) -> f64 {
353-
self.loop_control.loop_end()
322+
self.inner_state.borrow().loop_state.end
354323
}
355324

356325
pub fn set_loop_end(&self, value: f64) {
357-
self.loop_control.set_loop_end(value);
326+
self.inner_state.borrow_mut().loop_state.end = value;
358327
self.registration
359328
.post_message(ControlMessage::LoopEnd(value));
360329
}
@@ -385,28 +354,26 @@ struct AudioBufferSourceRenderer {
385354
stop_time: f64,
386355
offset: f64,
387356
duration: f64,
388-
loop_: bool,
389-
loop_start: f64,
390-
loop_end: f64,
391357
buffer: Option<AudioBuffer>,
392358
detune: AudioParamId,
393359
playback_rate: AudioParamId,
360+
loop_state: LoopState,
394361
render_state: AudioBufferRendererState,
395362
ended_triggered: bool,
396363
}
397364

398365
impl AudioBufferSourceRenderer {
399-
fn handle_control_message(&mut self, control: ControlMessage) {
366+
fn handle_control_message(&mut self, control: &ControlMessage) {
400367
match control {
401368
ControlMessage::StartWithOffsetAndDuration(when, offset, duration) => {
402-
self.start_time = when;
403-
self.offset = offset;
404-
self.duration = duration;
369+
self.start_time = *when;
370+
self.offset = *offset;
371+
self.duration = *duration;
405372
}
406-
ControlMessage::Stop(when) => self.stop_time = when,
407-
ControlMessage::Loop(loop_) => self.loop_ = loop_,
408-
ControlMessage::LoopStart(loop_start) => self.loop_start = loop_start,
409-
ControlMessage::LoopEnd(loop_end) => self.loop_end = loop_end,
373+
ControlMessage::Stop(when) => self.stop_time = *when,
374+
ControlMessage::Loop(is_looping) => self.loop_state.is_looping = *is_looping,
375+
ControlMessage::LoopStart(loop_start) => self.loop_state.start = *loop_start,
376+
ControlMessage::LoopEnd(loop_end) => self.loop_state.end = *loop_end,
410377
}
411378
}
412379
}
@@ -427,10 +394,11 @@ impl AudioProcessor for AudioBufferSourceRenderer {
427394
let block_duration = dt * RENDER_QUANTUM_SIZE as f64;
428395
let next_block_time = scope.current_time + block_duration;
429396

430-
// grab all timing information
431-
let loop_ = self.loop_;
432-
let loop_start = self.loop_start;
433-
let loop_end = self.loop_end;
397+
let LoopState {
398+
is_looping,
399+
start: loop_start,
400+
end: loop_end,
401+
} = self.loop_state.clone();
434402

435403
// these will only be used if `loop_` is true, so no need for `Option`
436404
let mut actual_loop_start = 0.;
@@ -485,7 +453,7 @@ impl AudioProcessor for AudioBufferSourceRenderer {
485453
}
486454

487455
// 3. the end of the buffer has been reached.
488-
if !loop_ {
456+
if !is_looping {
489457
if computed_playback_rate > 0. && self.render_state.buffer_time >= buffer_duration {
490458
output.make_silent(); // also converts to mono
491459
if !self.ended_triggered {
@@ -590,15 +558,15 @@ impl AudioProcessor for AudioBufferSourceRenderer {
590558
*o = if buffer_index < end_index {
591559
buffer_channel[buffer_index]
592560
} else {
593-
if loop_ && buffer_index == end_index {
561+
if is_looping && buffer_index == end_index {
594562
loop_point_index = Some(index);
595563
// reset values for the rest of the block
596564
start_index = 0;
597565
offset = index;
598566
buffer_index = 0;
599567
}
600568

601-
if loop_ {
569+
if is_looping {
602570
buffer_channel[buffer_index]
603571
} else {
604572
0.
@@ -639,7 +607,7 @@ impl AudioProcessor for AudioBufferSourceRenderer {
639607
// ---------------------------------------------------------------
640608
// Slow track
641609
// ---------------------------------------------------------------
642-
if loop_ {
610+
if is_looping {
643611
if loop_start >= 0. && loop_end > 0. && loop_start < loop_end {
644612
actual_loop_start = loop_start;
645613
actual_loop_end = loop_end.min(buffer_duration);
@@ -671,19 +639,19 @@ impl AudioProcessor for AudioBufferSourceRenderer {
671639
if !self.render_state.started {
672640
self.offset += current_time - self.start_time;
673641

674-
if loop_ && computed_playback_rate >= 0. && self.offset >= actual_loop_end {
642+
if is_looping && computed_playback_rate >= 0. && self.offset >= actual_loop_end {
675643
self.offset = actual_loop_end;
676644
}
677645

678-
if loop_ && computed_playback_rate < 0. && self.offset < actual_loop_start {
646+
if is_looping && computed_playback_rate < 0. && self.offset < actual_loop_start {
679647
self.offset = actual_loop_start;
680648
}
681649

682650
self.render_state.buffer_time = self.offset;
683651
self.render_state.started = true;
684652
}
685653

686-
if loop_ {
654+
if is_looping {
687655
if !self.render_state.entered_loop {
688656
// playback began before or within loop, and playhead is now past loop start
689657
if self.offset < actual_loop_end
@@ -772,7 +740,7 @@ impl AudioProcessor for AudioBufferSourceRenderer {
772740

773741
fn onmessage(&mut self, msg: &mut dyn Any) {
774742
if let Some(control) = msg.downcast_ref::<ControlMessage>() {
775-
self.handle_control_message(*control);
743+
self.handle_control_message(control);
776744
return;
777745
};
778746

0 commit comments

Comments
 (0)