@@ -14,6 +14,12 @@ use super::{
1414 ChannelConfigOptions , TABLE_LENGTH_USIZE ,
1515} ;
1616
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+
1723/// Options for constructing an [`OscillatorNode`]
1824// dictionary OscillatorOptions : AudioNodeOptions {
1925// OscillatorType type = "sine";
@@ -404,52 +410,21 @@ impl AudioProcessor for OscillatorRenderer {
404410 self . start_time = current_time;
405411 }
406412
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+ }
453428
454429 true
455430 }
@@ -485,6 +460,50 @@ impl AudioProcessor for OscillatorRenderer {
485460}
486461
487462impl 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+
488507 #[ inline]
489508 fn generate_sine ( & mut self ) -> f32 {
490509 let position = self . phase * TABLE_LENGTH_USIZE as f64 ;
0 commit comments