Skip to main content

clack_common/events/io/
batcher.rs

1use crate::events::io::{InputEvents, InputEventsIter};
2use std::ops::Bound;
3
4#[derive(Copy, Clone, Debug)]
5enum State {
6    Started {
7        first_event_sample_time: Option<u32>,
8    },
9    HasNextEvent {
10        next_event_index: u32,
11        next_event_sample_time: u32,
12    },
13    Ended,
14}
15
16/// An iterator which batches input events by grouping them together.
17///
18/// See the [`InputEvents::batch`] method documentation for more details and usage examples.
19#[derive(Clone)]
20#[must_use = "iterators are lazy and do nothing unless consumed"]
21pub struct EventBatcher<'a> {
22    events: &'a InputEvents<'a>,
23    events_len: u32,
24    state: State,
25}
26
27impl<'a> EventBatcher<'a> {
28    pub(crate) fn new(events: &'a InputEvents<'a>) -> Self {
29        let events_len = events.len();
30
31        Self {
32            events,
33            events_len,
34            state: State::Started {
35                first_event_sample_time: if events_len == 0 {
36                    None
37                } else {
38                    events.get(0).map(|e| e.header().time())
39                },
40            },
41        }
42    }
43
44    fn next_non_matching(
45        &self,
46        current_event_index: u32,
47        current_sample: u32,
48    ) -> Option<(u32, u32)> {
49        for next_index in (current_event_index + 1)..self.events_len {
50            let Some(next_event) = self.events.get(next_index) else {
51                continue;
52            };
53
54            let next_event_sample_time = next_event.header().time();
55            if next_event_sample_time != current_sample {
56                return Some((next_index, next_event_sample_time));
57            }
58        }
59
60        None
61    }
62}
63
64impl<'a> Iterator for EventBatcher<'a> {
65    type Item = EventBatch<'a>;
66
67    fn next(&mut self) -> Option<Self::Item> {
68        use crate::events::io::batcher::State::*;
69
70        let (current_event_index, current_sample, next_non_matching_event) = match self.state {
71            Ended => return None,
72            HasNextEvent {
73                next_event_index,
74                next_event_sample_time,
75            } => (
76                next_event_index,
77                next_event_sample_time,
78                self.next_non_matching(next_event_index, next_event_sample_time),
79            ),
80            Started {
81                first_event_sample_time: Some(0),
82            } => (0, 0, self.next_non_matching(0, 0)),
83            Started {
84                first_event_sample_time: None,
85            } => (0, 0, None),
86            Started {
87                first_event_sample_time: Some(first_event_sample_time),
88            } => (0, 0, Some((0, first_event_sample_time))),
89        };
90
91        match next_non_matching_event {
92            None => {
93                self.state = Ended;
94
95                Some(EventBatch {
96                    events: InputEventsIter::new(self.events, current_event_index..self.events_len),
97                    first_sample: current_sample as usize,
98                    next_batch_first_sample: None,
99                })
100            }
101            Some((next_event_index, next_event_sample_time)) => {
102                self.state = HasNextEvent {
103                    next_event_sample_time,
104                    next_event_index,
105                };
106
107                Some(EventBatch {
108                    events: InputEventsIter::new(
109                        self.events,
110                        current_event_index..next_event_index,
111                    ),
112                    first_sample: current_sample as usize,
113                    next_batch_first_sample: Some(next_event_sample_time as usize),
114                })
115            }
116        }
117    }
118}
119
120/// A batch of events over a specific timeframe in samples.
121///
122/// This is what is produced by the [`EventBatcher`] iterator, i.e. the iterator returned by the
123/// [`InputEvents::batch`] method.
124pub struct EventBatch<'a> {
125    events: InputEventsIter<'a>,
126    first_sample: usize,
127    next_batch_first_sample: Option<usize>,
128}
129
130impl<'a> EventBatch<'a> {
131    /// Returns all events in this batch.
132    #[inline]
133    pub fn events(&self) -> InputEventsIter<'a> {
134        self.events.clone()
135    }
136
137    /// Returns the index of the first sample in this batch.
138    ///
139    /// This is used to locate the start of the batch when processing a block of samples.
140    #[inline]
141    pub fn first_sample(&self) -> usize {
142        self.first_sample
143    }
144
145    /// Returns the index of the first sample in the next batch.
146    ///
147    /// This is used to locate the end of the batch when processing a block of samples.
148    ///
149    /// If there is no next batch (i.e. if this is the last batch), then this returns `None`,
150    /// indicating that this batch extends to the end of the sample block that is being processed.
151    #[inline]
152    pub fn next_batch_first_sample(&self) -> Option<usize> {
153        self.next_batch_first_sample
154    }
155
156    /// Returns the batch's bounds as a pair of sample index bounds, which can be directly used
157    /// for indexing into a slice.
158    ///
159    /// This is equivalent to indexing with:
160    /// * `first_sample..next_batch_first_sample` if there is a next batch;
161    /// * or `first_sample..` if this is the last batch.
162    ///
163    /// # Example
164    ///
165    /// ```
166    /// use clack_common::events::io::EventBatch;
167    /// # fn index(slice: &[f32], batch: EventBatch) {
168    /// let slice: &[f32] = /* ... */
169    /// # slice;
170    /// let batch: EventBatch = /* ... */
171    /// # batch;
172    ///
173    /// // Easily get the subslice matching this event batch's bounds.
174    /// let subslice = &slice[batch.sample_bounds()];
175    ///
176    /// // Using sample_bounds is equivalent to doing this:
177    /// let manual_subslice = match batch.next_batch_first_sample() {
178    ///     Some(next_batch_first_sample) => &slice[batch.first_sample()..next_batch_first_sample],
179    ///     None => &slice[batch.first_sample()..]
180    /// };
181    ///
182    /// assert_eq!(subslice, manual_subslice)
183    /// # }
184    /// ```
185    #[inline]
186    pub fn sample_bounds(&self) -> (Bound<usize>, Bound<usize>) {
187        (
188            Bound::Included(self.first_sample),
189            match self.next_batch_first_sample {
190                None => Bound::Unbounded,
191                Some(end) => Bound::Excluded(end),
192            },
193        )
194    }
195}
196
197#[cfg(test)]
198mod tests {
199    use super::*;
200    use crate::events::event_types::ParamGestureBeginEvent;
201    use crate::utils::ClapId;
202
203    const PARAM: ClapId = ClapId::new(0);
204
205    #[test]
206    pub fn works_with_empty_events() {
207        let events = InputEvents::empty();
208        let mut events = events.batch();
209
210        {
211            let batch = events.next().unwrap();
212            assert_eq!(batch.first_sample(), 0);
213            assert_eq!(batch.next_batch_first_sample(), None);
214
215            let mut batch_events = batch.events();
216            assert!(batch_events.next().is_none());
217        }
218
219        assert!(events.next().is_none())
220    }
221
222    #[test]
223    pub fn works_with_single_zero_event() {
224        let buf = [ParamGestureBeginEvent::new(0, PARAM)];
225
226        let events = InputEvents::from_buffer(&buf);
227        let mut events = events.batch();
228
229        {
230            let batch = events.next().unwrap();
231            assert_eq!(batch.first_sample(), 0);
232            assert_eq!(batch.next_batch_first_sample(), None);
233
234            let mut batch_events = batch.events();
235            assert_eq!(&buf[0], batch_events.next().unwrap().as_event().unwrap());
236            assert!(batch_events.next().is_none());
237        }
238
239        assert!(events.next().is_none())
240    }
241
242    #[test]
243    pub fn works_with_single_nonzero_event() {
244        let buf = [ParamGestureBeginEvent::new(5, PARAM)];
245
246        let events = InputEvents::from_buffer(&buf);
247        let mut events = events.batch();
248
249        {
250            let batch = events.next().unwrap();
251            assert_eq!(batch.first_sample(), 0);
252            assert_eq!(batch.next_batch_first_sample(), Some(5));
253
254            let mut batch_events = batch.events();
255            assert!(batch_events.next().is_none());
256        }
257        {
258            let batch = events.next().unwrap();
259            assert_eq!(batch.first_sample(), 5);
260            assert_eq!(batch.next_batch_first_sample(), None);
261
262            let mut batch_events = batch.events();
263            assert_eq!(&buf[0], batch_events.next().unwrap().as_event().unwrap());
264            assert!(batch_events.next().is_none());
265        }
266
267        assert!(events.next().is_none())
268    }
269
270    #[test]
271    pub fn works_with_two_grouped_nonzero_events() {
272        let buf = [
273            ParamGestureBeginEvent::new(5, PARAM),
274            ParamGestureBeginEvent::new(5, PARAM),
275        ];
276
277        let events = InputEvents::from_buffer(&buf);
278        let mut events = events.batch();
279
280        {
281            let batch = events.next().unwrap();
282            assert_eq!(batch.first_sample(), 0);
283            assert_eq!(batch.next_batch_first_sample(), Some(5));
284
285            let mut batch_events = batch.events();
286            assert!(batch_events.next().is_none());
287        }
288        {
289            let batch = events.next().unwrap();
290            assert_eq!(batch.first_sample(), 5);
291            assert_eq!(batch.next_batch_first_sample(), None);
292
293            let mut batch_events = batch.events();
294            assert_eq!(&buf[0], batch_events.next().unwrap().as_event().unwrap());
295            assert_eq!(&buf[1], batch_events.next().unwrap().as_event().unwrap());
296            assert!(batch_events.next().is_none());
297        }
298
299        assert!(events.next().is_none())
300    }
301
302    #[test]
303    pub fn works_with_two_distinct_nonzero_events() {
304        let buf = [
305            ParamGestureBeginEvent::new(5, PARAM),
306            ParamGestureBeginEvent::new(10, PARAM),
307        ];
308
309        let events = InputEvents::from_buffer(&buf);
310        let mut events = events.batch();
311
312        {
313            let batch = events.next().unwrap();
314            assert_eq!(batch.first_sample(), 0);
315            assert_eq!(batch.next_batch_first_sample(), Some(5));
316
317            let mut batch_events = batch.events();
318            assert!(batch_events.next().is_none());
319        }
320        {
321            let batch = events.next().unwrap();
322            assert_eq!(batch.first_sample(), 5);
323            assert_eq!(batch.next_batch_first_sample(), Some(10));
324
325            let mut batch_events = batch.events();
326            assert_eq!(&buf[0], batch_events.next().unwrap().as_event().unwrap());
327            assert!(batch_events.next().is_none());
328        }
329        {
330            let batch = events.next().unwrap();
331            assert_eq!(batch.first_sample(), 10);
332            assert_eq!(batch.next_batch_first_sample(), None);
333
334            let mut batch_events = batch.events();
335            assert_eq!(&buf[1], batch_events.next().unwrap().as_event().unwrap());
336            assert!(batch_events.next().is_none());
337        }
338
339        assert!(events.next().is_none())
340    }
341
342    #[test]
343    pub fn three_distinct_nonzero_events() {
344        let buf = [
345            ParamGestureBeginEvent::new(5, PARAM),
346            ParamGestureBeginEvent::new(10, PARAM),
347            ParamGestureBeginEvent::new(15, PARAM),
348        ];
349
350        let events = InputEvents::from_buffer(&buf);
351        let mut events = events.batch();
352
353        {
354            let batch = events.next().unwrap();
355            assert_eq!(batch.first_sample(), 0);
356            assert_eq!(batch.next_batch_first_sample(), Some(5));
357
358            let mut batch_events = batch.events();
359            assert!(batch_events.next().is_none());
360        }
361        {
362            let batch = events.next().unwrap();
363            assert_eq!(batch.first_sample(), 5);
364            assert_eq!(batch.next_batch_first_sample(), Some(10));
365
366            let mut batch_events = batch.events();
367            assert_eq!(&buf[0], batch_events.next().unwrap().as_event().unwrap());
368            assert!(batch_events.next().is_none());
369        }
370        {
371            let batch = events.next().unwrap();
372            assert_eq!(batch.first_sample(), 10);
373            assert_eq!(batch.next_batch_first_sample(), Some(15));
374
375            let mut batch_events = batch.events();
376            assert_eq!(&buf[1], batch_events.next().unwrap().as_event().unwrap());
377            assert!(batch_events.next().is_none());
378        }
379        {
380            let batch = events.next().unwrap();
381            assert_eq!(batch.first_sample(), 15);
382            assert_eq!(batch.next_batch_first_sample(), None);
383
384            let mut batch_events = batch.events();
385            assert_eq!(&buf[2], batch_events.next().unwrap().as_event().unwrap());
386            assert!(batch_events.next().is_none());
387        }
388
389        assert!(events.next().is_none())
390    }
391}