1
1
//! AudioParam interface
2
2
use std:: any:: Any ;
3
3
use std:: slice:: { Iter , IterMut } ;
4
- use std:: sync:: atomic:: { AtomicBool , Ordering } ;
5
- use std:: sync:: { Arc , OnceLock } ;
4
+ use std:: sync:: atomic:: Ordering ;
5
+ use std:: sync:: { Arc , Mutex , OnceLock } ;
6
6
7
7
use arrayvec:: ArrayVec ;
8
8
@@ -265,18 +265,18 @@ impl AudioParamEventTimeline {
265
265
#[ derive( Clone ) ] // for the node bindings, see #378
266
266
pub struct AudioParam {
267
267
registration : Arc < AudioContextRegistration > ,
268
- raw_parts : AudioParamRaw ,
268
+ raw_parts : AudioParamInner ,
269
269
}
270
270
271
271
// helper struct to attach / detach to context (for borrow reasons)
272
272
#[ derive( Clone ) ]
273
- pub ( crate ) struct AudioParamRaw {
274
- default_value : f32 , // immutable
275
- min_value : f32 , // immutable
276
- max_value : f32 , // immutable
277
- automation_rate_constrained : bool ,
278
- // TODO Use `Weak` instead of ` Arc`. The `AudioParamProcessor` is the owner.
279
- shared_parts : Arc < AudioParamShared > ,
273
+ pub ( crate ) struct AudioParamInner {
274
+ default_value : f32 , // immutable
275
+ min_value : f32 , // immutable
276
+ max_value : f32 , // immutable
277
+ automation_rate_constrained : bool , // effectively immutable
278
+ automation_rate : Arc < Mutex < AutomationRate > > , // shared with clones
279
+ current_value : Arc < AtomicF32 > , // shared with clones and with render thread
280
280
}
281
281
282
282
impl AudioNode for AudioParam {
@@ -317,8 +317,9 @@ impl AudioNode for AudioParam {
317
317
318
318
impl AudioParam {
319
319
/// Current value of the automation rate of the AudioParam
320
+ #[ allow( clippy:: missing_panics_doc) ]
320
321
pub fn automation_rate ( & self ) -> AutomationRate {
321
- self . raw_parts . shared_parts . load_automation_rate ( )
322
+ * self . raw_parts . automation_rate . lock ( ) . unwrap ( )
322
323
}
323
324
324
325
/// Update the current value of the automation rate of the AudioParam
@@ -331,7 +332,11 @@ impl AudioParam {
331
332
panic ! ( "InvalidStateError: automation rate cannot be changed for this param" ) ;
332
333
}
333
334
335
+ let mut guard = self . raw_parts . automation_rate . lock ( ) . unwrap ( ) ;
336
+ * guard = value;
334
337
self . registration ( ) . post_message ( value) ;
338
+ drop ( guard) ; // drop guard after sending message to prevent out of order arrivals on
339
+ // concurrent access
335
340
}
336
341
337
342
pub ( crate ) fn set_automation_rate_constrained ( & mut self , value : bool ) {
@@ -362,7 +367,7 @@ impl AudioParam {
362
367
// test_exponential_ramp_a_rate_multiple_blocks
363
368
// test_exponential_ramp_k_rate_multiple_blocks
364
369
pub fn value ( & self ) -> f32 {
365
- self . raw_parts . shared_parts . load_current_value ( )
370
+ self . raw_parts . current_value . load ( Ordering :: Acquire )
366
371
}
367
372
368
373
/// Set the value of the `AudioParam`.
@@ -384,7 +389,9 @@ impl AudioParam {
384
389
assert_is_finite ( value) ;
385
390
// current_value should always be clamped
386
391
let clamped = value. clamp ( self . raw_parts . min_value , self . raw_parts . max_value ) ;
387
- self . raw_parts . shared_parts . store_current_value ( clamped) ;
392
+ self . raw_parts
393
+ . current_value
394
+ . store ( clamped, Ordering :: Release ) ;
388
395
389
396
// this event is meant to update param intrinsic value before any calculation
390
397
// is done, will behave as SetValueAtTime with `time == block_timestamp`
@@ -610,7 +617,7 @@ impl AudioParam {
610
617
}
611
618
612
619
// helper function to detach from context (for borrow reasons)
613
- pub ( crate ) fn into_raw_parts ( self ) -> AudioParamRaw {
620
+ pub ( crate ) fn into_raw_parts ( self ) -> AudioParamInner {
614
621
let Self {
615
622
registration : _,
616
623
raw_parts,
@@ -621,7 +628,7 @@ impl AudioParam {
621
628
// helper function to attach to context (for borrow reasons)
622
629
pub ( crate ) fn from_raw_parts (
623
630
registration : AudioContextRegistration ,
624
- raw_parts : AudioParamRaw ,
631
+ raw_parts : AudioParamInner ,
625
632
) -> Self {
626
633
Self {
627
634
registration : registration. into ( ) ,
@@ -635,47 +642,6 @@ impl AudioParam {
635
642
}
636
643
}
637
644
638
- // Atomic fields of `AudioParam` that could be safely shared between threads
639
- // when wrapped into an `Arc`.
640
- //
641
- // Uses the canonical ordering for handover of values, i.e. `Acquire` on load
642
- // and `Release` on store.
643
- #[ derive( Debug ) ]
644
- pub ( crate ) struct AudioParamShared {
645
- current_value : AtomicF32 ,
646
- is_a_rate : AtomicBool ,
647
- }
648
-
649
- impl AudioParamShared {
650
- pub ( crate ) fn new ( current_value : f32 , automation_rate : AutomationRate ) -> Self {
651
- Self {
652
- current_value : AtomicF32 :: new ( current_value) ,
653
- is_a_rate : AtomicBool :: new ( automation_rate. is_a_rate ( ) ) ,
654
- }
655
- }
656
-
657
- pub ( crate ) fn load_current_value ( & self ) -> f32 {
658
- self . current_value . load ( Ordering :: Acquire )
659
- }
660
-
661
- pub ( crate ) fn store_current_value ( & self , value : f32 ) {
662
- self . current_value . store ( value, Ordering :: Release ) ;
663
- }
664
-
665
- pub ( crate ) fn load_automation_rate ( & self ) -> AutomationRate {
666
- if self . is_a_rate . load ( Ordering :: Acquire ) {
667
- AutomationRate :: A
668
- } else {
669
- AutomationRate :: K
670
- }
671
- }
672
-
673
- pub ( crate ) fn store_automation_rate ( & self , automation_rate : AutomationRate ) {
674
- self . is_a_rate
675
- . store ( automation_rate. is_a_rate ( ) , Ordering :: Release ) ;
676
- }
677
- }
678
-
679
645
struct BlockInfos {
680
646
block_time : f64 ,
681
647
dt : f64 ,
@@ -691,7 +657,7 @@ pub(crate) struct AudioParamProcessor {
691
657
max_value : f32 , // immutable
692
658
intrinsic_value : f32 ,
693
659
automation_rate : AutomationRate ,
694
- shared_parts : Arc < AudioParamShared > ,
660
+ current_value : Arc < AtomicF32 > ,
695
661
event_timeline : AudioParamEventTimeline ,
696
662
last_event : Option < AudioParamEvent > ,
697
663
buffer : ArrayVec < f32 , RENDER_QUANTUM_SIZE > ,
@@ -719,7 +685,6 @@ impl AudioProcessor for AudioParamProcessor {
719
685
fn onmessage ( & mut self , msg : & mut dyn Any ) {
720
686
if let Some ( automation_rate) = msg. downcast_ref :: < AutomationRate > ( ) {
721
687
self . automation_rate = * automation_rate;
722
- self . shared_parts . store_automation_rate ( * automation_rate) ;
723
688
return ;
724
689
}
725
690
@@ -1509,7 +1474,7 @@ impl AudioParamProcessor {
1509
1474
// Set [[current value]] to the value of paramIntrinsicValue at the
1510
1475
// beginning of this render quantum.
1511
1476
let clamped = self . intrinsic_value . clamp ( self . min_value , self . max_value ) ;
1512
- self . shared_parts . store_current_value ( clamped) ;
1477
+ self . current_value . store ( clamped, Ordering :: Release ) ;
1513
1478
1514
1479
// clear the buffer for this block
1515
1480
self . buffer . clear ( ) ;
@@ -1614,26 +1579,27 @@ pub(crate) fn audio_param_pair(
1614
1579
..
1615
1580
} = descriptor;
1616
1581
1617
- let shared_parts = Arc :: new ( AudioParamShared :: new ( default_value, automation_rate ) ) ;
1582
+ let current_value = Arc :: new ( AtomicF32 :: new ( default_value) ) ;
1618
1583
1619
1584
let param = AudioParam {
1620
1585
registration : registration. into ( ) ,
1621
- raw_parts : AudioParamRaw {
1586
+ raw_parts : AudioParamInner {
1622
1587
default_value,
1623
1588
max_value,
1624
1589
min_value,
1625
1590
automation_rate_constrained : false ,
1626
- shared_parts : Arc :: clone ( & shared_parts) ,
1591
+ automation_rate : Arc :: new ( Mutex :: new ( automation_rate) ) ,
1592
+ current_value : Arc :: clone ( & current_value) ,
1627
1593
} ,
1628
1594
} ;
1629
1595
1630
1596
let processor = AudioParamProcessor {
1631
1597
intrinsic_value : default_value,
1598
+ current_value,
1632
1599
default_value,
1633
1600
min_value,
1634
1601
max_value,
1635
1602
automation_rate,
1636
- shared_parts,
1637
1603
event_timeline : AudioParamEventTimeline :: new ( ) ,
1638
1604
last_event : None ,
1639
1605
buffer : ArrayVec :: new ( ) ,
@@ -1705,6 +1671,52 @@ mod tests {
1705
1671
assert_float_eq ! ( param. value( ) , 0. , abs_all <= 0. ) ;
1706
1672
}
1707
1673
1674
+ #[ test]
1675
+ fn test_automation_rate_synchronicity_on_control_thread ( ) {
1676
+ let context = OfflineAudioContext :: new ( 1 , 0 , 48000. ) ;
1677
+
1678
+ let opts = AudioParamDescriptor {
1679
+ name : String :: new ( ) ,
1680
+ automation_rate : AutomationRate :: A ,
1681
+ default_value : 0. ,
1682
+ min_value : 0. ,
1683
+ max_value : 1. ,
1684
+ } ;
1685
+ let ( param, _render) = audio_param_pair ( opts, context. mock_registration ( ) ) ;
1686
+
1687
+ param. set_automation_rate ( AutomationRate :: K ) ;
1688
+ assert_eq ! ( param. automation_rate( ) , AutomationRate :: K ) ;
1689
+ }
1690
+
1691
+ #[ test]
1692
+ fn test_audioparam_clones_in_sync ( ) {
1693
+ let context = OfflineAudioContext :: new ( 1 , 0 , 48000. ) ;
1694
+
1695
+ let opts = AudioParamDescriptor {
1696
+ name : String :: new ( ) ,
1697
+ automation_rate : AutomationRate :: A ,
1698
+ default_value : 0. ,
1699
+ min_value : -10. ,
1700
+ max_value : 10. ,
1701
+ } ;
1702
+ let ( param1, mut render) = audio_param_pair ( opts, context. mock_registration ( ) ) ;
1703
+ let param2 = param1. clone ( ) ;
1704
+
1705
+ // changing automation rate on param1 should reflect in param2
1706
+ param1. set_automation_rate ( AutomationRate :: K ) ;
1707
+ assert_eq ! ( param2. automation_rate( ) , AutomationRate :: K ) ;
1708
+
1709
+ // setting value on param1 should reflect in param2
1710
+ render. handle_incoming_event ( param1. set_value_raw ( 2. ) ) ;
1711
+ assert_float_eq ! ( param1. value( ) , 2. , abs_all <= 0. ) ;
1712
+ assert_float_eq ! ( param2. value( ) , 2. , abs_all <= 0. ) ;
1713
+
1714
+ // setting value on param2 should reflect in param1
1715
+ render. handle_incoming_event ( param2. set_value_raw ( 3. ) ) ;
1716
+ assert_float_eq ! ( param1. value( ) , 3. , abs_all <= 0. ) ;
1717
+ assert_float_eq ! ( param2. value( ) , 3. , abs_all <= 0. ) ;
1718
+ }
1719
+
1708
1720
#[ test]
1709
1721
fn test_set_value ( ) {
1710
1722
{
0 commit comments