firefly_audio/
processors.rs1use crate::*;
2use micromath::F32Ext;
3
4pub struct Mix {}
6
7impl Mix {
8 #[must_use]
9 pub const fn new() -> Self {
10 Self {}
11 }
12}
13
14impl Processor for Mix {}
15
16pub struct AllForOne {}
18
19impl AllForOne {
20 #[must_use]
21 pub const fn new() -> Self {
22 Self {}
23 }
24}
25
26impl Processor for AllForOne {
27 fn process_children(&mut self, cn: &mut Nodes) -> Option<Frame> {
28 let mut sum = Frame::zero();
29 if cn.is_empty() {
30 return None;
31 }
32 for node in cn.iter_mut() {
33 sum = sum + &node.next_frame()?;
34 }
35 let f = sum / cn.len() as f32;
36 self.process_frame(f)
37 }
38}
39
40pub struct Gain {
42 lvl: f32,
43}
44
45impl Gain {
46 #[must_use]
47 pub const fn new(lvl: f32) -> Self {
48 Self { lvl }
49 }
50}
51
52impl Processor for Gain {
53 fn set(&mut self, param: u8, val: f32) {
54 if param == 0 {
55 self.lvl = val;
56 }
57 }
58
59 fn process_sample(&mut self, s: Sample) -> Option<Sample> {
60 Some(s * self.lvl)
61 }
62}
63
64pub struct Loop {}
69
70impl Loop {
71 #[must_use]
72 pub const fn new() -> Self {
73 Self {}
74 }
75}
76
77impl Processor for Loop {
78 fn process_children(&mut self, cn: &mut Nodes) -> Option<Frame> {
79 let mut sum = Frame::zero();
80 for node in cn.iter_mut() {
81 let f = if let Some(f) = node.next_frame() {
82 f
83 } else {
84 node.reset();
85 node.next_frame()?
86 };
87 sum = sum + &f;
88 }
89 Some(sum / cn.len() as f32)
90 }
91}
92
93pub struct Concat {}
95
96impl Concat {
97 #[must_use]
99 pub const fn new() -> Self {
100 Self {}
101 }
102}
103
104impl Processor for Concat {
105 fn process_children(&mut self, cn: &mut Nodes) -> Option<Frame> {
106 for node in cn {
107 if let Some(f) = node.next_frame() {
108 return Some(f);
109 }
110 }
111 None
112 }
113}
114
115pub struct Pan {
116 left_weight: f32,
117 right_weight: f32,
118}
119
120impl Pan {
121 #[must_use]
122 pub fn new(v: f32) -> Self {
123 let (left_weight, right_weight) = pan_weights(v);
124 Self {
125 left_weight,
126 right_weight,
127 }
128 }
129}
130
131#[inline]
132fn pan_weights(v: f32) -> (f32, f32) {
133 let v = v.clamp(-1., 1.);
134 let angle = (v + 1.) * core::f32::consts::FRAC_PI_4;
135 let (sin, cos) = F32Ext::sin_cos(angle);
136 (cos, sin)
137}
138
139impl Processor for Pan {
140 fn set(&mut self, param: u8, val: f32) {
141 if param == 0 {
142 (self.left_weight, self.right_weight) = pan_weights(val);
143 }
144 }
145
146 fn process_frame(&mut self, f: Frame) -> Option<Frame> {
147 let left = f.left * self.left_weight;
148 let right = f.right.map(|s| s * self.right_weight);
149 Some(Frame { left, right })
150 }
151}
152
153pub struct Mute {
155 muted: bool,
156}
157
158impl Mute {
159 #[must_use]
160 pub const fn new() -> Self {
161 Self { muted: false }
162 }
163
164 pub const fn mute(&mut self) {
165 self.muted = true;
166 }
167
168 pub const fn unmute(&mut self) {
169 self.muted = false;
170 }
171}
172
173impl Processor for Mute {
174 fn reset(&mut self) {
175 self.muted = false;
176 }
177
178 fn set(&mut self, param: u8, val: f32) {
179 if param == 0 {
180 self.muted = val < 0.5;
181 }
182 }
183
184 fn process_sample(&mut self, s: Sample) -> Option<Sample> {
185 if self.muted {
186 return Some(Sample::ZERO);
187 }
188 Some(s)
189 }
190}
191
192pub struct Pause {
194 paused: bool,
195}
196
197impl Pause {
198 #[must_use]
199 pub const fn new() -> Self {
200 Self { paused: false }
201 }
202
203 pub const fn pause(&mut self) {
204 self.paused = true;
205 }
206
207 pub const fn play(&mut self) {
208 self.paused = false;
209 }
210}
211
212impl Processor for Pause {
213 fn reset(&mut self) {
214 self.paused = false;
215 }
216
217 fn set(&mut self, param: u8, val: f32) {
218 if param == 0 {
219 self.paused = val < 0.5;
220 }
221 }
222
223 fn process_children(&mut self, cn: &mut Nodes) -> Option<Frame> {
224 if self.paused {
225 return None;
226 }
227 Mix::new().process_children(cn)
228 }
229}
230
231pub struct TrackPosition {
233 elapsed: Position,
234}
235
236impl TrackPosition {
237 #[must_use]
238 pub const fn new() -> Self {
239 Self { elapsed: 0 }
240 }
241}
242
243impl Processor for TrackPosition {
244 fn reset(&mut self) {
245 self.elapsed = 0;
246 }
247
248 fn process_sample(&mut self, s: Sample) -> Option<Sample> {
249 self.elapsed += 1;
250 Some(s)
251 }
252}
253
254#[derive(Default)]
256pub struct LowHighPass {
257 low: bool,
258 freq: f32,
259 q: f32,
260
261 x_n1: Sample,
262 x_n2: Sample,
263 y_n1: Sample,
264 y_n2: Sample,
265
266 b0: f32,
267 b1: f32,
268 b2: f32,
269 a1: f32,
270 a2: f32,
271}
272
273impl LowHighPass {
274 #[must_use]
275 pub fn new(low: bool, freq: f32, q: f32) -> Self {
276 let mut res = Self {
277 low,
278 freq,
279 q,
280 ..Default::default()
281 };
282 res.update_coefs();
283 res
284 }
285
286 fn update_coefs(&mut self) {
287 let w0 = core::f32::consts::TAU * self.freq / SAMPLE_RATE as f32;
288 let cos_w0 = w0.cos();
289 let alpha = w0.sin() / (2. * self.q);
290
291 if self.low {
292 let b1 = 1. - cos_w0;
293 let b0 = b1 / 2.;
294 let b2 = b0;
295 let a0 = 1. + alpha;
296 let a1 = -2. * cos_w0;
297 let a2 = 1. - alpha;
298
299 self.b0 = b0 / a0;
300 self.b1 = b1 / a0;
301 self.b2 = b2 / a0;
302 self.a1 = a1 / a0;
303 self.a2 = a2 / a0;
304 } else {
305 let b0 = f32::midpoint(1., cos_w0);
306 let b1 = -1. - cos_w0;
307 let b2 = b0;
308 let a0 = 1. + alpha;
309 let a1 = -2. * cos_w0;
310 let a2 = 1. - alpha;
311
312 self.b0 = b0 / a0;
313 self.b1 = b1 / a0;
314 self.b2 = b2 / a0;
315 self.a1 = a1 / a0;
316 self.a2 = a2 / a0;
317 }
318 }
319}
320
321impl Processor for LowHighPass {
322 fn reset(&mut self) {
323 self.y_n2 = Sample::ZERO;
324 self.x_n2 = Sample::ZERO;
325 self.y_n1 = Sample::ZERO;
326 self.x_n1 = Sample::ZERO;
327 }
328
329 fn set(&mut self, param: u8, val: f32) {
330 #[expect(clippy::float_cmp)]
331 if param == 0 && val != self.freq {
332 self.freq = val;
333 self.update_coefs();
334 }
335 }
336
337 fn process_sample(&mut self, s: Sample) -> Option<Sample> {
338 let bx0 = self.b0 * s;
339 let bx1 = self.b1 * self.x_n1;
340 let bx2 = self.b2 * self.x_n2;
341 let ay1 = self.a1 * self.y_n1;
342 let ay2 = self.a2 * self.y_n2;
343 let result = bx0 + bx1 + bx2 - ay1 - ay2;
344
345 self.y_n2 = self.y_n1;
346 self.x_n2 = self.x_n1;
347 self.y_n1 = result;
348 self.x_n1 = s;
349
350 Some(result)
351 }
352}
353
354pub struct TakeLeft {}
356
357impl TakeLeft {
358 #[must_use]
359 pub const fn new() -> Self {
360 Self {}
361 }
362}
363
364impl Processor for TakeLeft {
365 fn process_children(&mut self, cn: &mut Nodes) -> Option<Frame> {
366 let mut sum = Sample::ZERO;
367 for node in cn.iter_mut() {
368 sum += &node.next_frame()?.left;
369 }
370 let s = sum / cn.len() as f32;
371 Some(Frame::mono(s))
372 }
373}
374
375pub struct TakeRight {}
377
378impl TakeRight {
379 #[must_use]
380 pub const fn new() -> Self {
381 Self {}
382 }
383}
384
385impl Processor for TakeRight {
386 fn process_children(&mut self, cn: &mut Nodes) -> Option<Frame> {
387 let mut sum = Sample::ZERO;
388 for node in cn.iter_mut() {
389 if let Some(right) = &node.next_frame()?.right {
390 sum += right;
391 }
392 }
393 let s = sum / cn.len() as f32;
394 Some(Frame::mono(s))
395 }
396}
397
398pub struct Swap {}
400
401impl Swap {
402 #[must_use]
403 pub const fn new() -> Self {
404 Self {}
405 }
406}
407
408impl Processor for Swap {
409 fn process_frame(&mut self, f: Frame) -> Option<Frame> {
410 if let Some(right) = f.right {
411 Some(Frame::stereo(right, f.left))
412 } else {
413 Some(f)
414 }
415 }
416}
417
418pub struct Clip {
420 low: f32,
421 high: f32,
422}
423
424impl Clip {
425 #[must_use]
426 pub const fn new(low: f32, high: f32) -> Self {
427 Self { low, high }
428 }
429}
430
431impl Processor for Clip {
432 fn set(&mut self, param: u8, val: f32) {
433 if param == 0 {
434 let diff = self.high - self.low;
435 self.low = val;
436 self.high = val + diff;
437 } else if param == 1 {
438 self.low = val;
439 } else if param == 2 {
440 self.high = val;
441 }
442 }
443
444 fn process_sample(&mut self, s: Sample) -> Option<Sample> {
445 let s = s.fast_max(self.low.into());
446 let s = s.fast_min(self.high.into());
447 Some(s)
448 }
449}
450
451