aether_nodes/
karplus_strong.rs1use aether_core::{node::DspNode, param::ParamBlock, state::StateBlob, BUFFER_SIZE, MAX_INPUTS};
13
14const MAX_DELAY: usize = 4096; pub struct KarplusStrong {
17 delay_line: Box<[f32; MAX_DELAY]>,
18 write_pos: usize,
19 delay_len: usize,
20 prev_trigger: f32,
21 noise_seed: u32,
22}
23
24impl KarplusStrong {
25 pub fn new() -> Self {
26 Self {
27 delay_line: Box::new([0.0; MAX_DELAY]),
28 write_pos: 0,
29 delay_len: 100,
30 prev_trigger: 0.0,
31 noise_seed: 12345,
32 }
33 }
34
35 fn pluck(&mut self, brightness: f32) {
37 for i in 0..self.delay_len {
38 let noise = self.white_noise();
39 let filtered = if brightness > 0.5 {
42 noise
43 } else {
44 let alpha = brightness * 2.0;
46 noise * alpha
47 };
48 self.delay_line[(self.write_pos + i) % MAX_DELAY] = filtered;
49 }
50 }
51
52 #[inline(always)]
53 fn white_noise(&mut self) -> f32 {
54 self.noise_seed ^= self.noise_seed << 13;
56 self.noise_seed ^= self.noise_seed >> 17;
57 self.noise_seed ^= self.noise_seed << 5;
58 (self.noise_seed as f32 / u32::MAX as f32) * 2.0 - 1.0
59 }
60
61 #[inline(always)]
62 fn process_sample(&mut self, decay: f32) -> f32 {
63 let read_pos = (self.write_pos + MAX_DELAY - self.delay_len) % MAX_DELAY;
64 let next_pos = (read_pos + 1) % MAX_DELAY;
65
66 let output = (self.delay_line[read_pos] + self.delay_line[next_pos]) * 0.5 * decay;
68 self.delay_line[self.write_pos] = output;
69 self.write_pos = (self.write_pos + 1) % MAX_DELAY;
70 output
71 }
72}
73
74impl Default for KarplusStrong {
75 fn default() -> Self {
76 Self::new()
77 }
78}
79
80impl DspNode for KarplusStrong {
81 fn process(
82 &mut self,
83 _inputs: &[Option<&[f32; BUFFER_SIZE]>; MAX_INPUTS],
84 output: &mut [f32; BUFFER_SIZE],
85 params: &mut ParamBlock,
86 sample_rate: f32,
87 ) {
88 for sample in output.iter_mut() {
89 let freq = params.get(0).current.clamp(20.0, 4000.0);
90 let decay = params.get(1).current.clamp(0.9, 0.9999);
91 let brightness = params.get(2).current.clamp(0.0, 1.0);
92 let trigger = params.get(3).current;
93
94 let new_len = ((sample_rate / freq) as usize).clamp(2, MAX_DELAY - 1);
96 if new_len != self.delay_len {
97 self.delay_len = new_len;
98 }
99
100 if trigger > 0.5 && self.prev_trigger <= 0.5 {
102 self.pluck(brightness);
103 }
104 self.prev_trigger = trigger;
105
106 *sample = self.process_sample(decay);
107 params.tick_all();
108 }
109 }
110
111 fn capture_state(&self) -> StateBlob {
112 StateBlob::EMPTY }
114
115 fn type_name(&self) -> &'static str {
116 "KarplusStrong"
117 }
118}