sim_lib_midi_smf/
model.rs1#![forbid(unsafe_code)]
2
3use std::cmp::Ordering;
4
5use sim_lib_midi_core::{
6 ChannelMessage, MetaEvent, MidiEvent, MidiPayload, TickTime, TrackedMidiEvent, synthetic_origin,
7};
8
9#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
11pub enum SmfFormat {
12 SingleTrack,
14 Simultaneous,
16 Independent,
18}
19
20#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct SmfTrack {
23 pub events: Vec<MidiEvent>,
25}
26
27#[derive(Clone, Debug, PartialEq, Eq)]
29pub struct SmfFile {
30 pub format: SmfFormat,
32 pub tpq: u32,
34 pub tracks: Vec<SmfTrack>,
36}
37
38#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
40pub struct SmfWriteOptions {
41 pub running_status: bool,
44}
45
46pub struct SmfMergeCursor<'a> {
52 file: &'a SmfFile,
53 next_index: Vec<usize>,
54}
55
56impl SmfFile {
57 pub fn canonicalize(&mut self) {
60 for track in &mut self.tracks {
61 canonicalize_track(track, self.tpq);
62 }
63 }
64
65 pub fn merge_cursor(&self) -> SmfMergeCursor<'_> {
67 SmfMergeCursor {
68 file: self,
69 next_index: vec![0; self.tracks.len()],
70 }
71 }
72
73 pub fn merged_events(&self) -> Vec<TrackedMidiEvent> {
76 let mut merged = Vec::new();
77 for event in self.merge_cursor() {
78 merged.push(event);
79 }
80 merged
81 }
82}
83
84impl<'a> Iterator for SmfMergeCursor<'a> {
85 type Item = TrackedMidiEvent;
86
87 fn next(&mut self) -> Option<Self::Item> {
88 let mut best: Option<(usize, &MidiEvent)> = None;
89 for (track_idx, track) in self.file.tracks.iter().enumerate() {
90 let event = track.events.get(self.next_index[track_idx])?;
91 match best {
92 None => best = Some((track_idx, event)),
93 Some((best_track, best_event)) => {
94 if compare_event_order(event, track_idx, best_event, best_track)
95 == Ordering::Less
96 {
97 best = Some((track_idx, event));
98 }
99 }
100 }
101 }
102 let (track_idx, event) = best?;
103 self.next_index[track_idx] += 1;
104 Some(TrackedMidiEvent {
105 last_track: track_idx,
106 event: event.clone(),
107 })
108 }
109}
110
111pub(crate) fn canonicalize_track(track: &mut SmfTrack, tpq: u32) {
112 track.events.sort_by(compare_events_same_track);
113 if !track
114 .events
115 .iter()
116 .any(|event| matches!(event.payload, MidiPayload::Meta(MetaEvent::EndOfTrack)))
117 {
118 let last_ticks = track
119 .events
120 .last()
121 .map(|event| event.time.ticks)
122 .unwrap_or(0);
123 track.events.push(MidiEvent {
124 time: TickTime::new(last_ticks, tpq).unwrap_or(TickTime::ZERO),
125 origin: synthetic_origin(),
126 payload: MidiPayload::Meta(MetaEvent::EndOfTrack),
127 });
128 }
129 track.events.sort_by(compare_events_same_track);
130}
131
132pub(crate) fn compare_event_order(
133 left: &MidiEvent,
134 left_track: usize,
135 right: &MidiEvent,
136 right_track: usize,
137) -> Ordering {
138 compare_time(left.time, right.time)
139 .then_with(|| event_priority(left).cmp(&event_priority(right)))
140 .then_with(|| left_track.cmp(&right_track))
141}
142
143fn compare_events_same_track(left: &MidiEvent, right: &MidiEvent) -> Ordering {
144 compare_time(left.time, right.time)
145 .then_with(|| event_priority(left).cmp(&event_priority(right)))
146}
147
148fn compare_time(left: TickTime, right: TickTime) -> Ordering {
149 let left_scaled = i128::from(left.ticks) * i128::from(right.tpq);
150 let right_scaled = i128::from(right.ticks) * i128::from(left.tpq);
151 left_scaled.cmp(&right_scaled)
152}
153
154fn event_priority(event: &MidiEvent) -> u8 {
155 match event.payload {
156 MidiPayload::Meta(MetaEvent::EndOfTrack) => 4,
157 MidiPayload::Meta(_) => 0,
158 MidiPayload::Channel(ChannelMessage::NoteOff { .. }) => 1,
159 MidiPayload::Channel(ChannelMessage::NoteOn { .. }) => 2,
160 _ => 3,
161 }
162}