1use std::any::Any;
2use std::fmt::Debug;
3
4use crate::context::{AudioContextRegistration, AudioParamId, BaseAudioContext};
5use crate::param::{AudioParam, AudioParamDescriptor, AutomationRate};
6use crate::render::{
7    AudioParamValues, AudioProcessor, AudioRenderQuantum, AudioWorkletGlobalScope,
8};
9use crate::PeriodicWave;
10use crate::{assert_valid_time_value, RENDER_QUANTUM_SIZE};
11
12use super::{
13    precomputed_sine_table, AudioNode, AudioNodeOptions, AudioScheduledSourceNode, ChannelConfig,
14    TABLE_LENGTH_USIZE,
15};
16
17fn get_phase_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
23#[derive(Clone, Debug)]
36pub struct OscillatorOptions {
37    pub type_: OscillatorType,
39    pub frequency: f32,
41    pub detune: f32,
43    pub periodic_wave: Option<PeriodicWave>,
45    pub audio_node_options: AudioNodeOptions,
47}
48
49impl Default for OscillatorOptions {
50    fn default() -> Self {
51        Self {
52            type_: OscillatorType::default(),
53            frequency: 440.,
54            detune: 0.,
55            periodic_wave: None,
56            audio_node_options: AudioNodeOptions::default(),
57        }
58    }
59}
60
61#[derive(Debug, Copy, Clone, PartialEq, Eq)]
63pub enum OscillatorType {
64    Sine,
66    Square,
68    Sawtooth,
70    Triangle,
72    Custom,
74}
75
76impl Default for OscillatorType {
77    fn default() -> Self {
78        Self::Sine
79    }
80}
81
82impl From<u32> for OscillatorType {
83    fn from(i: u32) -> Self {
84        match i {
85            0 => OscillatorType::Sine,
86            1 => OscillatorType::Square,
87            2 => OscillatorType::Sawtooth,
88            3 => OscillatorType::Triangle,
89            4 => OscillatorType::Custom,
90            _ => unreachable!(),
91        }
92    }
93}
94
95#[derive(Debug, Copy, Clone)]
97enum Schedule {
98    Start(f64),
99    Stop(f64),
100}
101
102#[derive(Debug)]
132pub struct OscillatorNode {
133    registration: AudioContextRegistration,
135    channel_config: ChannelConfig,
137    frequency: AudioParam,
139    detune: AudioParam,
141    type_: OscillatorType,
143    start_stop_count: u8,
145}
146
147impl AudioNode for OscillatorNode {
148    fn registration(&self) -> &AudioContextRegistration {
149        &self.registration
150    }
151
152    fn channel_config(&self) -> &ChannelConfig {
153        &self.channel_config
154    }
155
156    fn number_of_inputs(&self) -> usize {
158        0
159    }
160
161    fn number_of_outputs(&self) -> usize {
163        1
164    }
165}
166
167impl AudioScheduledSourceNode for OscillatorNode {
168    fn start(&mut self) {
169        let when = self.registration.context().current_time();
170        self.start_at(when);
171    }
172
173    fn start_at(&mut self, when: f64) {
174        assert_valid_time_value(when);
175        assert_eq!(
176            self.start_stop_count, 0,
177            "InvalidStateError - Cannot call `start` twice"
178        );
179
180        self.start_stop_count += 1;
181        self.registration.post_message(Schedule::Start(when));
182    }
183
184    fn stop(&mut self) {
185        let when = self.registration.context().current_time();
186        self.stop_at(when);
187    }
188
189    fn stop_at(&mut self, when: f64) {
190        assert_valid_time_value(when);
191        assert_eq!(
192            self.start_stop_count, 1,
193            "InvalidStateError cannot stop before start"
194        );
195
196        self.start_stop_count += 1;
197        self.registration.post_message(Schedule::Stop(when));
198    }
199}
200
201impl OscillatorNode {
202    pub fn new<C: BaseAudioContext>(context: &C, options: OscillatorOptions) -> Self {
209        let OscillatorOptions {
210            type_,
211            frequency,
212            detune,
213            audio_node_options: channel_config,
214            periodic_wave,
215        } = options;
216
217        let mut node = context.base().register(move |registration| {
218            let sample_rate = context.sample_rate();
219            let nyquist = sample_rate / 2.;
220
221            let freq_param_options = AudioParamDescriptor {
223                name: String::new(),
224                min_value: -nyquist,
225                max_value: nyquist,
226                default_value: 440.,
227                automation_rate: AutomationRate::A,
228            };
229            let (f_param, f_proc) = context.create_audio_param(freq_param_options, ®istration);
230            f_param.set_value(frequency);
231
232            let det_param_options = AudioParamDescriptor {
234                name: String::new(),
235                min_value: -153_600.,
236                max_value: 153_600.,
237                default_value: 0.,
238                automation_rate: AutomationRate::A,
239            };
240            let (det_param, det_proc) =
241                context.create_audio_param(det_param_options, ®istration);
242            det_param.set_value(detune);
243
244            let renderer = OscillatorRenderer {
245                type_,
246                frequency: f_proc,
247                detune: det_proc,
248                phase: 0.,
249                start_time: f64::MAX,
250                stop_time: f64::MAX,
251                started: false,
252                periodic_wave: None,
253                ended_triggered: false,
254                sine_table: precomputed_sine_table(),
255            };
256
257            let node = Self {
258                registration,
259                channel_config: channel_config.into(),
260                frequency: f_param,
261                detune: det_param,
262                type_,
263                start_stop_count: 0,
264            };
265
266            (node, Box::new(renderer))
267        });
268
269        if let Some(p_wave) = periodic_wave {
271            node.set_periodic_wave(p_wave);
272        }
273
274        node
275    }
276
277    #[must_use]
282    pub fn frequency(&self) -> &AudioParam {
283        &self.frequency
284    }
285
286    #[must_use]
293    pub fn detune(&self) -> &AudioParam {
294        &self.detune
295    }
296
297    #[must_use]
299    pub fn type_(&self) -> OscillatorType {
300        self.type_
301    }
302
303    pub fn set_type(&mut self, type_: OscillatorType) {
313        assert_ne!(
314            type_,
315            OscillatorType::Custom,
316            "InvalidStateError: Custom type cannot be set manually"
317        );
318
319        if self.type_ == OscillatorType::Custom {
321            return;
322        }
323
324        self.type_ = type_;
325        self.registration.post_message(type_);
326    }
327
328    pub fn set_periodic_wave(&mut self, periodic_wave: PeriodicWave) {
333        self.type_ = OscillatorType::Custom;
334        self.registration.post_message(periodic_wave);
335    }
336}
337
338struct OscillatorRenderer {
340    type_: OscillatorType,
342    frequency: AudioParamId,
344    detune: AudioParamId,
346    phase: f64,
348    start_time: f64,
350    stop_time: f64,
352    started: bool,
354    periodic_wave: Option<PeriodicWave>,
356    ended_triggered: bool,
358    sine_table: &'static [f32],
360}
361
362impl AudioProcessor for OscillatorRenderer {
363    fn process(
364        &mut self,
365        _inputs: &[AudioRenderQuantum],
366        outputs: &mut [AudioRenderQuantum],
367        params: AudioParamValues<'_>,
368        scope: &AudioWorkletGlobalScope,
369    ) -> bool {
370        let output = &mut outputs[0];
372        output.set_number_of_channels(1);
374
375        let sample_rate = scope.sample_rate as f64;
376        let dt = 1. / sample_rate;
377        let num_frames = RENDER_QUANTUM_SIZE;
378        let next_block_time = scope.current_time + dt * num_frames as f64;
379
380        if self.start_time >= next_block_time {
381            output.make_silent();
382            return self.start_time != f64::MAX;
385        } else if self.stop_time < scope.current_time {
386            output.make_silent();
387
388            if !self.ended_triggered {
391                scope.send_ended_event();
392                self.ended_triggered = true;
393            }
394
395            return false;
396        }
397
398        let channel_data = output.channel_data_mut(0);
399        let frequency_values = params.get(&self.frequency);
400        let detune_values = params.get(&self.detune);
401
402        let mut current_time = scope.current_time;
403
404        if !self.started && self.start_time < current_time {
410            self.start_time = current_time;
411        }
412
413        if frequency_values.len() == 1 && detune_values.len() == 1 {
414            let phase_incr = get_phase_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_phase_incr(f, d, sample_rate);
425                    self.generate_sample(output, phase_incr, &mut current_time, dt)
426                });
427        }
428
429        true
430    }
431
432    fn onmessage(&mut self, msg: &mut dyn Any) {
433        if let Some(&type_) = msg.downcast_ref::<OscillatorType>() {
434            self.type_ = type_;
435            return;
436        }
437
438        if let Some(&schedule) = msg.downcast_ref::<Schedule>() {
439            match schedule {
440                Schedule::Start(v) => self.start_time = v,
441                Schedule::Stop(v) => self.stop_time = v,
442            }
443            return;
444        }
445
446        if let Some(periodic_wave) = msg.downcast_mut::<PeriodicWave>() {
447            if let Some(current_periodic_wave) = &mut self.periodic_wave {
448                std::mem::swap(current_periodic_wave, periodic_wave)
450            } else {
451                self.periodic_wave = Some(std::mem::take(periodic_wave));
453            }
454            self.type_ = OscillatorType::Custom; return;
456        }
457
458        log::warn!("OscillatorRenderer: Dropping incoming message {msg:?}");
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    }
467}
468
469impl OscillatorRenderer {
470    #[inline]
471    fn generate_sample(
472        &mut self,
473        output: &mut f32,
474        phase_incr: f64,
475        current_time: &mut f64,
476        dt: f64,
477    ) {
478        if *current_time < self.start_time || *current_time >= self.stop_time {
479            *output = 0.;
480            *current_time += dt;
481
482            return;
483        }
484
485        if !self.started {
487            if *current_time > self.start_time {
490                let ratio = (*current_time - self.start_time) / dt;
491                self.phase = Self::unroll_phase(phase_incr * ratio);
492            }
493
494            self.started = true;
495        }
496
497        *output = match self.type_ {
502            OscillatorType::Sine => self.generate_sine(),
503            OscillatorType::Sawtooth => self.generate_sawtooth(phase_incr),
504            OscillatorType::Square => self.generate_square(phase_incr),
505            OscillatorType::Triangle => self.generate_triangle(),
506            OscillatorType::Custom => self.generate_custom(),
507        };
508
509        *current_time += dt;
510
511        self.phase = Self::unroll_phase(self.phase + phase_incr);
512    }
513
514    #[inline]
515    fn generate_sine(&mut self) -> f32 {
516        let position = self.phase * TABLE_LENGTH_USIZE as f64;
517        let floored = position.floor();
518
519        let prev_index = floored as usize;
520        let mut next_index = prev_index + 1;
521        if next_index == TABLE_LENGTH_USIZE {
522            next_index = 0;
523        }
524
525        let k = (position - floored) as f32;
527        self.sine_table[prev_index].mul_add(1. - k, self.sine_table[next_index] * k)
528    }
529
530    #[inline]
531    fn generate_sawtooth(&mut self, phase_incr: f64) -> f32 {
532        let phase = Self::unroll_phase(self.phase + 0.5);
534        let mut sample = 2.0 * phase - 1.0;
535        sample -= Self::poly_blep(phase, phase_incr, cfg!(test));
536
537        sample as f32
538    }
539
540    #[inline]
541    fn generate_square(&mut self, phase_incr: f64) -> f32 {
542        let mut sample = if self.phase < 0.5 { 1.0 } else { -1.0 };
543        sample += Self::poly_blep(self.phase, phase_incr, cfg!(test));
544
545        let shift_phase = Self::unroll_phase(self.phase + 0.5);
546        sample -= Self::poly_blep(shift_phase, phase_incr, cfg!(test));
547
548        sample as f32
549    }
550
551    #[inline]
552    fn generate_triangle(&mut self) -> f32 {
553        let mut sample = -4. * self.phase + 2.;
554
555        if sample > 1. {
556            sample = 2. - sample;
557        } else if sample < -1. {
558            sample = -2. - sample;
559        }
560
561        sample as f32
562    }
563
564    #[inline]
565    fn generate_custom(&mut self) -> f32 {
566        let periodic_wave = self.periodic_wave.as_ref().unwrap().as_slice();
567        let position = self.phase * TABLE_LENGTH_USIZE as f64;
568        let floored = position.floor();
569
570        let prev_index = floored as usize;
571        let mut next_index = prev_index + 1;
572        if next_index == TABLE_LENGTH_USIZE {
573            next_index = 0;
574        }
575
576        let k = (position - floored) as f32;
578        periodic_wave[prev_index].mul_add(1. - k, periodic_wave[next_index] * k)
579    }
580
581    #[inline]
589    fn poly_blep(mut t: f64, dt: f64, is_test: bool) -> f64 {
590        if is_test {
591            0.
592        } else if t < dt {
593            t /= dt;
594            t + t - t * t - 1.0
595        } else if t > 1.0 - dt {
596            t = (t - 1.0) / dt;
597            t.mul_add(t, t) + t + 1.0
598        } else {
599            0.0
600        }
601    }
602
603    #[inline]
604    fn unroll_phase(mut phase: f64) -> f64 {
605        if phase >= 1. {
606            phase -= 1.
607        }
608
609        phase
610    }
611}
612
613#[cfg(test)]
614mod tests {
615    use float_eq::assert_float_eq;
616    use std::f64::consts::PI;
617
618    use crate::context::{BaseAudioContext, OfflineAudioContext};
619    use crate::node::{AudioNode, AudioScheduledSourceNode};
620    use crate::periodic_wave::{PeriodicWave, PeriodicWaveOptions};
621
622    use super::{OscillatorNode, OscillatorOptions, OscillatorRenderer, OscillatorType};
623
624    #[test]
625    fn assert_osc_default_build_with_factory_func() {
626        let default_freq = 440.;
627        let default_det = 0.;
628        let default_type = OscillatorType::Sine;
629
630        let mut context = OfflineAudioContext::new(2, 1, 44_100.);
631
632        let mut osc = context.create_oscillator();
633
634        let freq = osc.frequency.value();
635        assert_float_eq!(freq, default_freq, abs_all <= 0.);
636
637        let det = osc.detune.value();
638        assert_float_eq!(det, default_det, abs_all <= 0.);
639
640        assert_eq!(osc.type_(), default_type);
641
642        osc.start();
644        osc.connect(&context.destination());
645        let _ = context.start_rendering_sync();
646    }
647
648    #[test]
649    fn assert_osc_default_build() {
650        let default_freq = 440.;
651        let default_det = 0.;
652        let default_type = OscillatorType::Sine;
653
654        let mut context = OfflineAudioContext::new(2, 1, 44_100.);
655
656        let mut osc = OscillatorNode::new(&context, OscillatorOptions::default());
657
658        let freq = osc.frequency.value();
659        assert_float_eq!(freq, default_freq, abs_all <= 0.);
660
661        let det = osc.detune.value();
662        assert_float_eq!(det, default_det, abs_all <= 0.);
663
664        assert_eq!(osc.type_(), default_type);
665
666        osc.start();
668        osc.connect(&context.destination());
669        let _ = context.start_rendering_sync();
670    }
671
672    #[test]
673    #[should_panic]
674    fn set_type_to_custom_should_panic() {
675        let context = OfflineAudioContext::new(2, 1, 44_100.);
676        let mut osc = OscillatorNode::new(&context, OscillatorOptions::default());
677        osc.set_type(OscillatorType::Custom);
678    }
679
680    #[test]
681    fn type_is_custom_when_periodic_wave_is_some() {
682        let expected_type = OscillatorType::Custom;
683
684        let mut context = OfflineAudioContext::new(2, 1, 44_100.);
685
686        let periodic_wave = PeriodicWave::new(&context, PeriodicWaveOptions::default());
687
688        let options = OscillatorOptions {
689            periodic_wave: Some(periodic_wave),
690            ..OscillatorOptions::default()
691        };
692
693        let mut osc = OscillatorNode::new(&context, options);
694
695        assert_eq!(osc.type_(), expected_type);
696
697        osc.start();
699        osc.connect(&context.destination());
700        let _ = context.start_rendering_sync();
701    }
702
703    #[test]
704    fn set_type_is_ignored_when_periodic_wave_is_some() {
705        let expected_type = OscillatorType::Custom;
706
707        let mut context = OfflineAudioContext::new(2, 1, 44_100.);
708
709        let periodic_wave = PeriodicWave::new(&context, PeriodicWaveOptions::default());
710
711        let options = OscillatorOptions {
712            periodic_wave: Some(periodic_wave),
713            ..OscillatorOptions::default()
714        };
715
716        let mut osc = OscillatorNode::new(&context, options);
717
718        osc.set_type(OscillatorType::Sine);
719        assert_eq!(osc.type_(), expected_type);
720
721        osc.start();
723        osc.connect(&context.destination());
724        let _ = context.start_rendering_sync();
725    }
726
727    #[test]
741    fn sine_raw() {
742        for i in 0..5 {
744            let freq = 10_f32.powf(i as f32);
745            let sample_rate = 44_100;
746
747            let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
748
749            let mut osc = context.create_oscillator();
750            osc.connect(&context.destination());
751            osc.frequency().set_value(freq);
752            osc.start_at(0.);
753
754            let output = context.start_rendering_sync();
755            let result = output.get_channel_data(0);
756
757            let mut expected = Vec::<f32>::with_capacity(sample_rate);
758            let mut phase: f64 = 0.;
759            let phase_incr = freq as f64 / sample_rate as f64;
760
761            for _i in 0..sample_rate {
762                let sample = (phase * 2. * PI).sin();
763
764                expected.push(sample as f32);
765
766                phase += phase_incr;
767                if phase >= 1. {
768                    phase -= 1.;
769                }
770            }
771
772            assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
773        }
774    }
775
776    #[test]
777    fn sine_raw_exact_phase() {
778        for i in 0..5 {
780            let freq = 10_f32.powf(i as f32);
781            let sample_rate = 44_100;
782
783            let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
784
785            let mut osc = context.create_oscillator();
786            osc.connect(&context.destination());
787            osc.frequency().set_value(freq);
788            osc.start_at(0.);
789
790            let output = context.start_rendering_sync();
791            let result = output.get_channel_data(0);
792            let mut expected = Vec::<f32>::with_capacity(sample_rate);
793
794            for i in 0..sample_rate {
795                let phase = freq as f64 * i as f64 / sample_rate as f64;
796                let sample = (phase * 2. * PI).sin();
797                expected.push(sample as f32);
799            }
800
801            assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
802        }
803    }
804
805    #[test]
806    fn square_raw() {
807        for i in 0..5 {
809            let freq = 10_f32.powf(i as f32);
810            let sample_rate = 44100;
811
812            let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
813
814            let mut osc = context.create_oscillator();
815            osc.connect(&context.destination());
816            osc.frequency().set_value(freq);
817            osc.set_type(OscillatorType::Square);
818            osc.start_at(0.);
819
820            let output = context.start_rendering_sync();
821            let result = output.get_channel_data(0);
822
823            let mut expected = Vec::<f32>::with_capacity(sample_rate);
824            let mut phase: f64 = 0.;
825            let phase_incr = freq as f64 / sample_rate as f64;
826
827            for _i in 0..sample_rate {
828                let sample = if phase < 0.5 { 1. } else { -1. };
830
831                expected.push(sample as f32);
832
833                phase += phase_incr;
834                if phase >= 1. {
835                    phase -= 1.;
836                }
837            }
838
839            assert_float_eq!(result[..], expected[..], abs_all <= 1e-10);
840        }
841    }
842
843    #[test]
844    fn triangle_raw() {
845        for i in 0..5 {
847            let freq = 10_f32.powf(i as f32);
848            let sample_rate = 44_100;
849
850            let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
851
852            let mut osc = context.create_oscillator();
853            osc.connect(&context.destination());
854            osc.frequency().set_value(freq);
855            osc.set_type(OscillatorType::Triangle);
856            osc.start_at(0.);
857
858            let output = context.start_rendering_sync();
859            let result = output.get_channel_data(0);
860
861            let mut expected = Vec::<f32>::with_capacity(sample_rate);
862            let mut phase: f64 = 0.;
863            let phase_incr = freq as f64 / sample_rate as f64;
864
865            for _i in 0..sample_rate {
866                let mut sample = -4. * phase + 2.;
871
872                if sample > 1. {
873                    sample = 2. - sample;
874                } else if sample < -1. {
875                    sample = -2. - sample;
876                }
877
878                expected.push(sample as f32);
879
880                phase += phase_incr;
881                if phase >= 1. {
882                    phase -= 1.;
883                }
884            }
885
886            assert_float_eq!(result[..], expected[..], abs_all <= 1e-10);
887        }
888    }
889
890    #[test]
891    fn sawtooth_raw() {
892        for i in 0..5 {
894            let freq = 10_f32.powf(i as f32);
895            let sample_rate = 44_100;
896
897            let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
898
899            let mut osc = context.create_oscillator();
900            osc.connect(&context.destination());
901            osc.frequency().set_value(freq);
902            osc.set_type(OscillatorType::Sawtooth);
903            osc.start_at(0.);
904
905            let output = context.start_rendering_sync();
906            let result = output.get_channel_data(0);
907
908            let mut expected = Vec::<f32>::with_capacity(sample_rate);
909            let mut phase: f64 = 0.;
910            let phase_incr = freq as f64 / sample_rate as f64;
911
912            for _i in 0..sample_rate {
913                let mut offset_phase = phase + 0.5;
917                if offset_phase >= 1. {
918                    offset_phase -= 1.;
919                }
920                let sample = 2. * offset_phase - 1.;
921
922                expected.push(sample as f32);
923
924                phase += phase_incr;
925                if phase >= 1. {
926                    phase -= 1.;
927                }
928            }
929
930            assert_float_eq!(result[..], expected[..], abs_all <= 1e-10);
931        }
932    }
933
934    #[test]
935    fn periodic_wave_1f() {
937        for i in 0..5 {
939            let freq = 10_f32.powf(i as f32);
940            let sample_rate = 44_100;
941
942            let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
943
944            let options = PeriodicWaveOptions {
945                real: Some(vec![0., 0.]),
946                imag: Some(vec![0., 1.]), disable_normalization: false,
948            };
949
950            let periodic_wave = context.create_periodic_wave(options);
951
952            let mut osc = context.create_oscillator();
953            osc.connect(&context.destination());
954            osc.set_periodic_wave(periodic_wave);
955            osc.frequency().set_value(freq);
956            osc.set_type(OscillatorType::Sawtooth);
957            osc.start_at(0.);
958
959            let output = context.start_rendering_sync();
960            let result = output.get_channel_data(0);
961
962            let mut expected = Vec::<f32>::with_capacity(sample_rate);
963            let mut phase: f64 = 0.;
964            let phase_incr = freq as f64 / sample_rate as f64;
965
966            for _i in 0..sample_rate {
967                let sample = (phase * 2. * PI).sin();
968
969                expected.push(sample as f32);
970
971                phase += phase_incr;
972                if phase >= 1. {
973                    phase -= 1.;
974                }
975            }
976
977            assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
978        }
979    }
980
981    #[test]
982    fn periodic_wave_2f() {
983        for i in 0..5 {
985            let freq = 10_f32.powf(i as f32);
986            let sample_rate = 44_100;
987
988            let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
989
990            let options = PeriodicWaveOptions {
991                real: Some(vec![0., 0., 0.]),
992                imag: Some(vec![0., 0.5, 0.5]),
993                disable_normalization: true,
995            };
996
997            let periodic_wave = context.create_periodic_wave(options);
998
999            let mut osc = context.create_oscillator();
1000            osc.connect(&context.destination());
1001            osc.set_periodic_wave(periodic_wave);
1002            osc.frequency().set_value(freq);
1003            osc.start_at(0.);
1004
1005            let output = context.start_rendering_sync();
1006            let result = output.get_channel_data(0);
1007
1008            let mut expected = Vec::<f32>::with_capacity(sample_rate);
1009            let mut phase: f64 = 0.;
1010            let phase_incr = freq as f64 / sample_rate as f64;
1011
1012            for _i in 0..sample_rate {
1013                let mut sample = 0.;
1014                sample += 0.5 * (1. * phase * 2. * PI).sin();
1015                sample += 0.5 * (2. * phase * 2. * PI).sin();
1016
1017                expected.push(sample as f32);
1018
1019                phase += phase_incr;
1020                if phase >= 1. {
1021                    phase -= 1.;
1022                }
1023            }
1024
1025            assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1026        }
1027    }
1028
1029    #[test]
1030    fn polyblep_isolated() {
1031        {
1037            let mut signal = [1., 1., 1., 1., -1., -1., -1., -1.];
1038            let len = signal.len() as f64;
1039            let dt = 1. / len;
1040
1041            for (index, s) in signal.iter_mut().enumerate() {
1042                let phase = index as f64 / len;
1043
1044                *s += OscillatorRenderer::poly_blep(phase, dt, false);
1045                *s -= OscillatorRenderer::poly_blep((phase + 0.5) % 1., dt, false);
1046            }
1047
1048            let expected = [0., 1., 1., 1., 0., -1., -1., -1.];
1049
1050            assert_float_eq!(signal[..], expected[..], abs_all <= 0.);
1051        }
1052
1053        {
1055            let mut signal = [0., 0.25, 0.75, 1., -1., -0.75, -0.5, -0.25];
1056            let len = signal.len() as f64;
1057            let dt = 1. / len;
1058
1059            for (index, s) in signal.iter_mut().enumerate() {
1060                let phase = index as f64 / len;
1061                *s -= OscillatorRenderer::poly_blep((phase + 0.5) % 1., dt, false);
1062            }
1063
1064            let expected = [0., 0.25, 0.75, 1., 0., -0.75, -0.5, -0.25];
1065            assert_float_eq!(signal[..], expected[..], abs_all <= 0.);
1066        }
1067    }
1068
1069    #[test]
1070    fn osc_sub_quantum_start() {
1071        let freq = 1.25;
1072        let sample_rate = 44_100;
1073
1074        let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1075        let mut osc = context.create_oscillator();
1076        osc.connect(&context.destination());
1077        osc.frequency().set_value(freq);
1078        osc.start_at(2. / sample_rate as f64);
1079
1080        let output = context.start_rendering_sync();
1081        let result = output.get_channel_data(0);
1082
1083        let mut expected = Vec::<f32>::with_capacity(sample_rate);
1084        let mut phase: f64 = 0.;
1085        let phase_incr = freq as f64 / sample_rate as f64;
1086
1087        expected.push(0.);
1088        expected.push(0.);
1089
1090        for _i in 2..sample_rate {
1091            let sample = (phase * 2. * PI).sin();
1092            phase += phase_incr;
1093            expected.push(sample as f32);
1094        }
1095
1096        assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1097    }
1098
1099    #[test]
1102    fn osc_sub_sample_start() {
1103        let freq = 1.;
1104        let sample_rate = 96000;
1105
1106        let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1107        let mut osc = context.create_oscillator();
1108        osc.connect(&context.destination());
1109        osc.frequency().set_value(freq);
1110        osc.start_at(1.3 / sample_rate as f64);
1112
1113        let output = context.start_rendering_sync();
1114        let result = output.get_channel_data(0);
1115
1116        let mut expected = Vec::<f32>::with_capacity(sample_rate);
1117        let phase_incr = freq as f64 / sample_rate as f64;
1118        let mut phase: f64 = 0.7 * phase_incr;
1120
1121        expected.push(0.);
1122        expected.push(0.);
1123
1124        for _i in 2..sample_rate {
1125            let sample = (phase * 2. * PI).sin();
1126            phase += phase_incr;
1127            expected.push(sample as f32);
1128        }
1129
1130        assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1131    }
1132
1133    #[test]
1134    fn osc_sub_quantum_stop() {
1135        let freq = 2345.6;
1136        let sample_rate = 44_100;
1137
1138        let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1139        let mut osc = context.create_oscillator();
1140        osc.connect(&context.destination());
1141        osc.frequency().set_value(freq);
1142        osc.start_at(0.);
1143        osc.stop_at(6. / sample_rate as f64);
1144
1145        let output = context.start_rendering_sync();
1146        let result = output.get_channel_data(0);
1147
1148        let mut expected = Vec::<f32>::with_capacity(sample_rate);
1149        let mut phase: f64 = 0.;
1150        let phase_incr = freq as f64 / sample_rate as f64;
1151
1152        for i in 0..sample_rate {
1153            if i < 6 {
1154                let sample = (phase * 2. * PI).sin();
1155                phase += phase_incr;
1156                expected.push(sample as f32);
1157            } else {
1158                expected.push(0.);
1159            }
1160        }
1161
1162        assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1163    }
1164
1165    #[test]
1166    fn osc_sub_sample_stop() {
1167        let freq = 8910.1;
1168        let sample_rate = 44_100;
1169
1170        let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1171        let mut osc = context.create_oscillator();
1172        osc.connect(&context.destination());
1173        osc.frequency().set_value(freq);
1174        osc.start_at(0.);
1175        osc.stop_at(19.4 / sample_rate as f64);
1176
1177        let output = context.start_rendering_sync();
1178        let result = output.get_channel_data(0);
1179
1180        let mut expected = Vec::<f32>::with_capacity(sample_rate);
1181        let mut phase: f64 = 0.;
1182        let phase_incr = freq as f64 / sample_rate as f64;
1183
1184        for i in 0..sample_rate {
1185            if i < 20 {
1186                let sample = (phase * 2. * PI).sin();
1187                phase += phase_incr;
1188                expected.push(sample as f32);
1189            } else {
1190                expected.push(0.);
1191            }
1192        }
1193
1194        assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1195    }
1196
1197    #[test]
1198    fn test_start_in_the_past() {
1199        let freq = 8910.1;
1200        let sample_rate = 44_100;
1201
1202        let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1203
1204        context.suspend_sync(128. / sample_rate as f64, move |context| {
1205            let mut osc = context.create_oscillator();
1206            osc.connect(&context.destination());
1207            osc.frequency().set_value(freq);
1208            osc.start_at(0.);
1209        });
1210
1211        let output = context.start_rendering_sync();
1212        let result = output.get_channel_data(0);
1213
1214        let mut expected = Vec::<f32>::with_capacity(sample_rate);
1215        let mut phase: f64 = 0.;
1216        let phase_incr = freq as f64 / sample_rate as f64;
1217
1218        for i in 0..sample_rate {
1219            if i < 128 {
1220                expected.push(0.);
1221            } else {
1222                let sample = (phase * 2. * PI).sin();
1223                expected.push(sample as f32);
1224                phase += phase_incr;
1225            }
1226        }
1227
1228        assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1229    }
1230}