Skip to main content

sva_samples/
buffer.rs

1// Concern: the one sample container: planar components over a span of the grid, grown, cut and viewed | Non-concern: producing or measuring them | IO: (component, index) -> f64
2
3use std::borrow::Cow;
4use std::ops::Range;
5
6use crate::grid::Extent;
7
8#[derive(Clone, Debug, PartialEq)]
9pub struct Buffer {
10    pub rate: u32,
11    /// The grid sample the first of these stands at; sample 0 is t = 0.
12    pub start: i64,
13    pub planes: Vec<Vec<f64>>,
14}
15
16impl Buffer {
17    pub fn silence(rate: u32, width: usize, len: usize) -> Buffer {
18        Buffer {
19            rate,
20            start: 0,
21            planes: vec![vec![0.0; len]; width],
22        }
23    }
24
25    pub fn empty(rate: u32, width: usize, capacity: usize, start: i64) -> Buffer {
26        Buffer {
27            rate,
28            start,
29            planes: (0..width.max(1))
30                .map(|_| Vec::with_capacity(capacity))
31                .collect(),
32        }
33    }
34
35    pub fn mono(rate: u32, samples: Vec<f64>) -> Buffer {
36        Buffer {
37            rate,
38            start: 0,
39            planes: vec![samples],
40        }
41    }
42
43    pub fn of_planes(rate: u32, planes: Vec<Vec<f64>>) -> Buffer {
44        let len = planes.iter().map(Vec::len).min().unwrap_or(0);
45        Buffer {
46            rate,
47            start: 0,
48            planes: planes
49                .into_iter()
50                .map(|mut p| {
51                    p.truncate(len);
52                    p
53                })
54                .collect(),
55        }
56    }
57
58    pub fn width(&self) -> usize {
59        self.planes.len()
60    }
61
62    pub fn plane(&self, c: usize) -> &[f64] {
63        &self.planes[c]
64    }
65
66    pub fn plane_mut(&mut self, c: usize) -> &mut [f64] {
67        &mut self.planes[c]
68    }
69
70    pub fn slices(&self) -> Vec<&[f64]> {
71        self.planes.iter().map(Vec::as_slice).collect()
72    }
73
74    pub fn len(&self) -> usize {
75        self.planes.iter().map(Vec::len).min().unwrap_or(0)
76    }
77
78    pub fn is_empty(&self) -> bool {
79        self.len() == 0
80    }
81
82    pub fn at(&self, c: usize, i: usize) -> f64 {
83        self.planes[c][i]
84    }
85
86    pub fn origin_secs(&self) -> f64 {
87        self.start as f64 / f64::from(self.rate)
88    }
89
90    pub fn end(&self) -> i64 {
91        self.start + self.len() as i64
92    }
93
94    pub fn extent(&self) -> Extent {
95        Extent::new(self.start, self.end())
96    }
97
98    pub fn capacity(&self) -> usize {
99        self.planes[0].capacity()
100    }
101
102    pub fn push(&mut self, c: usize, value: f64) {
103        self.planes[c].push(value);
104    }
105
106    pub fn forget_before(&mut self, from: i64) {
107        let gone = (from - self.start).clamp(0, self.len() as i64) as usize;
108        if gone == 0 {
109            return;
110        }
111        for plane in &mut self.planes {
112            plane.drain(..gone);
113        }
114        self.start += gone as i64;
115    }
116
117    /// A node zero outside `support`.
118    pub fn within(&self, support: Extent) -> SampleView<'_> {
119        SampleView {
120            planes: &self.planes,
121            base: self.start,
122            support,
123            period: None,
124            offset: 0,
125        }
126    }
127
128    /// These samples over `over`, a node that is zero outside `support`.
129    pub fn over(&self, over: Extent, support: Extent) -> Buffer {
130        let view = self.within(support);
131        let planes = match !over.is_empty() && self.extent().intersect(over) == over {
132            true => self
133                .planes
134                .iter()
135                .map(|p| p[(over.start - self.start) as usize..][..over.len()].to_vec())
136                .collect(),
137            false => (0..self.width())
138                .map(|c| (over.start..over.end).map(|n| view.at(c, n)).collect())
139                .collect(),
140        };
141        let mut out = Buffer::of_planes(self.rate, planes);
142        out.start = over.start;
143        out
144    }
145
146    pub fn plane_slice(&self, c: usize, range: Range<usize>) -> Cow<'_, [f64]> {
147        let plane = self.plane(c);
148        if range.end <= plane.len() {
149            return Cow::Borrowed(&plane[range]);
150        }
151        let mut out = vec![0.0; range.len()];
152        let held = plane.len().clamp(range.start, range.end) - range.start;
153        out[..held].copy_from_slice(&plane[range.start.min(plane.len())..][..held]);
154        Cow::Owned(out)
155    }
156
157    /// The only narrowing in the crate: a WAV file and the wasm boundary are both f32.
158    pub fn as_f32(&self, c: usize) -> Vec<f32> {
159        self.plane(c).iter().map(|&x| x as f32).collect()
160    }
161}
162
163/// A view of held samples; a periodic value's samples are held over one period from 0, and
164/// sample `k` is sample `k mod period`.
165#[derive(Clone, Copy, Debug)]
166pub struct SampleView<'a> {
167    planes: &'a [Vec<f64>],
168    base: i64,
169    support: Extent,
170    period: Option<i64>,
171    /// Sample `k` of the view is sample `k + offset` of what it views.
172    offset: i64,
173}
174
175impl<'a> SampleView<'a> {
176    pub fn folded(self, period: Option<i64>) -> SampleView<'a> {
177        SampleView { period, ..self }
178    }
179
180    pub fn shifted(self, by: i64) -> SampleView<'a> {
181        SampleView {
182            offset: self.offset + by,
183            ..self
184        }
185    }
186
187    fn fold(&self, k: i64) -> i64 {
188        let k = k.saturating_add(self.offset);
189        match self.period {
190            Some(n) if self.support.contains(k) => k.rem_euclid(n),
191            _ => k,
192        }
193    }
194
195    /// `None` for a sample inside the support this view does not hold.
196    pub fn get(&self, c: usize, k: i64) -> Option<f64> {
197        let k = self.fold(k);
198        let plane = &self.planes[c];
199        let held = k
200            .checked_sub(self.base)
201            .and_then(|at| usize::try_from(at).ok())
202            .and_then(|at| plane.get(at));
203        match held {
204            Some(v) => Some(*v),
205            None => (!self.support.contains(k)).then_some(0.0),
206        }
207    }
208
209    /// Samples `[k, k + len)` of component `c`, where held unfolded.
210    pub fn run(&self, c: usize, k: i64, len: usize) -> Option<&'a [f64]> {
211        if self.period.is_some() {
212            return None;
213        }
214        let at = usize::try_from(k.checked_add(self.offset)?.checked_sub(self.base)?).ok()?;
215        self.planes.get(c)?.get(at..at.checked_add(len)?)
216    }
217
218    /// A sample not held inside the support is a reader past its extent.
219    pub fn at(&self, c: usize, k: i64) -> f64 {
220        let k = self.fold(k);
221        let plane = &self.planes[c];
222        if let Some(held) = k
223            .checked_sub(self.base)
224            .and_then(|at| usize::try_from(at).ok())
225            .and_then(|at| plane.get(at))
226        {
227            return *held;
228        }
229        if !self.support.contains(k) {
230            return 0.0;
231        }
232        panic!(
233            "sample {k} is not held: this node holds [{}, {}) and is nonzero over [{}, {})",
234            self.base,
235            self.base + plane.len() as i64,
236            self.support.start,
237            self.support.end
238        )
239    }
240}