aether_nodes/
oscillator.rs1use aether_core::{node::DspNode, param::ParamBlock, state::StateBlob, BUFFER_SIZE, MAX_INPUTS};
10use std::f32::consts::TAU;
11
12#[derive(Clone, Copy)]
13struct OscState {
14 phase: f32,
15}
16
17pub struct Oscillator {
18 phase: f32,
19 tuning: Option<Box<[f32; 128]>>,
22}
23
24impl Oscillator {
25 pub fn new() -> Self {
26 Self { phase: 0.0, tuning: None }
27 }
28
29 pub fn set_tuning(&mut self, frequencies: [f32; 128]) {
32 self.tuning = Some(Box::new(frequencies));
33 }
34
35 pub fn clear_tuning(&mut self) {
36 self.tuning = None;
37 }
38
39 #[inline(always)]
40 fn generate_sample(&mut self, freq: f32, amp: f32, waveform: f32, sr: f32) -> f32 {
41 let phase_inc = freq / sr;
42
43 let sample = match waveform as u32 {
45 0 => (self.phase * TAU).sin(),
46 1 => {
47 let mut saw = 2.0 * self.phase - 1.0;
49 saw -= blep(self.phase, phase_inc);
50 saw
51 }
52 2 => {
53 let mut sq = if self.phase < 0.5 { 1.0f32 } else { -1.0f32 };
55 sq += blep(self.phase, phase_inc);
56 sq -= blep((self.phase + 0.5).fract(), phase_inc);
57 sq
58 }
59 _ => {
60 if self.phase < 0.5 {
62 4.0 * self.phase - 1.0
63 } else {
64 3.0 - 4.0 * self.phase
65 }
66 }
67 };
68 self.phase = (self.phase + phase_inc).fract();
69 sample * amp
70 }
71}
72
73#[inline(always)]
77fn blep(t: f32, dt: f32) -> f32 {
78 if t < dt {
79 let t = t / dt;
80 2.0 * t - t * t - 1.0
81 } else if t > 1.0 - dt {
82 let t = (t - 1.0) / dt;
83 t * t + 2.0 * t + 1.0
84 } else {
85 0.0
86 }
87}
88
89impl Default for Oscillator {
90 fn default() -> Self {
91 Self::new()
92 }
93}
94
95impl DspNode for Oscillator {
96 fn process(
97 &mut self,
98 _inputs: &[Option<&[f32; BUFFER_SIZE]>; MAX_INPUTS],
99 output: &mut [f32; BUFFER_SIZE],
100 params: &mut ParamBlock,
101 sample_rate: f32,
102 ) {
103 for sample in output.iter_mut() {
104 let amp = params.get(1).current.clamp(0.0, 1.0);
105 let wave = params.get(2).current;
106
107 let freq = if let Some(ref tuning) = self.tuning {
109 let midi = params.get(3).current;
110 if midi >= 0.0 {
111 let note = (midi as usize).min(127);
112 tuning[note].max(0.01)
113 } else {
114 params.get(0).current.max(0.01)
115 }
116 } else {
117 params.get(0).current.max(0.01)
118 };
119
120 *sample = self.generate_sample(freq, amp, wave, sample_rate);
121 params.tick_all();
122 }
123 }
124
125 fn capture_state(&self) -> StateBlob {
126 StateBlob::from_value(&OscState { phase: self.phase })
127 }
128
129 fn restore_state(&mut self, state: StateBlob) {
130 let s: OscState = state.to_value();
131 self.phase = s.phase;
132 }
133
134 fn type_name(&self) -> &'static str {
135 "Oscillator"
136 }
137}