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, Default)]
63pub enum OscillatorType {
64 #[default]
66 Sine,
67 Square,
69 Sawtooth,
71 Triangle,
73 Custom,
75}
76
77impl From<u32> for OscillatorType {
78 fn from(i: u32) -> Self {
79 match i {
80 0 => OscillatorType::Sine,
81 1 => OscillatorType::Square,
82 2 => OscillatorType::Sawtooth,
83 3 => OscillatorType::Triangle,
84 4 => OscillatorType::Custom,
85 _ => unreachable!(),
86 }
87 }
88}
89
90#[derive(Debug, Copy, Clone)]
92enum Schedule {
93 Start(f64),
94 Stop(f64),
95}
96
97#[derive(Debug)]
127pub struct OscillatorNode {
128 registration: AudioContextRegistration,
130 channel_config: ChannelConfig,
132 frequency: AudioParam,
134 detune: AudioParam,
136 type_: OscillatorType,
138 has_start: bool,
140}
141
142impl AudioNode for OscillatorNode {
143 fn registration(&self) -> &AudioContextRegistration {
144 &self.registration
145 }
146
147 fn channel_config(&self) -> &ChannelConfig {
148 &self.channel_config
149 }
150
151 fn number_of_inputs(&self) -> usize {
153 0
154 }
155
156 fn number_of_outputs(&self) -> usize {
158 1
159 }
160}
161
162impl AudioScheduledSourceNode for OscillatorNode {
163 fn start(&mut self) {
164 let when = self.registration.context().current_time();
165 self.start_at(when);
166 }
167
168 fn start_at(&mut self, when: f64) {
169 assert_valid_time_value(when);
170 assert!(
171 !self.has_start,
172 "InvalidStateError - Cannot call `start` twice"
173 );
174
175 self.has_start = true;
176 self.registration.post_message(Schedule::Start(when));
177 }
178
179 fn stop(&mut self) {
180 let when = self.registration.context().current_time();
181 self.stop_at(when);
182 }
183
184 fn stop_at(&mut self, when: f64) {
185 assert_valid_time_value(when);
186 assert!(self.has_start, "InvalidStateError cannot stop before start");
187
188 self.registration.post_message(Schedule::Stop(when));
189 }
190}
191
192impl OscillatorNode {
193 pub fn new<C: BaseAudioContext>(context: &C, options: OscillatorOptions) -> Self {
200 let OscillatorOptions {
201 type_,
202 frequency,
203 detune,
204 audio_node_options: channel_config,
205 periodic_wave,
206 } = options;
207
208 let mut node = context.base().register(move |registration| {
209 let sample_rate = context.sample_rate();
210 let nyquist = sample_rate / 2.;
211
212 let freq_param_options = AudioParamDescriptor {
214 name: String::new(),
215 min_value: -nyquist,
216 max_value: nyquist,
217 default_value: 440.,
218 automation_rate: AutomationRate::A,
219 };
220 let (f_param, f_proc) = context.create_audio_param(freq_param_options, ®istration);
221 f_param.set_value(frequency);
222
223 let det_param_options = AudioParamDescriptor {
225 name: String::new(),
226 min_value: -153_600.,
227 max_value: 153_600.,
228 default_value: 0.,
229 automation_rate: AutomationRate::A,
230 };
231 let (det_param, det_proc) =
232 context.create_audio_param(det_param_options, ®istration);
233 det_param.set_value(detune);
234
235 let renderer = OscillatorRenderer {
236 type_,
237 frequency: f_proc,
238 detune: det_proc,
239 phase: 0.,
240 start_time: f64::MAX,
241 stop_time: f64::MAX,
242 started: false,
243 periodic_wave: None,
244 ended_triggered: false,
245 sine_table: precomputed_sine_table(),
246 };
247
248 let node = Self {
249 registration,
250 channel_config: channel_config.into(),
251 frequency: f_param,
252 detune: det_param,
253 type_,
254 has_start: false,
255 };
256
257 (node, Box::new(renderer))
258 });
259
260 if let Some(p_wave) = periodic_wave {
262 node.set_periodic_wave(p_wave);
263 }
264
265 node
266 }
267
268 #[must_use]
273 pub fn frequency(&self) -> &AudioParam {
274 &self.frequency
275 }
276
277 #[must_use]
284 pub fn detune(&self) -> &AudioParam {
285 &self.detune
286 }
287
288 #[must_use]
290 pub fn type_(&self) -> OscillatorType {
291 self.type_
292 }
293
294 pub fn set_type(&mut self, type_: OscillatorType) {
304 assert_ne!(
305 type_,
306 OscillatorType::Custom,
307 "InvalidStateError: Custom type cannot be set manually"
308 );
309
310 if self.type_ == OscillatorType::Custom {
312 return;
313 }
314
315 self.type_ = type_;
316 self.registration.post_message(type_);
317 }
318
319 pub fn set_periodic_wave(&mut self, periodic_wave: PeriodicWave) {
324 self.type_ = OscillatorType::Custom;
325 self.registration.post_message(periodic_wave);
326 }
327}
328
329struct OscillatorRenderer {
331 type_: OscillatorType,
333 frequency: AudioParamId,
335 detune: AudioParamId,
337 phase: f64,
339 start_time: f64,
341 stop_time: f64,
343 started: bool,
345 periodic_wave: Option<PeriodicWave>,
347 ended_triggered: bool,
349 sine_table: &'static [f32],
351}
352
353impl AudioProcessor for OscillatorRenderer {
354 fn process(
355 &mut self,
356 _inputs: &[AudioRenderQuantum],
357 outputs: &mut [AudioRenderQuantum],
358 params: AudioParamValues<'_>,
359 scope: &AudioWorkletGlobalScope,
360 ) -> bool {
361 let output = &mut outputs[0];
363 output.set_number_of_channels(1);
365
366 let sample_rate = scope.sample_rate as f64;
367 let dt = 1. / sample_rate;
368 let num_frames = RENDER_QUANTUM_SIZE;
369 let next_block_time = scope.current_time + dt * num_frames as f64;
370
371 if self.stop_time <= scope.current_time {
372 output.make_silent();
373
374 if !self.ended_triggered {
375 scope.send_ended_event();
376 self.ended_triggered = true;
377 }
378
379 return false;
380 } else if self.start_time >= next_block_time {
381 output.make_silent();
382
383 if self.stop_time <= next_block_time {
384 if !self.ended_triggered {
385 scope.send_ended_event();
386 self.ended_triggered = true;
387 }
388
389 return false;
390 }
391
392 return self.start_time != f64::MAX;
395 }
396
397 let channel_data = output.channel_data_mut(0);
398 let frequency_values = params.get(&self.frequency);
399 let detune_values = params.get(&self.detune);
400
401 let mut current_time = scope.current_time;
402
403 if !self.started && self.start_time < current_time {
409 self.start_time = current_time;
410 }
411
412 if frequency_values.len() == 1 && detune_values.len() == 1 {
413 let phase_incr = get_phase_incr(frequency_values[0], detune_values[0], sample_rate);
414 channel_data
415 .iter_mut()
416 .for_each(|output| self.generate_sample(output, phase_incr, &mut current_time, dt));
417 } else {
418 channel_data
419 .iter_mut()
420 .zip(frequency_values.iter().cycle())
421 .zip(detune_values.iter().cycle())
422 .for_each(|((output, &f), &d)| {
423 let phase_incr = get_phase_incr(f, d, sample_rate);
424 self.generate_sample(output, phase_incr, &mut current_time, dt)
425 });
426 }
427
428 if self.stop_time <= next_block_time {
429 if !self.ended_triggered {
430 scope.send_ended_event();
431 self.ended_triggered = true;
432 }
433
434 return false;
435 }
436
437 true
438 }
439
440 fn onmessage(&mut self, msg: &mut dyn Any) {
441 if let Some(&type_) = msg.downcast_ref::<OscillatorType>() {
442 self.type_ = type_;
443 return;
444 }
445
446 if let Some(&schedule) = msg.downcast_ref::<Schedule>() {
447 match schedule {
448 Schedule::Start(v) => self.start_time = v,
449 Schedule::Stop(v) => self.stop_time = v,
450 }
451 return;
452 }
453
454 if let Some(periodic_wave) = msg.downcast_mut::<PeriodicWave>() {
455 if let Some(current_periodic_wave) = &mut self.periodic_wave {
456 std::mem::swap(current_periodic_wave, periodic_wave)
458 } else {
459 self.periodic_wave = Some(std::mem::take(periodic_wave));
461 }
462 self.type_ = OscillatorType::Custom; return;
464 }
465
466 log::warn!("OscillatorRenderer: Dropping incoming message {msg:?}");
467 }
468
469 fn before_drop(&mut self, scope: &AudioWorkletGlobalScope) {
470 if !self.ended_triggered
471 && (scope.current_time >= self.start_time || scope.current_time >= self.stop_time)
472 {
473 scope.send_ended_event();
474 self.ended_triggered = true;
475 }
476 }
477}
478impl OscillatorRenderer {
479 #[inline]
480 fn generate_sample(
481 &mut self,
482 output: &mut f32,
483 phase_incr: f64,
484 current_time: &mut f64,
485 dt: f64,
486 ) {
487 if *current_time < self.start_time || *current_time >= self.stop_time {
488 *output = 0.;
489 *current_time += dt;
490
491 return;
492 }
493
494 if !self.started {
496 if *current_time > self.start_time {
499 let ratio = (*current_time - self.start_time) / dt;
500 self.phase = Self::unroll_phase(phase_incr * ratio);
501 }
502
503 self.started = true;
504 }
505
506 *output = match self.type_ {
511 OscillatorType::Sine => self.generate_sine(),
512 OscillatorType::Sawtooth => self.generate_sawtooth(phase_incr),
513 OscillatorType::Square => self.generate_square(phase_incr),
514 OscillatorType::Triangle => self.generate_triangle(),
515 OscillatorType::Custom => self.generate_custom(),
516 };
517
518 *current_time += dt;
519
520 self.phase = Self::unroll_phase(self.phase + phase_incr);
521 }
522
523 #[inline]
524 fn generate_sine(&mut self) -> f32 {
525 let position = self.phase * TABLE_LENGTH_USIZE as f64;
526 let floored = position.floor();
527
528 let prev_index = floored as usize;
529 let mut next_index = prev_index + 1;
530 if next_index == TABLE_LENGTH_USIZE {
531 next_index = 0;
532 }
533
534 let k = (position - floored) as f32;
536 self.sine_table[prev_index].mul_add(1. - k, self.sine_table[next_index] * k)
537 }
538
539 #[inline]
540 fn generate_sawtooth(&mut self, phase_incr: f64) -> f32 {
541 let phase = Self::unroll_phase(self.phase + 0.5);
543 let mut sample = 2.0 * phase - 1.0;
544 sample -= Self::poly_blep(phase, phase_incr, cfg!(test));
545
546 sample as f32
547 }
548
549 #[inline]
550 fn generate_square(&mut self, phase_incr: f64) -> f32 {
551 let mut sample = if self.phase < 0.5 { 1.0 } else { -1.0 };
552 sample += Self::poly_blep(self.phase, phase_incr, cfg!(test));
553
554 let shift_phase = Self::unroll_phase(self.phase + 0.5);
555 sample -= Self::poly_blep(shift_phase, phase_incr, cfg!(test));
556
557 sample as f32
558 }
559
560 #[inline]
561 fn generate_triangle(&mut self) -> f32 {
562 let mut sample = -4. * self.phase + 2.;
563
564 if sample > 1. {
565 sample = 2. - sample;
566 } else if sample < -1. {
567 sample = -2. - sample;
568 }
569
570 sample as f32
571 }
572
573 #[inline]
574 fn generate_custom(&mut self) -> f32 {
575 let periodic_wave = self.periodic_wave.as_ref().unwrap().as_slice();
576 let position = self.phase * TABLE_LENGTH_USIZE as f64;
577 let floored = position.floor();
578
579 let prev_index = floored as usize;
580 let mut next_index = prev_index + 1;
581 if next_index == TABLE_LENGTH_USIZE {
582 next_index = 0;
583 }
584
585 let k = (position - floored) as f32;
587 periodic_wave[prev_index].mul_add(1. - k, periodic_wave[next_index] * k)
588 }
589
590 #[inline]
598 fn poly_blep(mut t: f64, dt: f64, is_test: bool) -> f64 {
599 if is_test {
600 0.
601 } else if t < dt {
602 t /= dt;
603 t + t - t * t - 1.0
604 } else if t > 1.0 - dt {
605 t = (t - 1.0) / dt;
606 t.mul_add(t, t) + t + 1.0
607 } else {
608 0.0
609 }
610 }
611
612 #[inline]
613 fn unroll_phase(mut phase: f64) -> f64 {
614 if phase >= 1. {
615 phase -= 1.
616 }
617
618 phase
619 }
620}
621
622#[cfg(test)]
623mod tests {
624 use float_eq::assert_float_eq;
625 use std::f64::consts::PI;
626
627 use crate::context::{BaseAudioContext, OfflineAudioContext};
628 use crate::node::{AudioNode, AudioScheduledSourceNode};
629 use crate::periodic_wave::{PeriodicWave, PeriodicWaveOptions};
630 use crate::RENDER_QUANTUM_SIZE;
631
632 use super::{OscillatorNode, OscillatorOptions, OscillatorRenderer, OscillatorType};
633
634 #[test]
635 fn assert_osc_default_build_with_factory_func() {
636 let default_freq = 440.;
637 let default_det = 0.;
638 let default_type = OscillatorType::Sine;
639
640 let mut context = OfflineAudioContext::new(2, 1, 44_100.);
641
642 let mut osc = context.create_oscillator();
643
644 let freq = osc.frequency.value();
645 assert_float_eq!(freq, default_freq, abs_all <= 0.);
646
647 let det = osc.detune.value();
648 assert_float_eq!(det, default_det, abs_all <= 0.);
649
650 assert_eq!(osc.type_(), default_type);
651
652 osc.start();
654 osc.connect(&context.destination());
655 let _ = context.start_rendering_sync();
656 }
657
658 #[test]
659 fn assert_osc_default_build() {
660 let default_freq = 440.;
661 let default_det = 0.;
662 let default_type = OscillatorType::Sine;
663
664 let mut context = OfflineAudioContext::new(2, 1, 44_100.);
665
666 let mut osc = OscillatorNode::new(&context, OscillatorOptions::default());
667
668 let freq = osc.frequency.value();
669 assert_float_eq!(freq, default_freq, abs_all <= 0.);
670
671 let det = osc.detune.value();
672 assert_float_eq!(det, default_det, abs_all <= 0.);
673
674 assert_eq!(osc.type_(), default_type);
675
676 osc.start();
678 osc.connect(&context.destination());
679 let _ = context.start_rendering_sync();
680 }
681
682 #[test]
683 #[should_panic]
684 fn set_type_to_custom_should_panic() {
685 let context = OfflineAudioContext::new(2, 1, 44_100.);
686 let mut osc = OscillatorNode::new(&context, OscillatorOptions::default());
687 osc.set_type(OscillatorType::Custom);
688 }
689
690 #[test]
691 fn type_is_custom_when_periodic_wave_is_some() {
692 let expected_type = OscillatorType::Custom;
693
694 let mut context = OfflineAudioContext::new(2, 1, 44_100.);
695
696 let periodic_wave = PeriodicWave::new(&context, PeriodicWaveOptions::default());
697
698 let options = OscillatorOptions {
699 periodic_wave: Some(periodic_wave),
700 ..OscillatorOptions::default()
701 };
702
703 let mut osc = OscillatorNode::new(&context, options);
704
705 assert_eq!(osc.type_(), expected_type);
706
707 osc.start();
709 osc.connect(&context.destination());
710 let _ = context.start_rendering_sync();
711 }
712
713 #[test]
714 fn set_type_is_ignored_when_periodic_wave_is_some() {
715 let expected_type = OscillatorType::Custom;
716
717 let mut context = OfflineAudioContext::new(2, 1, 44_100.);
718
719 let periodic_wave = PeriodicWave::new(&context, PeriodicWaveOptions::default());
720
721 let options = OscillatorOptions {
722 periodic_wave: Some(periodic_wave),
723 ..OscillatorOptions::default()
724 };
725
726 let mut osc = OscillatorNode::new(&context, options);
727
728 osc.set_type(OscillatorType::Sine);
729 assert_eq!(osc.type_(), expected_type);
730
731 osc.start();
733 osc.connect(&context.destination());
734 let _ = context.start_rendering_sync();
735 }
736
737 #[test]
751 fn sine_raw() {
752 for i in 0..5 {
754 let freq = 10_f32.powf(i as f32);
755 let sample_rate = 44_100;
756
757 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
758
759 let mut osc = context.create_oscillator();
760 osc.connect(&context.destination());
761 osc.frequency().set_value(freq);
762 osc.start_at(0.);
763
764 let output = context.start_rendering_sync();
765 let result = output.get_channel_data(0);
766
767 let mut expected = Vec::<f32>::with_capacity(sample_rate);
768 let mut phase: f64 = 0.;
769 let phase_incr = freq as f64 / sample_rate as f64;
770
771 for _i in 0..sample_rate {
772 let sample = (phase * 2. * PI).sin();
773
774 expected.push(sample as f32);
775
776 phase += phase_incr;
777 if phase >= 1. {
778 phase -= 1.;
779 }
780 }
781
782 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
783 }
784 }
785
786 #[test]
787 fn sine_raw_exact_phase() {
788 for i in 0..5 {
790 let freq = 10_f32.powf(i as f32);
791 let sample_rate = 44_100;
792
793 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
794
795 let mut osc = context.create_oscillator();
796 osc.connect(&context.destination());
797 osc.frequency().set_value(freq);
798 osc.start_at(0.);
799
800 let output = context.start_rendering_sync();
801 let result = output.get_channel_data(0);
802 let mut expected = Vec::<f32>::with_capacity(sample_rate);
803
804 for i in 0..sample_rate {
805 let phase = freq as f64 * i as f64 / sample_rate as f64;
806 let sample = (phase * 2. * PI).sin();
807 expected.push(sample as f32);
809 }
810
811 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
812 }
813 }
814
815 #[test]
816 fn square_raw() {
817 for i in 0..5 {
819 let freq = 10_f32.powf(i as f32);
820 let sample_rate = 44100;
821
822 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
823
824 let mut osc = context.create_oscillator();
825 osc.connect(&context.destination());
826 osc.frequency().set_value(freq);
827 osc.set_type(OscillatorType::Square);
828 osc.start_at(0.);
829
830 let output = context.start_rendering_sync();
831 let result = output.get_channel_data(0);
832
833 let mut expected = Vec::<f32>::with_capacity(sample_rate);
834 let mut phase: f64 = 0.;
835 let phase_incr = freq as f64 / sample_rate as f64;
836
837 for _i in 0..sample_rate {
838 let sample = if phase < 0.5 { 1. } else { -1. };
840
841 expected.push(sample as f32);
842
843 phase += phase_incr;
844 if phase >= 1. {
845 phase -= 1.;
846 }
847 }
848
849 assert_float_eq!(result[..], expected[..], abs_all <= 1e-10);
850 }
851 }
852
853 #[test]
854 fn triangle_raw() {
855 for i in 0..5 {
857 let freq = 10_f32.powf(i as f32);
858 let sample_rate = 44_100;
859
860 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
861
862 let mut osc = context.create_oscillator();
863 osc.connect(&context.destination());
864 osc.frequency().set_value(freq);
865 osc.set_type(OscillatorType::Triangle);
866 osc.start_at(0.);
867
868 let output = context.start_rendering_sync();
869 let result = output.get_channel_data(0);
870
871 let mut expected = Vec::<f32>::with_capacity(sample_rate);
872 let mut phase: f64 = 0.;
873 let phase_incr = freq as f64 / sample_rate as f64;
874
875 for _i in 0..sample_rate {
876 let mut sample = -4. * phase + 2.;
881
882 if sample > 1. {
883 sample = 2. - sample;
884 } else if sample < -1. {
885 sample = -2. - sample;
886 }
887
888 expected.push(sample as f32);
889
890 phase += phase_incr;
891 if phase >= 1. {
892 phase -= 1.;
893 }
894 }
895
896 assert_float_eq!(result[..], expected[..], abs_all <= 1e-10);
897 }
898 }
899
900 #[test]
901 fn sawtooth_raw() {
902 for i in 0..5 {
904 let freq = 10_f32.powf(i as f32);
905 let sample_rate = 44_100;
906
907 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
908
909 let mut osc = context.create_oscillator();
910 osc.connect(&context.destination());
911 osc.frequency().set_value(freq);
912 osc.set_type(OscillatorType::Sawtooth);
913 osc.start_at(0.);
914
915 let output = context.start_rendering_sync();
916 let result = output.get_channel_data(0);
917
918 let mut expected = Vec::<f32>::with_capacity(sample_rate);
919 let mut phase: f64 = 0.;
920 let phase_incr = freq as f64 / sample_rate as f64;
921
922 for _i in 0..sample_rate {
923 let mut offset_phase = phase + 0.5;
927 if offset_phase >= 1. {
928 offset_phase -= 1.;
929 }
930 let sample = 2. * offset_phase - 1.;
931
932 expected.push(sample as f32);
933
934 phase += phase_incr;
935 if phase >= 1. {
936 phase -= 1.;
937 }
938 }
939
940 assert_float_eq!(result[..], expected[..], abs_all <= 1e-10);
941 }
942 }
943
944 #[test]
945 fn periodic_wave_1f() {
947 for i in 0..5 {
949 let freq = 10_f32.powf(i as f32);
950 let sample_rate = 44_100;
951
952 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
953
954 let options = PeriodicWaveOptions {
955 real: Some(vec![0., 0.]),
956 imag: Some(vec![0., 1.]), disable_normalization: false,
958 };
959
960 let periodic_wave = context.create_periodic_wave(options);
961
962 let mut osc = context.create_oscillator();
963 osc.connect(&context.destination());
964 osc.set_periodic_wave(periodic_wave);
965 osc.frequency().set_value(freq);
966 osc.set_type(OscillatorType::Sawtooth);
967 osc.start_at(0.);
968
969 let output = context.start_rendering_sync();
970 let result = output.get_channel_data(0);
971
972 let mut expected = Vec::<f32>::with_capacity(sample_rate);
973 let mut phase: f64 = 0.;
974 let phase_incr = freq as f64 / sample_rate as f64;
975
976 for _i in 0..sample_rate {
977 let sample = (phase * 2. * PI).sin();
978
979 expected.push(sample as f32);
980
981 phase += phase_incr;
982 if phase >= 1. {
983 phase -= 1.;
984 }
985 }
986
987 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
988 }
989 }
990
991 #[test]
992 fn periodic_wave_2f() {
993 for i in 0..5 {
995 let freq = 10_f32.powf(i as f32);
996 let sample_rate = 44_100;
997
998 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
999
1000 let options = PeriodicWaveOptions {
1001 real: Some(vec![0., 0., 0.]),
1002 imag: Some(vec![0., 0.5, 0.5]),
1003 disable_normalization: true,
1005 };
1006
1007 let periodic_wave = context.create_periodic_wave(options);
1008
1009 let mut osc = context.create_oscillator();
1010 osc.connect(&context.destination());
1011 osc.set_periodic_wave(periodic_wave);
1012 osc.frequency().set_value(freq);
1013 osc.start_at(0.);
1014
1015 let output = context.start_rendering_sync();
1016 let result = output.get_channel_data(0);
1017
1018 let mut expected = Vec::<f32>::with_capacity(sample_rate);
1019 let mut phase: f64 = 0.;
1020 let phase_incr = freq as f64 / sample_rate as f64;
1021
1022 for _i in 0..sample_rate {
1023 let mut sample = 0.;
1024 sample += 0.5 * (1. * phase * 2. * PI).sin();
1025 sample += 0.5 * (2. * phase * 2. * PI).sin();
1026
1027 expected.push(sample as f32);
1028
1029 phase += phase_incr;
1030 if phase >= 1. {
1031 phase -= 1.;
1032 }
1033 }
1034
1035 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1036 }
1037 }
1038
1039 #[test]
1040 fn polyblep_isolated() {
1041 {
1047 let mut signal = [1., 1., 1., 1., -1., -1., -1., -1.];
1048 let len = signal.len() as f64;
1049 let dt = 1. / len;
1050
1051 for (index, s) in signal.iter_mut().enumerate() {
1052 let phase = index as f64 / len;
1053
1054 *s += OscillatorRenderer::poly_blep(phase, dt, false);
1055 *s -= OscillatorRenderer::poly_blep((phase + 0.5) % 1., dt, false);
1056 }
1057
1058 let expected = [0., 1., 1., 1., 0., -1., -1., -1.];
1059
1060 assert_float_eq!(signal[..], expected[..], abs_all <= 0.);
1061 }
1062
1063 {
1065 let mut signal = [0., 0.25, 0.75, 1., -1., -0.75, -0.5, -0.25];
1066 let len = signal.len() as f64;
1067 let dt = 1. / len;
1068
1069 for (index, s) in signal.iter_mut().enumerate() {
1070 let phase = index as f64 / len;
1071 *s -= OscillatorRenderer::poly_blep((phase + 0.5) % 1., dt, false);
1072 }
1073
1074 let expected = [0., 0.25, 0.75, 1., 0., -0.75, -0.5, -0.25];
1075 assert_float_eq!(signal[..], expected[..], abs_all <= 0.);
1076 }
1077 }
1078
1079 #[test]
1080 fn osc_sub_quantum_start() {
1081 let freq = 1.25;
1082 let sample_rate = 44_100;
1083
1084 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1085 let mut osc = context.create_oscillator();
1086 osc.connect(&context.destination());
1087 osc.frequency().set_value(freq);
1088 osc.start_at(2. / sample_rate as f64);
1089
1090 let output = context.start_rendering_sync();
1091 let result = output.get_channel_data(0);
1092
1093 let mut expected = Vec::<f32>::with_capacity(sample_rate);
1094 let mut phase: f64 = 0.;
1095 let phase_incr = freq as f64 / sample_rate as f64;
1096
1097 expected.push(0.);
1098 expected.push(0.);
1099
1100 for _i in 2..sample_rate {
1101 let sample = (phase * 2. * PI).sin();
1102 phase += phase_incr;
1103 expected.push(sample as f32);
1104 }
1105
1106 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1107 }
1108
1109 #[test]
1112 fn osc_sub_sample_start() {
1113 let freq = 1.;
1114 let sample_rate = 96000;
1115
1116 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1117 let mut osc = context.create_oscillator();
1118 osc.connect(&context.destination());
1119 osc.frequency().set_value(freq);
1120 osc.start_at(1.3 / sample_rate as f64);
1122
1123 let output = context.start_rendering_sync();
1124 let result = output.get_channel_data(0);
1125
1126 let mut expected = Vec::<f32>::with_capacity(sample_rate);
1127 let phase_incr = freq as f64 / sample_rate as f64;
1128 let mut phase: f64 = 0.7 * phase_incr;
1130
1131 expected.push(0.);
1132 expected.push(0.);
1133
1134 for _i in 2..sample_rate {
1135 let sample = (phase * 2. * PI).sin();
1136 phase += phase_incr;
1137 expected.push(sample as f32);
1138 }
1139
1140 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1141 }
1142
1143 #[test]
1144 fn osc_sub_quantum_stop() {
1145 let freq = 2345.6;
1146 let sample_rate = 44_100;
1147
1148 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1149 let mut osc = context.create_oscillator();
1150 osc.connect(&context.destination());
1151 osc.frequency().set_value(freq);
1152 osc.start_at(0.);
1153 osc.stop_at(6. / sample_rate as f64);
1154
1155 let output = context.start_rendering_sync();
1156 let result = output.get_channel_data(0);
1157
1158 let mut expected = Vec::<f32>::with_capacity(sample_rate);
1159 let mut phase: f64 = 0.;
1160 let phase_incr = freq as f64 / sample_rate as f64;
1161
1162 for i in 0..sample_rate {
1163 if i < 6 {
1164 let sample = (phase * 2. * PI).sin();
1165 phase += phase_incr;
1166 expected.push(sample as f32);
1167 } else {
1168 expected.push(0.);
1169 }
1170 }
1171
1172 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1173 }
1174
1175 #[test]
1176 fn osc_stop_disarms_future_start() {
1177 let sample_rate = 44_100;
1178 let future_start = 2. / sample_rate as f64;
1179
1180 let mut context = OfflineAudioContext::new(1, 128, sample_rate as f32);
1181 let mut osc = context.create_oscillator();
1182 osc.connect(&context.destination());
1183 osc.start_at(future_start);
1184 osc.stop();
1185
1186 let output = context.start_rendering_sync();
1187 let result = output.get_channel_data(0);
1188
1189 assert_float_eq!(result[..], vec![0.; 128][..], abs_all <= 0.);
1190 }
1191
1192 #[test]
1193 fn osc_stop_before_start_triggers_onended_without_waiting_for_start_time() {
1194 use std::sync::atomic::{AtomicBool, Ordering};
1195 use std::sync::Arc;
1196
1197 let sample_rate = 44_100.;
1198 let future_start = 2. * RENDER_QUANTUM_SIZE as f64 / sample_rate;
1199 let suspend_at = RENDER_QUANTUM_SIZE as f64 / sample_rate;
1200
1201 let ended = Arc::new(AtomicBool::new(false));
1202 let ended_in_callback = Arc::clone(&ended);
1203 let ended_after_render = Arc::clone(&ended);
1204
1205 let mut context = OfflineAudioContext::new(1, RENDER_QUANTUM_SIZE * 4, sample_rate as f32);
1206 let mut osc = context.create_oscillator();
1207 osc.connect(&context.destination());
1208 osc.start_at(future_start);
1209 osc.set_onended(move |_| {
1210 ended_in_callback.store(true, Ordering::Relaxed);
1211 });
1212 osc.stop();
1213
1214 context.suspend_sync(suspend_at, move |_| {
1215 assert!(ended_after_render.load(Ordering::Relaxed));
1216 });
1217
1218 let _ = context.start_rendering_sync();
1219 assert!(ended.load(Ordering::Relaxed));
1220 }
1221
1222 #[test]
1223 fn osc_sub_sample_stop() {
1224 let freq = 8910.1;
1225 let sample_rate = 44_100;
1226
1227 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1228 let mut osc = context.create_oscillator();
1229 osc.connect(&context.destination());
1230 osc.frequency().set_value(freq);
1231 osc.start_at(0.);
1232 osc.stop_at(19.4 / sample_rate as f64);
1233
1234 let output = context.start_rendering_sync();
1235 let result = output.get_channel_data(0);
1236
1237 let mut expected = Vec::<f32>::with_capacity(sample_rate);
1238 let mut phase: f64 = 0.;
1239 let phase_incr = freq as f64 / sample_rate as f64;
1240
1241 for i in 0..sample_rate {
1242 if i < 20 {
1243 let sample = (phase * 2. * PI).sin();
1244 phase += phase_incr;
1245 expected.push(sample as f32);
1246 } else {
1247 expected.push(0.);
1248 }
1249 }
1250
1251 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1252 }
1253
1254 #[test]
1255 fn test_start_in_the_past() {
1256 let freq = 8910.1;
1257 let sample_rate = 44_100;
1258
1259 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1260
1261 context.suspend_sync(128. / sample_rate as f64, move |context| {
1262 let mut osc = context.create_oscillator();
1263 osc.connect(&context.destination());
1264 osc.frequency().set_value(freq);
1265 osc.start_at(0.);
1266 });
1267
1268 let output = context.start_rendering_sync();
1269 let result = output.get_channel_data(0);
1270
1271 let mut expected = Vec::<f32>::with_capacity(sample_rate);
1272 let mut phase: f64 = 0.;
1273 let phase_incr = freq as f64 / sample_rate as f64;
1274
1275 for i in 0..sample_rate {
1276 if i < 128 {
1277 expected.push(0.);
1278 } else {
1279 let sample = (phase * 2. * PI).sin();
1280 expected.push(sample as f32);
1281 phase += phase_incr;
1282 }
1283 }
1284
1285 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1286 }
1287}