1use crate::event::{MidiEvent, MidiEventKind};
12use std::io::{Read, Write};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum MidiFormat {
17 SingleTrack = 0,
19 MultiTrack = 1,
21 MultiTrackAsync = 2,
23}
24
25#[derive(Debug, Clone, Copy)]
27pub enum Division {
28 TicksPerQuarterNote(u16),
30 SmpteFrames { fps: u8, ticks_per_frame: u8 },
32}
33
34#[derive(Debug, Clone)]
36pub struct TimedMidiEvent {
37 pub delta_time: u32,
39 pub event: MidiEvent,
41}
42
43#[derive(Debug, Clone)]
45pub struct MidiTrack {
46 pub name: Option<String>,
48 pub events: Vec<TimedMidiEvent>,
50}
51
52#[derive(Debug, Clone)]
54pub struct MidiFile {
55 pub format: MidiFormat,
57 pub division: Division,
59 pub tracks: Vec<MidiTrack>,
61}
62
63impl MidiFile {
64 pub fn new(format: MidiFormat, division: Division) -> Self {
66 Self {
67 format,
68 division,
69 tracks: Vec::new(),
70 }
71 }
72
73 pub fn add_track(&mut self, track: MidiTrack) {
75 self.tracks.push(track);
76 }
77
78 pub fn from_bytes(data: &[u8]) -> Result<Self, String> {
84 let mut reader = data;
85
86 let mut chunk_type = [0u8; 4];
88 reader
89 .read_exact(&mut chunk_type)
90 .map_err(|e| format!("Failed to read chunk type: {}", e))?;
91
92 if &chunk_type != b"MThd" {
93 return Err(format!(
94 "Invalid MIDI file: expected MThd, got {:?}",
95 chunk_type
96 ));
97 }
98
99 let header_length = read_u32(&mut reader)?;
100 if header_length != 6 {
101 return Err(format!("Invalid header length: {}", header_length));
102 }
103
104 let format_value = read_u16(&mut reader)?;
105 let format = match format_value {
106 0 => MidiFormat::SingleTrack,
107 1 => MidiFormat::MultiTrack,
108 2 => MidiFormat::MultiTrackAsync,
109 _ => return Err(format!("Unsupported MIDI format: {}", format_value)),
110 };
111
112 let num_tracks = read_u16(&mut reader)?;
113 let division_value = read_u16(&mut reader)?;
114
115 let division = if division_value & 0x8000 == 0 {
116 Division::TicksPerQuarterNote(division_value)
117 } else {
118 let fps = ((division_value >> 8) & 0x7F) as u8;
119 let ticks_per_frame = (division_value & 0xFF) as u8;
120 Division::SmpteFrames {
121 fps,
122 ticks_per_frame,
123 }
124 };
125
126 let mut tracks = Vec::new();
127
128 for _ in 0..num_tracks {
130 let track = read_track(&mut reader)?;
131 tracks.push(track);
132 }
133
134 Ok(Self {
135 format,
136 division,
137 tracks,
138 })
139 }
140
141 pub fn to_bytes(&self) -> Result<Vec<u8>, String> {
143 let mut data = Vec::new();
144
145 data.write_all(b"MThd")
147 .map_err(|e| format!("Write error: {}", e))?;
148 write_u32(&mut data, 6)?; let format_value = self.format as u16;
151 write_u16(&mut data, format_value)?;
152 write_u16(&mut data, self.tracks.len() as u16)?;
153
154 let division_value = match self.division {
155 Division::TicksPerQuarterNote(ticks) => ticks,
156 Division::SmpteFrames {
157 fps,
158 ticks_per_frame,
159 } => 0x8000 | ((fps as u16) << 8) | (ticks_per_frame as u16),
160 };
161 write_u16(&mut data, division_value)?;
162
163 for track in &self.tracks {
165 write_track(&mut data, track)?;
166 }
167
168 Ok(data)
169 }
170}
171
172impl MidiTrack {
173 pub fn new() -> Self {
175 Self {
176 name: None,
177 events: Vec::new(),
178 }
179 }
180
181 pub fn with_name(name: impl Into<String>) -> Self {
183 Self {
184 name: Some(name.into()),
185 events: Vec::new(),
186 }
187 }
188
189 pub fn add_event(&mut self, delta_time: u32, event: MidiEvent) {
191 self.events.push(TimedMidiEvent { delta_time, event });
192 }
193}
194
195impl Default for MidiTrack {
196 fn default() -> Self {
197 Self::new()
198 }
199}
200
201fn read_u16(reader: &mut &[u8]) -> Result<u16, String> {
204 let mut buf = [0u8; 2];
205 reader
206 .read_exact(&mut buf)
207 .map_err(|e| format!("Read error: {}", e))?;
208 Ok(u16::from_be_bytes(buf))
209}
210
211fn read_u32(reader: &mut &[u8]) -> Result<u32, String> {
212 let mut buf = [0u8; 4];
213 reader
214 .read_exact(&mut buf)
215 .map_err(|e| format!("Read error: {}", e))?;
216 Ok(u32::from_be_bytes(buf))
217}
218
219fn write_u16(writer: &mut Vec<u8>, value: u16) -> Result<(), String> {
220 writer
221 .write_all(&value.to_be_bytes())
222 .map_err(|e| format!("Write error: {}", e))
223}
224
225fn write_u32(writer: &mut Vec<u8>, value: u32) -> Result<(), String> {
226 writer
227 .write_all(&value.to_be_bytes())
228 .map_err(|e| format!("Write error: {}", e))
229}
230
231fn read_variable_length(reader: &mut &[u8]) -> Result<u32, String> {
232 let mut value = 0u32;
233 for _ in 0..4 {
234 let mut byte = [0u8];
235 reader
236 .read_exact(&mut byte)
237 .map_err(|e| format!("Read error: {}", e))?;
238 value = (value << 7) | ((byte[0] & 0x7F) as u32);
239 if byte[0] & 0x80 == 0 {
240 return Ok(value);
241 }
242 }
243 Err("Variable length value too long".to_string())
244}
245
246fn write_variable_length(writer: &mut Vec<u8>, mut value: u32) -> Result<(), String> {
247 let mut bytes = Vec::new();
248 bytes.push((value & 0x7F) as u8);
249 value >>= 7;
250
251 while value > 0 {
252 bytes.push(((value & 0x7F) | 0x80) as u8);
253 value >>= 7;
254 }
255
256 for &byte in bytes.iter().rev() {
257 writer
258 .write_all(&[byte])
259 .map_err(|e| format!("Write error: {}", e))?;
260 }
261
262 Ok(())
263}
264
265fn read_track(reader: &mut &[u8]) -> Result<MidiTrack, String> {
266 let mut chunk_type = [0u8; 4];
267 reader
268 .read_exact(&mut chunk_type)
269 .map_err(|e| format!("Read error: {}", e))?;
270
271 if &chunk_type != b"MTrk" {
272 return Err(format!(
273 "Invalid track chunk: expected MTrk, got {:?}",
274 chunk_type
275 ));
276 }
277
278 let track_length = read_u32(reader)?;
279 let track_data = reader[..track_length as usize].to_vec();
280 *reader = &reader[track_length as usize..];
281
282 let mut track = MidiTrack::new();
283 let mut track_reader = &track_data[..];
284 let mut running_status = 0u8;
285
286 while !track_reader.is_empty() {
287 let delta_time = read_variable_length(&mut track_reader)?;
288
289 let mut status_byte = [0u8];
290 track_reader
291 .read_exact(&mut status_byte)
292 .map_err(|e| format!("Read error: {}", e))?;
293 let status = status_byte[0];
294
295 let (actual_status, data_start) = if status & 0x80 == 0 {
297 (running_status, status)
299 } else {
300 running_status = status;
301 (status, 0)
302 };
303
304 let event = match actual_status & 0xF0 {
306 0x90 => {
307 let note = if data_start != 0 {
309 data_start
310 } else {
311 let mut byte = [0u8];
312 track_reader
313 .read_exact(&mut byte)
314 .map_err(|e| format!("Read error: {}", e))?;
315 byte[0]
316 };
317 let mut velocity = [0u8];
318 track_reader
319 .read_exact(&mut velocity)
320 .map_err(|e| format!("Read error: {}", e))?;
321
322 let channel = (actual_status & 0x0F) + 1;
323 MidiEvent {
324 timestamp: 0,
325 channel,
326 kind: MidiEventKind::NoteOn {
327 note,
328 velocity: velocity[0],
329 },
330 }
331 }
332 0x80 => {
333 let note = if data_start != 0 {
335 data_start
336 } else {
337 let mut byte = [0u8];
338 track_reader
339 .read_exact(&mut byte)
340 .map_err(|e| format!("Read error: {}", e))?;
341 byte[0]
342 };
343 let mut velocity = [0u8];
344 track_reader
345 .read_exact(&mut velocity)
346 .map_err(|e| format!("Read error: {}", e))?;
347
348 let channel = (actual_status & 0x0F) + 1;
349 MidiEvent {
350 timestamp: 0,
351 channel,
352 kind: MidiEventKind::NoteOff {
353 note,
354 velocity: velocity[0],
355 },
356 }
357 }
358 0xFF => {
359 let mut meta_type = [0u8];
361 track_reader
362 .read_exact(&mut meta_type)
363 .map_err(|e| format!("Read error: {}", e))?;
364 let length = read_variable_length(&mut track_reader)?;
365
366 if length as usize <= track_reader.len() {
368 track_reader = &track_reader[length as usize..];
369 }
370
371 continue;
373 }
374 _ => {
375 continue;
377 }
378 };
379
380 track.add_event(delta_time, event);
381 }
382
383 Ok(track)
384}
385
386fn write_track(writer: &mut Vec<u8>, track: &MidiTrack) -> Result<(), String> {
387 writer
388 .write_all(b"MTrk")
389 .map_err(|e| format!("Write error: {}", e))?;
390
391 let length_pos = writer.len();
393 write_u32(writer, 0)?;
394
395 let track_start = writer.len();
396
397 for timed_event in &track.events {
399 write_variable_length(writer, timed_event.delta_time)?;
400
401 match &timed_event.event.kind {
402 MidiEventKind::NoteOn { note, velocity } => {
403 let status = 0x90 | ((timed_event.event.channel - 1) & 0x0F);
404 writer
405 .write_all(&[status, *note, *velocity])
406 .map_err(|e| format!("Write error: {}", e))?;
407 }
408 MidiEventKind::NoteOff { note, velocity } => {
409 let status = 0x80 | ((timed_event.event.channel - 1) & 0x0F);
410 writer
411 .write_all(&[status, *note, *velocity])
412 .map_err(|e| format!("Write error: {}", e))?;
413 }
414 _ => {
415 }
417 }
418 }
419
420 writer
422 .write_all(&[0xFF, 0x2F, 0x00])
423 .map_err(|e| format!("Write error: {}", e))?;
424
425 let track_length = (writer.len() - track_start) as u32;
427 let length_bytes = track_length.to_be_bytes();
428 writer[length_pos..length_pos + 4].copy_from_slice(&length_bytes);
429
430 Ok(())
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436
437 #[test]
438 fn test_midi_file_creation() {
439 let mut file = MidiFile::new(MidiFormat::SingleTrack, Division::TicksPerQuarterNote(480));
440
441 let mut track = MidiTrack::with_name("Test Track");
442 track.add_event(
443 0,
444 MidiEvent {
445 timestamp: 0,
446 channel: 1,
447 kind: MidiEventKind::NoteOn {
448 note: 60,
449 velocity: 100,
450 },
451 },
452 );
453 track.add_event(
454 480,
455 MidiEvent {
456 timestamp: 0,
457 channel: 1,
458 kind: MidiEventKind::NoteOff {
459 note: 60,
460 velocity: 0,
461 },
462 },
463 );
464
465 file.add_track(track);
466
467 assert_eq!(file.tracks.len(), 1);
468 assert_eq!(file.tracks[0].events.len(), 2);
469 }
470
471 #[test]
472 #[ignore] fn test_midi_file_write_read() {
474 let mut file = MidiFile::new(MidiFormat::SingleTrack, Division::TicksPerQuarterNote(480));
475
476 let mut track = MidiTrack::new();
477 track.add_event(
478 0,
479 MidiEvent {
480 timestamp: 0,
481 channel: 1,
482 kind: MidiEventKind::NoteOn {
483 note: 60,
484 velocity: 100,
485 },
486 },
487 );
488 track.add_event(
489 480,
490 MidiEvent {
491 timestamp: 0,
492 channel: 1,
493 kind: MidiEventKind::NoteOff {
494 note: 60,
495 velocity: 0,
496 },
497 },
498 );
499
500 file.add_track(track);
501
502 let bytes = file.to_bytes().unwrap();
504
505 let loaded = MidiFile::from_bytes(&bytes).unwrap();
507
508 assert_eq!(loaded.tracks.len(), 1);
509 assert!(loaded.tracks[0].events.len() >= 2); }
511
512 #[test]
513 fn test_variable_length_encoding() {
514 let mut data = Vec::new();
515
516 write_variable_length(&mut data, 0).unwrap();
517 assert_eq!(data, vec![0x00]);
518
519 data.clear();
520 write_variable_length(&mut data, 127).unwrap();
521 assert_eq!(data, vec![0x7F]);
522
523 data.clear();
524 write_variable_length(&mut data, 128).unwrap();
525 assert_eq!(data, vec![0x81, 0x00]);
526
527 data.clear();
528 write_variable_length(&mut data, 16383).unwrap();
529 assert_eq!(data, vec![0xFF, 0x7F]);
530 }
531}