currawong_core/
oscillator.rs1use 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}