@@ -14,6 +14,7 @@ use crate::spatial::AudioListenerParams;
14
14
use crate :: AudioListener ;
15
15
16
16
use crossbeam_channel:: { SendError , Sender } ;
17
+ use std:: collections:: HashSet ;
17
18
use std:: sync:: atomic:: { AtomicU64 , AtomicU8 , Ordering } ;
18
19
use std:: sync:: { Arc , Mutex , RwLock , RwLockWriteGuard } ;
19
20
@@ -109,6 +110,8 @@ struct ConcreteBaseAudioContextInner {
109
110
event_loop : EventLoop ,
110
111
/// Sender for events that will be handled by the EventLoop
111
112
event_send : Sender < EventDispatch > ,
113
+ /// Current audio graph connections
114
+ connections : Mutex < HashSet < ( AudioNodeId , usize , AudioNodeId , usize ) > > ,
112
115
}
113
116
114
117
impl BaseAudioContext for ConcreteBaseAudioContext {
@@ -147,6 +150,7 @@ impl ConcreteBaseAudioContext {
147
150
state,
148
151
event_loop,
149
152
event_send,
153
+ connections : Mutex :: new ( HashSet :: new ( ) ) ,
150
154
} ;
151
155
let base = Self {
152
156
inner : Arc :: new ( base_inner) ,
@@ -393,6 +397,11 @@ impl ConcreteBaseAudioContext {
393
397
394
398
/// Connects the output of the `from` audio node to the input of the `to` audio node
395
399
pub ( crate ) fn connect ( & self , from : AudioNodeId , to : AudioNodeId , output : usize , input : usize ) {
400
+ self . inner
401
+ . connections
402
+ . lock ( )
403
+ . unwrap ( )
404
+ . insert ( ( from, output, to, input) ) ;
396
405
let message = ControlMessage :: ConnectNode {
397
406
from,
398
407
to,
@@ -406,6 +415,8 @@ impl ConcreteBaseAudioContext {
406
415
///
407
416
/// It is not performed immediately as the `AudioNode` is not registered at this point.
408
417
pub ( super ) fn queue_audio_param_connect ( & self , param : & AudioParam , audio_node : AudioNodeId ) {
418
+ // no need to store these type of connections in self.inner.connections
419
+
409
420
let message = ControlMessage :: ConnectNode {
410
421
from : param. registration ( ) . id ( ) ,
411
422
to : audio_node,
@@ -416,13 +427,32 @@ impl ConcreteBaseAudioContext {
416
427
}
417
428
418
429
/// Disconnects all outputs of the audio node that go to a specific destination node.
430
+ ///
431
+ /// # Panics
432
+ ///
433
+ /// Panics if this node was not connected to the target node
419
434
pub ( crate ) fn disconnect_from ( & self , from : AudioNodeId , to : AudioNodeId ) {
435
+ // check if the node was connected, otherwise panic
436
+ let mut connections = self . inner . connections . lock ( ) . unwrap ( ) ;
437
+ let prev_len = connections. len ( ) ;
438
+ connections. retain ( |c| c. 0 != from || c. 2 != to) ;
439
+ let len = connections. len ( ) ;
440
+ drop ( connections) ;
441
+ if prev_len == len {
442
+ panic ! ( "InvalidAccessError - attempting to disconnect unconnected nodes" ) ;
443
+ }
444
+
420
445
let message = ControlMessage :: DisconnectNode { from, to } ;
421
446
self . send_control_msg ( message) ;
422
447
}
423
448
424
449
/// Disconnects all outgoing connections from the audio node.
425
450
pub ( crate ) fn disconnect ( & self , from : AudioNodeId ) {
451
+ self . inner
452
+ . connections
453
+ . lock ( )
454
+ . unwrap ( )
455
+ . retain ( |c| c. 0 != from) ;
426
456
let message = ControlMessage :: DisconnectAll { from } ;
427
457
self . send_control_msg ( message) ;
428
458
}
@@ -470,6 +500,7 @@ impl ConcreteBaseAudioContext {
470
500
#[ cfg( test) ]
471
501
mod tests {
472
502
use super :: * ;
503
+ use crate :: context:: OfflineAudioContext ;
473
504
474
505
#[ test]
475
506
fn test_provide_node_id ( ) {
@@ -481,4 +512,29 @@ mod tests {
481
512
assert_eq ! ( provider. get( ) . 0 , 0 ) ; // reused
482
513
assert_eq ! ( provider. get( ) . 0 , 2 ) ; // newly assigned
483
514
}
515
+
516
+ #[ test]
517
+ fn test_connect_disconnect ( ) {
518
+ let context = OfflineAudioContext :: new ( 1 , 128 , 48000. ) ;
519
+ let node1 = context. create_constant_source ( ) ;
520
+ let node2 = context. create_gain ( ) ;
521
+
522
+ node1. disconnect ( ) ; // never panic for plain disconnect calls
523
+
524
+ node1. connect ( & node2) ;
525
+ node1. disconnect ( ) ;
526
+
527
+ node1. connect ( & node2) ;
528
+ node1. disconnect_from ( & node2) ;
529
+ }
530
+
531
+ #[ test]
532
+ #[ should_panic]
533
+ fn test_disconnect_not_existing ( ) {
534
+ let context = OfflineAudioContext :: new ( 1 , 128 , 48000. ) ;
535
+ let node1 = context. create_constant_source ( ) ;
536
+ let node2 = context. create_gain ( ) ;
537
+
538
+ node1. disconnect_from ( & node2) ;
539
+ }
484
540
}
0 commit comments