Skip to main content

sim_lib_midi_live/
ring.rs

1use std::{collections::VecDeque, convert::Infallible};
2
3use sim_lib_midi_core::{MidiEvent, MidiSink, MidiSource, TrackedMidiEvent, TrackedMidiSource};
4
5use crate::LiveMidiError;
6
7/// A fixed-capacity ring buffer of [`MidiEvent`]s usable as both a
8/// [`MidiSource`] and a [`MidiSink`].
9///
10/// Writing to a full buffer evicts the oldest event and increments the dropped
11/// count; reading pops from the front.
12#[derive(Clone, Debug)]
13pub struct RingMidiBuffer {
14    tpq: u32,
15    capacity: usize,
16    dropped_events: usize,
17    events: VecDeque<MidiEvent>,
18}
19
20impl RingMidiBuffer {
21    /// Creates a buffer at `tpq` resolution holding up to `capacity` events.
22    ///
23    /// Fails with [`LiveMidiError::ZeroCapacity`] when `capacity` is zero.
24    pub fn new(tpq: u32, capacity: usize) -> Result<Self, LiveMidiError> {
25        if capacity == 0 {
26            return Err(LiveMidiError::ZeroCapacity);
27        }
28        Ok(Self {
29            tpq,
30            capacity,
31            dropped_events: 0,
32            events: VecDeque::with_capacity(capacity),
33        })
34    }
35
36    /// Returns the buffer's maximum capacity.
37    pub fn capacity(&self) -> usize {
38        self.capacity
39    }
40
41    /// Returns the number of buffered events.
42    pub fn len(&self) -> usize {
43        self.events.len()
44    }
45
46    /// Returns whether the buffer is empty.
47    pub fn is_empty(&self) -> bool {
48        self.events.is_empty()
49    }
50
51    /// Returns the running count of events dropped due to overflow.
52    pub fn dropped_events(&self) -> usize {
53        self.dropped_events
54    }
55
56    /// Removes all buffered events (the dropped count is preserved).
57    pub fn clear(&mut self) {
58        self.events.clear();
59    }
60
61    /// Returns a copy of the currently buffered events without consuming them.
62    pub fn snapshot(&self) -> Vec<MidiEvent> {
63        self.events.iter().cloned().collect()
64    }
65
66    fn push_back(&mut self, event: MidiEvent) {
67        if self.events.len() == self.capacity {
68            let _ = self.events.pop_front();
69            self.dropped_events += 1;
70        }
71        self.events.push_back(event);
72    }
73}
74
75impl MidiSource for RingMidiBuffer {
76    type Err = Infallible;
77
78    fn tpq(&self) -> u32 {
79        self.tpq
80    }
81
82    fn next(&mut self) -> Result<Option<MidiEvent>, Self::Err> {
83        Ok(self.events.pop_front())
84    }
85}
86
87impl MidiSink for RingMidiBuffer {
88    type Err = Infallible;
89
90    fn tpq(&self) -> u32 {
91        self.tpq
92    }
93
94    fn write(&mut self, event: &MidiEvent) -> Result<(), Self::Err> {
95        self.push_back(event.clone());
96        Ok(())
97    }
98
99    fn flush(&mut self) -> Result<(), Self::Err> {
100        Ok(())
101    }
102}
103
104/// A fixed-capacity ring buffer of [`TrackedMidiEvent`]s usable as a
105/// [`TrackedMidiSource`], tracking the highest track index seen.
106#[derive(Clone, Debug)]
107pub struct RingTrackedMidiBuffer {
108    tpq: u32,
109    capacity: usize,
110    dropped_events: usize,
111    last_track: usize,
112    n_tracks: usize,
113    events: VecDeque<TrackedMidiEvent>,
114}
115
116impl RingTrackedMidiBuffer {
117    /// Creates a tracked buffer at `tpq` resolution holding up to `capacity`
118    /// events.
119    ///
120    /// Fails with [`LiveMidiError::ZeroCapacity`] when `capacity` is zero.
121    pub fn new(tpq: u32, capacity: usize) -> Result<Self, LiveMidiError> {
122        if capacity == 0 {
123            return Err(LiveMidiError::ZeroCapacity);
124        }
125        Ok(Self {
126            tpq,
127            capacity,
128            dropped_events: 0,
129            last_track: 0,
130            n_tracks: 0,
131            events: VecDeque::with_capacity(capacity),
132        })
133    }
134
135    /// Returns the buffer's maximum capacity.
136    pub fn capacity(&self) -> usize {
137        self.capacity
138    }
139
140    /// Returns the number of buffered events.
141    pub fn len(&self) -> usize {
142        self.events.len()
143    }
144
145    /// Returns whether the buffer is empty.
146    pub fn is_empty(&self) -> bool {
147        self.events.is_empty()
148    }
149
150    /// Returns the track index of the most recently read event.
151    pub fn last_track(&self) -> usize {
152        self.last_track
153    }
154
155    /// Returns the running count of events dropped due to overflow.
156    pub fn dropped_events(&self) -> usize {
157        self.dropped_events
158    }
159
160    /// Removes all buffered events and resets the track counters.
161    pub fn clear(&mut self) {
162        self.events.clear();
163        self.last_track = 0;
164        self.n_tracks = 0;
165    }
166
167    /// Returns a copy of the currently buffered tracked events.
168    pub fn snapshot(&self) -> Vec<TrackedMidiEvent> {
169        self.events.iter().cloned().collect()
170    }
171
172    /// Pushes a tracked event, updating the track count and dropping the oldest
173    /// event if the buffer is full.
174    pub fn push_tracked_event(&mut self, event: TrackedMidiEvent) {
175        self.n_tracks = self.n_tracks.max(event.last_track + 1);
176        if self.events.len() == self.capacity {
177            let _ = self.events.pop_front();
178            self.dropped_events += 1;
179        }
180        self.events.push_back(event);
181    }
182}
183
184impl MidiSource for RingTrackedMidiBuffer {
185    type Err = Infallible;
186
187    fn tpq(&self) -> u32 {
188        self.tpq
189    }
190
191    fn next(&mut self) -> Result<Option<MidiEvent>, Self::Err> {
192        let event = self.events.pop_front();
193        if let Some(event) = &event {
194            self.last_track = event.last_track;
195        }
196        Ok(event.map(|item| item.event))
197    }
198}
199
200impl TrackedMidiSource for RingTrackedMidiBuffer {
201    fn last_track(&self) -> usize {
202        self.last_track
203    }
204
205    fn n_tracks(&self) -> usize {
206        self.n_tracks
207    }
208
209    fn next_tracked(&mut self) -> Result<Option<TrackedMidiEvent>, Self::Err> {
210        let event = self.events.pop_front();
211        if let Some(event) = &event {
212            self.last_track = event.last_track;
213        }
214        Ok(event)
215    }
216}