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 => {
56 if self.phase < 0.5 {
57 1.0
58 } else {
59 -1.0
60 }
61 }
62 3 => {
63 if crossed_zero {
65 self.held_value = pseudo_random(self.phase) * 2.0 - 1.0;
66 }
67 self.held_value
68 }
69 _ => {
70 if crossed_zero {
72 self.smooth_target = pseudo_random(self.phase) * 2.0 - 1.0;
73 }
74 let smooth_rate = rate / sr * 0.1;
75 self.smooth_current += (self.smooth_target - self.smooth_current) * smooth_rate;
76 self.smooth_current
77 }
78 };
79
80 self.prev_phase = self.phase;
81 self.phase = (self.phase + phase_inc).fract();
82 raw * depth
83 }
84}
85
86#[inline(always)]
88fn pseudo_random(seed: f32) -> f32 {
89 let x = (seed * 127.1 + 311.7).sin() * 43_758.547;
90 x - x.floor()
91}
92
93impl Default for Lfo {
94 fn default() -> Self {
95 Self::new()
96 }
97}
98
99impl DspNode for Lfo {
100 fn process(
101 &mut self,
102 _inputs: &[Option<&[f32; BUFFER_SIZE]>; MAX_INPUTS],
103 output: &mut [f32; BUFFER_SIZE],
104 params: &mut ParamBlock,
105 sample_rate: f32,
106 ) {
107 for sample in output.iter_mut() {
108 let rate = params.get(0).current.clamp(0.01, 20.0);
109 let depth = params.get(1).current.clamp(0.0, 1.0);
110 let waveform = params.get(2).current as u32;
111 *sample = self.next_sample(rate, depth, waveform, sample_rate);
112 params.tick_all();
113 }
114 }
115
116 fn capture_state(&self) -> StateBlob {
117 StateBlob::from_value(&LfoState {
118 phase: self.phase,
119 held_value: self.held_value,
120 smooth_target: self.smooth_target,
121 smooth_current: self.smooth_current,
122 })
123 }
124
125 fn restore_state(&mut self, state: StateBlob) {
126 let s: LfoState = state.to_value();
127 self.phase = s.phase;
128 self.held_value = s.held_value;
129 self.smooth_target = s.smooth_target;
130 self.smooth_current = s.smooth_current;
131 }
132
133 fn type_name(&self) -> &'static str {
134 "Lfo"
135 }
136}