@@ -457,6 +457,13 @@ impl AudioProcessor for OscillatorRenderer {
457
457
458
458
log:: warn!( "OscillatorRenderer: Dropping incoming message {msg:?}" ) ;
459
459
}
460
+
461
+ fn before_drop ( & mut self , scope : & AudioWorkletGlobalScope ) {
462
+ if !self . ended_triggered && scope. current_time >= self . start_time {
463
+ scope. send_ended_event ( ) ;
464
+ self . ended_triggered = true ;
465
+ }
466
+ }
460
467
}
461
468
462
469
impl OscillatorRenderer {
@@ -608,6 +615,9 @@ mod tests {
608
615
use float_eq:: assert_float_eq;
609
616
use std:: f64:: consts:: PI ;
610
617
618
+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
619
+ use std:: sync:: Arc ;
620
+
611
621
use crate :: context:: { BaseAudioContext , OfflineAudioContext } ;
612
622
use crate :: node:: { AudioNode , AudioScheduledSourceNode } ;
613
623
use crate :: periodic_wave:: { PeriodicWave , PeriodicWaveOptions } ;
@@ -1247,4 +1257,72 @@ mod tests {
1247
1257
osc. stop ( ) ;
1248
1258
osc. stop ( ) ;
1249
1259
}
1260
+
1261
+ #[ test]
1262
+ fn test_ended_event ( ) {
1263
+ let mut context = OfflineAudioContext :: new ( 2 , 44_100 , 44_100. ) ;
1264
+ let mut src = context. create_oscillator ( ) ;
1265
+ src. start_at ( 0. ) ;
1266
+ src. stop_at ( 0.5 ) ;
1267
+
1268
+ let ended = Arc :: new ( AtomicBool :: new ( false ) ) ;
1269
+ let ended_clone = Arc :: clone ( & ended) ;
1270
+ src. set_onended ( move |_event| {
1271
+ ended_clone. store ( true , Ordering :: Relaxed ) ;
1272
+ } ) ;
1273
+
1274
+ let _ = context. start_rendering_sync ( ) ;
1275
+ assert ! ( ended. load( Ordering :: Relaxed ) ) ;
1276
+ }
1277
+
1278
+ #[ test]
1279
+ fn test_no_ended_event ( ) {
1280
+ let mut context = OfflineAudioContext :: new ( 2 , 44_100 , 44_100. ) ;
1281
+ let src = context. create_oscillator ( ) ;
1282
+
1283
+ // do not start the node
1284
+
1285
+ let ended = Arc :: new ( AtomicBool :: new ( false ) ) ;
1286
+ let ended_clone = Arc :: clone ( & ended) ;
1287
+ src. set_onended ( move |_event| {
1288
+ ended_clone. store ( true , Ordering :: Relaxed ) ;
1289
+ } ) ;
1290
+
1291
+ let _ = context. start_rendering_sync ( ) ;
1292
+ assert ! ( !ended. load( Ordering :: Relaxed ) ) ; // should not have triggered
1293
+ }
1294
+
1295
+ #[ test]
1296
+ fn test_exact_ended_event ( ) {
1297
+ let mut context = OfflineAudioContext :: new ( 2 , 44_100 , 44_100. ) ;
1298
+ let mut src = context. create_oscillator ( ) ;
1299
+ src. start_at ( 0. ) ;
1300
+ src. stop_at ( 1. ) ; // end right at the end of the offline buffer
1301
+
1302
+ let ended = Arc :: new ( AtomicBool :: new ( false ) ) ;
1303
+ let ended_clone = Arc :: clone ( & ended) ;
1304
+ src. set_onended ( move |_event| {
1305
+ ended_clone. store ( true , Ordering :: Relaxed ) ;
1306
+ } ) ;
1307
+
1308
+ let _ = context. start_rendering_sync ( ) ;
1309
+ assert ! ( ended. load( Ordering :: Relaxed ) ) ;
1310
+ }
1311
+
1312
+ #[ test]
1313
+ fn test_implicit_ended_event ( ) {
1314
+ let mut context = OfflineAudioContext :: new ( 2 , 44_100 , 44_100. ) ;
1315
+ let mut src = context. create_oscillator ( ) ;
1316
+ src. start_at ( 0. ) ;
1317
+ // no explicit stop, so we stop at end of offline context
1318
+
1319
+ let ended = Arc :: new ( AtomicBool :: new ( false ) ) ;
1320
+ let ended_clone = Arc :: clone ( & ended) ;
1321
+ src. set_onended ( move |_event| {
1322
+ ended_clone. store ( true , Ordering :: Relaxed ) ;
1323
+ } ) ;
1324
+
1325
+ let _ = context. start_rendering_sync ( ) ;
1326
+ assert ! ( ended. load( Ordering :: Relaxed ) ) ;
1327
+ }
1250
1328
}
0 commit comments