1use aether_core::{node::DspNode, param::ParamBlock, state::StateBlob, BUFFER_SIZE, MAX_INPUTS};
12use std::f32::consts::TAU;
13
14#[derive(Clone, Copy)]
15struct LfoState {
16 phase: f32,
17 held_value: f32,
18 smooth_target: f32,
19 smooth_current: f32,
20}
21
22pub struct Lfo {
23 phase: f32,
24 held_value: f32,
25 smooth_target: f32,
26 smooth_current: f32,
27 prev_phase: f32,
28}
29
30impl Lfo {
31 pub fn new() -> Self {
32 Self {
33 phase: 0.0,
34 held_value: 0.0,
35 smooth_target: 0.0,
36 smooth_current: 0.0,
37 prev_phase: 0.0,
38 }
39 }
40
41 #[inline(always)]
42 fn next_sample(&mut self, rate: f32, depth: f32, waveform: u32, sr: f32) -> f32 {
43 let phase_inc = rate / sr;
44 let crossed_zero = self.phase < self.prev_phase; let raw = match waveform {
47 0 => (self.phase * TAU).sin(),
48 1 => {
49 if self.phase < 0.5 {
50 4.0 * self.phase - 1.0
51 } else {
52 3.0 - 4.0 * self.phase
53 }
54 }
55 2 => if self.phase < 0.5 { 1.0 } else { -1.0 },
56 3 => {
57 if crossed_zero {
59 self.held_value = pseudo_random(self.phase) * 2.0 - 1.0;
60 }
61 self.held_value
62 }
63 _ => {
64 if crossed_zero {
66 self.smooth_target = pseudo_random(self.phase) * 2.0 - 1.0;
67 }
68 let smooth_rate = rate / sr * 0.1;
69 self.smooth_current += (self.smooth_target - self.smooth_current) * smooth_rate;
70 self.smooth_current
71 }
72 };
73
74 self.prev_phase = self.phase;
75 self.phase = (self.phase + phase_inc).fract();
76 raw * depth
77 }
78}
79
80#[inline(always)]
82fn pseudo_random(seed: f32) -> f32 {
83 let x = (seed * 127.1 + 311.7).sin() * 43_758.547;
84 x - x.floor()
85}
86
87impl Default for Lfo {
88 fn default() -> Self {
89 Self::new()
90 }
91}
92
93impl DspNode for Lfo {
94 fn process(
95 &mut self,
96 _inputs: &[Option<&[f32; BUFFER_SIZE]>; MAX_INPUTS],
97 output: &mut [f32; BUFFER_SIZE],
98 params: &mut ParamBlock,
99 sample_rate: f32,
100 ) {
101 for sample in output.iter_mut() {
102 let rate = params.get(0).current.clamp(0.01, 20.0);
103 let depth = params.get(1).current.clamp(0.0, 1.0);
104 let waveform = params.get(2).current as u32;
105 *sample = self.next_sample(rate, depth, waveform, sample_rate);
106 params.tick_all();
107 }
108 }
109
110 fn capture_state(&self) -> StateBlob {
111 StateBlob::from_value(&LfoState {
112 phase: self.phase,
113 held_value: self.held_value,
114 smooth_target: self.smooth_target,
115 smooth_current: self.smooth_current,
116 })
117 }
118
119 fn restore_state(&mut self, state: StateBlob) {
120 let s: LfoState = state.to_value();
121 self.phase = s.phase;
122 self.held_value = s.held_value;
123 self.smooth_target = s.smooth_target;
124 self.smooth_current = s.smooth_current;
125 }
126
127 fn type_name(&self) -> &'static str {
128 "Lfo"
129 }
130}