dsp_process/
adapters.rs

1use crate::{SplitInplace, SplitProcess};
2
3/// Adapts an interpolator to output chunk mode
4///
5/// The inner processor is called with `Some(x)` once and then `None` `N-1` times
6#[derive(Clone, Debug, Default)]
7pub struct Interpolator<P>(pub P);
8impl<X: Copy, Y, C: SplitProcess<Option<X>, Y, S>, S, const N: usize> SplitProcess<X, [Y; N], S>
9    for Interpolator<C>
10{
11    fn process(&self, state: &mut S, x: X) -> [Y; N] {
12        core::array::from_fn(|i| self.0.process(state, (i == 0).then_some(x)))
13    }
14}
15impl<X: Copy, C, S> SplitInplace<X, S> for Interpolator<C> where Self: SplitProcess<X, X, S> {}
16
17/// Adapts a decimator to input chunk mode
18///
19/// Synchronizes to the inner tick by discarding samples after tick.
20/// Panics if tick does not match `N`.
21#[derive(Clone, Debug, Default)]
22pub struct Decimator<P>(pub P);
23impl<X: Copy, Y, C: SplitProcess<X, Option<Y>, S>, S, const N: usize> SplitProcess<[X; N], Y, S>
24    for Decimator<C>
25{
26    fn process(&self, state: &mut S, x: [X; N]) -> Y {
27        const { assert!(N > 0) }
28        x.into_iter()
29            .find_map(|x| self.0.process(state, x))
30            .unwrap()
31    }
32}
33impl<X: Copy, C, S> SplitInplace<X, S> for Decimator<C> where Self: SplitProcess<X, X, S> {}
34
35/// Map `Option` and `Result`
36#[derive(Clone, Debug, Default)]
37pub struct Map<P>(pub P);
38impl<X: Copy, Y, C: SplitProcess<X, Y, S>, S> SplitProcess<Option<X>, Option<Y>, S> for Map<C> {
39    fn process(&self, state: &mut S, x: Option<X>) -> Option<Y> {
40        x.map(|x| self.0.process(state, x))
41    }
42}
43impl<X: Copy, Y, C: SplitProcess<X, Y, S>, S, E: Copy> SplitProcess<Result<X, E>, Result<Y, E>, S>
44    for Map<C>
45{
46    fn process(&self, state: &mut S, x: Result<X, E>) -> Result<Y, E> {
47        x.map(|x| self.0.process(state, x))
48    }
49}
50impl<X: Copy, C: SplitInplace<X, S>, S> SplitInplace<X, S> for Map<C> where
51    Self: SplitProcess<X, X, S>
52{
53}
54
55/// Chunked processing
56///
57/// Adapt` a `X->Y` processor into a `[X; N] -> [Y; N]` processor
58/// by flattening input and output.
59#[derive(Debug, Copy, Clone, Default)]
60pub struct Chunk<P>(pub P);
61impl<C: SplitProcess<X, Y, S>, S, X: Copy, Y, const N: usize> SplitProcess<[X; N], [Y; N], S>
62    for Chunk<C>
63{
64    fn process(&self, state: &mut S, x: [X; N]) -> [Y; N] {
65        x.map(|x| self.0.process(state, x))
66    }
67
68    fn block(&self, state: &mut S, x: &[[X; N]], y: &mut [[Y; N]]) {
69        self.0.block(state, x.as_flattened(), y.as_flattened_mut())
70    }
71}
72impl<C: SplitInplace<X, S>, S, X: Copy, const N: usize> SplitInplace<[X; N], S> for Chunk<C> {
73    fn inplace(&self, state: &mut S, xy: &mut [[X; N]]) {
74        self.0.inplace(state, xy.as_flattened_mut())
75    }
76}
77
78/// Chunked input
79///
80/// Adapt a `[X; R] -> Y` processor to `[X; N=R*M]->[Y; M]` for any `M`
81/// by flattening and re-chunking input.
82#[derive(Debug, Copy, Clone, Default)]
83pub struct ChunkIn<P, const R: usize>(pub P);
84impl<C: SplitProcess<[X; R], Y, S>, S, X: Copy, Y, const N: usize, const R: usize, const M: usize>
85    SplitProcess<[X; N], [Y; M], S> for ChunkIn<C, R>
86{
87    fn process(&self, state: &mut S, x: [X; N]) -> [Y; M] {
88        const { assert!(R * M == N) }
89        let (x, []) = x.as_chunks() else {
90            unreachable!()
91        };
92        core::array::from_fn(|i| self.0.process(state, x[i]))
93    }
94
95    fn block(&self, state: &mut S, x: &[[X; N]], y: &mut [[Y; M]]) {
96        const { assert!(R * M == N) }
97        let (x, []) = x.as_flattened().as_chunks() else {
98            unreachable!()
99        };
100        self.0.block(state, x, y.as_flattened_mut())
101    }
102}
103impl<C: SplitInplace<[X; 1], S>, S, X: Copy, const N: usize> SplitInplace<[X; N], S>
104    for ChunkIn<C, 1>
105where
106    Self: SplitProcess<[X; N], [X; N], S>,
107{
108    fn inplace(&self, state: &mut S, xy: &mut [[X; N]]) {
109        let (xy, []) = xy.as_flattened_mut().as_chunks_mut() else {
110            unreachable!()
111        };
112        self.0.inplace(state, xy)
113    }
114}
115
116/// Chunked output
117///
118/// Adapt a `X -> [Y; R]` processor to `[X; N]->[Y; M = R*N]` for any `N`
119/// by flattening and re-chunking output.
120#[derive(Debug, Copy, Clone, Default)]
121pub struct ChunkOut<P, const R: usize>(pub P);
122impl<C, S, X: Copy, Y: Default + Copy, const N: usize, const R: usize, const M: usize>
123    SplitProcess<[X; N], [Y; M], S> for ChunkOut<C, R>
124where
125    C: SplitProcess<X, [Y; R], S>,
126{
127    fn process(&self, state: &mut S, x: [X; N]) -> [Y; M] {
128        const { assert!(R * N == M) }
129        // TODO: bytemuck y
130        // x.map(|x| self.0.process(x)).as_flattened().as_array()
131        let mut y = [Y::default(); M];
132        let (yy, []) = y.as_chunks_mut() else {
133            unreachable!()
134        };
135        for (x, y) in x.into_iter().zip(yy) {
136            *y = self.0.process(state, x);
137        }
138        y
139    }
140
141    fn block(&self, state: &mut S, x: &[[X; N]], y: &mut [[Y; M]]) {
142        const { assert!(R * N == M) }
143        let (y, []) = y.as_flattened_mut().as_chunks_mut() else {
144            unreachable!()
145        };
146        self.0.block(state, x.as_flattened(), y)
147    }
148}
149impl<C: SplitInplace<[X; 1], S>, S, X: Copy, const N: usize> SplitInplace<[X; N], S>
150    for ChunkOut<C, 1>
151where
152    Self: SplitProcess<[X; N], [X; N], S>,
153{
154    fn inplace(&self, state: &mut S, xy: &mut [[X; N]]) {
155        let (xy, []) = xy.as_flattened_mut().as_chunks_mut() else {
156            unreachable!()
157        };
158        self.0.inplace(state, xy)
159    }
160}
161
162/// Chunked input and output
163///
164/// Adapt a `[X; Q] -> [Y; R]` processor to `[X; N = Q*I]->[Y; M = R*I]` for any `I`
165/// by flattening and re-chunking input and output.
166#[derive(Debug, Copy, Clone, Default)]
167pub struct ChunkInOut<P, const Q: usize, const R: usize>(pub P);
168impl<
169    C,
170    S,
171    X: Copy,
172    Y: Default + Copy,
173    const Q: usize,
174    const N: usize,
175    const R: usize,
176    const M: usize,
177> SplitProcess<[X; N], [Y; M], S> for ChunkInOut<C, Q, R>
178where
179    C: SplitProcess<[X; Q], [Y; R], S>,
180{
181    fn process(&self, state: &mut S, x: [X; N]) -> [Y; M] {
182        const { assert!(N.is_multiple_of(Q)) }
183        const { assert!(M.is_multiple_of(R)) }
184        // TODO: bytemuck y
185        let mut y = [Y::default(); M];
186        let (yy, []) = y.as_chunks_mut() else {
187            unreachable!()
188        };
189        let (x, []) = x.as_chunks() else {
190            unreachable!()
191        };
192        for (x, y) in x.iter().zip(yy) {
193            *y = self.0.process(state, *x);
194        }
195        y
196    }
197
198    fn block(&self, state: &mut S, x: &[[X; N]], y: &mut [[Y; M]]) {
199        const { assert!(N.is_multiple_of(Q)) }
200        const { assert!(M.is_multiple_of(R)) }
201        let (x, []) = x.as_flattened().as_chunks() else {
202            unreachable!()
203        };
204        let (y, []) = y.as_flattened_mut().as_chunks_mut() else {
205            unreachable!()
206        };
207        self.0.block(state, x, y)
208    }
209}
210impl<C: SplitInplace<[X; 1], S>, S, X: Copy, const N: usize> SplitInplace<[X; N], S>
211    for ChunkInOut<C, 1, 1>
212where
213    Self: SplitProcess<[X; N], [X; N], S>,
214{
215    fn inplace(&self, state: &mut S, xy: &mut [[X; N]]) {
216        let (xy, []) = xy.as_flattened_mut().as_chunks_mut() else {
217            unreachable!()
218        };
219        self.0.inplace(state, xy)
220    }
221}