euphony_dsp/
tertiary.rs

1use crate::prelude::*;
2
3macro_rules! tertiary {
4    ($(#[doc = $doc:literal])* $id:literal, $name:ident, | $a:ident, $b:ident, $c:ident | $value:expr) => {
5        #[derive(Debug, Clone, Copy, Default, Node)]
6        #[node(id = $id, module = "tertiary")]
7        #[input($a)]
8        #[input($b)]
9        #[input($c)]
10        $(#[doc = $doc])*
11        pub struct $name;
12
13        impl $name {
14            fn render(&mut self, a: Input, b: Input, c: Input, output: &mut [Sample]) {
15                match (a, b, c) {
16                    (Input::Constant($a), Input::Constant($b), Input::Constant($c)) => {
17                        let v = $value;
18                        for sample in output.iter_mut() {
19                            *sample = v;
20                        }
21                    }
22                    (Input::Constant($a), Input::Constant($b), Input::Buffer(c)) => {
23                        for (sample, $c) in output.iter_mut().zip(c.iter().copied()) {
24                            *sample = $value;
25                        }
26                    }
27                    (Input::Constant($a), Input::Buffer(b), Input::Constant($c)) => {
28                        for (sample, $b) in output.iter_mut().zip(b.iter().copied()) {
29                            *sample = $value;
30                        }
31                    }
32                    (Input::Constant($a), Input::Buffer(b), Input::Buffer(c)) => {
33                        for (sample, ($b, $c)) in output.iter_mut().zip(b.iter().copied().zip(c.iter().copied())) {
34                            *sample = $value;
35                        }
36                    }
37                    (Input::Buffer(a), Input::Constant($b), Input::Constant($c)) => {
38                        for (sample, $a) in output.iter_mut().zip(a.iter().copied()) {
39                            *sample = $value;
40                        }
41                    }
42                    (Input::Buffer(a), Input::Constant($b), Input::Buffer(c)) => {
43                        for (sample, ($a, $c)) in output.iter_mut().zip(a.iter().copied().zip(c.iter().copied())) {
44                            *sample = $value;
45                        }
46                    }
47                    (Input::Buffer(a), Input::Buffer(b), Input::Constant($c)) => {
48                        for (sample, ($a, $b)) in output.iter_mut().zip(a.iter().copied().zip(b.iter().copied())) {
49                            *sample = $value;
50                        }
51                    }
52                    (Input::Buffer(a), Input::Buffer(b), Input::Buffer(c)) => {
53                        unsafe {
54                            unsafe_assert!(a.len() == b.len());
55                            unsafe_assert!(a.len() == c.len());
56                        }
57
58                        let a = a.iter().copied();
59                        let b = b.iter().copied();
60                        let c = c.iter().copied();
61
62                        for (sample, ($a, ($b, $c))) in output.iter_mut().zip(a.zip(b.zip(c))) {
63                            *sample = $value;
64                        }
65                    }
66                }
67            }
68        }
69    };
70}
71
72tertiary!(
73    /// Fused multiply-add. Computes `(input + add) * mul` with only one rounding
74    /// error, yielding a more accurate result than an unfused add-multiply.
75    75,
76    AddMul,
77    |input, add, mul| (input + add) * mul
78);
79tertiary!(
80    /// Restrict a value to a certain interval unless it is NaN.
81    ///
82    /// Returns `max` if `input` is greater than `max`, and `min` if `input` is
83    /// less than `min`. Otherwise this returns `input`.
84    ///
85    /// Note that this function returns NaN if the initial value was NaN as
86    /// well or `min > max`
87    76,
88    Clamp,
89    |input, min, max| {
90        if min <= max {
91            input.clamp(min, max)
92        } else {
93            f64::NAN
94        }
95    }
96);
97tertiary!(
98    /// Fused multiply-add. Computes `(input * mul) + add` with only one rounding
99    /// error, yielding a more accurate result than an unfused multiply-add.
100    77,
101    MulAdd,
102    |input, mul, add| input.mul_add(mul, add)
103);
104tertiary!(
105    /// If `cond` is positive, then `positive` is returned. Otherwise `negative`
106    /// is returned.
107    78,
108    Select,
109    |cond, positive, negative| if cond.is_sign_positive() {
110        positive
111    } else {
112        negative
113    }
114);