1
1
use std:: any:: Any ;
2
- use std:: cell:: { OnceCell , RefCell } ;
2
+ use std:: cell:: OnceCell ;
3
+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
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 } ;
9
+ use crate :: AtomicF64 ;
8
10
use crate :: RENDER_QUANTUM_SIZE ;
9
11
10
12
use super :: { AudioNode , AudioScheduledSourceNode , ChannelConfig } ;
@@ -18,6 +20,10 @@ use super::{AudioNode, AudioScheduledSourceNode, ChannelConfig};
18
20
// double loopStart = 0;
19
21
// float playbackRate = 1;
20
22
// };
23
+ //
24
+ // @note - Does not extend AudioNodeOptions because AudioNodeOptions are
25
+ // useless for source nodes as they instruct how to upmix the inputs.
26
+ // This is a common source of confusion, see e.g. https://github.com/mdn/content/pull/18472
21
27
#[ derive( Clone , Debug ) ]
22
28
pub struct AudioBufferSourceOptions {
23
29
pub buffer : Option < AudioBuffer > ,
@@ -47,15 +53,54 @@ struct PlaybackInfo {
47
53
k : f32 ,
48
54
}
49
55
50
- #[ derive( Debug , Clone ) ]
51
- struct LoopState {
52
- pub is_looping : bool ,
53
- pub start : f64 ,
54
- pub end : f64 ,
56
+ // The strategy for loop parameters is as follow: store the given values
57
+ // in the `loop_control` thread safe instance which only lives in control
58
+ // thread(s) and send a message to the render thread which stores the raw values.
59
+ // Values between control and render side might be desynchronised for little while
60
+ // but the developer experience will appear more logical, i.e.
61
+ // ```no_run
62
+ // node.set_loop(true);
63
+ // println!("{:?}", node.loop_());
64
+ // > true // is guaranteed
65
+ // ```
66
+ // Note that this seems to be the strategy used by Firefox
67
+ #[ derive( Debug ) ]
68
+ struct LoopControl {
69
+ loop_ : AtomicBool ,
70
+ loop_start : AtomicF64 ,
71
+ loop_end : AtomicF64 ,
72
+ }
73
+
74
+ // Uses the canonical ordering for handover of values,
75
+ // i.e. `Acquire` on load and `Release` on store.
76
+ impl LoopControl {
77
+ fn loop_ ( & self ) -> bool {
78
+ self . loop_ . load ( Ordering :: Acquire )
79
+ }
80
+
81
+ fn set_loop ( & self , loop_ : bool ) {
82
+ self . loop_ . store ( loop_, Ordering :: Release ) ;
83
+ }
84
+
85
+ fn loop_start ( & self ) -> f64 {
86
+ self . loop_start . load ( Ordering :: Acquire )
87
+ }
88
+
89
+ fn set_loop_start ( & self , loop_start : f64 ) {
90
+ self . loop_start . store ( loop_start, Ordering :: Release ) ;
91
+ }
92
+
93
+ fn loop_end ( & self ) -> f64 {
94
+ self . loop_end . load ( Ordering :: Acquire )
95
+ }
96
+
97
+ fn set_loop_end ( & self , loop_end : f64 ) {
98
+ self . loop_end . store ( loop_end, Ordering :: Release ) ;
99
+ }
55
100
}
56
101
57
102
/// Instructions to start or stop processing
58
- #[ derive( Debug , Clone ) ]
103
+ #[ derive( Debug , Copy , Clone ) ]
59
104
enum ControlMessage {
60
105
StartWithOffsetAndDuration ( f64 , f64 , f64 ) ,
61
106
Stop ( f64 ) ,
@@ -98,17 +143,12 @@ enum ControlMessage {
98
143
///
99
144
pub struct AudioBufferSourceNode {
100
145
registration : AudioContextRegistration ,
146
+ loop_control : LoopControl ,
101
147
channel_config : ChannelConfig ,
102
148
detune : AudioParam , // has constraints, no a-rate
103
149
playback_rate : AudioParam , // has constraints, no a-rate
104
150
buffer : OnceCell < AudioBuffer > ,
105
- inner_state : RefCell < InnerState > ,
106
- }
107
-
108
- #[ derive( Debug , Clone ) ]
109
- struct InnerState {
110
- loop_state : LoopState ,
111
- source_started : bool ,
151
+ source_started : AtomicBool ,
112
152
}
113
153
114
154
impl AudioNode for AudioBufferSourceNode {
@@ -145,10 +185,9 @@ impl AudioScheduledSourceNode for AudioBufferSourceNode {
145
185
}
146
186
147
187
fn stop_at ( & self , when : f64 ) {
148
- assert ! (
149
- self . inner_state. borrow( ) . source_started,
150
- "InvalidStateError cannot stop before start"
151
- ) ;
188
+ if !self . source_started . load ( Ordering :: SeqCst ) {
189
+ panic ! ( "InvalidStateError cannot stop before start" ) ;
190
+ }
152
191
153
192
self . registration . post_message ( ControlMessage :: Stop ( when) ) ;
154
193
}
@@ -191,36 +230,35 @@ impl AudioBufferSourceNode {
191
230
pr_param. set_automation_rate_constrained ( true ) ;
192
231
pr_param. set_value ( playback_rate) ;
193
232
194
- let loop_state = LoopState {
195
- is_looping : loop_,
196
- start : loop_start,
197
- end : loop_end,
233
+ let loop_control = LoopControl {
234
+ loop_ : AtomicBool :: new ( loop_) ,
235
+ loop_start : AtomicF64 :: new ( loop_start) ,
236
+ loop_end : AtomicF64 :: new ( loop_end) ,
198
237
} ;
199
238
200
239
let renderer = AudioBufferSourceRenderer {
201
240
start_time : f64:: MAX ,
202
241
stop_time : f64:: MAX ,
203
242
duration : f64:: MAX ,
204
243
offset : 0. ,
244
+ loop_,
245
+ loop_start,
246
+ loop_end,
205
247
buffer : None ,
206
248
detune : d_proc,
207
249
playback_rate : pr_proc,
208
- loop_state : loop_state. clone ( ) ,
209
250
render_state : AudioBufferRendererState :: default ( ) ,
210
251
ended_triggered : false ,
211
252
} ;
212
253
213
- let inner_state = InnerState {
214
- loop_state,
215
- source_started : false ,
216
- } ;
217
254
let node = Self {
218
255
registration,
256
+ loop_control,
219
257
channel_config : ChannelConfig :: default ( ) ,
220
258
detune : d_param,
221
259
playback_rate : pr_param,
222
260
buffer : OnceCell :: new ( ) ,
223
- inner_state : RefCell :: new ( inner_state ) ,
261
+ source_started : AtomicBool :: new ( false ) ,
224
262
} ;
225
263
226
264
if let Some ( buf) = buffer {
@@ -246,12 +284,9 @@ impl AudioBufferSourceNode {
246
284
///
247
285
/// Panics if the source was already started
248
286
pub fn start_at_with_offset_and_duration ( & self , start : f64 , offset : f64 , duration : f64 ) {
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 ;
287
+ if self . source_started . swap ( true , Ordering :: SeqCst ) {
288
+ panic ! ( "InvalidStateError: Cannot call `start` twice" ) ;
289
+ }
255
290
256
291
let control = ControlMessage :: StartWithOffsetAndDuration ( start, offset, duration) ;
257
292
self . registration . post_message ( control) ;
@@ -298,32 +333,32 @@ impl AudioBufferSourceNode {
298
333
299
334
/// Defines if the playback the [`AudioBuffer`] should be looped
300
335
pub fn loop_ ( & self ) -> bool {
301
- self . inner_state . borrow ( ) . loop_state . is_looping
336
+ self . loop_control . loop_ ( )
302
337
}
303
338
304
339
pub fn set_loop ( & self , value : bool ) {
305
- self . inner_state . borrow_mut ( ) . loop_state . is_looping = value;
340
+ self . loop_control . set_loop ( value) ;
306
341
self . registration . post_message ( ControlMessage :: Loop ( value) ) ;
307
342
}
308
343
309
344
/// Defines the loop start point, in the time reference of the [`AudioBuffer`]
310
345
pub fn loop_start ( & self ) -> f64 {
311
- self . inner_state . borrow ( ) . loop_state . start
346
+ self . loop_control . loop_start ( )
312
347
}
313
348
314
349
pub fn set_loop_start ( & self , value : f64 ) {
315
- self . inner_state . borrow_mut ( ) . loop_state . start = value;
350
+ self . loop_control . set_loop_start ( value) ;
316
351
self . registration
317
352
. post_message ( ControlMessage :: LoopStart ( value) ) ;
318
353
}
319
354
320
355
/// Defines the loop end point, in the time reference of the [`AudioBuffer`]
321
356
pub fn loop_end ( & self ) -> f64 {
322
- self . inner_state . borrow ( ) . loop_state . end
357
+ self . loop_control . loop_end ( )
323
358
}
324
359
325
360
pub fn set_loop_end ( & self , value : f64 ) {
326
- self . inner_state . borrow_mut ( ) . loop_state . end = value;
361
+ self . loop_control . set_loop_end ( value) ;
327
362
self . registration
328
363
. post_message ( ControlMessage :: LoopEnd ( value) ) ;
329
364
}
@@ -354,26 +389,28 @@ struct AudioBufferSourceRenderer {
354
389
stop_time : f64 ,
355
390
offset : f64 ,
356
391
duration : f64 ,
392
+ loop_ : bool ,
393
+ loop_start : f64 ,
394
+ loop_end : f64 ,
357
395
buffer : Option < AudioBuffer > ,
358
396
detune : AudioParamId ,
359
397
playback_rate : AudioParamId ,
360
- loop_state : LoopState ,
361
398
render_state : AudioBufferRendererState ,
362
399
ended_triggered : bool ,
363
400
}
364
401
365
402
impl AudioBufferSourceRenderer {
366
- fn handle_control_message ( & mut self , control : & ControlMessage ) {
403
+ fn handle_control_message ( & mut self , control : ControlMessage ) {
367
404
match control {
368
405
ControlMessage :: StartWithOffsetAndDuration ( when, offset, duration) => {
369
- self . start_time = * when;
370
- self . offset = * offset;
371
- self . duration = * duration;
406
+ self . start_time = when;
407
+ self . offset = offset;
408
+ self . duration = duration;
372
409
}
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,
410
+ ControlMessage :: Stop ( when) => self . stop_time = when,
411
+ ControlMessage :: Loop ( loop_ ) => self . loop_ = loop_ ,
412
+ ControlMessage :: LoopStart ( loop_start) => self . loop_start = loop_start,
413
+ ControlMessage :: LoopEnd ( loop_end) => self . loop_end = loop_end,
377
414
}
378
415
}
379
416
}
@@ -394,11 +431,10 @@ impl AudioProcessor for AudioBufferSourceRenderer {
394
431
let block_duration = dt * RENDER_QUANTUM_SIZE as f64 ;
395
432
let next_block_time = scope. current_time + block_duration;
396
433
397
- let LoopState {
398
- is_looping,
399
- start : loop_start,
400
- end : loop_end,
401
- } = self . loop_state . clone ( ) ;
434
+ // grab all timing information
435
+ let loop_ = self . loop_ ;
436
+ let loop_start = self . loop_start ;
437
+ let loop_end = self . loop_end ;
402
438
403
439
// these will only be used if `loop_` is true, so no need for `Option`
404
440
let mut actual_loop_start = 0. ;
@@ -453,7 +489,7 @@ impl AudioProcessor for AudioBufferSourceRenderer {
453
489
}
454
490
455
491
// 3. the end of the buffer has been reached.
456
- if !is_looping {
492
+ if !loop_ {
457
493
if computed_playback_rate > 0. && self . render_state . buffer_time >= buffer_duration {
458
494
output. make_silent ( ) ; // also converts to mono
459
495
if !self . ended_triggered {
@@ -558,15 +594,15 @@ impl AudioProcessor for AudioBufferSourceRenderer {
558
594
* o = if buffer_index < end_index {
559
595
buffer_channel[ buffer_index]
560
596
} else {
561
- if is_looping && buffer_index == end_index {
597
+ if loop_ && buffer_index == end_index {
562
598
loop_point_index = Some ( index) ;
563
599
// reset values for the rest of the block
564
600
start_index = 0 ;
565
601
offset = index;
566
602
buffer_index = 0 ;
567
603
}
568
604
569
- if is_looping {
605
+ if loop_ {
570
606
buffer_channel[ buffer_index]
571
607
} else {
572
608
0.
@@ -607,7 +643,7 @@ impl AudioProcessor for AudioBufferSourceRenderer {
607
643
// ---------------------------------------------------------------
608
644
// Slow track
609
645
// ---------------------------------------------------------------
610
- if is_looping {
646
+ if loop_ {
611
647
if loop_start >= 0. && loop_end > 0. && loop_start < loop_end {
612
648
actual_loop_start = loop_start;
613
649
actual_loop_end = loop_end. min ( buffer_duration) ;
@@ -639,19 +675,19 @@ impl AudioProcessor for AudioBufferSourceRenderer {
639
675
if !self . render_state . started {
640
676
self . offset += current_time - self . start_time ;
641
677
642
- if is_looping && computed_playback_rate >= 0. && self . offset >= actual_loop_end {
678
+ if loop_ && computed_playback_rate >= 0. && self . offset >= actual_loop_end {
643
679
self . offset = actual_loop_end;
644
680
}
645
681
646
- if is_looping && computed_playback_rate < 0. && self . offset < actual_loop_start {
682
+ if loop_ && computed_playback_rate < 0. && self . offset < actual_loop_start {
647
683
self . offset = actual_loop_start;
648
684
}
649
685
650
686
self . render_state . buffer_time = self . offset ;
651
687
self . render_state . started = true ;
652
688
}
653
689
654
- if is_looping {
690
+ if loop_ {
655
691
if !self . render_state . entered_loop {
656
692
// playback began before or within loop, and playhead is now past loop start
657
693
if self . offset < actual_loop_end
@@ -740,7 +776,7 @@ impl AudioProcessor for AudioBufferSourceRenderer {
740
776
741
777
fn onmessage ( & mut self , msg : & mut dyn Any ) {
742
778
if let Some ( control) = msg. downcast_ref :: < ControlMessage > ( ) {
743
- self . handle_control_message ( control) ;
779
+ self . handle_control_message ( * control) ;
744
780
return ;
745
781
} ;
746
782
0 commit comments