currawong_core/
oscillator.rs

1use crate::signal::{const_, Sf64, Sfreq, Signal, Trigger};
2use std::{cell::Cell, f64::consts::PI};
3
4#[derive(Default, Clone, Copy, Debug)]
5pub enum Waveform {
6    #[default]
7    Sine,
8    Saw,
9    Triangle,
10    Pulse,
11}
12
13impl From<Waveform> for Signal<Waveform> {
14    fn from(value: Waveform) -> Self {
15        const_(value)
16    }
17}
18
19pub struct Oscillator {
20    pub waveform: Signal<Waveform>,
21    pub freq: Sfreq,
22    pub pulse_width_01: Sf64,
23    pub reset_trigger: Trigger,
24    pub reset_offset_01: Sf64,
25}
26
27impl Oscillator {
28    pub fn signal(self) -> Sf64 {
29        let state_opt = Cell::new(None);
30        let prev_sample_inedx = Cell::new(0);
31        Signal::from_fn(move |ctx| {
32            let sample_index_delta = ctx.sample_index - prev_sample_inedx.get();
33            prev_sample_inedx.set(ctx.sample_index);
34            if sample_index_delta == 0 {
35                return 0.0;
36            }
37            let state = match state_opt.get() {
38                None => self.reset_offset_01.sample(ctx),
39                Some(state) => {
40                    if self.reset_trigger.sample(ctx) {
41                        self.reset_offset_01.sample(ctx)
42                    } else {
43                        state
44                    }
45                }
46            };
47            let state_delta =
48                (sample_index_delta as f64 * self.freq.sample(ctx).hz()) / ctx.sample_rate_hz;
49            let try_state = (state + state_delta).rem_euclid(1.0);
50            let state = if try_state.is_nan() { state } else { try_state };
51            state_opt.set(Some(state));
52            match self.waveform.sample(ctx) {
53                Waveform::Sine => (state * PI * 2.0).sin(),
54                Waveform::Saw => (state * 2.0) - 1.0,
55                Waveform::Triangle => (((state * 2.0) - 1.0).abs() * 2.0) - 1.0,
56                Waveform::Pulse => {
57                    if state < self.pulse_width_01.sample(ctx) {
58                        -1.0
59                    } else {
60                        1.0
61                    }
62                }
63            }
64        })
65    }
66}