Skip to main content

midi_msg/
file.rs

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)] // This will be unused unless on an embedded target
11use micromath::F32Ext;
12
13use core::error;
14
15use super::{
16    Channel, ChannelVoiceMsg, HighResTimeCode, MidiMsg, ParseError, ReceiverContext,
17    SystemExclusiveMsg, TimeCodeType, util::*,
18};
19
20// Standard Midi File 1.0 (SMF): RP-001 support
21
22/// Errors that can occur when parsing a [`MidiFile`]
23#[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/// A Standard Midi File (SMF)
63#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
64#[derive(Debug, Clone, PartialEq, Default)]
65pub struct MidiFile {
66    /// The header chunk: Contains the file format, number of tracks, and division
67    pub header: Header,
68    /// The track chunks: Contains the actual midi events
69    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    /// Turn a series of bytes into a `MidiFile`.
127    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    /// Turn a `MidiFile` into a series of bytes.
174    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    /// Add a track to the file. Increments the `num_tracks` field in the header.
184    pub fn add_track(&mut self, track: Track) {
185        self.tracks.push(track);
186        self.header.num_tracks += 1;
187    }
188
189    /// Remove a track from the file. Decrements the `num_tracks` field in the header.
190    pub fn remove_track(&mut self, track_num: usize) {
191        self.tracks.remove(track_num);
192        self.header.num_tracks -= 1;
193    }
194
195    /// Add a midi event to a track in the file, given its absolute beat or frame time. The event delta time is calculated from the previous event in the track and the time division of the file.
196    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/// The header chunk of a Standard Midi File
218#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
219#[derive(Debug, Clone, PartialEq, Default)]
220pub struct Header {
221    /// The format of the file
222    pub format: SMFFormat,
223    /// How many tracks are in the file
224    pub num_tracks: u16,
225    /// The "division" of the file, which specifies the meaning of the delta times in the file
226    pub division: Division,
227}
228
229impl Header {
230    // We pass the file to the from_midi function, so that we can have a running context of what's been parsed so far.
231    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); // Length of header, always 6 bytes
273
274        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/// The format of a Standard Midi File
292#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
293#[derive(Debug, Clone, PartialEq, Default)]
294pub enum SMFFormat {
295    /// A single track file
296    SingleTrack,
297    /// The file contains multiple tracks, but they are all meant to be played simultaneously
298    #[default]
299    MultiTrack,
300    /// The file contains multiple tracks, but they are independent of each other
301    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            // Big endian 16 bit value: Only the LSB is used
311            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/// The division of a Standard Midi File, which specifies the meaning of the delta times in the file
331#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
332#[derive(Debug, Clone, Copy, PartialEq)]
333pub enum Division {
334    /// Metrical time. Number of "ticks" per quarter note.
335    TicksPerQuarterNote(u16),
336    /// Time code based time.
337    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    /// Convert a beat or frame to the number of "ticks" in a file with this division.
351    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    /// Convert a number of file "ticks" to a beat or frame in a file with this division.
361    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/// A track in a Standard Midi File
372#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
373#[derive(Debug, Clone, PartialEq)]
374pub enum Track {
375    /// A standard "MTrk" chunk
376    Midi(Vec<TrackEvent>),
377    /// Any other chunk data.
378    ///
379    /// This includes the entire chuck data, include whatever chunk type and length.
380    AlienChunk(Vec<u8>),
381}
382
383impl Default for Track {
384    fn default() -> Self {
385        Track::Midi(vec![])
386    }
387}
388
389impl Track {
390    /// Get the number of events in the track, or the length in bytes of an `AlienChunk`.
391    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    /// Get the [`TrackEvent`] events in the track. Will be empty for an `AlienChunk`.
404    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); // We will fill this in after we know the length
466
467                for event in events {
468                    event.extend_midi(v);
469                }
470                let e = v.len();
471                // Fill in the length
472                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/// An event occurring in a track in a Standard Midi File
482#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
483#[derive(Debug, Clone, PartialEq)]
484pub struct TrackEvent {
485    /// The time since the last event. The meaning of this value is determined by the file header's [`Division`].
486    pub delta_time: u32,
487    /// The actual midi event.
488    pub event: MidiMsg,
489    /// Given the file's [`Division`], the time in beats or frames at which this event occurs.
490    ///
491    /// When deserializing, this is derived from the `delta_time` and the previous event's `beat_or_frame`.
492    ///
493    /// When manually constructing `TrackEvent`s (i.e. when not using the [`MidiFile::extend_track`] convenience function), this field can set to any value, as it is not used when serializing the file.
494    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                                // len is not the length of the entire message, since we don't have the status byte
516                                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                                // len _is_ the length of the entire message
544                                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        // Compound channel-voice messages (14-bit CCs, `CCHighRes`, `Parameter`,
613        // and high-resolution note on/off) serialize to multiple individual
614        // MIDI messages' worth of bytes. In a live MIDI stream these are sent
615        // back-to-back using running status, but in an SMF every such sub-message
616        // must be its own track event with its own delta-time, or re-parsing
617        // will misinterpret the stream. Emit them split here, with the track
618        // event's delta-time preceding the first sub-message and 0-delta
619        // preceding each subsequent one.
620        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        // Any kind of system event
631        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            // We always use the 0xF7 format for system events, since it can be used for all system events, not just system exclusive
641            v.push(0xF7);
642            push_vlq(event.len() as u32, v);
643        }
644        v.extend_from_slice(&event);
645    }
646
647    /// If `msg` is a compound channel-voice message that must be split into
648    /// multiple SMF events, write all the sub-events (delta-time + status byte
649    /// + payload, with 0-delta between them) and return `true`. Otherwise
650    ///   return `false` so the caller can fall back to the single-event path.
651    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                // These messages are a note-on/off followed by a HighResVelocity
676                // CC (control 0x58). Emit them as two separate SMF events.
677                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); // HighResVelocity CC
690                v.push(lsb);
691                true
692            }
693            _ => false,
694        }
695    }
696}
697
698/// A meta event in a Standard Midi File
699#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
700#[derive(Debug, Clone, PartialEq)]
701pub enum Meta {
702    /// Must occur at the start of a track, and specifies the sequence number of the track. In a MultiSong file, this is the "pattern" number that identifies the song for cueing purposes.
703    SequenceNumber(u16),
704    /// Any text, describing anything
705    Text(String),
706    /// A copyright notice
707    Copyright(String),
708    /// The name of the track
709    TrackName(String),
710    /// The name of the instrument used in the track
711    InstrumentName(String),
712    /// A lyric. See RP-017 for guidance on the use of this meta event.
713    Lyric(String),
714    /// Normally only used in a SingleTrack file, or the first track of a MultiTrack file. Used to mark significant points in the music.
715    Marker(String),
716    /// A description of something happening at a point in time
717    CuePoint(String),
718    /// The MIDI channel that the following track events are intended for. Effective until the next event that specifies a channel.
719    ChannelPrefix(Channel),
720    /// Marks the end of a track. This event is not optional. It must be the last event in every track.
721    EndOfTrack,
722    /// The tempo in microseconds per quarter note.
723    SetTempo(u32),
724    /// If present, the time at which the track is supposed to start. Should be present at the start of the track.
725    SmpteOffset(HighResTimeCode),
726    /// A time signature.
727    TimeSignature(FileTimeSignature),
728    /// A key signature.
729    KeySignature(KeySignature),
730    /// A chunk of data that is specific to the sequencer that created the file.
731    SequencerSpecific(Vec<u8>),
732    // TODO: RP-32
733    // TODO: RP-19
734    /// Any other meta event that is not recognized
735    Unknown { meta_type: u8, data: Vec<u8> },
736}
737
738impl Meta {
739    // We do not extend with 0xFF, as this is done in TrackEvent::extend_midi
740    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/// A time signature occurring in a Standard Midi File.
934#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
935#[derive(Debug, Clone, PartialEq)]
936pub struct FileTimeSignature {
937    /// The numerator of the time signature, as it would be notated.
938    pub numerator: u8,
939    /// The denominator of the time signature, as it would be notated.
940    ///
941    /// This is tranformed from the power-of-two representation used in the file.
942    pub denominator: u16,
943    /// The number of MIDI clocks per metronome tick.
944    pub clocks_per_metronome_tick: u8,
945    /// How many 32nd notes are in a MIDI quarter note, which should usually be 8.
946    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/// A key signature occurring in a Standard Midi File.
971#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
972#[derive(Debug, Clone, PartialEq)]
973pub struct KeySignature {
974    /// Negative for number of flats, positive for number of sharps
975    pub key: i8,
976    /// 0 for major, 1 for minor
977    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        // Create a simple MIDI file
1054        let mut file = MidiFile::default();
1055        // Set the division
1056        file.header.division = Division::TicksPerQuarterNote(480);
1057
1058        file.add_track(Track::default());
1059
1060        // Add some events to the track
1061        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        // Convert the file to bytes
1114        let bytes = file.to_midi();
1115
1116        // Assert that we've created a valid MIDI file
1117        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        // The system reset message should not be included in the track, since it is not a valid MIDI file message
1146        assert_eq!(deserialized_file.tracks[0].events().len(), 0);
1147    }
1148}