1use byteorder::{BigEndian, ByteOrder};
2use midi_msg::*;
3
4#[inline]
6pub fn u32_to_bytes(x: u32) -> [u8; 4] {
7 let mut buf = [0; 4];
8 BigEndian::write_u32(&mut buf, x);
9 buf
10}
11
12#[inline]
13pub fn push_u32(x: u32, v: &mut Vec<u8>) {
14 let [b1, b2, b3, b4] = u32_to_bytes(x);
15 v.push(b1);
16 v.push(b2);
17 v.push(b3);
18 v.push(b4);
19}
20
21#[inline]
22pub fn u16_to_bytes(x: u16) -> [u8; 2] {
23 let mut buf = [0; 2];
24 BigEndian::write_u16(&mut buf, x);
25 buf
26}
27
28#[inline]
29pub fn push_u16(x: u16, v: &mut Vec<u8>) {
30 let [b1, b2] = u16_to_bytes(x);
31 v.push(b1);
32 v.push(b2);
33}
34
35pub fn push_vari(x: u32, v: &mut Vec<u8>) {
36 if x < 0x00000080 {
37 v.push(x as u8 & 0b01111111);
38 } else if x < 0x00004000 {
39 v.push(((x >> 7) as u8 & 0b01111111) + 0b10000000);
40 v.push(x as u8 & 0b01111111);
41 } else if x < 0x00200000 {
42 v.push(((x >> 14) as u8 & 0b01111111) + 0b10000000);
43 v.push(((x >> 7) as u8 & 0b01111111) + 0b10000000);
44 v.push(x as u8 & 0b01111111);
45 } else if x <= 0x0FFFFFFF {
46 v.push(((x >> 21) as u8 & 0b01111111) + 0b10000000);
47 v.push(((x >> 14) as u8 & 0b01111111) + 0b10000000);
48 v.push(((x >> 7) as u8 & 0b01111111) + 0b10000000);
49 v.push(x as u8 & 0b01111111);
50 } else {
51 panic!("Cannot use such a large number as a variable quantity")
52 }
53}
54
55#[derive(Debug, Copy, Clone)]
56#[repr(u16)]
57pub enum MidiFileFormat {
58 SingleTrack = 0,
59 SimultaniousTracks = 1,
60 IndependantTracks = 2,
61}
62
63#[derive(Debug)]
64pub struct MidiFile {
65 pub ticks_per_quarter_note: u16,
66 pub format: MidiFileFormat,
68 pub tracks: Vec<MidiFileTrack>,
69}
70
71impl MidiFile {
72 pub fn to_midi(&self) -> Vec<u8> {
73 let mut r: Vec<u8> = vec![];
74 self.extend_midi(&mut r);
75 r
76 }
77
78 pub fn track_to_midi(&self, track_num: usize) -> Vec<u8> {
79 let mut v: Vec<u8> = vec![];
80 v.extend_from_slice(b"MThd");
81 push_u32(6, &mut v); push_u16(self.format as u16, &mut v);
83 push_u16(1_u16, &mut v); if self.ticks_per_quarter_note > 0x7FFF {
85 panic!("Ticks per quarter note must be less than {}", 0x7FFF);
86 }
87 push_u16(self.ticks_per_quarter_note, &mut v);
88 self.tracks[track_num].extend_midi(&mut v);
89
90 v
91 }
92
93 pub fn extend_midi(&self, v: &mut Vec<u8>) {
94 v.extend_from_slice(b"MThd");
95 push_u32(6, v); push_u16(self.format as u16, v);
97 push_u16(self.tracks.len() as u16, v); if self.ticks_per_quarter_note > 0x7FFF {
99 panic!("Ticks per quarter note must be less than {}", 0x7FFF);
100 }
101 push_u16(self.ticks_per_quarter_note, v);
102 for track in self.tracks.iter() {
103 track.extend_midi(v);
104 }
105 }
106}
107
108#[derive(Debug)]
109pub struct MidiFileTrack {
110 pub events: Vec<(u32, MidiMsg)>,
114 pub name: Option<String>,
116 pub n_ticks: u32,
118}
119impl MidiFileTrack {
120 pub fn extend_midi(&self, v: &mut Vec<u8>) {
121 v.extend_from_slice(b"MTrk");
122 let mut events: Vec<u8> = vec![];
123
124 if let Some(name) = &self.name {
125 let len = name.len().min(127);
126 events.extend_from_slice(&[0x00, 0xFF, 0x03, len as u8]);
127 events.extend_from_slice(&name.as_bytes()[..len]);
128 }
129
130 let mut last_tick = 0;
131 for (ticks, msg) in self.events.iter() {
132 push_vari(*ticks - last_tick, &mut events);
133 msg.extend_midi(&mut events);
134 last_tick = *ticks;
135 }
136 push_vari(self.n_ticks - last_tick + 1, &mut events);
137 events.extend_from_slice(&[0xFF, 0x2F, 0x00]);
138 push_u32(events.len() as u32, v);
139 v.extend_from_slice(&events);
140 }
141
142 pub fn sort_events(&mut self) {
144 self.events.sort_by(|a, b| a.0.cmp(&b.0));
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use crate::midi_file::*;
151
152 #[test]
153 fn test_u32_to_bytes() {
154 let mut v: Vec<u8> = vec![];
155 push_u32(6, &mut v);
156 assert_eq!(v, vec![0, 0, 0, 6]);
157 }
158
159 fn validate_vari(n: u32, bytes: Vec<u8>) {
160 let mut v: Vec<u8> = vec![];
161 push_vari(n, &mut v);
162 assert_eq!(
163 v, bytes,
164 "{:#02X} should have been {:#02X?} not {:#02X?}",
165 n, &bytes, &v
166 );
167 }
168
169 #[test]
170 fn test_push_vari() {
171 validate_vari(6, vec![6]);
172 validate_vari(0x7F, vec![0x7F]);
173 validate_vari(0x80, vec![0x81, 0x00]);
174 validate_vari(0xE89, vec![0x9D, 0x09]);
175 validate_vari(0x3C0, vec![0x87, 0x40]);
176 validate_vari(0x2000, vec![0xC0, 0x00]);
177 validate_vari(0x3FFF, vec![0xFF, 0x7F]);
178 validate_vari(0x4000, vec![0x81, 0x80, 0x00]);
179 validate_vari(0x1FFFFF, vec![0xFF, 0xFF, 0x7F]);
180 validate_vari(0x0FFFFFFF, vec![0xFF, 0xFF, 0xFF, 0x7F]);
181 }
182
183 #[test]
184 fn test_midi_file() {
185 let quarter_note: u32 = 960;
186 let midi_file = MidiFile {
187 format: MidiFileFormat::SimultaniousTracks,
188 ticks_per_quarter_note: quarter_note as u16,
189 tracks: vec![
190 MidiFileTrack {
191 name: Some("Track 1".to_string()),
192 events: vec![
193 (
194 0,
195 MidiMsg::ChannelVoice {
196 channel: Channel::Ch1,
197 msg: ChannelVoiceMsg::NoteOn {
198 note: 72,
199 velocity: 100,
200 },
201 },
202 ),
203 (
204 quarter_note / 8,
205 MidiMsg::ChannelVoice {
206 channel: Channel::Ch1,
207 msg: ChannelVoiceMsg::NoteOff {
208 note: 72,
209 velocity: 100,
210 },
211 },
212 ),
213 ],
214 n_ticks: quarter_note * 4,
215 },
216 MidiFileTrack {
217 name: Some("Track 2".to_string()),
218 events: vec![
219 (
220 0,
221 MidiMsg::ChannelVoice {
222 channel: Channel::Ch1,
223 msg: ChannelVoiceMsg::NoteOn {
224 note: 72,
225 velocity: 100,
226 },
227 },
228 ),
229 (
230 quarter_note / 8,
231 MidiMsg::ChannelVoice {
232 channel: Channel::Ch1,
233 msg: ChannelVoiceMsg::NoteOff {
234 note: 72,
235 velocity: 100,
236 },
237 },
238 ),
239 ],
240 n_ticks: quarter_note * 4,
241 },
242 ],
243 }
244 .to_midi();
245
246 assert_eq!(
247 &midi_file,
248 &[
249 0x4d, 0x54, 0x68, 0x64, 0x0, 0x0, 0x0, 0x6, 0x0, 0x1, 0x0, 0x2, 0x3, 0xc0, 0x4d,
250 0x54, 0x72, 0x6b, 0x0, 0x0, 0x0, 0x18, 0x0, 0xff, 0x3, 0x7, 0x54, 0x72, 0x61, 0x63,
251 0x6b, 0x20, 0x31, 0x0, 0x90, 0x48, 0x64, 0x78, 0x80, 0x48, 0x64, 0x9d, 0x9, 0xff,
252 0x2f, 0x0, 0x4d, 0x54, 0x72, 0x6b, 0x0, 0x0, 0x0, 0x18, 0x0, 0xff, 0x3, 0x7, 0x54,
253 0x72, 0x61, 0x63, 0x6b, 0x20, 0x32, 0x0, 0x90, 0x48, 0x64, 0x78, 0x80, 0x48, 0x64,
254 0x9d, 0x9, 0xff, 0x2f, 0x0
255 ]
256 );
257 }
258}