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, ChannelVoiceMsg, HighResTimeCode, MidiMsg, ParseError, ReceiverContext,
17 SystemExclusiveMsg, 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 if let MidiMsg::ChannelVoice { channel, msg } = &self.event
615 && self.extend_midi_split_channel_voice(*channel, msg, v)
616 {
617 return;
618 }
619
620 push_vlq(self.delta_time, v);
621 let event = self.event.to_midi();
622
623 let is_meta = matches!(self.event, MidiMsg::Meta { .. });
624 let is_system = matches!(
626 self.event,
627 MidiMsg::SystemExclusive { .. }
628 | MidiMsg::SystemCommon { .. }
629 | MidiMsg::SystemRealTime { .. }
630 );
631 if is_meta {
632 v.push(0xFF);
633 } else if is_system {
634 v.push(0xF7);
636 push_vlq(event.len() as u32, v);
637 }
638 v.extend_from_slice(&event);
639 }
640
641 fn extend_midi_split_channel_voice(
646 &self,
647 channel: Channel,
648 msg: &ChannelVoiceMsg,
649 v: &mut Vec<u8>,
650 ) -> bool {
651 let ch = channel as u8;
652 match msg {
653 ChannelVoiceMsg::ControlChange { control } => match control.sub_ccs() {
654 Some(sub_ccs) => {
655 let status = 0xB0 | ch;
656 for (i, (cc, val)) in sub_ccs.iter().enumerate() {
657 let dt = if i == 0 { self.delta_time } else { 0 };
658 push_vlq(dt, v);
659 v.push(status);
660 v.push(*cc);
661 v.push(*val);
662 }
663 true
664 }
665 None => false,
666 },
667 ChannelVoiceMsg::HighResNoteOn { note, velocity }
668 | ChannelVoiceMsg::HighResNoteOff { note, velocity } => {
669 let [msb, lsb] = to_u14(*velocity);
672 let note_status = if matches!(msg, ChannelVoiceMsg::HighResNoteOn { .. }) {
673 0x90 | ch
674 } else {
675 0x80 | ch
676 };
677 push_vlq(self.delta_time, v);
678 v.push(note_status);
679 v.push(*note & 0x7F);
680 v.push(msb);
681 push_vlq(0, v);
682 v.push(0xB0 | ch);
683 v.push(88); v.push(lsb);
685 true
686 }
687 _ => false,
688 }
689 }
690}
691
692#[derive(Debug, Clone, PartialEq)]
694pub enum Meta {
695 SequenceNumber(u16),
697 Text(String),
699 Copyright(String),
701 TrackName(String),
703 InstrumentName(String),
705 Lyric(String),
707 Marker(String),
709 CuePoint(String),
711 ChannelPrefix(Channel),
713 EndOfTrack,
715 SetTempo(u32),
717 SmpteOffset(HighResTimeCode),
719 TimeSignature(FileTimeSignature),
721 KeySignature(KeySignature),
723 SequencerSpecific(Vec<u8>),
725 Unknown { meta_type: u8, data: Vec<u8> },
729}
730
731impl Meta {
732 pub(crate) fn from_midi(v: &[u8]) -> Result<(Self, usize), ParseError> {
734 if v.len() < 2 {
735 return Err(ParseError::UnexpectedEnd);
736 }
737 let meta_type = v[0];
738 let (len, len_offset) = read_vlq(&v[1..])?;
739 if v.len() < len as usize + len_offset + 1 {
740 return Err(ParseError::UnexpectedEnd);
741 }
742 let end = len as usize + len_offset + 1;
743 let data = &v[len_offset + 1..end];
744 match meta_type {
745 0x00 => {
746 if data.len() != 2 {
747 return Err(ParseError::Invalid(
748 "Sequence number meta event must have exactly 2 bytes",
749 ));
750 }
751 Ok((
752 Self::SequenceNumber(u16::from_be_bytes([data[0], data[1]])),
753 end,
754 ))
755 }
756 0x01 => Ok((Self::Text(String::from_utf8_lossy(data).to_string()), end)),
757 0x02 => Ok((
758 Self::Copyright(String::from_utf8_lossy(data).to_string()),
759 end,
760 )),
761 0x03 => Ok((
762 Self::TrackName(String::from_utf8_lossy(data).to_string()),
763 end,
764 )),
765 0x04 => Ok((
766 Self::InstrumentName(String::from_utf8_lossy(data).to_string()),
767 end,
768 )),
769 0x05 => Ok((Self::Lyric(String::from_utf8_lossy(data).to_string()), end)),
770 0x06 => Ok((Self::Marker(String::from_utf8_lossy(data).to_string()), end)),
771 0x07 => Ok((
772 Self::CuePoint(String::from_utf8_lossy(data).to_string()),
773 end,
774 )),
775 0x20 => {
776 if data.len() != 1 {
777 return Err(ParseError::Invalid(
778 "ChannelPrefix meta event must have exactly 1 byte",
779 ));
780 }
781 Ok((Self::ChannelPrefix(Channel::from_u8(data[0])), end))
782 }
783 0x2F => Ok((Self::EndOfTrack, end)),
784 0x51 => {
785 if data.len() != 3 {
786 return Err(ParseError::Invalid(
787 "SetTempo meta event must have exactly 3 bytes",
788 ));
789 }
790 Ok((
791 Self::SetTempo(u32::from_be_bytes([0, data[0], data[1], data[2]])),
792 end,
793 ))
794 }
795 0x54 => {
796 if data.len() != 5 {
797 return Err(ParseError::Invalid(
798 "SmpteOffset meta event must have exactly 5 bytes",
799 ));
800 }
801 let (time, _) = HighResTimeCode::from_midi(data)?;
802 Ok((Self::SmpteOffset(time), end))
803 }
804 0x58 => {
805 if data.len() != 4 {
806 return Err(ParseError::Invalid(
807 "TimeSignature meta event must have exactly 4 bytes",
808 ));
809 }
810 Ok((
811 Self::TimeSignature(FileTimeSignature::from_midi(data)?),
812 end,
813 ))
814 }
815 0x59 => {
816 if data.len() != 2 {
817 return Err(ParseError::Invalid(
818 "KeySignature meta event must have exactly 2 bytes",
819 ));
820 }
821 Ok((Self::KeySignature(KeySignature::from_midi(data)?), end))
822 }
823 0x7F => Ok((Self::SequencerSpecific(data.to_vec()), end)),
824 _ => Ok((
825 Self::Unknown {
826 meta_type,
827 data: data.to_vec(),
828 },
829 end,
830 )),
831 }
832 }
833
834 pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
835 match self {
836 Meta::SequenceNumber(n) => {
837 v.push(0x00);
838 push_vlq(2, v);
839 v.extend_from_slice(&n.to_be_bytes());
840 }
841 Meta::Text(s) => {
842 v.push(0x01);
843 let bytes = s.as_bytes();
844 push_vlq(bytes.len() as u32, v);
845 v.extend_from_slice(bytes);
846 }
847 Meta::Copyright(s) => {
848 v.push(0x02);
849 let bytes = s.as_bytes();
850 push_vlq(bytes.len() as u32, v);
851 v.extend_from_slice(bytes);
852 }
853 Meta::TrackName(s) => {
854 v.push(0x03);
855 let bytes = s.as_bytes();
856 push_vlq(bytes.len() as u32, v);
857 v.extend_from_slice(bytes);
858 }
859 Meta::InstrumentName(s) => {
860 v.push(0x04);
861 let bytes = s.as_bytes();
862 push_vlq(bytes.len() as u32, v);
863 v.extend_from_slice(bytes);
864 }
865 Meta::Lyric(s) => {
866 v.push(0x05);
867 let bytes = s.as_bytes();
868 push_vlq(bytes.len() as u32, v);
869 v.extend_from_slice(bytes);
870 }
871 Meta::Marker(s) => {
872 v.push(0x06);
873 let bytes = s.as_bytes();
874 push_vlq(bytes.len() as u32, v);
875 v.extend_from_slice(bytes);
876 }
877 Meta::CuePoint(s) => {
878 v.push(0x07);
879 let bytes = s.as_bytes();
880 push_vlq(bytes.len() as u32, v);
881 v.extend_from_slice(bytes);
882 }
883 Meta::ChannelPrefix(n) => {
884 v.push(0x20);
885 push_vlq(1, v);
886 v.push(*n as u8);
887 }
888 Meta::EndOfTrack => {
889 v.push(0x2F);
890 push_vlq(0, v);
891 }
892 Meta::SetTempo(n) => {
893 v.push(0x51);
894 push_vlq(3, v);
895 v.extend_from_slice(&n.to_be_bytes()[1..]);
896 }
897 Meta::SmpteOffset(t) => {
898 v.push(0x54);
899 push_vlq(5, v);
900 t.extend_midi(v);
901 }
902 Meta::TimeSignature(s) => {
903 v.push(0x58);
904 push_vlq(4, v);
905 s.extend_midi(v);
906 }
907 Meta::KeySignature(k) => {
908 v.push(0x59);
909 push_vlq(2, v);
910 k.extend_midi(v);
911 }
912 Meta::SequencerSpecific(d) => {
913 v.push(0x7F);
914 push_vlq(d.len() as u32, v);
915 v.extend_from_slice(d);
916 }
917 Meta::Unknown { meta_type, data } => {
918 v.push(*meta_type);
919 push_vlq(data.len() as u32, v);
920 v.extend_from_slice(data);
921 }
922 }
923 }
924}
925
926#[derive(Debug, Clone, PartialEq)]
928pub struct FileTimeSignature {
929 pub numerator: u8,
931 pub denominator: u16,
935 pub clocks_per_metronome_tick: u8,
937 pub thirty_second_notes_per_24_clocks: u8,
939}
940
941impl FileTimeSignature {
942 pub(crate) fn from_midi(m: &[u8]) -> Result<Self, ParseError> {
943 if m.len() < 4 {
944 return Err(ParseError::UnexpectedEnd);
945 }
946 Ok(Self {
947 numerator: m[0],
948 denominator: u16::pow(2, m[1] as u32),
949 clocks_per_metronome_tick: m[2],
950 thirty_second_notes_per_24_clocks: m[3],
951 })
952 }
953
954 pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
955 v.push(self.numerator);
956 v.push((self.denominator as f32).log2() as u8);
957 v.push(self.clocks_per_metronome_tick);
958 v.push(self.thirty_second_notes_per_24_clocks);
959 }
960}
961
962#[derive(Debug, Clone, PartialEq)]
964pub struct KeySignature {
965 pub key: i8,
967 pub scale: u8,
969}
970
971impl KeySignature {
972 pub(crate) fn from_midi(m: &[u8]) -> Result<Self, ParseError> {
973 if m.len() < 2 {
974 return Err(ParseError::UnexpectedEnd);
975 }
976 Ok(Self {
977 key: m[0] as i8,
978 scale: m[1],
979 })
980 }
981
982 pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
983 v.push(self.key as u8);
984 v.push(self.scale);
985 }
986}
987
988#[cfg(test)]
989mod tests {
990 use super::*;
991
992 #[test]
993 fn test_file_time_signature() {
994 let midi_data = vec![4, 2, 24, 8];
995 let time_sig = FileTimeSignature::from_midi(&midi_data).unwrap();
996
997 assert_eq!(time_sig.numerator, 4);
998 assert_eq!(time_sig.denominator, 4);
999 assert_eq!(time_sig.clocks_per_metronome_tick, 24);
1000 assert_eq!(time_sig.thirty_second_notes_per_24_clocks, 8);
1001
1002 let mut output = Vec::new();
1003 time_sig.extend_midi(&mut output);
1004 assert_eq!(output, midi_data);
1005 }
1006
1007 #[test]
1008 fn test_file_time_signature_error() {
1009 let midi_data = vec![4, 2, 24];
1010 assert!(matches!(
1011 FileTimeSignature::from_midi(&midi_data),
1012 Err(ParseError::UnexpectedEnd)
1013 ));
1014 }
1015
1016 #[test]
1017 fn test_key_signature() {
1018 let midi_data = vec![2, 0];
1019 let key_sig = KeySignature::from_midi(&midi_data).unwrap();
1020
1021 assert_eq!(key_sig.key, 2);
1022 assert_eq!(key_sig.scale, 0);
1023
1024 let mut output = Vec::new();
1025 key_sig.extend_midi(&mut output);
1026 assert_eq!(output, midi_data);
1027 }
1028
1029 #[test]
1030 fn test_key_signature_error() {
1031 let midi_data = vec![2];
1032 assert!(matches!(
1033 KeySignature::from_midi(&midi_data),
1034 Err(ParseError::UnexpectedEnd)
1035 ));
1036 }
1037
1038 #[test]
1039 fn test_file_serde() {
1040 use crate::Channel;
1041 use crate::ChannelVoiceMsg;
1042 use crate::message::MidiMsg;
1043
1044 let mut file = MidiFile::default();
1046 file.header.division = Division::TicksPerQuarterNote(480);
1048
1049 file.add_track(Track::default());
1050
1051 file.extend_track(
1053 0,
1054 MidiMsg::Meta {
1055 msg: Meta::TrackName("Test Track".to_string()),
1056 },
1057 0.0,
1058 );
1059
1060 file.extend_track(
1061 0,
1062 MidiMsg::Meta {
1063 msg: Meta::TimeSignature(FileTimeSignature {
1064 numerator: 4,
1065 denominator: 4,
1066 clocks_per_metronome_tick: 24,
1067 thirty_second_notes_per_24_clocks: 8,
1068 }),
1069 },
1070 0.0,
1071 );
1072
1073 file.extend_track(
1074 0,
1075 MidiMsg::ChannelVoice {
1076 channel: Channel::Ch1,
1077 msg: ChannelVoiceMsg::NoteOn {
1078 note: 60,
1079 velocity: 64,
1080 },
1081 },
1082 0.0,
1083 );
1084
1085 file.extend_track(
1086 0,
1087 MidiMsg::ChannelVoice {
1088 channel: Channel::Ch1,
1089 msg: ChannelVoiceMsg::NoteOff {
1090 note: 60,
1091 velocity: 64,
1092 },
1093 },
1094 1.0,
1095 );
1096 file.extend_track(
1097 0,
1098 MidiMsg::Meta {
1099 msg: Meta::EndOfTrack,
1100 },
1101 2.0,
1102 );
1103
1104 let bytes = file.to_midi();
1106
1107 assert!(bytes.starts_with(b"MThd"));
1109 assert!(bytes[14..].starts_with(b"MTrk"));
1110
1111 let deserialized_file = MidiFile::from_midi(&bytes).unwrap();
1112 assert_eq!(deserialized_file.tracks.len(), 1);
1113 assert_eq!(deserialized_file.tracks[0].events().len(), 5);
1114 assert_eq!(
1115 deserialized_file.header.division,
1116 Division::TicksPerQuarterNote(480)
1117 );
1118 assert_eq!(deserialized_file, file);
1119 }
1120
1121 #[test]
1122 fn test_file_system_reset() {
1123 let mut file = MidiFile::default();
1124 file.add_track(Track::default());
1125 file.extend_track(
1126 0,
1127 MidiMsg::SystemRealTime {
1128 msg: crate::SystemRealTimeMsg::SystemReset,
1129 },
1130 0.0,
1131 );
1132 let bytes = file.to_midi();
1133
1134 let deserialized_file = MidiFile::from_midi(&bytes).unwrap();
1135 assert_eq!(deserialized_file.tracks.len(), 1);
1136 assert_eq!(deserialized_file.tracks[0].events().len(), 0);
1138 }
1139}