1use aether_core::{node::DspNode, param::ParamBlock, BUFFER_SIZE, MAX_INPUTS};
12
13const VOWEL_FORMANTS: [[f32; 3]; 5] = [
16 [800.0, 1200.0, 2500.0], [400.0, 2000.0, 2800.0], [350.0, 2800.0, 3300.0], [450.0, 800.0, 2830.0], [325.0, 700.0, 2700.0], ];
22
23const FORMANT_BW: [f32; 3] = [80.0, 90.0, 120.0];
25
26struct BandpassFilter {
27 x1: f32, x2: f32,
28 y1: f32, y2: f32,
29}
30
31impl BandpassFilter {
32 fn new() -> Self { Self { x1: 0.0, x2: 0.0, y1: 0.0, y2: 0.0 } }
33
34 #[inline(always)]
35 fn process(&mut self, input: f32, freq: f32, bw: f32, sr: f32) -> f32 {
36 let r = 1.0 - std::f32::consts::PI * bw / sr;
37 let cos_w = 2.0 * r * (2.0 * std::f32::consts::PI * freq / sr).cos();
38 let a0 = 1.0 - r * r;
39 let y = a0 * input + cos_w * self.y1 - r * r * self.y2;
40 self.y2 = self.y1;
41 self.y1 = y;
42 self.x2 = self.x1;
43 self.x1 = input;
44 y
45 }
46}
47
48pub struct FormantFilter {
49 bp: [[BandpassFilter; 3]; 2], }
51
52impl FormantFilter {
53 pub fn new() -> Self {
54 Self {
55 bp: [
56 [BandpassFilter::new(), BandpassFilter::new(), BandpassFilter::new()],
57 [BandpassFilter::new(), BandpassFilter::new(), BandpassFilter::new()],
58 ],
59 }
60 }
61
62 fn get_formants(vowel_idx: usize, shift_semitones: f32) -> [f32; 3] {
63 let v = vowel_idx.min(4);
64 let shift_ratio = 2.0f32.powf(shift_semitones / 12.0);
65 [
66 VOWEL_FORMANTS[v][0] * shift_ratio,
67 VOWEL_FORMANTS[v][1] * shift_ratio,
68 VOWEL_FORMANTS[v][2] * shift_ratio,
69 ]
70 }
71}
72
73impl Default for FormantFilter {
74 fn default() -> Self { Self::new() }
75}
76
77impl DspNode for FormantFilter {
78 fn process(
79 &mut self,
80 inputs: &[Option<&[f32; BUFFER_SIZE]>; MAX_INPUTS],
81 output: &mut [f32; BUFFER_SIZE],
82 params: &mut ParamBlock,
83 sample_rate: f32,
84 ) {
85 let silence = [0.0f32; BUFFER_SIZE];
86 let input = inputs[0].unwrap_or(&silence);
87
88 for (i, out) in output.iter_mut().enumerate() {
89 let vowel_f = params.get(0).current.clamp(0.0, 4.0);
90 let shift = params.get(1).current.clamp(-12.0, 12.0);
91 let wet = params.get(2).current.clamp(0.0, 1.0);
92
93 let v0 = vowel_f as usize;
94 let v1 = (v0 + 1).min(4);
95 let frac = vowel_f.fract();
96
97 let f0 = Self::get_formants(v0, shift);
98 let f1 = Self::get_formants(v1, shift);
99
100 let mut wet_signal = 0.0f32;
102 for k in 0..3 {
103 let freq0 = f0[k] * (1.0 - frac) + f1[k] * frac;
104 wet_signal += self.bp[0][k].process(input[i], freq0, FORMANT_BW[k], sample_rate);
105 }
106 wet_signal *= 0.333; *out = input[i] * (1.0 - wet) + wet_signal * wet;
109 params.tick_all();
110 }
111 }
112
113 fn type_name(&self) -> &'static str { "FormantFilter" }
114}