rust_audio_api/nodes/
filter.rs1use crate::types::AudioUnit;
2use std::f32::consts::PI;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum FilterType {
7 LowPass,
9 HighPass,
11 BandPass,
13}
14
15#[derive(Debug, Clone, Copy)]
17struct BiquadCoefficients {
18 b0: f32,
19 b1: f32,
20 b2: f32,
21 a1: f32,
22 a2: f32,
23}
24
25#[derive(Debug, Clone, Copy, Default)]
27struct ChannelState {
28 x1: f32, x2: f32, y1: f32, y2: f32, }
33
34pub struct FilterNode {
65 filter_type: FilterType,
66 sample_rate: f32,
67 cutoff: f32,
68 q: f32,
69 coeffs: BiquadCoefficients,
70 state: [ChannelState; 2], }
72
73impl FilterNode {
74 pub fn new(filter_type: FilterType, sample_rate: u32, cutoff_hz: f32, q: f32) -> Self {
82 let mut node = Self {
83 filter_type,
84 sample_rate: sample_rate as f32,
85 cutoff: cutoff_hz,
86 q,
87 coeffs: BiquadCoefficients {
88 b0: 0.0,
89 b1: 0.0,
90 b2: 0.0,
91 a1: 0.0,
92 a2: 0.0,
93 },
94 state: [ChannelState::default(); 2],
95 };
96 node.recalculate_coefficients();
97 node
98 }
99
100 pub fn set_cutoff(&mut self, cutoff_hz: f32) {
102 self.cutoff = cutoff_hz;
103 self.recalculate_coefficients();
104 }
105
106 pub fn set_q(&mut self, q: f32) {
108 self.q = q;
109 self.recalculate_coefficients();
110 }
111
112 pub fn set_filter_type(&mut self, filter_type: FilterType) {
114 self.filter_type = filter_type;
115 self.recalculate_coefficients();
116 }
117
118 fn recalculate_coefficients(&mut self) {
120 let w0 = 2.0 * PI * self.cutoff / self.sample_rate;
121 let cos_w0 = w0.cos();
122 let sin_w0 = w0.sin();
123 let alpha = sin_w0 / (2.0 * self.q);
124
125 let (b0, b1, b2, a0, a1, a2) = match self.filter_type {
126 FilterType::LowPass => {
127 let b1 = 1.0 - cos_w0;
128 let b0 = b1 / 2.0;
129 let b2 = b0;
130 let a0 = 1.0 + alpha;
131 let a1 = -2.0 * cos_w0;
132 let a2 = 1.0 - alpha;
133 (b0, b1, b2, a0, a1, a2)
134 }
135 FilterType::HighPass => {
136 let b1_raw = 1.0 + cos_w0;
137 let b0 = b1_raw / 2.0;
138 let b1 = -(1.0 + cos_w0);
139 let b2 = b0;
140 let a0 = 1.0 + alpha;
141 let a1 = -2.0 * cos_w0;
142 let a2 = 1.0 - alpha;
143 (b0, b1, b2, a0, a1, a2)
144 }
145 FilterType::BandPass => {
146 let b0 = alpha;
147 let b1 = 0.0;
148 let b2 = -alpha;
149 let a0 = 1.0 + alpha;
150 let a1 = -2.0 * cos_w0;
151 let a2 = 1.0 - alpha;
152 (b0, b1, b2, a0, a1, a2)
153 }
154 };
155
156 let inv_a0 = 1.0 / a0;
158 self.coeffs = BiquadCoefficients {
159 b0: b0 * inv_a0,
160 b1: b1 * inv_a0,
161 b2: b2 * inv_a0,
162 a1: a1 * inv_a0,
163 a2: a2 * inv_a0,
164 };
165 }
166
167 #[inline(always)]
169 fn process_sample(coeffs: &BiquadCoefficients, state: &mut ChannelState, x: f32) -> f32 {
170 let y = coeffs.b0 * x + coeffs.b1 * state.x1 + coeffs.b2 * state.x2
171 - coeffs.a1 * state.y1
172 - coeffs.a2 * state.y2;
173
174 state.x2 = state.x1;
175 state.x1 = x;
176 state.y2 = state.y1;
177 state.y1 = y;
178
179 y
180 }
181
182 #[inline(always)]
183 pub fn process(&mut self, input: Option<&AudioUnit>, output: &mut AudioUnit) {
184 if let Some(in_unit) = input {
185 let coeffs = self.coeffs;
186 output.copy_from_slice(in_unit);
187
188 dasp::slice::map_in_place(&mut output[..], |frame| {
189 let left = Self::process_sample(&coeffs, &mut self.state[0], frame[0]);
190 let right = Self::process_sample(&coeffs, &mut self.state[1], frame[1]);
191 [left, right]
192 });
193 } else {
194 dasp::slice::equilibrium(&mut output[..]);
195 }
196 }
197}