Skip to main content

dsp_process/
view.rs

1use core::marker::PhantomData;
2
3use crate::{Split, SplitInplace, SplitProcess};
4
5/// Frame-major view layout marker.
6///
7/// A frame-major view corresponds to the ordinary `[[T; L]]` interpretation:
8/// frames are contiguous and each frame holds `L` lane values.
9#[derive(Clone, Copy, Debug, Default)]
10pub struct FrameMajor;
11
12/// Lane-major view layout marker.
13///
14/// A lane-major view stores `L` contiguous lane slices of equal length. This is
15/// useful when each lane should be processed as one long contiguous run.
16#[derive(Clone, Copy, Debug, Default)]
17pub struct LaneMajor;
18
19/// Immutable typed view of a DSP slice.
20///
21/// `Layout` describes how the flat storage should be interpreted. The view
22/// never allocates or physically transposes memory.
23#[derive(Clone, Copy, Debug)]
24pub struct View<'a, T, Layout, const L: usize> {
25    flat: &'a [T],
26    frames: usize,
27    _layout: PhantomData<Layout>,
28}
29
30/// Mutable typed view of a DSP slice.
31#[derive(Debug)]
32pub struct ViewMut<'a, T, Layout, const L: usize> {
33    flat: &'a mut [T],
34    frames: usize,
35    _layout: PhantomData<Layout>,
36}
37
38/// Apply a chunk-based processor frame by frame to a frame-major view.
39///
40/// This is the bridge between chunk semantics and typed views:
41/// `SplitProcess<[X; Q], [Y; R], S>` becomes per-frame processing from
42/// `View<FrameMajor, Q>` to `ViewMut<FrameMajor, R>`.
43///
44/// Use this when each frame is one logical chunk sample and the backing storage
45/// is already frame-major. For lane-major layout-sensitive processing, use
46/// [`crate::Lanes`] or [`crate::ByLane`] with explicit [`LaneMajor`] views
47/// instead.
48#[derive(Clone, Copy, Debug, Default)]
49pub struct PerFrame<C>(pub C);
50
51impl<'a, T, Layout, const L: usize> View<'a, T, Layout, L> {
52    /// Flat underlying storage.
53    #[must_use]
54    pub fn flat(self) -> &'a [T] {
55        self.flat
56    }
57
58    /// Number of frames in the view.
59    #[must_use]
60    pub const fn frames(self) -> usize {
61        self.frames
62    }
63
64    /// Reinterpret the same backing storage under another layout marker.
65    #[must_use]
66    pub fn as_layout<Other>(self) -> View<'a, T, Other, L> {
67        View {
68            flat: self.flat,
69            frames: self.frames,
70            _layout: PhantomData,
71        }
72    }
73}
74
75impl<'a, T, Layout, const L: usize> ViewMut<'a, T, Layout, L> {
76    /// Flat underlying storage.
77    #[must_use]
78    pub fn flat(&self) -> &[T] {
79        self.flat
80    }
81
82    /// Mutable flat underlying storage.
83    #[must_use]
84    pub fn flat_mut(&mut self) -> &mut [T] {
85        self.flat
86    }
87
88    /// Number of frames in the view.
89    #[must_use]
90    pub const fn frames(&self) -> usize {
91        self.frames
92    }
93
94    /// Reinterpret the same backing storage under another layout marker.
95    #[must_use]
96    pub fn as_layout<Other>(self) -> ViewMut<'a, T, Other, L> {
97        let Self { flat, frames, .. } = self;
98        ViewMut {
99            flat,
100            frames,
101            _layout: PhantomData,
102        }
103    }
104}
105
106impl<'a, T, const L: usize> View<'a, T, FrameMajor, L> {
107    /// Borrow a conventional frame-major view.
108    #[must_use]
109    pub fn from_frames(frames: &'a [[T; L]]) -> Self {
110        Self {
111            flat: frames.as_flattened(),
112            frames: frames.len(),
113            _layout: PhantomData,
114        }
115    }
116
117    /// Borrow the view as exact frames.
118    #[must_use]
119    pub fn as_frames(self) -> &'a [[T; L]] {
120        let (frames, []) = self.flat.as_chunks::<L>() else {
121            unreachable!()
122        };
123        frames
124    }
125
126    /// Borrow one frame.
127    #[must_use]
128    pub fn frame(self, i: usize) -> &'a [T; L] {
129        &self.as_frames()[i]
130    }
131}
132
133impl<'a, T, const L: usize> ViewMut<'a, T, FrameMajor, L> {
134    /// Borrow a mutable conventional frame-major view.
135    #[must_use]
136    pub fn from_frames(frames: &'a mut [[T; L]]) -> Self {
137        let frames_len = frames.len();
138        Self {
139            flat: frames.as_flattened_mut(),
140            frames: frames_len,
141            _layout: PhantomData,
142        }
143    }
144
145    /// Borrow the view as exact frames.
146    #[must_use]
147    pub fn as_frames(&self) -> &[[T; L]] {
148        let (frames, []) = self.flat.as_chunks::<L>() else {
149            unreachable!()
150        };
151        frames
152    }
153
154    /// Borrow the view as exact mutable frames.
155    #[must_use]
156    pub fn as_frames_mut(&mut self) -> &mut [[T; L]] {
157        let (frames, []) = self.flat.as_chunks_mut::<L>() else {
158            unreachable!()
159        };
160        frames
161    }
162
163    /// Borrow one frame.
164    #[must_use]
165    pub fn frame(&self, i: usize) -> &[T; L] {
166        &self.as_frames()[i]
167    }
168
169    /// Borrow one frame mutably.
170    #[must_use]
171    pub fn frame_mut(&mut self, i: usize) -> &mut [T; L] {
172        &mut self.as_frames_mut()[i]
173    }
174}
175
176impl<'a, T, const L: usize> View<'a, T, LaneMajor, L> {
177    /// Borrow a lane-major flat view.
178    ///
179    /// `frames` must satisfy `flat.len() == frames * L`.
180    #[must_use]
181    pub fn from_flat(flat: &'a [T], frames: usize) -> Self {
182        assert_eq!(flat.len(), frames * L);
183        Self {
184            flat,
185            frames,
186            _layout: PhantomData,
187        }
188    }
189
190    /// Borrow one contiguous lane slice.
191    #[must_use]
192    pub fn lane(self, i: usize) -> &'a [T] {
193        let start = i * self.frames;
194        &self.flat[start..start + self.frames]
195    }
196}
197
198impl<'a, T, const L: usize> ViewMut<'a, T, LaneMajor, L> {
199    /// Borrow a mutable lane-major flat view.
200    ///
201    /// `frames` must satisfy `flat.len() == frames * L`.
202    #[must_use]
203    pub fn from_flat(flat: &'a mut [T], frames: usize) -> Self {
204        assert_eq!(flat.len(), frames * L);
205        Self {
206            flat,
207            frames,
208            _layout: PhantomData,
209        }
210    }
211
212    /// Borrow one contiguous lane slice.
213    #[must_use]
214    pub fn lane(&self, i: usize) -> &[T] {
215        let start = i * self.frames;
216        &self.flat[start..start + self.frames]
217    }
218
219    /// Borrow one contiguous mutable lane slice.
220    #[must_use]
221    pub fn lane_mut(&mut self, i: usize) -> &mut [T] {
222        let start = i * self.frames;
223        &mut self.flat[start..start + self.frames]
224    }
225}
226
227/// Explicit processing API over typed views.
228///
229/// This is the typed-view companion to [`crate::Process`]. It is primarily
230/// useful for processors that care about view layout rather than just slice
231/// length.
232///
233/// # Examples
234///
235/// ```rust
236/// use dsp_process::{Offset, Split, View, ViewMut, ViewProcess};
237///
238/// let mut p = Split::stateless(Offset(3));
239/// let x = View::from_frames(&[[1, 2], [3, 4]]);
240/// let mut y = [[0; 2]; 2];
241/// let yv = ViewMut::from_frames(&mut y);
242/// ViewProcess::process_view(&mut p, x, yv);
243/// assert_eq!(y, [[4, 5], [6, 7]]);
244/// ```
245pub trait ViewProcess<X, Y = X> {
246    /// Process one typed input view into one typed output view.
247    fn process_view(&mut self, x: X, y: Y);
248}
249
250/// Explicit in-place processing API over typed views.
251pub trait ViewInplace<X> {
252    /// Process one typed view in place.
253    fn inplace_view(&mut self, xy: X);
254}
255
256/// Split-state processing API over typed views.
257pub trait SplitViewProcess<X, Y = X, S: ?Sized = ()> {
258    /// Process one typed input view into one typed output view.
259    fn process_view(&self, state: &mut S, x: X, y: Y);
260}
261
262/// Split-state in-place processing API over typed views.
263pub trait SplitViewInplace<X, S: ?Sized = ()> {
264    /// Process one typed view in place.
265    fn inplace_view(&self, state: &mut S, xy: X);
266}
267
268impl<'a, 'b, X, Y, S: ?Sized, T, const L: usize>
269    SplitViewProcess<View<'a, X, FrameMajor, L>, ViewMut<'b, Y, FrameMajor, L>, S> for T
270where
271    X: Copy,
272    T: SplitProcess<X, Y, S>,
273{
274    fn process_view(
275        &self,
276        state: &mut S,
277        x: View<'a, X, FrameMajor, L>,
278        mut y: ViewMut<'b, Y, FrameMajor, L>,
279    ) {
280        debug_assert_eq!(x.frames(), y.frames());
281        SplitProcess::block(self, state, x.flat(), y.flat_mut());
282    }
283}
284
285impl<'a, X, S: ?Sized, T, const L: usize> SplitViewInplace<ViewMut<'a, X, FrameMajor, L>, S> for T
286where
287    X: Copy,
288    T: SplitInplace<X, S>,
289{
290    fn inplace_view(&self, state: &mut S, mut xy: ViewMut<'a, X, FrameMajor, L>) {
291        SplitInplace::inplace(self, state, xy.flat_mut());
292    }
293}
294
295impl<X, Y, C, S> ViewProcess<X, Y> for Split<C, S>
296where
297    C: SplitViewProcess<X, Y, S>,
298{
299    fn process_view(&mut self, x: X, y: Y) {
300        self.config.process_view(&mut self.state, x, y);
301    }
302}
303
304impl<X, C, S> ViewInplace<X> for Split<C, S>
305where
306    C: SplitViewInplace<X, S>,
307{
308    fn inplace_view(&mut self, xy: X) {
309        self.config.inplace_view(&mut self.state, xy);
310    }
311}
312
313impl<C, S> Split<PerFrame<C>, S> {
314    /// Process a frame-major view frame by frame.
315    ///
316    /// ```rust
317    /// use dsp_process::{ChunkInOut, FnSplitProcess, Split, View, ViewMut};
318    ///
319    /// let mut p = Split::stateless(ChunkInOut::<_, 2, 1>(FnSplitProcess(
320    ///     |_: &mut (), [x0, x1]: [i32; 2]| [x0 + x1],
321    /// )))
322    /// .per_frame();
323    /// let x = View::from_frames(&[[1, 2], [3, 4]]);
324    /// let mut y = [[0; 1]; 2];
325    /// let yv = ViewMut::from_frames(&mut y);
326    /// p.process_frames(x, yv);
327    /// assert_eq!(y, [[3], [7]]);
328    /// ```
329    pub fn process_frames<'a, 'b, X, Y, const Q: usize, const R: usize>(
330        &mut self,
331        x: View<'a, X, FrameMajor, Q>,
332        mut y: ViewMut<'b, Y, FrameMajor, R>,
333    ) where
334        X: Copy,
335        C: SplitProcess<[X; Q], [Y; R], S>,
336    {
337        debug_assert_eq!(x.frames(), y.frames());
338        self.config
339            .0
340            .block(&mut self.state, x.as_frames(), y.as_frames_mut());
341    }
342
343    /// Process a frame-major view in place frame by frame.
344    pub fn inplace_frames<'a, X, const L: usize>(&mut self, mut xy: ViewMut<'a, X, FrameMajor, L>)
345    where
346        X: Copy,
347        C: SplitInplace<[X; L], S>,
348    {
349        self.config.0.inplace(&mut self.state, xy.as_frames_mut());
350    }
351}