@@ -14,6 +14,12 @@ use super::{
14
14
ChannelConfigOptions , TABLE_LENGTH_USIZE ,
15
15
} ;
16
16
17
+ fn get_fase_incr ( freq : f32 , detune : f32 , sample_rate : f64 ) -> f64 {
18
+ let computed_freq = freq as f64 * ( detune as f64 / 1200. ) . exp2 ( ) ;
19
+ let clamped = computed_freq. clamp ( -sample_rate / 2. , sample_rate / 2. ) ;
20
+ clamped / sample_rate
21
+ }
22
+
17
23
/// Options for constructing an [`OscillatorNode`]
18
24
// dictionary OscillatorOptions : AudioNodeOptions {
19
25
// OscillatorType type = "sine";
@@ -404,52 +410,21 @@ impl AudioProcessor for OscillatorRenderer {
404
410
self . start_time = current_time;
405
411
}
406
412
407
- channel_data
408
- . iter_mut ( )
409
- . zip ( frequency_values. iter ( ) . cycle ( ) )
410
- . zip ( detune_values. iter ( ) . cycle ( ) )
411
- . for_each ( |( ( o, & frequency) , & detune) | {
412
- if current_time < self . start_time || current_time >= self . stop_time {
413
- * o = 0. ;
414
- current_time += dt;
415
-
416
- return ;
417
- }
418
-
419
- // @todo: we could avoid recompute that if both param lengths are 1
420
- let computed_frequency = frequency * ( detune / 1200. ) . exp2 ( ) ;
421
-
422
- // first sample to render
423
- if !self . started {
424
- // if start time was between last frame and current frame
425
- // we need to adjust the phase first
426
- if current_time > self . start_time {
427
- let phase_incr = computed_frequency as f64 / sample_rate;
428
- let ratio = ( current_time - self . start_time ) / dt;
429
- self . phase = Self :: unroll_phase ( phase_incr * ratio) ;
430
- }
431
-
432
- self . started = true ;
433
- }
434
-
435
- let phase_incr = computed_frequency as f64 / sample_rate;
436
-
437
- // @note: per spec all default oscillators should be rendered from a
438
- // wavetable, define if it worth the assle...
439
- // e.g. for now `generate_sine` and `generate_custom` are almost the sames
440
- // cf. https://webaudio.github.io/web-audio-api/#oscillator-coefficients
441
- * o = match self . type_ {
442
- OscillatorType :: Sine => self . generate_sine ( ) ,
443
- OscillatorType :: Sawtooth => self . generate_sawtooth ( phase_incr) ,
444
- OscillatorType :: Square => self . generate_square ( phase_incr) ,
445
- OscillatorType :: Triangle => self . generate_triangle ( ) ,
446
- OscillatorType :: Custom => self . generate_custom ( ) ,
447
- } ;
448
-
449
- current_time += dt;
450
-
451
- self . phase = Self :: unroll_phase ( self . phase + phase_incr) ;
452
- } ) ;
413
+ if frequency_values. len ( ) == 1 && detune_values. len ( ) == 1 {
414
+ let phase_incr = get_fase_incr ( frequency_values[ 0 ] , detune_values[ 0 ] , sample_rate) ;
415
+ channel_data
416
+ . iter_mut ( )
417
+ . for_each ( |output| self . generate_sample ( output, phase_incr, & mut current_time, dt) ) ;
418
+ } else {
419
+ channel_data
420
+ . iter_mut ( )
421
+ . zip ( frequency_values. iter ( ) . cycle ( ) )
422
+ . zip ( detune_values. iter ( ) . cycle ( ) )
423
+ . for_each ( |( ( output, & f) , & d) | {
424
+ let phase_incr = get_fase_incr ( f, d, sample_rate) ;
425
+ self . generate_sample ( output, phase_incr, & mut current_time, dt)
426
+ } ) ;
427
+ }
453
428
454
429
true
455
430
}
@@ -485,6 +460,50 @@ impl AudioProcessor for OscillatorRenderer {
485
460
}
486
461
487
462
impl OscillatorRenderer {
463
+ #[ inline]
464
+ fn generate_sample (
465
+ & mut self ,
466
+ output : & mut f32 ,
467
+ phase_incr : f64 ,
468
+ current_time : & mut f64 ,
469
+ dt : f64 ,
470
+ ) {
471
+ if * current_time < self . start_time || * current_time >= self . stop_time {
472
+ * output = 0. ;
473
+ * current_time += dt;
474
+
475
+ return ;
476
+ }
477
+
478
+ // first sample to render
479
+ if !self . started {
480
+ // if start time was between last frame and current frame
481
+ // we need to adjust the phase first
482
+ if * current_time > self . start_time {
483
+ let ratio = ( * current_time - self . start_time ) / dt;
484
+ self . phase = Self :: unroll_phase ( phase_incr * ratio) ;
485
+ }
486
+
487
+ self . started = true ;
488
+ }
489
+
490
+ // @note: per spec all default oscillators should be rendered from a
491
+ // wavetable, define if it worth the assle...
492
+ // e.g. for now `generate_sine` and `generate_custom` are almost the sames
493
+ // cf. https://webaudio.github.io/web-audio-api/#oscillator-coefficients
494
+ * output = match self . type_ {
495
+ OscillatorType :: Sine => self . generate_sine ( ) ,
496
+ OscillatorType :: Sawtooth => self . generate_sawtooth ( phase_incr) ,
497
+ OscillatorType :: Square => self . generate_square ( phase_incr) ,
498
+ OscillatorType :: Triangle => self . generate_triangle ( ) ,
499
+ OscillatorType :: Custom => self . generate_custom ( ) ,
500
+ } ;
501
+
502
+ * current_time += dt;
503
+
504
+ self . phase = Self :: unroll_phase ( self . phase + phase_incr) ;
505
+ }
506
+
488
507
#[ inline]
489
508
fn generate_sine ( & mut self ) -> f32 {
490
509
let position = self . phase * TABLE_LENGTH_USIZE as f64 ;
0 commit comments