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}