@@ -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,55 +410,21 @@ impl AudioProcessor for OscillatorRenderer {
404
410
self . start_time = current_time;
405
411
}
406
412
407
- let nyquist = scope. sample_rate / 2. ;
408
-
409
- channel_data
410
- . iter_mut ( )
411
- . zip ( frequency_values. iter ( ) . cycle ( ) )
412
- . zip ( detune_values. iter ( ) . cycle ( ) )
413
- . for_each ( |( ( o, & frequency) , & detune) | {
414
- if current_time < self . start_time || current_time >= self . stop_time {
415
- * o = 0. ;
416
- current_time += dt;
417
-
418
- return ;
419
- }
420
-
421
- // @todo: we could avoid recompute that if both param lengths are 1
422
- let computed_frequency = frequency * ( detune / 1200. ) . exp2 ( ) ;
423
- let computed_frequency = computed_frequency. clamp ( -nyquist, nyquist) ;
424
-
425
- // first sample to render
426
- if !self . started {
427
- // if start time was between last frame and current frame
428
- // we need to adjust the phase first
429
- if current_time > self . start_time {
430
- let phase_incr = computed_frequency as f64 / sample_rate;
431
- let ratio = ( current_time - self . start_time ) / dt;
432
- self . phase = Self :: unroll_phase ( phase_incr * ratio) ;
433
- }
434
-
435
- self . started = true ;
436
- }
437
-
438
- let phase_incr = computed_frequency as f64 / sample_rate;
439
-
440
- // @note: per spec all default oscillators should be rendered from a
441
- // wavetable, define if it worth the assle...
442
- // e.g. for now `generate_sine` and `generate_custom` are almost the sames
443
- // cf. https://webaudio.github.io/web-audio-api/#oscillator-coefficients
444
- * o = match self . type_ {
445
- OscillatorType :: Sine => self . generate_sine ( ) ,
446
- OscillatorType :: Sawtooth => self . generate_sawtooth ( phase_incr) ,
447
- OscillatorType :: Square => self . generate_square ( phase_incr) ,
448
- OscillatorType :: Triangle => self . generate_triangle ( ) ,
449
- OscillatorType :: Custom => self . generate_custom ( ) ,
450
- } ;
451
-
452
- current_time += dt;
453
-
454
- self . phase = Self :: unroll_phase ( self . phase + phase_incr) ;
455
- } ) ;
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
+ }
456
428
457
429
true
458
430
}
@@ -488,6 +460,50 @@ impl AudioProcessor for OscillatorRenderer {
488
460
}
489
461
490
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
+
491
507
#[ inline]
492
508
fn generate_sine ( & mut self ) -> f32 {
493
509
let position = self . phase * TABLE_LENGTH_USIZE as f64 ;
0 commit comments