pointillism/effects/
pan.rs1use std::marker::PhantomData;
4
5use crate::prelude::*;
6
7pub trait Law: Copy + Default {
9 fn new(angle: f64) -> Self;
11
12 fn angle(&self) -> f64;
16
17 fn angle_mut(&mut self) -> &mut f64;
19
20 fn gain(&self) -> (f64, f64);
22}
23
24#[derive(Clone, Copy, Debug)]
28pub struct Linear {
29 pub angle: f64,
31}
32
33impl Default for Linear {
34 fn default() -> Self {
35 Self { angle: 0.5 }
36 }
37}
38
39macro_rules! pan_boilerplate {
41 () => {
42 fn new(angle: f64) -> Self {
43 Self { angle }
44 }
45
46 fn angle(&self) -> f64 {
47 self.angle
48 }
49
50 fn angle_mut(&mut self) -> &mut f64 {
51 &mut self.angle
52 }
53 };
54}
55
56#[must_use]
58pub fn linear_gain(angle: f64) -> (f64, f64) {
59 (1.0 - angle, angle)
60}
61
62impl Law for Linear {
63 pan_boilerplate!();
64
65 fn gain(&self) -> (f64, f64) {
66 linear_gain(self.angle)
67 }
68}
69
70#[derive(Clone, Copy, Debug)]
74pub struct Power {
75 pub angle: f64,
77}
78
79impl Default for Power {
80 fn default() -> Self {
81 Self { angle: 0.5 }
82 }
83}
84
85#[must_use]
87pub fn power_gain(angle: f64) -> (f64, f64) {
88 let (r, l) = (std::f64::consts::FRAC_PI_2 * angle).sin_cos();
89 (l, r)
90}
91
92impl Law for Power {
93 pan_boilerplate!();
94
95 fn gain(&self) -> (f64, f64) {
96 power_gain(self.angle)
97 }
98}
99
100#[derive(Clone, Copy, Debug)]
105pub struct Mixed {
106 pub angle: f64,
108}
109
110impl Default for Mixed {
111 fn default() -> Self {
112 Self { angle: 0.5 }
113 }
114}
115
116#[must_use]
118pub fn mixed_gain(angle: f64) -> (f64, f64) {
119 let linear = linear_gain(angle);
120 let power = power_gain(angle);
121 ((linear.0 * power.0).sqrt(), (linear.1 * power.1).sqrt())
122}
123
124impl Law for Mixed {
125 pan_boilerplate!();
126
127 fn gain(&self) -> (f64, f64) {
128 mixed_gain(self.angle)
129 }
130}
131
132#[derive(Clone, Copy, Debug)]
134pub struct Wrapper<A: Audio, P: Law> {
135 pub pan_law: P,
137 phantom: PhantomData<A>,
139}
140
141impl<A: Audio, P: Law> Wrapper<A, P> {
142 pub const fn new(pan_law: P) -> Self {
144 Self {
145 phantom: PhantomData,
146 pan_law,
147 }
148 }
149}
150
151impl<A: Audio, P: Law> Map for Wrapper<A, P> {
152 type Input = A;
153 type Output = smp::Stereo;
154
155 fn eval(&self, sample: A) -> smp::Stereo {
156 let smp::Stereo(sl, sr) = sample.duplicate();
157 let (gl, gr) = self.pan_law.gain();
158 smp::Stereo(sl * gl, sr * gr)
159 }
160}
161
162pub type Panner<S, P> = eff::MapSgn<S, Wrapper<<S as Signal>::Sample, P>>;
164
165impl<S: Signal, P: Law> Panner<S, P>
166where
167 S::Sample: Audio,
168{
169 pub const fn new_pan_law(sgn: S, pan_law: P) -> Self {
171 eff::MapSgn::new(sgn, Wrapper::new(pan_law))
172 }
173
174 pub fn new_pan(sgn: S, angle: f64) -> Self {
176 Self::new_pan_law(sgn, P::new(angle))
177 }
178
179 pub fn angle(&self) -> f64 {
181 self.map().pan_law.angle()
182 }
183
184 pub fn angle_mut(&mut self) -> &mut f64 {
186 self.map_mut().pan_law.angle_mut()
187 }
188}
189
190pub type LinearPanner<S> = Panner<S, Linear>;
192
193impl<S: Signal> LinearPanner<S>
194where
195 S::Sample: Audio,
196{
197 pub const fn linear(sgn: S, angle: f64) -> Self {
199 Self::new_pan_law(sgn, Linear { angle })
200 }
201}
202
203pub type PowerPanner<S> = Panner<S, Power>;
205
206impl<S: Signal> Panner<S, Power>
207where
208 S::Sample: Audio,
209{
210 pub const fn power(sgn: S, angle: f64) -> Self {
212 Self::new_pan_law(sgn, Power { angle })
213 }
214}
215
216pub type MixedPanner<S> = Panner<S, Mixed>;
218
219impl<S: Signal> Panner<S, Mixed>
220where
221 S::Sample: Audio,
222{
223 pub const fn mixed(sgn: S, angle: f64) -> Self {
225 Self::new_pan_law(sgn, Mixed { angle })
226 }
227}