1use std::any::Any;
2use std::f32::consts::PI;
3use std::fmt::Debug;
4use std::sync::OnceLock;
5
6use crate::context::{AudioContextRegistration, AudioParamId, BaseAudioContext};
7use crate::param::{AudioParam, AudioParamDescriptor, AutomationRate};
8use crate::render::{
9 AudioParamValues, AudioProcessor, AudioRenderQuantum, AudioWorkletGlobalScope,
10};
11use crate::PeriodicWave;
12use crate::{assert_valid_time_value, RENDER_QUANTUM_SIZE};
13
14use super::{AudioNode, AudioNodeOptions, AudioScheduledSourceNode, ChannelConfig};
15
16const SINE_TABLE_LENGTH_USIZE: usize = 2048;
17const SINE_TABLE_LENGTH_F32: f32 = SINE_TABLE_LENGTH_USIZE as f32;
18
19fn precomputed_sine_table() -> &'static [f32] {
21 static INSTANCE: OnceLock<Vec<f32>> = OnceLock::new();
22 INSTANCE.get_or_init(|| {
23 (0..SINE_TABLE_LENGTH_USIZE)
25 .map(|x| ((x as f32) * 2.0 * PI * (1. / (SINE_TABLE_LENGTH_F32))).sin())
26 .collect()
27 })
28}
29
30fn get_phase_incr(freq: f32, detune: f32, sample_rate: f64) -> f64 {
31 let computed_freq = freq as f64 * (detune as f64 / 1200.).exp2();
32 let clamped = computed_freq.clamp(-sample_rate / 2., sample_rate / 2.);
33 clamped / sample_rate
34}
35
36#[derive(Clone, Debug)]
49pub struct OscillatorOptions {
50 pub type_: OscillatorType,
52 pub frequency: f32,
54 pub detune: f32,
56 pub periodic_wave: Option<PeriodicWave>,
58 pub audio_node_options: AudioNodeOptions,
60}
61
62impl Default for OscillatorOptions {
63 fn default() -> Self {
64 Self {
65 type_: OscillatorType::default(),
66 frequency: 440.,
67 detune: 0.,
68 periodic_wave: None,
69 audio_node_options: AudioNodeOptions::default(),
70 }
71 }
72}
73
74#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
76pub enum OscillatorType {
77 #[default]
79 Sine,
80 Square,
82 Sawtooth,
84 Triangle,
86 Custom,
88}
89
90impl From<u32> for OscillatorType {
91 fn from(i: u32) -> Self {
92 match i {
93 0 => OscillatorType::Sine,
94 1 => OscillatorType::Square,
95 2 => OscillatorType::Sawtooth,
96 3 => OscillatorType::Triangle,
97 4 => OscillatorType::Custom,
98 _ => unreachable!(),
99 }
100 }
101}
102
103#[derive(Debug, Copy, Clone)]
105enum Schedule {
106 Start(f64),
107 Stop(f64),
108}
109
110#[derive(Debug)]
140pub struct OscillatorNode {
141 registration: AudioContextRegistration,
143 channel_config: ChannelConfig,
145 frequency: AudioParam,
147 detune: AudioParam,
149 type_: OscillatorType,
151 has_start: bool,
153}
154
155impl AudioNode for OscillatorNode {
156 fn registration(&self) -> &AudioContextRegistration {
157 &self.registration
158 }
159
160 fn channel_config(&self) -> &ChannelConfig {
161 &self.channel_config
162 }
163
164 fn number_of_inputs(&self) -> usize {
166 0
167 }
168
169 fn number_of_outputs(&self) -> usize {
171 1
172 }
173}
174
175impl AudioScheduledSourceNode for OscillatorNode {
176 fn start(&mut self) {
177 let when = self.registration.context().current_time();
178 self.start_at(when);
179 }
180
181 fn start_at(&mut self, when: f64) {
182 assert_valid_time_value(when);
183 assert!(
184 !self.has_start,
185 "InvalidStateError - Cannot call `start` twice"
186 );
187
188 self.has_start = true;
189 self.registration.post_message(Schedule::Start(when));
190 }
191
192 fn stop(&mut self) {
193 let when = self.registration.context().current_time();
194 self.stop_at(when);
195 }
196
197 fn stop_at(&mut self, when: f64) {
198 assert_valid_time_value(when);
199 assert!(self.has_start, "InvalidStateError cannot stop before start");
200
201 self.registration.post_message(Schedule::Stop(when));
202 }
203}
204
205impl OscillatorNode {
206 pub fn new<C: BaseAudioContext>(context: &C, options: OscillatorOptions) -> Self {
213 let OscillatorOptions {
214 type_,
215 frequency,
216 detune,
217 audio_node_options: channel_config,
218 periodic_wave,
219 } = options;
220
221 let mut node = context.base().register(move |registration| {
222 let sample_rate = context.sample_rate();
223 let nyquist = sample_rate / 2.;
224
225 let freq_param_options = AudioParamDescriptor {
227 name: String::new(),
228 min_value: -nyquist,
229 max_value: nyquist,
230 default_value: 440.,
231 automation_rate: AutomationRate::A,
232 };
233 let (f_param, f_proc) = context.create_audio_param(freq_param_options, ®istration);
234 f_param.set_value(frequency);
235
236 let det_param_options = AudioParamDescriptor {
238 name: String::new(),
239 min_value: -153_600.,
240 max_value: 153_600.,
241 default_value: 0.,
242 automation_rate: AutomationRate::A,
243 };
244 let (det_param, det_proc) =
245 context.create_audio_param(det_param_options, ®istration);
246 det_param.set_value(detune);
247
248 let renderer = OscillatorRenderer {
249 type_,
250 frequency: f_proc,
251 detune: det_proc,
252 phase: 0.,
253 start_time: f64::MAX,
254 stop_time: f64::MAX,
255 started: false,
256 periodic_wave: None,
257 ended_triggered: false,
258 sine_table: precomputed_sine_table(),
259 };
260
261 let node = Self {
262 registration,
263 channel_config: channel_config.into(),
264 frequency: f_param,
265 detune: det_param,
266 type_,
267 has_start: false,
268 };
269
270 (node, Box::new(renderer))
271 });
272
273 if let Some(p_wave) = periodic_wave {
275 node.set_periodic_wave(p_wave);
276 }
277
278 node
279 }
280
281 #[must_use]
286 pub fn frequency(&self) -> &AudioParam {
287 &self.frequency
288 }
289
290 #[must_use]
297 pub fn detune(&self) -> &AudioParam {
298 &self.detune
299 }
300
301 #[must_use]
303 pub fn type_(&self) -> OscillatorType {
304 self.type_
305 }
306
307 pub fn set_type(&mut self, type_: OscillatorType) {
317 assert_ne!(
318 type_,
319 OscillatorType::Custom,
320 "InvalidStateError: Custom type cannot be set manually"
321 );
322
323 if self.type_ == OscillatorType::Custom {
325 return;
326 }
327
328 self.type_ = type_;
329 self.registration.post_message(type_);
330 }
331
332 pub fn set_periodic_wave(&mut self, periodic_wave: PeriodicWave) {
337 self.type_ = OscillatorType::Custom;
338 self.registration.post_message(periodic_wave);
339 }
340}
341
342struct OscillatorRenderer {
344 type_: OscillatorType,
346 frequency: AudioParamId,
348 detune: AudioParamId,
350 phase: f64,
352 start_time: f64,
354 stop_time: f64,
356 started: bool,
358 periodic_wave: Option<PeriodicWave>,
360 ended_triggered: bool,
362 sine_table: &'static [f32],
364}
365
366impl AudioProcessor for OscillatorRenderer {
367 fn process(
368 &mut self,
369 _inputs: &[AudioRenderQuantum],
370 outputs: &mut [AudioRenderQuantum],
371 params: AudioParamValues<'_>,
372 scope: &AudioWorkletGlobalScope,
373 ) -> bool {
374 let output = &mut outputs[0];
376 output.set_number_of_channels(1);
378
379 let sample_rate = scope.sample_rate as f64;
380 let dt = 1. / sample_rate;
381 let num_frames = RENDER_QUANTUM_SIZE;
382 let next_block_time = scope.current_time + dt * num_frames as f64;
383
384 if self.stop_time <= scope.current_time {
385 output.make_silent();
386
387 if !self.ended_triggered {
388 scope.send_ended_event();
389 self.ended_triggered = true;
390 }
391
392 return false;
393 } else if self.start_time >= next_block_time {
394 output.make_silent();
395
396 if self.stop_time <= next_block_time {
397 if !self.ended_triggered {
398 scope.send_ended_event();
399 self.ended_triggered = true;
400 }
401
402 return false;
403 }
404
405 return self.start_time != f64::MAX;
408 }
409
410 let channel_data = output.channel_data_mut(0);
411 let frequency_values = params.get(&self.frequency);
412 let detune_values = params.get(&self.detune);
413
414 let mut current_time = scope.current_time;
415
416 if !self.started && self.start_time < current_time {
422 self.start_time = current_time;
423 }
424
425 if frequency_values.len() == 1 && detune_values.len() == 1 {
426 let phase_incr = get_phase_incr(frequency_values[0], detune_values[0], sample_rate);
427 channel_data
428 .iter_mut()
429 .for_each(|output| self.generate_sample(output, phase_incr, &mut current_time, dt));
430 } else {
431 channel_data
432 .iter_mut()
433 .zip(frequency_values.iter().cycle())
434 .zip(detune_values.iter().cycle())
435 .for_each(|((output, &f), &d)| {
436 let phase_incr = get_phase_incr(f, d, sample_rate);
437 self.generate_sample(output, phase_incr, &mut current_time, dt)
438 });
439 }
440
441 if self.stop_time <= next_block_time {
442 if !self.ended_triggered {
443 scope.send_ended_event();
444 self.ended_triggered = true;
445 }
446
447 return false;
448 }
449
450 true
451 }
452
453 fn onmessage(&mut self, msg: &mut dyn Any) {
454 if let Some(&type_) = msg.downcast_ref::<OscillatorType>() {
455 self.type_ = type_;
456 return;
457 }
458
459 if let Some(&schedule) = msg.downcast_ref::<Schedule>() {
460 match schedule {
461 Schedule::Start(v) => self.start_time = v,
462 Schedule::Stop(v) => self.stop_time = v,
463 }
464 return;
465 }
466
467 if let Some(periodic_wave) = msg.downcast_mut::<PeriodicWave>() {
468 if let Some(current_periodic_wave) = &mut self.periodic_wave {
469 std::mem::swap(current_periodic_wave, periodic_wave)
471 } else {
472 self.periodic_wave = Some(std::mem::take(periodic_wave));
474 }
475 self.type_ = OscillatorType::Custom; return;
477 }
478
479 log::warn!("OscillatorRenderer: Dropping incoming message {msg:?}");
480 }
481
482 fn before_drop(&mut self, scope: &AudioWorkletGlobalScope) {
483 if !self.ended_triggered
484 && (scope.current_time >= self.start_time || scope.current_time >= self.stop_time)
485 {
486 scope.send_ended_event();
487 self.ended_triggered = true;
488 }
489 }
490}
491impl OscillatorRenderer {
492 #[inline]
493 fn generate_sample(
494 &mut self,
495 output: &mut f32,
496 phase_incr: f64,
497 current_time: &mut f64,
498 dt: f64,
499 ) {
500 if *current_time < self.start_time || *current_time >= self.stop_time {
501 *output = 0.;
502 *current_time += dt;
503
504 return;
505 }
506
507 if !self.started {
509 if *current_time > self.start_time {
512 let ratio = (*current_time - self.start_time) / dt;
513 self.phase = Self::unroll_phase(phase_incr * ratio);
514 }
515
516 self.started = true;
517 }
518
519 *output = match self.type_ {
524 OscillatorType::Sine => self.generate_sine(),
525 OscillatorType::Sawtooth => self.generate_sawtooth(phase_incr),
526 OscillatorType::Square => self.generate_square(phase_incr),
527 OscillatorType::Triangle => self.generate_triangle(),
528 OscillatorType::Custom => self.generate_custom(),
529 };
530
531 *current_time += dt;
532
533 self.phase = Self::unroll_phase(self.phase + phase_incr);
534 }
535
536 #[inline]
537 fn generate_sine(&mut self) -> f32 {
538 let position = self.phase * SINE_TABLE_LENGTH_USIZE as f64;
539 let floored = position.floor();
540
541 let prev_index = floored as usize;
542 let mut next_index = prev_index + 1;
543 if next_index == SINE_TABLE_LENGTH_USIZE {
544 next_index = 0;
545 }
546
547 let k = (position - floored) as f32;
549 self.sine_table[prev_index].mul_add(1. - k, self.sine_table[next_index] * k)
550 }
551
552 #[inline]
553 fn generate_sawtooth(&mut self, phase_incr: f64) -> f32 {
554 let phase = Self::unroll_phase(self.phase + 0.5);
556 let mut sample = 2.0 * phase - 1.0;
557 sample -= Self::poly_blep(phase, phase_incr, cfg!(test));
558
559 sample as f32
560 }
561
562 #[inline]
563 fn generate_square(&mut self, phase_incr: f64) -> f32 {
564 let mut sample = if self.phase < 0.5 { 1.0 } else { -1.0 };
565 sample += Self::poly_blep(self.phase, phase_incr, cfg!(test));
566
567 let shift_phase = Self::unroll_phase(self.phase + 0.5);
568 sample -= Self::poly_blep(shift_phase, phase_incr, cfg!(test));
569
570 sample as f32
571 }
572
573 #[inline]
574 fn generate_triangle(&mut self) -> f32 {
575 let mut sample = -4. * self.phase + 2.;
576
577 if sample > 1. {
578 sample = 2. - sample;
579 } else if sample < -1. {
580 sample = -2. - sample;
581 }
582
583 sample as f32
584 }
585
586 #[inline]
587 fn generate_custom(&mut self) -> f32 {
588 let periodic_wave = self.periodic_wave.as_ref().unwrap().as_slice();
589 let table_length = periodic_wave.len();
590 let position = self.phase * table_length as f64;
591 let floored = position.floor();
592
593 let prev_index = floored as usize;
594 let mut next_index = prev_index + 1;
595 if next_index == table_length {
596 next_index = 0;
597 }
598
599 let k = (position - floored) as f32;
601 periodic_wave[prev_index].mul_add(1. - k, periodic_wave[next_index] * k)
602 }
603
604 #[inline]
612 fn poly_blep(mut t: f64, dt: f64, is_test: bool) -> f64 {
613 if is_test {
614 0.
615 } else if t < dt {
616 t /= dt;
617 t + t - t * t - 1.0
618 } else if t > 1.0 - dt {
619 t = (t - 1.0) / dt;
620 t.mul_add(t, t) + t + 1.0
621 } else {
622 0.0
623 }
624 }
625
626 #[inline]
627 fn unroll_phase(mut phase: f64) -> f64 {
628 if phase >= 1. {
629 phase -= 1.
630 }
631
632 phase
633 }
634}
635
636#[cfg(test)]
637mod tests {
638 use float_eq::assert_float_eq;
639 use std::f64::consts::PI;
640
641 use crate::context::{BaseAudioContext, OfflineAudioContext};
642 use crate::node::{AudioNode, AudioScheduledSourceNode};
643 use crate::periodic_wave::{PeriodicWave, PeriodicWaveOptions};
644 use crate::RENDER_QUANTUM_SIZE;
645
646 use super::{OscillatorNode, OscillatorOptions, OscillatorRenderer, OscillatorType};
647
648 #[test]
649 fn assert_osc_default_build_with_factory_func() {
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 = context.create_oscillator();
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 fn assert_osc_default_build() {
674 let default_freq = 440.;
675 let default_det = 0.;
676 let default_type = OscillatorType::Sine;
677
678 let mut context = OfflineAudioContext::new(2, 1, 44_100.);
679
680 let mut osc = OscillatorNode::new(&context, OscillatorOptions::default());
681
682 let freq = osc.frequency.value();
683 assert_float_eq!(freq, default_freq, abs_all <= 0.);
684
685 let det = osc.detune.value();
686 assert_float_eq!(det, default_det, abs_all <= 0.);
687
688 assert_eq!(osc.type_(), default_type);
689
690 osc.start();
692 osc.connect(&context.destination());
693 let _ = context.start_rendering_sync();
694 }
695
696 #[test]
697 #[should_panic]
698 fn set_type_to_custom_should_panic() {
699 let context = OfflineAudioContext::new(2, 1, 44_100.);
700 let mut osc = OscillatorNode::new(&context, OscillatorOptions::default());
701 osc.set_type(OscillatorType::Custom);
702 }
703
704 #[test]
705 fn type_is_custom_when_periodic_wave_is_some() {
706 let expected_type = OscillatorType::Custom;
707
708 let mut context = OfflineAudioContext::new(2, 1, 44_100.);
709
710 let periodic_wave = PeriodicWave::new(&context, PeriodicWaveOptions::default());
711
712 let options = OscillatorOptions {
713 periodic_wave: Some(periodic_wave),
714 ..OscillatorOptions::default()
715 };
716
717 let mut osc = OscillatorNode::new(&context, options);
718
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]
728 fn set_type_is_ignored_when_periodic_wave_is_some() {
729 let expected_type = OscillatorType::Custom;
730
731 let mut context = OfflineAudioContext::new(2, 1, 44_100.);
732
733 let periodic_wave = PeriodicWave::new(&context, PeriodicWaveOptions::default());
734
735 let options = OscillatorOptions {
736 periodic_wave: Some(periodic_wave),
737 ..OscillatorOptions::default()
738 };
739
740 let mut osc = OscillatorNode::new(&context, options);
741
742 osc.set_type(OscillatorType::Sine);
743 assert_eq!(osc.type_(), expected_type);
744
745 osc.start();
747 osc.connect(&context.destination());
748 let _ = context.start_rendering_sync();
749 }
750
751 #[test]
765 fn sine_raw() {
766 for i in 0..5 {
768 let freq = 10_f32.powf(i as f32);
769 let sample_rate = 44_100;
770
771 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
772
773 let mut osc = context.create_oscillator();
774 osc.connect(&context.destination());
775 osc.frequency().set_value(freq);
776 osc.start_at(0.);
777
778 let output = context.start_rendering_sync();
779 let result = output.get_channel_data(0);
780
781 let mut expected = Vec::<f32>::with_capacity(sample_rate);
782 let mut phase: f64 = 0.;
783 let phase_incr = freq as f64 / sample_rate as f64;
784
785 for _i in 0..sample_rate {
786 let sample = (phase * 2. * PI).sin();
787
788 expected.push(sample as f32);
789
790 phase += phase_incr;
791 if phase >= 1. {
792 phase -= 1.;
793 }
794 }
795
796 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
797 }
798 }
799
800 #[test]
801 fn sine_raw_exact_phase() {
802 for i in 0..5 {
804 let freq = 10_f32.powf(i as f32);
805 let sample_rate = 44_100;
806
807 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
808
809 let mut osc = context.create_oscillator();
810 osc.connect(&context.destination());
811 osc.frequency().set_value(freq);
812 osc.start_at(0.);
813
814 let output = context.start_rendering_sync();
815 let result = output.get_channel_data(0);
816 let mut expected = Vec::<f32>::with_capacity(sample_rate);
817
818 for i in 0..sample_rate {
819 let phase = freq as f64 * i as f64 / sample_rate as f64;
820 let sample = (phase * 2. * PI).sin();
821 expected.push(sample as f32);
823 }
824
825 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
826 }
827 }
828
829 #[test]
830 fn square_raw() {
831 for i in 0..5 {
833 let freq = 10_f32.powf(i as f32);
834 let sample_rate = 44100;
835
836 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
837
838 let mut osc = context.create_oscillator();
839 osc.connect(&context.destination());
840 osc.frequency().set_value(freq);
841 osc.set_type(OscillatorType::Square);
842 osc.start_at(0.);
843
844 let output = context.start_rendering_sync();
845 let result = output.get_channel_data(0);
846
847 let mut expected = Vec::<f32>::with_capacity(sample_rate);
848 let mut phase: f64 = 0.;
849 let phase_incr = freq as f64 / sample_rate as f64;
850
851 for _i in 0..sample_rate {
852 let sample = if phase < 0.5 { 1. } else { -1. };
854
855 expected.push(sample as f32);
856
857 phase += phase_incr;
858 if phase >= 1. {
859 phase -= 1.;
860 }
861 }
862
863 assert_float_eq!(result[..], expected[..], abs_all <= 1e-10);
864 }
865 }
866
867 #[test]
868 fn triangle_raw() {
869 for i in 0..5 {
871 let freq = 10_f32.powf(i as f32);
872 let sample_rate = 44_100;
873
874 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
875
876 let mut osc = context.create_oscillator();
877 osc.connect(&context.destination());
878 osc.frequency().set_value(freq);
879 osc.set_type(OscillatorType::Triangle);
880 osc.start_at(0.);
881
882 let output = context.start_rendering_sync();
883 let result = output.get_channel_data(0);
884
885 let mut expected = Vec::<f32>::with_capacity(sample_rate);
886 let mut phase: f64 = 0.;
887 let phase_incr = freq as f64 / sample_rate as f64;
888
889 for _i in 0..sample_rate {
890 let mut sample = -4. * phase + 2.;
895
896 if sample > 1. {
897 sample = 2. - sample;
898 } else if sample < -1. {
899 sample = -2. - sample;
900 }
901
902 expected.push(sample as f32);
903
904 phase += phase_incr;
905 if phase >= 1. {
906 phase -= 1.;
907 }
908 }
909
910 assert_float_eq!(result[..], expected[..], abs_all <= 1e-10);
911 }
912 }
913
914 #[test]
915 fn sawtooth_raw() {
916 for i in 0..5 {
918 let freq = 10_f32.powf(i as f32);
919 let sample_rate = 44_100;
920
921 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
922
923 let mut osc = context.create_oscillator();
924 osc.connect(&context.destination());
925 osc.frequency().set_value(freq);
926 osc.set_type(OscillatorType::Sawtooth);
927 osc.start_at(0.);
928
929 let output = context.start_rendering_sync();
930 let result = output.get_channel_data(0);
931
932 let mut expected = Vec::<f32>::with_capacity(sample_rate);
933 let mut phase: f64 = 0.;
934 let phase_incr = freq as f64 / sample_rate as f64;
935
936 for _i in 0..sample_rate {
937 let mut offset_phase = phase + 0.5;
941 if offset_phase >= 1. {
942 offset_phase -= 1.;
943 }
944 let sample = 2. * offset_phase - 1.;
945
946 expected.push(sample as f32);
947
948 phase += phase_incr;
949 if phase >= 1. {
950 phase -= 1.;
951 }
952 }
953
954 assert_float_eq!(result[..], expected[..], abs_all <= 1e-10);
955 }
956 }
957
958 #[test]
959 fn periodic_wave_1f() {
961 for i in 0..5 {
963 let freq = 10_f32.powf(i as f32);
964 let sample_rate = 44_100;
965
966 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
967
968 let options = PeriodicWaveOptions {
969 real: Some(vec![0., 0.]),
970 imag: Some(vec![0., 1.]), disable_normalization: false,
972 };
973
974 let periodic_wave = context.create_periodic_wave(options);
975
976 let mut osc = context.create_oscillator();
977 osc.connect(&context.destination());
978 osc.set_periodic_wave(periodic_wave);
979 osc.frequency().set_value(freq);
980 osc.set_type(OscillatorType::Sawtooth);
981 osc.start_at(0.);
982
983 let output = context.start_rendering_sync();
984 let result = output.get_channel_data(0);
985
986 let mut expected = Vec::<f32>::with_capacity(sample_rate);
987 let mut phase: f64 = 0.;
988 let phase_incr = freq as f64 / sample_rate as f64;
989
990 for _i in 0..sample_rate {
991 let sample = (phase * 2. * PI).sin();
992
993 expected.push(sample as f32);
994
995 phase += phase_incr;
996 if phase >= 1. {
997 phase -= 1.;
998 }
999 }
1000
1001 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1002 }
1003 }
1004
1005 #[test]
1006 fn periodic_wave_2f() {
1007 for i in 0..5 {
1009 let freq = 10_f32.powf(i as f32);
1010 let sample_rate = 44_100;
1011
1012 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1013
1014 let options = PeriodicWaveOptions {
1015 real: Some(vec![0., 0., 0.]),
1016 imag: Some(vec![0., 0.5, 0.5]),
1017 disable_normalization: true,
1019 };
1020
1021 let periodic_wave = context.create_periodic_wave(options);
1022
1023 let mut osc = context.create_oscillator();
1024 osc.connect(&context.destination());
1025 osc.set_periodic_wave(periodic_wave);
1026 osc.frequency().set_value(freq);
1027 osc.start_at(0.);
1028
1029 let output = context.start_rendering_sync();
1030 let result = output.get_channel_data(0);
1031
1032 let mut expected = Vec::<f32>::with_capacity(sample_rate);
1033 let mut phase: f64 = 0.;
1034 let phase_incr = freq as f64 / sample_rate as f64;
1035
1036 for _i in 0..sample_rate {
1037 let mut sample = 0.;
1038 sample += 0.5 * (1. * phase * 2. * PI).sin();
1039 sample += 0.5 * (2. * phase * 2. * PI).sin();
1040
1041 expected.push(sample as f32);
1042
1043 phase += phase_incr;
1044 if phase >= 1. {
1045 phase -= 1.;
1046 }
1047 }
1048
1049 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1050 }
1051 }
1052
1053 #[test]
1054 fn polyblep_isolated() {
1055 {
1061 let mut signal = [1., 1., 1., 1., -1., -1., -1., -1.];
1062 let len = signal.len() as f64;
1063 let dt = 1. / len;
1064
1065 for (index, s) in signal.iter_mut().enumerate() {
1066 let phase = index as f64 / len;
1067
1068 *s += OscillatorRenderer::poly_blep(phase, dt, false);
1069 *s -= OscillatorRenderer::poly_blep((phase + 0.5) % 1., dt, false);
1070 }
1071
1072 let expected = [0., 1., 1., 1., 0., -1., -1., -1.];
1073
1074 assert_float_eq!(signal[..], expected[..], abs_all <= 0.);
1075 }
1076
1077 {
1079 let mut signal = [0., 0.25, 0.75, 1., -1., -0.75, -0.5, -0.25];
1080 let len = signal.len() as f64;
1081 let dt = 1. / len;
1082
1083 for (index, s) in signal.iter_mut().enumerate() {
1084 let phase = index as f64 / len;
1085 *s -= OscillatorRenderer::poly_blep((phase + 0.5) % 1., dt, false);
1086 }
1087
1088 let expected = [0., 0.25, 0.75, 1., 0., -0.75, -0.5, -0.25];
1089 assert_float_eq!(signal[..], expected[..], abs_all <= 0.);
1090 }
1091 }
1092
1093 #[test]
1094 fn osc_sub_quantum_start() {
1095 let freq = 1.25;
1096 let sample_rate = 44_100;
1097
1098 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1099 let mut osc = context.create_oscillator();
1100 osc.connect(&context.destination());
1101 osc.frequency().set_value(freq);
1102 osc.start_at(2. / sample_rate as f64);
1103
1104 let output = context.start_rendering_sync();
1105 let result = output.get_channel_data(0);
1106
1107 let mut expected = Vec::<f32>::with_capacity(sample_rate);
1108 let mut phase: f64 = 0.;
1109 let phase_incr = freq as f64 / sample_rate as f64;
1110
1111 expected.push(0.);
1112 expected.push(0.);
1113
1114 for _i in 2..sample_rate {
1115 let sample = (phase * 2. * PI).sin();
1116 phase += phase_incr;
1117 expected.push(sample as f32);
1118 }
1119
1120 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1121 }
1122
1123 #[test]
1126 fn osc_sub_sample_start() {
1127 let freq = 1.;
1128 let sample_rate = 96000;
1129
1130 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1131 let mut osc = context.create_oscillator();
1132 osc.connect(&context.destination());
1133 osc.frequency().set_value(freq);
1134 osc.start_at(1.3 / sample_rate as f64);
1136
1137 let output = context.start_rendering_sync();
1138 let result = output.get_channel_data(0);
1139
1140 let mut expected = Vec::<f32>::with_capacity(sample_rate);
1141 let phase_incr = freq as f64 / sample_rate as f64;
1142 let mut phase: f64 = 0.7 * phase_incr;
1144
1145 expected.push(0.);
1146 expected.push(0.);
1147
1148 for _i in 2..sample_rate {
1149 let sample = (phase * 2. * PI).sin();
1150 phase += phase_incr;
1151 expected.push(sample as f32);
1152 }
1153
1154 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1155 }
1156
1157 #[test]
1158 fn osc_sub_quantum_stop() {
1159 let freq = 2345.6;
1160 let sample_rate = 44_100;
1161
1162 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1163 let mut osc = context.create_oscillator();
1164 osc.connect(&context.destination());
1165 osc.frequency().set_value(freq);
1166 osc.start_at(0.);
1167 osc.stop_at(6. / sample_rate as f64);
1168
1169 let output = context.start_rendering_sync();
1170 let result = output.get_channel_data(0);
1171
1172 let mut expected = Vec::<f32>::with_capacity(sample_rate);
1173 let mut phase: f64 = 0.;
1174 let phase_incr = freq as f64 / sample_rate as f64;
1175
1176 for i in 0..sample_rate {
1177 if i < 6 {
1178 let sample = (phase * 2. * PI).sin();
1179 phase += phase_incr;
1180 expected.push(sample as f32);
1181 } else {
1182 expected.push(0.);
1183 }
1184 }
1185
1186 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1187 }
1188
1189 #[test]
1190 fn osc_stop_disarms_future_start() {
1191 let sample_rate = 44_100;
1192 let future_start = 2. / sample_rate as f64;
1193
1194 let mut context = OfflineAudioContext::new(1, 128, sample_rate as f32);
1195 let mut osc = context.create_oscillator();
1196 osc.connect(&context.destination());
1197 osc.start_at(future_start);
1198 osc.stop();
1199
1200 let output = context.start_rendering_sync();
1201 let result = output.get_channel_data(0);
1202
1203 assert_float_eq!(result[..], vec![0.; 128][..], abs_all <= 0.);
1204 }
1205
1206 #[test]
1207 fn osc_stop_before_start_triggers_onended_without_waiting_for_start_time() {
1208 use std::sync::atomic::{AtomicBool, Ordering};
1209 use std::sync::Arc;
1210
1211 let sample_rate = 44_100.;
1212 let future_start = 2. * RENDER_QUANTUM_SIZE as f64 / sample_rate;
1213 let suspend_at = RENDER_QUANTUM_SIZE as f64 / sample_rate;
1214
1215 let ended = Arc::new(AtomicBool::new(false));
1216 let ended_in_callback = Arc::clone(&ended);
1217 let ended_after_render = Arc::clone(&ended);
1218
1219 let mut context = OfflineAudioContext::new(1, RENDER_QUANTUM_SIZE * 4, sample_rate as f32);
1220 let mut osc = context.create_oscillator();
1221 osc.connect(&context.destination());
1222 osc.start_at(future_start);
1223 osc.set_onended(move |_| {
1224 ended_in_callback.store(true, Ordering::Relaxed);
1225 });
1226 osc.stop();
1227
1228 context.suspend_sync(suspend_at, move |_| {
1229 assert!(ended_after_render.load(Ordering::Relaxed));
1230 });
1231
1232 let _ = context.start_rendering_sync();
1233 assert!(ended.load(Ordering::Relaxed));
1234 }
1235
1236 #[test]
1237 fn osc_sub_sample_stop() {
1238 let freq = 8910.1;
1239 let sample_rate = 44_100;
1240
1241 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1242 let mut osc = context.create_oscillator();
1243 osc.connect(&context.destination());
1244 osc.frequency().set_value(freq);
1245 osc.start_at(0.);
1246 osc.stop_at(19.4 / sample_rate as f64);
1247
1248 let output = context.start_rendering_sync();
1249 let result = output.get_channel_data(0);
1250
1251 let mut expected = Vec::<f32>::with_capacity(sample_rate);
1252 let mut phase: f64 = 0.;
1253 let phase_incr = freq as f64 / sample_rate as f64;
1254
1255 for i in 0..sample_rate {
1256 if i < 20 {
1257 let sample = (phase * 2. * PI).sin();
1258 phase += phase_incr;
1259 expected.push(sample as f32);
1260 } else {
1261 expected.push(0.);
1262 }
1263 }
1264
1265 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1266 }
1267
1268 #[test]
1269 fn test_start_in_the_past() {
1270 let freq = 8910.1;
1271 let sample_rate = 44_100;
1272
1273 let mut context = OfflineAudioContext::new(1, sample_rate, sample_rate as f32);
1274
1275 context.suspend_sync(128. / sample_rate as f64, move |context| {
1276 let mut osc = context.create_oscillator();
1277 osc.connect(&context.destination());
1278 osc.frequency().set_value(freq);
1279 osc.start_at(0.);
1280 });
1281
1282 let output = context.start_rendering_sync();
1283 let result = output.get_channel_data(0);
1284
1285 let mut expected = Vec::<f32>::with_capacity(sample_rate);
1286 let mut phase: f64 = 0.;
1287 let phase_incr = freq as f64 / sample_rate as f64;
1288
1289 for i in 0..sample_rate {
1290 if i < 128 {
1291 expected.push(0.);
1292 } else {
1293 let sample = (phase * 2. * PI).sin();
1294 expected.push(sample as f32);
1295 phase += phase_incr;
1296 }
1297 }
1298
1299 assert_float_eq!(result[..], expected[..], abs_all <= 1e-5);
1300 }
1301}