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,
28 x2: f32,
29 y1: f32,
30 y2: f32,
31}
32
33impl BandpassFilter {
34 fn new() -> Self {
35 Self {
36 x1: 0.0,
37 x2: 0.0,
38 y1: 0.0,
39 y2: 0.0,
40 }
41 }
42
43 #[inline(always)]
44 fn process(&mut self, input: f32, freq: f32, bw: f32, sr: f32) -> f32 {
45 let r = 1.0 - std::f32::consts::PI * bw / sr;
46 let cos_w = 2.0 * r * (2.0 * std::f32::consts::PI * freq / sr).cos();
47 let a0 = 1.0 - r * r;
48 let y = a0 * input + cos_w * self.y1 - r * r * self.y2;
49 self.y2 = self.y1;
50 self.y1 = y;
51 self.x2 = self.x1;
52 self.x1 = input;
53 y
54 }
55}
56
57pub struct FormantFilter {
58 bp: [[BandpassFilter; 3]; 2], }
60
61impl FormantFilter {
62 pub fn new() -> Self {
63 Self {
64 bp: [
65 [
66 BandpassFilter::new(),
67 BandpassFilter::new(),
68 BandpassFilter::new(),
69 ],
70 [
71 BandpassFilter::new(),
72 BandpassFilter::new(),
73 BandpassFilter::new(),
74 ],
75 ],
76 }
77 }
78
79 fn get_formants(vowel_idx: usize, shift_semitones: f32) -> [f32; 3] {
80 let v = vowel_idx.min(4);
81 let shift_ratio = 2.0f32.powf(shift_semitones / 12.0);
82 [
83 VOWEL_FORMANTS[v][0] * shift_ratio,
84 VOWEL_FORMANTS[v][1] * shift_ratio,
85 VOWEL_FORMANTS[v][2] * shift_ratio,
86 ]
87 }
88}
89
90impl Default for FormantFilter {
91 fn default() -> Self {
92 Self::new()
93 }
94}
95
96impl DspNode for FormantFilter {
97 fn process(
98 &mut self,
99 inputs: &[Option<&[f32; BUFFER_SIZE]>; MAX_INPUTS],
100 output: &mut [f32; BUFFER_SIZE],
101 params: &mut ParamBlock,
102 sample_rate: f32,
103 ) {
104 let silence = [0.0f32; BUFFER_SIZE];
105 let input = inputs[0].unwrap_or(&silence);
106
107 for (i, out) in output.iter_mut().enumerate() {
108 let vowel_f = params.get(0).current.clamp(0.0, 4.0);
109 let shift = params.get(1).current.clamp(-12.0, 12.0);
110 let wet = params.get(2).current.clamp(0.0, 1.0);
111
112 let v0 = vowel_f as usize;
113 let v1 = (v0 + 1).min(4);
114 let frac = vowel_f.fract();
115
116 let f0 = Self::get_formants(v0, shift);
117 let f1 = Self::get_formants(v1, shift);
118
119 let mut wet_signal = 0.0f32;
121 for k in 0..3 {
122 let freq0 = f0[k] * (1.0 - frac) + f1[k] * frac;
123 wet_signal += self.bp[0][k].process(input[i], freq0, FORMANT_BW[k], sample_rate);
124 }
125 wet_signal *= 0.333; *out = input[i] * (1.0 - wet) + wet_signal * wet;
128 params.tick_all();
129 }
130 }
131
132 fn type_name(&self) -> &'static str {
133 "FormantFilter"
134 }
135}