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#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
64#[derive(Debug, Clone, PartialEq, Default)]
65pub struct MidiFile {
66 pub header: Header,
68 pub tracks: Vec<Track>,
70}
71
72#[derive(Debug)]
73struct ParseCtx<'a, 'b> {
74 input: &'a [u8],
75 offset: usize,
76 parsing: String,
77 file: &'b mut MidiFile,
78 track_end: usize,
79}
80
81impl<'a, 'b> ParseCtx<'a, 'b> {
82 fn new(input: &'a [u8], file: &'b mut MidiFile) -> Self {
83 Self {
84 input,
85 offset: 0,
86 parsing: "header".into(),
87 file,
88 track_end: 0,
89 }
90 }
91
92 fn advance(&mut self, offset: usize) {
93 self.offset += offset;
94 }
95
96 fn data(&self) -> &[u8] {
97 &self.input[self.offset..]
98 }
99
100 fn slice(&self, range: ops::Range<usize>) -> &[u8] {
101 &self.input[range.start + self.offset..range.end + self.offset]
102 }
103
104 fn remaining(&self) -> usize {
105 self.input.len() - self.offset
106 }
107
108 fn parsing<S: Into<String>>(&mut self, s: S) {
109 self.parsing = s.into();
110 }
111
112 fn add_track(&mut self, track: Track) {
113 self.file.tracks.push(track);
114 }
115
116 fn track_length(&mut self, len: usize) {
117 self.track_end = self.offset + len;
118 }
119
120 fn extend_track(&mut self, event: TrackEvent) {
121 self.file.tracks.last_mut().unwrap().extend(event);
122 }
123}
124
125impl MidiFile {
126 pub fn from_midi(v: &[u8]) -> Result<Self, MidiFileParseError> {
128 let mut file = MidiFile {
129 header: Header::default(),
130 tracks: vec![],
131 };
132 let mut ctx = ParseCtx::new(v, &mut file);
133 match Header::parse_midi_file(&mut ctx) {
134 Ok(_) => (),
135 Err(error) => {
136 let offset = ctx.offset;
137 let parsing = ctx.parsing.clone();
138 let remaining_bytes = ctx.remaining();
139 let next_bytes = ctx.slice(0..(20.min(ctx.remaining()))).to_vec();
140 return Err(MidiFileParseError {
141 error,
142 file,
143 offset,
144 parsing,
145 remaining_bytes,
146 next_bytes,
147 });
148 }
149 }
150
151 for i in 0..ctx.file.header.num_tracks {
152 match Track::parse_midi_file(&mut ctx, i) {
153 Ok(_) => (),
154 Err(error) => {
155 let offset = ctx.offset;
156 let parsing = ctx.parsing.clone();
157 let remaining_bytes = ctx.remaining();
158 let next_bytes = ctx.slice(0..(20.min(ctx.remaining()))).to_vec();
159 return Err(MidiFileParseError {
160 error,
161 file,
162 offset,
163 parsing,
164 remaining_bytes,
165 next_bytes,
166 });
167 }
168 }
169 }
170 Ok(file)
171 }
172
173 pub fn to_midi(&self) -> Vec<u8> {
175 let mut r: Vec<u8> = vec![];
176 self.header.extend_midi(&mut r);
177 for track in &self.tracks {
178 track.extend_midi(&mut r);
179 }
180 r
181 }
182
183 pub fn add_track(&mut self, track: Track) {
185 self.tracks.push(track);
186 self.header.num_tracks += 1;
187 }
188
189 pub fn remove_track(&mut self, track_num: usize) {
191 self.tracks.remove(track_num);
192 self.header.num_tracks -= 1;
193 }
194
195 pub fn extend_track(&mut self, track_num: usize, event: MidiMsg, beat_or_frame: f32) {
197 match &mut self.tracks[track_num] {
198 Track::Midi(events) => {
199 let last_beat_or_frame = events.last().map(|e| e.beat_or_frame).unwrap_or(0.0);
200 let last_event_tick = self
201 .header
202 .division
203 .beat_or_frame_to_tick(last_beat_or_frame);
204 let this_event_tick = self.header.division.beat_or_frame_to_tick(beat_or_frame);
205 events.push(TrackEvent {
206 delta_time: this_event_tick - last_event_tick,
207 event,
208 beat_or_frame,
209 })
210 }
211
212 Track::AlienChunk(_) => panic!("Cannot extend an alien chunk"),
213 }
214 }
215}
216
217#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
219#[derive(Debug, Clone, PartialEq, Default)]
220pub struct Header {
221 pub format: SMFFormat,
223 pub num_tracks: u16,
225 pub division: Division,
227}
228
229impl Header {
230 fn parse_midi_file(ctx: &mut ParseCtx) -> Result<(), ParseError> {
232 if ctx.remaining() < 14 {
233 return Err(ParseError::UnexpectedEnd);
234 }
235 if str::from_utf8(ctx.slice(0..4)) != Ok("MThd") {
236 return Err(ParseError::Invalid("Invalid header"));
237 }
238 ctx.advance(4);
239 if u32_from_midi(ctx.slice(0..4)) != Ok(6) {
240 return Err(ParseError::Invalid("Invalid header length"));
241 }
242 ctx.advance(4);
243 let v = ctx.data();
244
245 let (format, _) = SMFFormat::from_midi(v)?;
246 let num_tracks = u16::from_be_bytes([v[2], v[3]]);
247 let division = if v[4] & 0b1000_0000 == 0 {
248 Division::TicksPerQuarterNote(u16::from_be_bytes([v[4], v[5]]))
249 } else {
250 Division::TimeCode {
251 frames_per_second: match v[4] & 0b0111_1111 {
252 0 => TimeCodeType::FPS24,
253 1 => TimeCodeType::FPS25,
254 2 => TimeCodeType::DF30,
255 3 => TimeCodeType::NDF30,
256 _ => return Err(ParseError::Invalid("Invalid time code type")),
257 },
258 ticks_per_frame: v[5],
259 }
260 };
261 ctx.advance(6);
262 ctx.file.header = Self {
263 format,
264 num_tracks,
265 division,
266 };
267 Ok(())
268 }
269
270 fn extend_midi(&self, v: &mut Vec<u8>) {
271 v.extend_from_slice(b"MThd");
272 push_u32(6, v); self.format.extend_midi(v);
275 push_u16(self.num_tracks, v);
276 match self.division {
277 Division::TicksPerQuarterNote(tpqn) => {
278 push_u16(tpqn, v);
279 }
280 Division::TimeCode {
281 frames_per_second,
282 ticks_per_frame,
283 } => {
284 v.push(0b1000_0000 | frames_per_second as u8);
285 v.push(ticks_per_frame);
286 }
287 }
288 }
289}
290
291#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
293#[derive(Debug, Clone, PartialEq, Default)]
294pub enum SMFFormat {
295 SingleTrack,
297 #[default]
299 MultiTrack,
300 MultiSong,
302}
303
304impl SMFFormat {
305 fn from_midi(v: &[u8]) -> Result<(Self, usize), ParseError> {
306 if v.len() < 2 {
307 return Err(ParseError::UnexpectedEnd);
308 }
309 Ok((
310 match v[1] {
312 0 => SMFFormat::SingleTrack,
313 1 => SMFFormat::MultiTrack,
314 2 => SMFFormat::MultiSong,
315 _ => return Err(ParseError::Invalid("Invalid SMF format")),
316 },
317 2,
318 ))
319 }
320
321 fn extend_midi(&self, v: &mut Vec<u8>) {
322 match self {
323 SMFFormat::SingleTrack => v.extend_from_slice(&[0, 0]),
324 SMFFormat::MultiTrack => v.extend_from_slice(&[0, 1]),
325 SMFFormat::MultiSong => v.extend_from_slice(&[0, 2]),
326 }
327 }
328}
329
330#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
332#[derive(Debug, Clone, Copy, PartialEq)]
333pub enum Division {
334 TicksPerQuarterNote(u16),
336 TimeCode {
338 frames_per_second: TimeCodeType,
339 ticks_per_frame: u8,
340 },
341}
342
343impl Default for Division {
344 fn default() -> Self {
345 Division::TicksPerQuarterNote(96)
346 }
347}
348
349impl Division {
350 pub fn beat_or_frame_to_tick(&self, beat_or_frame: f32) -> u32 {
352 match self {
353 Division::TicksPerQuarterNote(tpqn) => (beat_or_frame * *tpqn as f32) as u32,
354 Division::TimeCode {
355 ticks_per_frame, ..
356 } => (beat_or_frame * *ticks_per_frame as f32) as u32,
357 }
358 }
359
360 pub fn ticks_to_beats_or_frames(&self, ticks: u32) -> f32 {
362 match self {
363 Division::TicksPerQuarterNote(tpqn) => ticks as f32 / *tpqn as f32,
364 Division::TimeCode {
365 ticks_per_frame, ..
366 } => ticks as f32 / *ticks_per_frame as f32,
367 }
368 }
369}
370
371#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
373#[derive(Debug, Clone, PartialEq)]
374pub enum Track {
375 Midi(Vec<TrackEvent>),
377 AlienChunk(Vec<u8>),
381}
382
383impl Default for Track {
384 fn default() -> Self {
385 Track::Midi(vec![])
386 }
387}
388
389impl Track {
390 pub fn len(&self) -> usize {
392 match self {
393 Track::Midi(events) => events.len(),
394 Track::AlienChunk(data) => data.len(),
395 }
396 }
397
398 #[must_use]
399 pub fn is_empty(&self) -> bool {
400 self.len() == 0
401 }
402
403 pub fn events(&self) -> &[TrackEvent] {
405 match self {
406 Track::Midi(events) => events,
407 Track::AlienChunk(_) => &[],
408 }
409 }
410
411 fn extend(&mut self, event: TrackEvent) {
412 match self {
413 Track::Midi(events) => events.push(event),
414 Track::AlienChunk(_) => panic!("Cannot extend an alien chunk"),
415 }
416 }
417
418 fn parse_midi_file(ctx: &mut ParseCtx, track_num: u16) -> Result<(), ParseError> {
419 if ctx.remaining() < 8 {
420 return Err(ParseError::UnexpectedEnd);
421 }
422 ctx.parsing(format!("track {}", track_num));
423 let len = u32_from_midi(ctx.slice(4..8))? as usize;
424 if ctx.remaining() < len + 8 {
425 return Err(ParseError::UnexpectedEnd);
426 }
427 if str::from_utf8(ctx.slice(0..4)) != Ok("MTrk") {
428 ctx.add_track(Self::AlienChunk(ctx.slice(0..len + 8).to_vec()));
429 ctx.advance(len + 8);
430 return Ok(());
431 }
432 ctx.add_track(Self::Midi(vec![]));
433 ctx.advance(8);
434 ctx.track_length(len);
435 let reciever_ctx = &mut ReceiverContext::default().parsing_smf();
436
437 let mut i = 0;
438 let mut last_beat_or_frame = 0.0;
439 while ctx.offset < ctx.track_end {
440 ctx.parsing(format!("track {} event {}", track_num, i));
441 let (event, event_len) = TrackEvent::from_midi(
442 ctx.data(),
443 reciever_ctx,
444 &ctx.file.header.division,
445 last_beat_or_frame,
446 )?;
447 last_beat_or_frame = event.beat_or_frame;
448 ctx.extend_track(event);
449 ctx.advance(event_len);
450 i += 1;
451 }
452 if ctx.offset > ctx.track_end {
453 return Err(ParseError::Invalid(
454 "Track length exceeded the provided length",
455 ));
456 }
457 Ok(())
458 }
459
460 fn extend_midi(&self, v: &mut Vec<u8>) {
461 match self {
462 Track::Midi(events) => {
463 v.extend_from_slice(b"MTrk");
464 let s = v.len();
465 push_u32(0, v); for event in events {
468 event.extend_midi(v);
469 }
470 let e = v.len();
471 v[s..s + 4].copy_from_slice(&(e as u32 - s as u32 - 4).to_be_bytes());
473 }
474 Track::AlienChunk(data) => {
475 v.extend_from_slice(data);
476 }
477 }
478 }
479}
480
481#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
483#[derive(Debug, Clone, PartialEq)]
484pub struct TrackEvent {
485 pub delta_time: u32,
487 pub event: MidiMsg,
489 pub beat_or_frame: f32,
495}
496
497impl TrackEvent {
498 fn from_midi(
499 v: &[u8],
500 ctx: &mut ReceiverContext,
501 division: &Division,
502 last_beat_or_frame: f32,
503 ) -> Result<(Self, usize), ParseError> {
504 let (delta_time, time_offset) = read_vlq(v)?;
505 let beat_or_frame = last_beat_or_frame + division.ticks_to_beats_or_frames(delta_time);
506 match v[time_offset..].first() {
507 Some(b) => match b >> 4 {
508 0xF => match b & 0b0000_1111 {
509 0x0 => {
510 let (len, len_offset) = read_vlq(&v[time_offset + 1..])?;
511 let p = time_offset + len_offset + 1;
512 ctx.is_smf_sysex = true;
513 let event = match SystemExclusiveMsg::from_midi(&v[p..], ctx) {
514 Ok((event, event_len)) => {
515 if event_len - 1 != len as usize {
517 return Err(ParseError::Invalid(
518 "Invalid system exclusive message",
519 ));
520 }
521 MidiMsg::SystemExclusive { msg: event }
522 }
523 Err(e) => MidiMsg::Invalid {
524 bytes: v[p..p + len as usize].to_vec(),
525 error: e,
526 },
527 };
528 Ok((
529 Self {
530 delta_time,
531 event,
532 beat_or_frame,
533 },
534 p + len as usize,
535 ))
536 }
537 0x7 => {
538 let (len, len_offset) = read_vlq(&v[time_offset + 1..])?;
539 let p = time_offset + len_offset + 1;
540 ctx.is_smf_sysex = false;
541 let event = match MidiMsg::from_midi_with_context(&v[p..], ctx) {
542 Ok((event, event_len)) => {
543 if event_len != len as usize {
545 return Err(ParseError::Invalid(
546 "Invalid system exclusive message",
547 ));
548 }
549 event
550 }
551 Err(e) => MidiMsg::Invalid {
552 bytes: v[p..p + len as usize].to_vec(),
553 error: e,
554 },
555 };
556
557 Ok((
558 Self {
559 delta_time,
560 event,
561 beat_or_frame,
562 },
563 p + len as usize,
564 ))
565 }
566 0xF => {
567 let p = time_offset + 1;
568 let (event, event_len) = Meta::from_midi(&v[p..])?;
569 Ok((
570 Self {
571 delta_time,
572 event: MidiMsg::Meta { msg: event },
573 beat_or_frame,
574 },
575 p + event_len,
576 ))
577 }
578 _ => Err(ParseError::Invalid("Invalid track event")),
579 },
580 _ => {
581 ctx.is_smf_sysex = false;
582 let (event, event_len) =
583 MidiMsg::from_midi_with_context(&v[time_offset..], ctx)?;
584 Ok((
585 Self {
586 delta_time,
587 event,
588 beat_or_frame,
589 },
590 time_offset + event_len,
591 ))
592 }
593 },
594 None => Err(ParseError::UnexpectedEnd),
595 }
596 }
597
598 fn extend_midi(&self, v: &mut Vec<u8>) {
599 if matches!(
600 self.event,
601 MidiMsg::SystemRealTime {
602 msg: crate::SystemRealTimeMsg::SystemReset,
603 }
604 ) {
605 #[cfg(feature = "std")]
606 log::warn!("SMF contains System Reset event, which is not valid. Skipping.");
607 return;
608 } else if self.event.is_invalid() {
609 return;
610 }
611
612 if let MidiMsg::ChannelVoice { channel, msg } = &self.event
621 && self.extend_midi_split_channel_voice(*channel, msg, v)
622 {
623 return;
624 }
625
626 push_vlq(self.delta_time, v);
627 let event = self.event.to_midi();
628
629 let is_meta = matches!(self.event, MidiMsg::Meta { .. });
630 let is_system = matches!(
632 self.event,
633 MidiMsg::SystemExclusive { .. }
634 | MidiMsg::SystemCommon { .. }
635 | MidiMsg::SystemRealTime { .. }
636 );
637 if is_meta {
638 v.push(0xFF);
639 } else if is_system {
640 v.push(0xF7);
642 push_vlq(event.len() as u32, v);
643 }
644 v.extend_from_slice(&event);
645 }
646
647 fn extend_midi_split_channel_voice(
652 &self,
653 channel: Channel,
654 msg: &ChannelVoiceMsg,
655 v: &mut Vec<u8>,
656 ) -> bool {
657 let ch = channel as u8;
658 match msg {
659 ChannelVoiceMsg::ControlChange { control } => match control.sub_ccs() {
660 Some(sub_ccs) => {
661 let status = 0xB0 | ch;
662 for (i, (cc, val)) in sub_ccs.iter().enumerate() {
663 let dt = if i == 0 { self.delta_time } else { 0 };
664 push_vlq(dt, v);
665 v.push(status);
666 v.push(*cc);
667 v.push(*val);
668 }
669 true
670 }
671 None => false,
672 },
673 ChannelVoiceMsg::HighResNoteOn { note, velocity }
674 | ChannelVoiceMsg::HighResNoteOff { note, velocity } => {
675 let [msb, lsb] = to_u14(*velocity);
678 let note_status = if matches!(msg, ChannelVoiceMsg::HighResNoteOn { .. }) {
679 0x90 | ch
680 } else {
681 0x80 | ch
682 };
683 push_vlq(self.delta_time, v);
684 v.push(note_status);
685 v.push(*note & 0x7F);
686 v.push(msb);
687 push_vlq(0, v);
688 v.push(0xB0 | ch);
689 v.push(88); v.push(lsb);
691 true
692 }
693 _ => false,
694 }
695 }
696}
697
698#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
700#[derive(Debug, Clone, PartialEq)]
701pub enum Meta {
702 SequenceNumber(u16),
704 Text(String),
706 Copyright(String),
708 TrackName(String),
710 InstrumentName(String),
712 Lyric(String),
714 Marker(String),
716 CuePoint(String),
718 ChannelPrefix(Channel),
720 EndOfTrack,
722 SetTempo(u32),
724 SmpteOffset(HighResTimeCode),
726 TimeSignature(FileTimeSignature),
728 KeySignature(KeySignature),
730 SequencerSpecific(Vec<u8>),
732 Unknown { meta_type: u8, data: Vec<u8> },
736}
737
738impl Meta {
739 pub(crate) fn from_midi(v: &[u8]) -> Result<(Self, usize), ParseError> {
741 if v.len() < 2 {
742 return Err(ParseError::UnexpectedEnd);
743 }
744 let meta_type = v[0];
745 let (len, len_offset) = read_vlq(&v[1..])?;
746 if v.len() < len as usize + len_offset + 1 {
747 return Err(ParseError::UnexpectedEnd);
748 }
749 let end = len as usize + len_offset + 1;
750 let data = &v[len_offset + 1..end];
751 match meta_type {
752 0x00 => {
753 if data.len() != 2 {
754 return Err(ParseError::Invalid(
755 "Sequence number meta event must have exactly 2 bytes",
756 ));
757 }
758 Ok((
759 Self::SequenceNumber(u16::from_be_bytes([data[0], data[1]])),
760 end,
761 ))
762 }
763 0x01 => Ok((Self::Text(String::from_utf8_lossy(data).to_string()), end)),
764 0x02 => Ok((
765 Self::Copyright(String::from_utf8_lossy(data).to_string()),
766 end,
767 )),
768 0x03 => Ok((
769 Self::TrackName(String::from_utf8_lossy(data).to_string()),
770 end,
771 )),
772 0x04 => Ok((
773 Self::InstrumentName(String::from_utf8_lossy(data).to_string()),
774 end,
775 )),
776 0x05 => Ok((Self::Lyric(String::from_utf8_lossy(data).to_string()), end)),
777 0x06 => Ok((Self::Marker(String::from_utf8_lossy(data).to_string()), end)),
778 0x07 => Ok((
779 Self::CuePoint(String::from_utf8_lossy(data).to_string()),
780 end,
781 )),
782 0x20 => {
783 if data.len() != 1 {
784 return Err(ParseError::Invalid(
785 "ChannelPrefix meta event must have exactly 1 byte",
786 ));
787 }
788 Ok((Self::ChannelPrefix(Channel::from_u8(data[0])), end))
789 }
790 0x2F => Ok((Self::EndOfTrack, end)),
791 0x51 => {
792 if data.len() != 3 {
793 return Err(ParseError::Invalid(
794 "SetTempo meta event must have exactly 3 bytes",
795 ));
796 }
797 Ok((
798 Self::SetTempo(u32::from_be_bytes([0, data[0], data[1], data[2]])),
799 end,
800 ))
801 }
802 0x54 => {
803 if data.len() != 5 {
804 return Err(ParseError::Invalid(
805 "SmpteOffset meta event must have exactly 5 bytes",
806 ));
807 }
808 let (time, _) = HighResTimeCode::from_midi(data)?;
809 Ok((Self::SmpteOffset(time), end))
810 }
811 0x58 => {
812 if data.len() != 4 {
813 return Err(ParseError::Invalid(
814 "TimeSignature meta event must have exactly 4 bytes",
815 ));
816 }
817 Ok((
818 Self::TimeSignature(FileTimeSignature::from_midi(data)?),
819 end,
820 ))
821 }
822 0x59 => {
823 if data.len() != 2 {
824 return Err(ParseError::Invalid(
825 "KeySignature meta event must have exactly 2 bytes",
826 ));
827 }
828 Ok((Self::KeySignature(KeySignature::from_midi(data)?), end))
829 }
830 0x7F => Ok((Self::SequencerSpecific(data.to_vec()), end)),
831 _ => Ok((
832 Self::Unknown {
833 meta_type,
834 data: data.to_vec(),
835 },
836 end,
837 )),
838 }
839 }
840
841 pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
842 match self {
843 Meta::SequenceNumber(n) => {
844 v.push(0x00);
845 push_vlq(2, v);
846 v.extend_from_slice(&n.to_be_bytes());
847 }
848 Meta::Text(s) => {
849 v.push(0x01);
850 let bytes = s.as_bytes();
851 push_vlq(bytes.len() as u32, v);
852 v.extend_from_slice(bytes);
853 }
854 Meta::Copyright(s) => {
855 v.push(0x02);
856 let bytes = s.as_bytes();
857 push_vlq(bytes.len() as u32, v);
858 v.extend_from_slice(bytes);
859 }
860 Meta::TrackName(s) => {
861 v.push(0x03);
862 let bytes = s.as_bytes();
863 push_vlq(bytes.len() as u32, v);
864 v.extend_from_slice(bytes);
865 }
866 Meta::InstrumentName(s) => {
867 v.push(0x04);
868 let bytes = s.as_bytes();
869 push_vlq(bytes.len() as u32, v);
870 v.extend_from_slice(bytes);
871 }
872 Meta::Lyric(s) => {
873 v.push(0x05);
874 let bytes = s.as_bytes();
875 push_vlq(bytes.len() as u32, v);
876 v.extend_from_slice(bytes);
877 }
878 Meta::Marker(s) => {
879 v.push(0x06);
880 let bytes = s.as_bytes();
881 push_vlq(bytes.len() as u32, v);
882 v.extend_from_slice(bytes);
883 }
884 Meta::CuePoint(s) => {
885 v.push(0x07);
886 let bytes = s.as_bytes();
887 push_vlq(bytes.len() as u32, v);
888 v.extend_from_slice(bytes);
889 }
890 Meta::ChannelPrefix(n) => {
891 v.push(0x20);
892 push_vlq(1, v);
893 v.push(*n as u8);
894 }
895 Meta::EndOfTrack => {
896 v.push(0x2F);
897 push_vlq(0, v);
898 }
899 Meta::SetTempo(n) => {
900 v.push(0x51);
901 push_vlq(3, v);
902 v.extend_from_slice(&n.to_be_bytes()[1..]);
903 }
904 Meta::SmpteOffset(t) => {
905 v.push(0x54);
906 push_vlq(5, v);
907 t.extend_midi(v);
908 }
909 Meta::TimeSignature(s) => {
910 v.push(0x58);
911 push_vlq(4, v);
912 s.extend_midi(v);
913 }
914 Meta::KeySignature(k) => {
915 v.push(0x59);
916 push_vlq(2, v);
917 k.extend_midi(v);
918 }
919 Meta::SequencerSpecific(d) => {
920 v.push(0x7F);
921 push_vlq(d.len() as u32, v);
922 v.extend_from_slice(d);
923 }
924 Meta::Unknown { meta_type, data } => {
925 v.push(*meta_type);
926 push_vlq(data.len() as u32, v);
927 v.extend_from_slice(data);
928 }
929 }
930 }
931}
932
933#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
935#[derive(Debug, Clone, PartialEq)]
936pub struct FileTimeSignature {
937 pub numerator: u8,
939 pub denominator: u16,
943 pub clocks_per_metronome_tick: u8,
945 pub thirty_second_notes_per_24_clocks: u8,
947}
948
949impl FileTimeSignature {
950 pub(crate) fn from_midi(m: &[u8]) -> Result<Self, ParseError> {
951 if m.len() < 4 {
952 return Err(ParseError::UnexpectedEnd);
953 }
954 Ok(Self {
955 numerator: m[0],
956 denominator: u16::pow(2, m[1] as u32),
957 clocks_per_metronome_tick: m[2],
958 thirty_second_notes_per_24_clocks: m[3],
959 })
960 }
961
962 pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
963 v.push(self.numerator);
964 v.push((self.denominator as f32).log2() as u8);
965 v.push(self.clocks_per_metronome_tick);
966 v.push(self.thirty_second_notes_per_24_clocks);
967 }
968}
969
970#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
972#[derive(Debug, Clone, PartialEq)]
973pub struct KeySignature {
974 pub key: i8,
976 pub scale: u8,
978}
979
980impl KeySignature {
981 pub(crate) fn from_midi(m: &[u8]) -> Result<Self, ParseError> {
982 if m.len() < 2 {
983 return Err(ParseError::UnexpectedEnd);
984 }
985 Ok(Self {
986 key: m[0] as i8,
987 scale: m[1],
988 })
989 }
990
991 pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
992 v.push(self.key as u8);
993 v.push(self.scale);
994 }
995}
996
997#[cfg(test)]
998mod tests {
999 use super::*;
1000
1001 #[test]
1002 fn test_file_time_signature() {
1003 let midi_data = vec![4, 2, 24, 8];
1004 let time_sig = FileTimeSignature::from_midi(&midi_data).unwrap();
1005
1006 assert_eq!(time_sig.numerator, 4);
1007 assert_eq!(time_sig.denominator, 4);
1008 assert_eq!(time_sig.clocks_per_metronome_tick, 24);
1009 assert_eq!(time_sig.thirty_second_notes_per_24_clocks, 8);
1010
1011 let mut output = Vec::new();
1012 time_sig.extend_midi(&mut output);
1013 assert_eq!(output, midi_data);
1014 }
1015
1016 #[test]
1017 fn test_file_time_signature_error() {
1018 let midi_data = vec![4, 2, 24];
1019 assert!(matches!(
1020 FileTimeSignature::from_midi(&midi_data),
1021 Err(ParseError::UnexpectedEnd)
1022 ));
1023 }
1024
1025 #[test]
1026 fn test_key_signature() {
1027 let midi_data = vec![2, 0];
1028 let key_sig = KeySignature::from_midi(&midi_data).unwrap();
1029
1030 assert_eq!(key_sig.key, 2);
1031 assert_eq!(key_sig.scale, 0);
1032
1033 let mut output = Vec::new();
1034 key_sig.extend_midi(&mut output);
1035 assert_eq!(output, midi_data);
1036 }
1037
1038 #[test]
1039 fn test_key_signature_error() {
1040 let midi_data = vec![2];
1041 assert!(matches!(
1042 KeySignature::from_midi(&midi_data),
1043 Err(ParseError::UnexpectedEnd)
1044 ));
1045 }
1046
1047 #[test]
1048 fn test_file_serde() {
1049 use crate::Channel;
1050 use crate::ChannelVoiceMsg;
1051 use crate::message::MidiMsg;
1052
1053 let mut file = MidiFile::default();
1055 file.header.division = Division::TicksPerQuarterNote(480);
1057
1058 file.add_track(Track::default());
1059
1060 file.extend_track(
1062 0,
1063 MidiMsg::Meta {
1064 msg: Meta::TrackName("Test Track".to_string()),
1065 },
1066 0.0,
1067 );
1068
1069 file.extend_track(
1070 0,
1071 MidiMsg::Meta {
1072 msg: Meta::TimeSignature(FileTimeSignature {
1073 numerator: 4,
1074 denominator: 4,
1075 clocks_per_metronome_tick: 24,
1076 thirty_second_notes_per_24_clocks: 8,
1077 }),
1078 },
1079 0.0,
1080 );
1081
1082 file.extend_track(
1083 0,
1084 MidiMsg::ChannelVoice {
1085 channel: Channel::Ch1,
1086 msg: ChannelVoiceMsg::NoteOn {
1087 note: 60,
1088 velocity: 64,
1089 },
1090 },
1091 0.0,
1092 );
1093
1094 file.extend_track(
1095 0,
1096 MidiMsg::ChannelVoice {
1097 channel: Channel::Ch1,
1098 msg: ChannelVoiceMsg::NoteOff {
1099 note: 60,
1100 velocity: 64,
1101 },
1102 },
1103 1.0,
1104 );
1105 file.extend_track(
1106 0,
1107 MidiMsg::Meta {
1108 msg: Meta::EndOfTrack,
1109 },
1110 2.0,
1111 );
1112
1113 let bytes = file.to_midi();
1115
1116 assert!(bytes.starts_with(b"MThd"));
1118 assert!(bytes[14..].starts_with(b"MTrk"));
1119
1120 let deserialized_file = MidiFile::from_midi(&bytes).unwrap();
1121 assert_eq!(deserialized_file.tracks.len(), 1);
1122 assert_eq!(deserialized_file.tracks[0].events().len(), 5);
1123 assert_eq!(
1124 deserialized_file.header.division,
1125 Division::TicksPerQuarterNote(480)
1126 );
1127 assert_eq!(deserialized_file, file);
1128 }
1129
1130 #[test]
1131 fn test_file_system_reset() {
1132 let mut file = MidiFile::default();
1133 file.add_track(Track::default());
1134 file.extend_track(
1135 0,
1136 MidiMsg::SystemRealTime {
1137 msg: crate::SystemRealTimeMsg::SystemReset,
1138 },
1139 0.0,
1140 );
1141 let bytes = file.to_midi();
1142
1143 let deserialized_file = MidiFile::from_midi(&bytes).unwrap();
1144 assert_eq!(deserialized_file.tracks.len(), 1);
1145 assert_eq!(deserialized_file.tracks[0].events().len(), 0);
1147 }
1148}