Skip to main content

sim_lib_midi_core/
io.rs

1use std::convert::Infallible;
2
3use crate::{MidiEvent, PumpError, TickTime};
4
5/// A [`MidiEvent`] tagged with the track it belongs to.
6#[derive(Clone, Debug, PartialEq, Eq)]
7pub struct TrackedMidiEvent {
8    /// Index of the track that produced the event.
9    pub last_track: usize,
10    /// The event itself.
11    pub event: MidiEvent,
12}
13
14/// A pull-based stream of [`MidiEvent`]s at a fixed resolution.
15pub trait MidiSource {
16    /// Error type returned by [`next`](Self::next).
17    type Err;
18
19    /// Returns the source resolution in ticks per quarter note.
20    fn tpq(&self) -> u32;
21
22    /// Returns the next event, or `None` once the stream is exhausted.
23    fn next(&mut self) -> Result<Option<MidiEvent>, Self::Err>;
24}
25
26/// A [`MidiSource`] that also reports per-track membership.
27pub trait TrackedMidiSource: MidiSource {
28    /// Returns the track index of the most recently yielded event.
29    fn last_track(&self) -> usize;
30
31    /// Returns the total number of tracks in the stream.
32    fn n_tracks(&self) -> usize;
33
34    /// Returns the next event together with its track tag.
35    fn next_tracked(&mut self) -> Result<Option<TrackedMidiEvent>, Self::Err>;
36}
37
38/// A push-based sink that accepts [`MidiEvent`]s at a fixed resolution.
39pub trait MidiSink {
40    /// Error type returned by [`write`](Self::write) and [`flush`](Self::flush).
41    type Err;
42
43    /// Returns the sink resolution in ticks per quarter note.
44    fn tpq(&self) -> u32;
45
46    /// Writes one event to the sink.
47    fn write(&mut self, event: &MidiEvent) -> Result<(), Self::Err>;
48
49    /// Flushes any buffered state to the underlying destination.
50    fn flush(&mut self) -> Result<(), Self::Err>;
51}
52
53/// An in-memory [`MidiSource`] backed by a time-sorted event vector.
54#[derive(Clone, Debug, Default)]
55pub struct MemoryMidiSource {
56    tpq: u32,
57    events: Vec<MidiEvent>,
58    cursor: usize,
59}
60
61impl MemoryMidiSource {
62    /// Creates a source at `tpq` resolution, sorting `events` by tick.
63    pub fn new(tpq: u32, mut events: Vec<MidiEvent>) -> Self {
64        events.sort_by_key(|event| event.time.ticks);
65        Self {
66            tpq,
67            events,
68            cursor: 0,
69        }
70    }
71}
72
73/// An in-memory [`TrackedMidiSource`] backed by a time-sorted, track-tagged
74/// event vector.
75#[derive(Clone, Debug, Default)]
76pub struct MemoryTrackedMidiSource {
77    tpq: u32,
78    events: Vec<TrackedMidiEvent>,
79    cursor: usize,
80    last_track: usize,
81    n_tracks: usize,
82}
83
84impl MemoryTrackedMidiSource {
85    /// Creates a tracked source at `tpq` resolution, sorting `events` by tick
86    /// and deriving the track count from the highest track index.
87    pub fn new(tpq: u32, mut events: Vec<TrackedMidiEvent>) -> Self {
88        events.sort_by_key(|item| item.event.time.ticks);
89        let n_tracks = events
90            .iter()
91            .map(|item| item.last_track + 1)
92            .max()
93            .unwrap_or(0);
94        Self {
95            tpq,
96            events,
97            cursor: 0,
98            last_track: 0,
99            n_tracks,
100        }
101    }
102}
103
104impl MidiSource for MemoryMidiSource {
105    type Err = Infallible;
106
107    fn tpq(&self) -> u32 {
108        self.tpq
109    }
110
111    fn next(&mut self) -> Result<Option<MidiEvent>, Self::Err> {
112        let event = self.events.get(self.cursor).cloned();
113        if event.is_some() {
114            self.cursor += 1;
115        }
116        Ok(event)
117    }
118}
119
120impl MidiSource for MemoryTrackedMidiSource {
121    type Err = Infallible;
122
123    fn tpq(&self) -> u32 {
124        self.tpq
125    }
126
127    fn next(&mut self) -> Result<Option<MidiEvent>, Self::Err> {
128        let event = self.events.get(self.cursor).cloned();
129        if let Some(event) = &event {
130            self.last_track = event.last_track;
131            self.cursor += 1;
132        }
133        Ok(event.map(|item| item.event))
134    }
135}
136
137impl TrackedMidiSource for MemoryTrackedMidiSource {
138    fn last_track(&self) -> usize {
139        self.last_track
140    }
141
142    fn n_tracks(&self) -> usize {
143        self.n_tracks
144    }
145
146    fn next_tracked(&mut self) -> Result<Option<TrackedMidiEvent>, Self::Err> {
147        let event = self.events.get(self.cursor).cloned();
148        if let Some(event) = &event {
149            self.last_track = event.last_track;
150            self.cursor += 1;
151        }
152        Ok(event)
153    }
154}
155
156/// An in-memory [`MidiSink`] that collects written events in tick order.
157#[derive(Clone, Debug, Default)]
158pub struct MemoryMidiSink {
159    tpq: u32,
160    events: Vec<MidiEvent>,
161}
162
163impl MemoryMidiSink {
164    /// Creates an empty sink at `tpq` resolution.
165    pub fn new(tpq: u32) -> Self {
166        Self {
167            tpq,
168            events: Vec::new(),
169        }
170    }
171
172    /// Returns the events collected so far, sorted by tick.
173    pub fn events(&self) -> &[MidiEvent] {
174        &self.events
175    }
176}
177
178impl MidiSink for MemoryMidiSink {
179    type Err = Infallible;
180
181    fn tpq(&self) -> u32 {
182        self.tpq
183    }
184
185    fn write(&mut self, event: &MidiEvent) -> Result<(), Self::Err> {
186        self.events.push(event.clone());
187        self.events.sort_by_key(|item| item.time.ticks);
188        Ok(())
189    }
190
191    fn flush(&mut self) -> Result<(), Self::Err> {
192        Ok(())
193    }
194}
195
196/// Drains every event from `source` into `sink`, re-timing events to the sink's
197/// resolution, and returns the number of events transferred.
198///
199/// Mismatched resolutions are reconciled by [`TickTime::quantize`]; the sink is
200/// flushed once the source is exhausted. Errors are wrapped in [`PumpError`] to
201/// record which side failed.
202pub fn pump<S, T>(source: &mut S, sink: &mut T) -> Result<usize, PumpError<S::Err, T::Err>>
203where
204    S: MidiSource,
205    T: MidiSink,
206{
207    let mut count = 0usize;
208    let sink_tpq = sink.tpq();
209    let source_tpq = source.tpq();
210    while let Some(mut event) = source.next().map_err(PumpError::Source)? {
211        if source_tpq != sink_tpq {
212            event.time = event.time.quantize(sink_tpq);
213        } else if event.time.tpq != sink_tpq {
214            event.time = TickTime {
215                ticks: event.time.ticks,
216                tpq: sink_tpq,
217            };
218        }
219        sink.write(&event).map_err(PumpError::Sink)?;
220        count += 1;
221    }
222    sink.flush().map_err(PumpError::Sink)?;
223    Ok(count)
224}