1use alloc::fmt;
2use alloc::format;
3use alloc::string::{String, ToString};
4use alloc::vec;
5use alloc::vec::Vec;
6use core::ops;
7use core::str;
8
9#[cfg(not(feature = "std"))]
10#[allow(unused_imports)] use micromath::F32Ext;
12
13use core::error;
14
15use super::{
16 Channel, HighResTimeCode, MidiMsg, ParseError, ReceiverContext, SystemExclusiveMsg,
17 TimeCodeType, util::*,
18};
19
20#[derive(Debug, PartialEq)]
24pub struct MidiFileParseError {
25 pub error: ParseError,
26 pub file: MidiFile,
27 pub offset: usize,
28 pub parsing: String,
29 pub remaining_bytes: usize,
30 pub next_bytes: Vec<u8>,
31}
32
33impl error::Error for MidiFileParseError {}
34
35impl fmt::Display for MidiFileParseError {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 write!(
38 f,
39 "Error parsing MIDI file at position {}: {}",
40 &self.offset, &self.error
41 )?;
42 write!(
43 f,
44 "\nEncountered this error while parsing: {}",
45 &self.parsing
46 )?;
47 write!(
48 f,
49 "\nThe incomplete MidiFile that managed to be parsed: {:?}",
50 &self.file
51 )?;
52 write!(
53 f,
54 "\n\n{} bytes remain in the file. These are the next ones: {:x?}",
55 &self.remaining_bytes, &self.next_bytes
56 )?;
57
58 Ok(())
59 }
60}
61
62#[derive(Debug, Clone, PartialEq, Default)]
64pub struct MidiFile {
65 pub header: Header,
67 pub tracks: Vec<Track>,
69}
70
71#[derive(Debug)]
72struct ParseCtx<'a, 'b> {
73 input: &'a [u8],
74 offset: usize,
75 parsing: String,
76 file: &'b mut MidiFile,
77 track_end: usize,
78}
79
80impl<'a, 'b> ParseCtx<'a, 'b> {
81 fn new(input: &'a [u8], file: &'b mut MidiFile) -> Self {
82 Self {
83 input,
84 offset: 0,
85 parsing: "header".into(),
86 file,
87 track_end: 0,
88 }
89 }
90
91 fn advance(&mut self, offset: usize) {
92 self.offset += offset;
93 }
94
95 fn data(&self) -> &[u8] {
96 &self.input[self.offset..]
97 }
98
99 fn slice(&self, range: ops::Range<usize>) -> &[u8] {
100 &self.input[range.start + self.offset..range.end + self.offset]
101 }
102
103 fn remaining(&self) -> usize {
104 self.input.len() - self.offset
105 }
106
107 fn parsing<S: Into<String>>(&mut self, s: S) {
108 self.parsing = s.into();
109 }
110
111 fn add_track(&mut self, track: Track) {
112 self.file.tracks.push(track);
113 }
114
115 fn track_length(&mut self, len: usize) {
116 self.track_end = self.offset + len;
117 }
118
119 fn extend_track(&mut self, event: TrackEvent) {
120 self.file.tracks.last_mut().unwrap().extend(event);
121 }
122}
123
124impl MidiFile {
125 pub fn from_midi(v: &[u8]) -> Result<Self, MidiFileParseError> {
127 let mut file = MidiFile {
128 header: Header::default(),
129 tracks: vec![],
130 };
131 let mut ctx = ParseCtx::new(v, &mut file);
132 match Header::parse_midi_file(&mut ctx) {
133 Ok(_) => (),
134 Err(error) => {
135 let offset = ctx.offset;
136 let parsing = ctx.parsing.clone();
137 let remaining_bytes = ctx.remaining();
138 let next_bytes = ctx.slice(0..(20.min(ctx.remaining()))).to_vec();
139 return Err(MidiFileParseError {
140 error,
141 file,
142 offset,
143 parsing,
144 remaining_bytes,
145 next_bytes,
146 });
147 }
148 }
149
150 for i in 0..ctx.file.header.num_tracks {
151 match Track::parse_midi_file(&mut ctx, i) {
152 Ok(_) => (),
153 Err(error) => {
154 let offset = ctx.offset;
155 let parsing = ctx.parsing.clone();
156 let remaining_bytes = ctx.remaining();
157 let next_bytes = ctx.slice(0..(20.min(ctx.remaining()))).to_vec();
158 return Err(MidiFileParseError {
159 error,
160 file,
161 offset,
162 parsing,
163 remaining_bytes,
164 next_bytes,
165 });
166 }
167 }
168 }
169 Ok(file)
170 }
171
172 pub fn to_midi(&self) -> Vec<u8> {
174 let mut r: Vec<u8> = vec![];
175 self.header.extend_midi(&mut r);
176 for track in &self.tracks {
177 track.extend_midi(&mut r);
178 }
179 r
180 }
181
182 pub fn add_track(&mut self, track: Track) {
184 self.tracks.push(track);
185 self.header.num_tracks += 1;
186 }
187
188 pub fn remove_track(&mut self, track_num: usize) {
190 self.tracks.remove(track_num);
191 self.header.num_tracks -= 1;
192 }
193
194 pub fn extend_track(&mut self, track_num: usize, event: MidiMsg, beat_or_frame: f32) {
196 match &mut self.tracks[track_num] {
197 Track::Midi(events) => {
198 let last_beat_or_frame = events.last().map(|e| e.beat_or_frame).unwrap_or(0.0);
199 let last_event_tick = self
200 .header
201 .division
202 .beat_or_frame_to_tick(last_beat_or_frame);
203 let this_event_tick = self.header.division.beat_or_frame_to_tick(beat_or_frame);
204 events.push(TrackEvent {
205 delta_time: this_event_tick - last_event_tick,
206 event,
207 beat_or_frame,
208 })
209 }
210
211 Track::AlienChunk(_) => panic!("Cannot extend an alien chunk"),
212 }
213 }
214}
215
216#[derive(Debug, Clone, PartialEq, Default)]
218pub struct Header {
219 pub format: SMFFormat,
221 pub num_tracks: u16,
223 pub division: Division,
225}
226
227impl Header {
228 fn parse_midi_file(ctx: &mut ParseCtx) -> Result<(), ParseError> {
230 if ctx.remaining() < 14 {
231 return Err(ParseError::UnexpectedEnd);
232 }
233 if str::from_utf8(ctx.slice(0..4)) != Ok("MThd") {
234 return Err(ParseError::Invalid("Invalid header"));
235 }
236 ctx.advance(4);
237 if u32_from_midi(ctx.slice(0..4)) != Ok(6) {
238 return Err(ParseError::Invalid("Invalid header length"));
239 }
240 ctx.advance(4);
241 let v = ctx.data();
242
243 let (format, _) = SMFFormat::from_midi(v)?;
244 let num_tracks = u16::from_be_bytes([v[2], v[3]]);
245 let division = if v[4] & 0b1000_0000 == 0 {
246 Division::TicksPerQuarterNote(u16::from_be_bytes([v[4], v[5]]))
247 } else {
248 Division::TimeCode {
249 frames_per_second: match v[4] & 0b0111_1111 {
250 0 => TimeCodeType::FPS24,
251 1 => TimeCodeType::FPS25,
252 2 => TimeCodeType::DF30,
253 3 => TimeCodeType::NDF30,
254 _ => return Err(ParseError::Invalid("Invalid time code type")),
255 },
256 ticks_per_frame: v[5],
257 }
258 };
259 ctx.advance(6);
260 ctx.file.header = Self {
261 format,
262 num_tracks,
263 division,
264 };
265 Ok(())
266 }
267
268 fn extend_midi(&self, v: &mut Vec<u8>) {
269 v.extend_from_slice(b"MThd");
270 push_u32(6, v); self.format.extend_midi(v);
273 push_u16(self.num_tracks, v);
274 match self.division {
275 Division::TicksPerQuarterNote(tpqn) => {
276 push_u16(tpqn, v);
277 }
278 Division::TimeCode {
279 frames_per_second,
280 ticks_per_frame,
281 } => {
282 v.push(0b1000_0000 | frames_per_second as u8);
283 v.push(ticks_per_frame);
284 }
285 }
286 }
287}
288
289#[derive(Debug, Clone, PartialEq, Default)]
291pub enum SMFFormat {
292 SingleTrack,
294 #[default]
296 MultiTrack,
297 MultiSong,
299}
300
301impl SMFFormat {
302 fn from_midi(v: &[u8]) -> Result<(Self, usize), ParseError> {
303 if v.len() < 2 {
304 return Err(ParseError::UnexpectedEnd);
305 }
306 Ok((
307 match v[1] {
309 0 => SMFFormat::SingleTrack,
310 1 => SMFFormat::MultiTrack,
311 2 => SMFFormat::MultiSong,
312 _ => return Err(ParseError::Invalid("Invalid SMF format")),
313 },
314 2,
315 ))
316 }
317
318 fn extend_midi(&self, v: &mut Vec<u8>) {
319 match self {
320 SMFFormat::SingleTrack => v.extend_from_slice(&[0, 0]),
321 SMFFormat::MultiTrack => v.extend_from_slice(&[0, 1]),
322 SMFFormat::MultiSong => v.extend_from_slice(&[0, 2]),
323 }
324 }
325}
326
327#[derive(Debug, Clone, Copy, PartialEq)]
329pub enum Division {
330 TicksPerQuarterNote(u16),
332 TimeCode {
334 frames_per_second: TimeCodeType,
335 ticks_per_frame: u8,
336 },
337}
338
339impl Default for Division {
340 fn default() -> Self {
341 Division::TicksPerQuarterNote(96)
342 }
343}
344
345impl Division {
346 pub fn beat_or_frame_to_tick(&self, beat_or_frame: f32) -> u32 {
348 match self {
349 Division::TicksPerQuarterNote(tpqn) => (beat_or_frame * *tpqn as f32) as u32,
350 Division::TimeCode {
351 ticks_per_frame, ..
352 } => (beat_or_frame * *ticks_per_frame as f32) as u32,
353 }
354 }
355
356 pub fn ticks_to_beats_or_frames(&self, ticks: u32) -> f32 {
358 match self {
359 Division::TicksPerQuarterNote(tpqn) => ticks as f32 / *tpqn as f32,
360 Division::TimeCode {
361 ticks_per_frame, ..
362 } => ticks as f32 / *ticks_per_frame as f32,
363 }
364 }
365}
366
367#[derive(Debug, Clone, PartialEq)]
369pub enum Track {
370 Midi(Vec<TrackEvent>),
372 AlienChunk(Vec<u8>),
376}
377
378impl Default for Track {
379 fn default() -> Self {
380 Track::Midi(vec![])
381 }
382}
383
384impl Track {
385 pub fn len(&self) -> usize {
387 match self {
388 Track::Midi(events) => events.len(),
389 Track::AlienChunk(data) => data.len(),
390 }
391 }
392
393 #[must_use]
394 pub fn is_empty(&self) -> bool {
395 self.len() == 0
396 }
397
398 pub fn events(&self) -> &[TrackEvent] {
400 match self {
401 Track::Midi(events) => events,
402 Track::AlienChunk(_) => &[],
403 }
404 }
405
406 fn extend(&mut self, event: TrackEvent) {
407 match self {
408 Track::Midi(events) => events.push(event),
409 Track::AlienChunk(_) => panic!("Cannot extend an alien chunk"),
410 }
411 }
412
413 fn parse_midi_file(ctx: &mut ParseCtx, track_num: u16) -> Result<(), ParseError> {
414 if ctx.remaining() < 8 {
415 return Err(ParseError::UnexpectedEnd);
416 }
417 ctx.parsing(format!("track {}", track_num));
418 let len = u32_from_midi(ctx.slice(4..8))? as usize;
419 if ctx.remaining() < len + 8 {
420 return Err(ParseError::UnexpectedEnd);
421 }
422 if str::from_utf8(ctx.slice(0..4)) != Ok("MTrk") {
423 ctx.add_track(Self::AlienChunk(ctx.slice(0..len + 8).to_vec()));
424 ctx.advance(len + 8);
425 return Ok(());
426 }
427 ctx.add_track(Self::Midi(vec![]));
428 ctx.advance(8);
429 ctx.track_length(len);
430 let reciever_ctx = &mut ReceiverContext::default().parsing_smf();
431
432 let mut i = 0;
433 let mut last_beat_or_frame = 0.0;
434 while ctx.offset < ctx.track_end {
435 ctx.parsing(format!("track {} event {}", track_num, i));
436 let (event, event_len) = TrackEvent::from_midi(
437 ctx.data(),
438 reciever_ctx,
439 &ctx.file.header.division,
440 last_beat_or_frame,
441 )?;
442 last_beat_or_frame = event.beat_or_frame;
443 ctx.extend_track(event);
444 ctx.advance(event_len);
445 i += 1;
446 }
447 if ctx.offset > ctx.track_end {
448 return Err(ParseError::Invalid(
449 "Track length exceeded the provided length",
450 ));
451 }
452 Ok(())
453 }
454
455 fn extend_midi(&self, v: &mut Vec<u8>) {
456 match self {
457 Track::Midi(events) => {
458 v.extend_from_slice(b"MTrk");
459 let s = v.len();
460 push_u32(0, v); for event in events {
463 event.extend_midi(v);
464 }
465 let e = v.len();
466 v[s..s + 4].copy_from_slice(&(e as u32 - s as u32 - 4).to_be_bytes());
468 }
469 Track::AlienChunk(data) => {
470 v.extend_from_slice(data);
471 }
472 }
473 }
474}
475
476#[derive(Debug, Clone, PartialEq)]
478pub struct TrackEvent {
479 pub delta_time: u32,
481 pub event: MidiMsg,
483 pub beat_or_frame: f32,
489}
490
491impl TrackEvent {
492 fn from_midi(
493 v: &[u8],
494 ctx: &mut ReceiverContext,
495 division: &Division,
496 last_beat_or_frame: f32,
497 ) -> Result<(Self, usize), ParseError> {
498 let (delta_time, time_offset) = read_vlq(v)?;
499 let beat_or_frame = last_beat_or_frame + division.ticks_to_beats_or_frames(delta_time);
500 match v[time_offset..].first() {
501 Some(b) => match b >> 4 {
502 0xF => match b & 0b0000_1111 {
503 0x0 => {
504 let (len, len_offset) = read_vlq(&v[time_offset + 1..])?;
505 let p = time_offset + len_offset + 1;
506 ctx.is_smf_sysex = true;
507 let event = match SystemExclusiveMsg::from_midi(&v[p..], ctx) {
508 Ok((event, event_len)) => {
509 if event_len - 1 != len as usize {
511 return Err(ParseError::Invalid(
512 "Invalid system exclusive message",
513 ));
514 }
515 MidiMsg::SystemExclusive { msg: event }
516 }
517 Err(e) => MidiMsg::Invalid {
518 bytes: v[p..p + len as usize].to_vec(),
519 error: e,
520 },
521 };
522 Ok((
523 Self {
524 delta_time,
525 event,
526 beat_or_frame,
527 },
528 p + len as usize,
529 ))
530 }
531 0x7 => {
532 let (len, len_offset) = read_vlq(&v[time_offset + 1..])?;
533 let p = time_offset + len_offset + 1;
534 ctx.is_smf_sysex = false;
535 let event = match MidiMsg::from_midi_with_context(&v[p..], ctx) {
536 Ok((event, event_len)) => {
537 if event_len != len as usize {
539 return Err(ParseError::Invalid(
540 "Invalid system exclusive message",
541 ));
542 }
543 event
544 }
545 Err(e) => MidiMsg::Invalid {
546 bytes: v[p..p + len as usize].to_vec(),
547 error: e,
548 },
549 };
550
551 Ok((
552 Self {
553 delta_time,
554 event,
555 beat_or_frame,
556 },
557 p + len as usize,
558 ))
559 }
560 0xF => {
561 let p = time_offset + 1;
562 let (event, event_len) = Meta::from_midi(&v[p..])?;
563 Ok((
564 Self {
565 delta_time,
566 event: MidiMsg::Meta { msg: event },
567 beat_or_frame,
568 },
569 p + event_len,
570 ))
571 }
572 _ => Err(ParseError::Invalid("Invalid track event")),
573 },
574 _ => {
575 ctx.is_smf_sysex = false;
576 let (event, event_len) =
577 MidiMsg::from_midi_with_context(&v[time_offset..], ctx)?;
578 Ok((
579 Self {
580 delta_time,
581 event,
582 beat_or_frame,
583 },
584 time_offset + event_len,
585 ))
586 }
587 },
588 None => Err(ParseError::UnexpectedEnd),
589 }
590 }
591
592 fn extend_midi(&self, v: &mut Vec<u8>) {
593 if matches!(
594 self.event,
595 MidiMsg::SystemRealTime {
596 msg: crate::SystemRealTimeMsg::SystemReset,
597 }
598 ) {
599 #[cfg(feature = "std")]
600 log::warn!("SMF contains System Reset event, which is not valid. Skipping.");
601 return;
602 } else if self.event.is_invalid() {
603 return;
604 }
605
606 push_vlq(self.delta_time, v);
607 let event = self.event.to_midi();
609
610 let is_meta = matches!(self.event, MidiMsg::Meta { .. });
611 let is_system = matches!(
613 self.event,
614 MidiMsg::SystemExclusive { .. }
615 | MidiMsg::SystemCommon { .. }
616 | MidiMsg::SystemRealTime { .. }
617 );
618 if is_meta {
619 v.push(0xFF);
620 } else if is_system {
621 v.push(0xF7);
623 push_vlq(event.len() as u32, v);
624 }
625 v.extend_from_slice(&event);
626 }
627}
628
629#[derive(Debug, Clone, PartialEq)]
631pub enum Meta {
632 SequenceNumber(u16),
634 Text(String),
636 Copyright(String),
638 TrackName(String),
640 InstrumentName(String),
642 Lyric(String),
644 Marker(String),
646 CuePoint(String),
648 ChannelPrefix(Channel),
650 EndOfTrack,
652 SetTempo(u32),
654 SmpteOffset(HighResTimeCode),
656 TimeSignature(FileTimeSignature),
658 KeySignature(KeySignature),
660 SequencerSpecific(Vec<u8>),
662 Unknown { meta_type: u8, data: Vec<u8> },
666}
667
668impl Meta {
669 pub(crate) fn from_midi(v: &[u8]) -> Result<(Self, usize), ParseError> {
671 if v.len() < 2 {
672 return Err(ParseError::UnexpectedEnd);
673 }
674 let meta_type = v[0];
675 let (len, len_offset) = read_vlq(&v[1..])?;
676 if v.len() < len as usize + len_offset + 1 {
677 return Err(ParseError::UnexpectedEnd);
678 }
679 let end = len as usize + len_offset + 1;
680 let data = &v[len_offset + 1..end];
681 match meta_type {
682 0x00 => {
683 if data.len() != 2 {
684 return Err(ParseError::Invalid(
685 "Sequence number meta event must have exactly 2 bytes",
686 ));
687 }
688 Ok((
689 Self::SequenceNumber(u16::from_be_bytes([data[0], data[1]])),
690 end,
691 ))
692 }
693 0x01 => Ok((Self::Text(String::from_utf8_lossy(data).to_string()), end)),
694 0x02 => Ok((
695 Self::Copyright(String::from_utf8_lossy(data).to_string()),
696 end,
697 )),
698 0x03 => Ok((
699 Self::TrackName(String::from_utf8_lossy(data).to_string()),
700 end,
701 )),
702 0x04 => Ok((
703 Self::InstrumentName(String::from_utf8_lossy(data).to_string()),
704 end,
705 )),
706 0x05 => Ok((Self::Lyric(String::from_utf8_lossy(data).to_string()), end)),
707 0x06 => Ok((Self::Marker(String::from_utf8_lossy(data).to_string()), end)),
708 0x07 => Ok((
709 Self::CuePoint(String::from_utf8_lossy(data).to_string()),
710 end,
711 )),
712 0x20 => {
713 if data.len() != 1 {
714 return Err(ParseError::Invalid(
715 "ChannelPrefix meta event must have exactly 1 byte",
716 ));
717 }
718 Ok((Self::ChannelPrefix(Channel::from_u8(data[0])), end))
719 }
720 0x2F => Ok((Self::EndOfTrack, end)),
721 0x51 => {
722 if data.len() != 3 {
723 return Err(ParseError::Invalid(
724 "SetTempo meta event must have exactly 3 bytes",
725 ));
726 }
727 Ok((
728 Self::SetTempo(u32::from_be_bytes([0, data[0], data[1], data[2]])),
729 end,
730 ))
731 }
732 0x54 => {
733 if data.len() != 5 {
734 return Err(ParseError::Invalid(
735 "SmpteOffset meta event must have exactly 5 bytes",
736 ));
737 }
738 let (time, _) = HighResTimeCode::from_midi(data)?;
739 Ok((Self::SmpteOffset(time), end))
740 }
741 0x58 => {
742 if data.len() != 4 {
743 return Err(ParseError::Invalid(
744 "TimeSignature meta event must have exactly 4 bytes",
745 ));
746 }
747 Ok((
748 Self::TimeSignature(FileTimeSignature::from_midi(data)?),
749 end,
750 ))
751 }
752 0x59 => {
753 if data.len() != 2 {
754 return Err(ParseError::Invalid(
755 "KeySignature meta event must have exactly 2 bytes",
756 ));
757 }
758 Ok((Self::KeySignature(KeySignature::from_midi(data)?), end))
759 }
760 0x7F => Ok((Self::SequencerSpecific(data.to_vec()), end)),
761 _ => Ok((
762 Self::Unknown {
763 meta_type,
764 data: data.to_vec(),
765 },
766 end,
767 )),
768 }
769 }
770
771 pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
772 match self {
773 Meta::SequenceNumber(n) => {
774 v.push(0x00);
775 push_vlq(2, v);
776 v.extend_from_slice(&n.to_be_bytes());
777 }
778 Meta::Text(s) => {
779 v.push(0x01);
780 let bytes = s.as_bytes();
781 push_vlq(bytes.len() as u32, v);
782 v.extend_from_slice(bytes);
783 }
784 Meta::Copyright(s) => {
785 v.push(0x02);
786 let bytes = s.as_bytes();
787 push_vlq(bytes.len() as u32, v);
788 v.extend_from_slice(bytes);
789 }
790 Meta::TrackName(s) => {
791 v.push(0x03);
792 let bytes = s.as_bytes();
793 push_vlq(bytes.len() as u32, v);
794 v.extend_from_slice(bytes);
795 }
796 Meta::InstrumentName(s) => {
797 v.push(0x04);
798 let bytes = s.as_bytes();
799 push_vlq(bytes.len() as u32, v);
800 v.extend_from_slice(bytes);
801 }
802 Meta::Lyric(s) => {
803 v.push(0x05);
804 let bytes = s.as_bytes();
805 push_vlq(bytes.len() as u32, v);
806 v.extend_from_slice(bytes);
807 }
808 Meta::Marker(s) => {
809 v.push(0x06);
810 let bytes = s.as_bytes();
811 push_vlq(bytes.len() as u32, v);
812 v.extend_from_slice(bytes);
813 }
814 Meta::CuePoint(s) => {
815 v.push(0x07);
816 let bytes = s.as_bytes();
817 push_vlq(bytes.len() as u32, v);
818 v.extend_from_slice(bytes);
819 }
820 Meta::ChannelPrefix(n) => {
821 v.push(0x20);
822 push_vlq(1, v);
823 v.push(*n as u8);
824 }
825 Meta::EndOfTrack => {
826 v.push(0x2F);
827 push_vlq(0, v);
828 }
829 Meta::SetTempo(n) => {
830 v.push(0x51);
831 push_vlq(3, v);
832 v.extend_from_slice(&n.to_be_bytes()[1..]);
833 }
834 Meta::SmpteOffset(t) => {
835 v.push(0x54);
836 push_vlq(5, v);
837 t.extend_midi(v);
838 }
839 Meta::TimeSignature(s) => {
840 v.push(0x58);
841 push_vlq(4, v);
842 s.extend_midi(v);
843 }
844 Meta::KeySignature(k) => {
845 v.push(0x59);
846 push_vlq(2, v);
847 k.extend_midi(v);
848 }
849 Meta::SequencerSpecific(d) => {
850 v.push(0x7F);
851 push_vlq(d.len() as u32, v);
852 v.extend_from_slice(d);
853 }
854 Meta::Unknown { meta_type, data } => {
855 v.push(*meta_type);
856 push_vlq(data.len() as u32, v);
857 v.extend_from_slice(data);
858 }
859 }
860 }
861}
862
863#[derive(Debug, Clone, PartialEq)]
865pub struct FileTimeSignature {
866 pub numerator: u8,
868 pub denominator: u16,
872 pub clocks_per_metronome_tick: u8,
874 pub thirty_second_notes_per_24_clocks: u8,
876}
877
878impl FileTimeSignature {
879 pub(crate) fn from_midi(m: &[u8]) -> Result<Self, ParseError> {
880 if m.len() < 4 {
881 return Err(ParseError::UnexpectedEnd);
882 }
883 Ok(Self {
884 numerator: m[0],
885 denominator: u16::pow(2, m[1] as u32),
886 clocks_per_metronome_tick: m[2],
887 thirty_second_notes_per_24_clocks: m[3],
888 })
889 }
890
891 pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
892 v.push(self.numerator);
893 v.push((self.denominator as f32).log2() as u8);
894 v.push(self.clocks_per_metronome_tick);
895 v.push(self.thirty_second_notes_per_24_clocks);
896 }
897}
898
899#[derive(Debug, Clone, PartialEq)]
901pub struct KeySignature {
902 pub key: i8,
904 pub scale: u8,
906}
907
908impl KeySignature {
909 pub(crate) fn from_midi(m: &[u8]) -> Result<Self, ParseError> {
910 if m.len() < 2 {
911 return Err(ParseError::UnexpectedEnd);
912 }
913 Ok(Self {
914 key: m[0] as i8,
915 scale: m[1],
916 })
917 }
918
919 pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
920 v.push(self.key as u8);
921 v.push(self.scale);
922 }
923}
924
925#[cfg(test)]
926mod tests {
927 use super::*;
928
929 #[test]
930 fn test_file_time_signature() {
931 let midi_data = vec![4, 2, 24, 8];
932 let time_sig = FileTimeSignature::from_midi(&midi_data).unwrap();
933
934 assert_eq!(time_sig.numerator, 4);
935 assert_eq!(time_sig.denominator, 4);
936 assert_eq!(time_sig.clocks_per_metronome_tick, 24);
937 assert_eq!(time_sig.thirty_second_notes_per_24_clocks, 8);
938
939 let mut output = Vec::new();
940 time_sig.extend_midi(&mut output);
941 assert_eq!(output, midi_data);
942 }
943
944 #[test]
945 fn test_file_time_signature_error() {
946 let midi_data = vec![4, 2, 24];
947 assert!(matches!(
948 FileTimeSignature::from_midi(&midi_data),
949 Err(ParseError::UnexpectedEnd)
950 ));
951 }
952
953 #[test]
954 fn test_key_signature() {
955 let midi_data = vec![2, 0];
956 let key_sig = KeySignature::from_midi(&midi_data).unwrap();
957
958 assert_eq!(key_sig.key, 2);
959 assert_eq!(key_sig.scale, 0);
960
961 let mut output = Vec::new();
962 key_sig.extend_midi(&mut output);
963 assert_eq!(output, midi_data);
964 }
965
966 #[test]
967 fn test_key_signature_error() {
968 let midi_data = vec![2];
969 assert!(matches!(
970 KeySignature::from_midi(&midi_data),
971 Err(ParseError::UnexpectedEnd)
972 ));
973 }
974
975 #[test]
976 fn test_file_serde() {
977 use crate::Channel;
978 use crate::ChannelVoiceMsg;
979 use crate::message::MidiMsg;
980
981 let mut file = MidiFile::default();
983 file.header.division = Division::TicksPerQuarterNote(480);
985
986 file.add_track(Track::default());
987
988 file.extend_track(
990 0,
991 MidiMsg::Meta {
992 msg: Meta::TrackName("Test Track".to_string()),
993 },
994 0.0,
995 );
996
997 file.extend_track(
998 0,
999 MidiMsg::Meta {
1000 msg: Meta::TimeSignature(FileTimeSignature {
1001 numerator: 4,
1002 denominator: 4,
1003 clocks_per_metronome_tick: 24,
1004 thirty_second_notes_per_24_clocks: 8,
1005 }),
1006 },
1007 0.0,
1008 );
1009
1010 file.extend_track(
1011 0,
1012 MidiMsg::ChannelVoice {
1013 channel: Channel::Ch1,
1014 msg: ChannelVoiceMsg::NoteOn {
1015 note: 60,
1016 velocity: 64,
1017 },
1018 },
1019 0.0,
1020 );
1021
1022 file.extend_track(
1023 0,
1024 MidiMsg::ChannelVoice {
1025 channel: Channel::Ch1,
1026 msg: ChannelVoiceMsg::NoteOff {
1027 note: 60,
1028 velocity: 64,
1029 },
1030 },
1031 1.0,
1032 );
1033 file.extend_track(
1034 0,
1035 MidiMsg::Meta {
1036 msg: Meta::EndOfTrack,
1037 },
1038 2.0,
1039 );
1040
1041 let bytes = file.to_midi();
1043
1044 assert!(bytes.starts_with(b"MThd"));
1046 assert!(bytes[14..].starts_with(b"MTrk"));
1047
1048 let deserialized_file = MidiFile::from_midi(&bytes).unwrap();
1049 assert_eq!(deserialized_file.tracks.len(), 1);
1050 assert_eq!(deserialized_file.tracks[0].events().len(), 5);
1051 assert_eq!(
1052 deserialized_file.header.division,
1053 Division::TicksPerQuarterNote(480)
1054 );
1055 assert_eq!(deserialized_file, file);
1056 }
1057
1058 #[test]
1059 fn test_file_system_reset() {
1060 let mut file = MidiFile::default();
1061 file.add_track(Track::default());
1062 file.extend_track(
1063 0,
1064 MidiMsg::SystemRealTime {
1065 msg: crate::SystemRealTimeMsg::SystemReset,
1066 },
1067 0.0,
1068 );
1069 let bytes = file.to_midi();
1070
1071 let deserialized_file = MidiFile::from_midi(&bytes).unwrap();
1072 assert_eq!(deserialized_file.tracks.len(), 1);
1073 assert_eq!(deserialized_file.tracks[0].events().len(), 0);
1075 }
1076}