1use crate::dsp::DelayLine;
10use rg3d_core::visitor::{Visit, VisitResult, Visitor};
11
12#[derive(Debug, Clone)]
15pub struct OnePole {
16 a0: f32,
17 b1: f32,
18 last: f32,
19}
20
21impl Default for OnePole {
22 fn default() -> Self {
23 Self {
24 a0: 1.0,
25 b1: 0.0,
26 last: 0.0,
27 }
28 }
29}
30
31fn get_b1(fc: f32) -> f32 {
32 (-2.0 * std::f32::consts::PI * fc.min(1.0).max(0.0)).exp()
33}
34
35impl OnePole {
36 pub fn new(fc: f32) -> Self {
38 let b1 = get_b1(fc);
39 Self {
40 b1,
41 a0: 1.0 - b1,
42 last: 0.0,
43 }
44 }
45
46 pub fn set_fc(&mut self, fc: f32) {
48 self.b1 = get_b1(fc);
49 self.a0 = 1.0 - self.b1;
50 }
51
52 pub fn set_pole(&mut self, pole: f32) {
54 self.b1 = pole.min(1.0).max(0.0);
55 self.a0 = 1.0 - self.b1;
56 }
57
58 pub fn feed(&mut self, sample: f32) -> f32 {
60 let result = sample * self.a0 + self.last * self.b1;
61 self.last = result;
62 result
63 }
64}
65
66impl Visit for OnePole {
67 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
68 visitor.enter_region(name)?;
69
70 self.a0.visit("A0", visitor)?;
71 self.b1.visit("B1", visitor)?;
72 self.last.visit("Last", visitor)?;
73
74 visitor.leave_region()
75 }
76}
77
78#[derive(Debug, Clone)]
81pub struct LpfComb {
82 low_pass: OnePole,
83 delay_line: DelayLine,
84 feedback: f32,
85}
86
87impl Default for LpfComb {
88 fn default() -> Self {
89 Self {
90 low_pass: Default::default(),
91 delay_line: Default::default(),
92 feedback: 0.0,
93 }
94 }
95}
96
97impl LpfComb {
98 pub fn new(len: usize, fc: f32, feedback: f32) -> Self {
100 Self {
101 low_pass: OnePole::new(fc),
102 delay_line: DelayLine::new(len),
103 feedback,
104 }
105 }
106
107 pub fn set_feedback(&mut self, feedback: f32) {
109 self.feedback = feedback;
110 }
111
112 pub fn feedback(&self) -> f32 {
114 self.feedback
115 }
116
117 pub fn set_fc(&mut self, fc: f32) {
119 self.low_pass.set_fc(fc)
120 }
121
122 pub fn len(&self) -> usize {
124 self.delay_line.len()
125 }
126
127 pub fn feed(&mut self, sample: f32) -> f32 {
129 let result = sample + self.feedback * self.low_pass.feed(self.delay_line.last());
130 self.delay_line.feed(result);
131 result
132 }
133}
134
135impl Visit for LpfComb {
136 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
137 visitor.enter_region(name)?;
138
139 self.delay_line.visit("DelayLine", visitor)?;
140 self.feedback.visit("Feedback", visitor)?;
141 self.low_pass.visit("LowPass", visitor)?;
142
143 visitor.leave_region()
144 }
145}
146
147#[derive(Debug, Clone)]
150pub struct AllPass {
151 delay_line: DelayLine,
152 gain: f32,
153}
154
155impl Default for AllPass {
156 fn default() -> Self {
157 Self {
158 delay_line: Default::default(),
159 gain: 1.0,
160 }
161 }
162}
163
164impl AllPass {
165 pub fn new(len: usize, gain: f32) -> Self {
167 Self {
168 delay_line: DelayLine::new(len),
169 gain,
170 }
171 }
172
173 pub fn set_gain(&mut self, gain: f32) {
175 self.gain = gain;
176 }
177
178 pub fn len(&self) -> usize {
180 self.delay_line.len()
181 }
182
183 pub fn feed(&mut self, sample: f32) -> f32 {
185 let delay_line_output = self.delay_line.last();
186 let am_arm = -self.gain * delay_line_output;
187 let sum_left = sample + am_arm;
188 let b0_arm = sum_left * self.gain;
189 self.delay_line.feed(sum_left);
190 delay_line_output + b0_arm
191 }
192}
193
194impl Visit for AllPass {
195 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
196 visitor.enter_region(name)?;
197
198 self.delay_line.visit("DelayLine", visitor)?;
199 self.gain.visit("Gain", visitor)?;
200
201 visitor.leave_region()
202 }
203}
204
205pub enum BiquadKind {
208 LowPass,
210
211 HighPass,
213
214 BandPass,
216
217 AllPass,
219
220 LowShelf,
223
224 HighShelf,
227}
228
229#[derive(Clone, Debug)]
232pub struct Biquad {
233 b0: f32,
234 b1: f32,
235 b2: f32,
236 a1: f32,
237 a2: f32,
238 prev1: f32,
239 prev2: f32,
240}
241
242impl Biquad {
243 pub fn new(kind: BiquadKind, fc: f32, gain: f32, quality: f32) -> Self {
249 let mut filter = Self::default();
250
251 filter.tune(kind, fc, gain, quality);
252
253 filter
254 }
255
256 pub fn from_coefficients(b0: f32, b1: f32, b2: f32, a1: f32, a2: f32) -> Self {
258 Self {
259 b0,
260 b1,
261 b2,
262 a1,
263 a2,
264 prev1: 0.0,
265 prev2: 0.0,
266 }
267 }
268
269 pub fn tune(&mut self, kind: BiquadKind, fc: f32, gain: f32, quality: f32) {
276 let w0 = 2.0 * std::f32::consts::PI * fc;
277 let w0_cos = w0.cos();
278 let w0_sin = w0.sin();
279 let alpha = w0_sin / (2.0 * quality);
280
281 let (b0, b1, b2, a0, a1, a2) = match kind {
282 BiquadKind::LowPass => {
283 let b0 = (1.0 - w0_cos) / 2.0;
284 let b1 = 1.0 - w0_cos;
285 let b2 = b0;
286 let a0 = 1.0 + alpha;
287 let a1 = -2.0 * w0_cos;
288 let a2 = 1.0 - alpha;
289 (b0, b1, b2, a0, a1, a2)
290 }
291 BiquadKind::HighPass => {
292 let b0 = (1.0 + w0_cos) / 2.0;
293 let b1 = -(1.0 + w0_cos);
294 let b2 = b0;
295 let a0 = 1.0 + alpha;
296 let a1 = -2.0 * w0_cos;
297 let a2 = 1.0 - alpha;
298 (b0, b1, b2, a0, a1, a2)
299 }
300 BiquadKind::BandPass => {
301 let b0 = w0_sin / 2.0;
302 let b1 = 0.0;
303 let b2 = -b0;
304 let a0 = 1.0 + alpha;
305 let a1 = -2.0 * w0_cos;
306 let a2 = 1.0 - alpha;
307 (b0, b1, b2, a0, a1, a2)
308 }
309 BiquadKind::AllPass => {
310 let b0 = 1.0 - alpha;
311 let b1 = -2.0 * w0_cos;
312 let b2 = 1.0 + alpha;
313 let a0 = b2;
314 let a1 = -2.0 * w0_cos;
315 let a2 = 1.0 - alpha;
316 (b0, b1, b2, a0, a1, a2)
317 }
318 BiquadKind::LowShelf => {
319 let sq = 2.0 * gain.sqrt() * alpha;
320 let b0 = gain * ((gain + 1.0) - (gain - 1.0) * w0_cos + sq);
321 let b1 = 2.0 * gain * ((gain - 1.0) - (gain + 1.0) * w0_cos);
322 let b2 = gain * ((gain + 1.0) - (gain - 1.0) * w0_cos - sq);
323 let a0 = (gain + 1.0) + (gain - 1.0) * w0_cos + sq;
324 let a1 = -2.0 * ((gain - 1.0) + (gain + 1.0) * w0_cos);
325 let a2 = (gain + 1.0) + (gain - 1.0) * w0_cos - sq;
326 (b0, b1, b2, a0, a1, a2)
327 }
328 BiquadKind::HighShelf => {
329 let sq = 2.0 * gain.sqrt() * alpha;
330 let b0 = gain * ((gain + 1.0) + (gain - 1.0) * w0_cos + sq);
331 let b1 = -2.0 * gain * ((gain - 1.0) + (gain + 1.0) * w0_cos);
332 let b2 = gain * ((gain + 1.0) + (gain - 1.0) * w0_cos - sq);
333 let a0 = (gain + 1.0) - (gain - 1.0) * w0_cos + sq;
334 let a1 = 2.0 * ((gain - 1.0) - (gain + 1.0) * w0_cos);
335 let a2 = (gain + 1.0) - (gain - 1.0) * w0_cos - sq;
336 (b0, b1, b2, a0, a1, a2)
337 }
338 };
339
340 self.b0 = b0 / a0;
341 self.b1 = b1 / a0;
342 self.b2 = b2 / a0;
343 self.a1 = a1 / a0;
344 self.a2 = a2 / a0;
345 }
346
347 pub fn feed(&mut self, sample: f32) -> f32 {
349 let result = sample * self.b0 + self.prev1;
350 self.prev1 = sample * self.b1 - result * self.a1 + self.prev2;
351 self.prev2 = sample * self.b2 - result * self.a2;
352 result
353 }
354}
355
356impl Default for Biquad {
357 fn default() -> Self {
358 Self {
359 b0: 1.0,
360 b1: 0.0,
361 b2: 0.0,
362 a1: 0.0,
363 a2: 0.0,
364 prev1: 0.0,
365 prev2: 0.0,
366 }
367 }
368}
369
370impl Visit for Biquad {
371 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
372 visitor.enter_region(name)?;
373
374 self.b0.visit("b0", visitor)?;
375 self.b1.visit("b1", visitor)?;
376 self.b2.visit("b2", visitor)?;
377 self.a1.visit("a1", visitor)?;
378 self.a2.visit("a2", visitor)?;
379 self.prev1.visit("prev1", visitor)?;
380 self.prev2.visit("prev2", visitor)?;
381
382 visitor.leave_region()
383 }
384}