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#[derive(Debug, Clone, PartialEq, Default)]
64pub struct MidiFile {
65    /// The header chunk: Contains the file format, number of tracks, and division
66    pub header: Header,
67    /// The track chunks: Contains the actual midi events
68    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    /// Turn a series of bytes into a `MidiFile`.
126    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    /// Turn a `MidiFile` into a series of bytes.
173    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    /// Add a track to the file. Increments the `num_tracks` field in the header.
183    pub fn add_track(&mut self, track: Track) {
184        self.tracks.push(track);
185        self.header.num_tracks += 1;
186    }
187
188    /// Remove a track from the file. Decrements the `num_tracks` field in the header.
189    pub fn remove_track(&mut self, track_num: usize) {
190        self.tracks.remove(track_num);
191        self.header.num_tracks -= 1;
192    }
193
194    /// 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.
195    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/// The header chunk of a Standard Midi File
217#[derive(Debug, Clone, PartialEq, Default)]
218pub struct Header {
219    /// The format of the file
220    pub format: SMFFormat,
221    /// How many tracks are in the file
222    pub num_tracks: u16,
223    /// The "division" of the file, which specifies the meaning of the delta times in the file
224    pub division: Division,
225}
226
227impl Header {
228    // We pass the file to the from_midi function, so that we can have a running context of what's been parsed so far.
229    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); // Length of header, always 6 bytes
271
272        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/// The format of a Standard Midi File
290#[derive(Debug, Clone, PartialEq, Default)]
291pub enum SMFFormat {
292    /// A single track file
293    SingleTrack,
294    /// The file contains multiple tracks, but they are all meant to be played simultaneously
295    #[default]
296    MultiTrack,
297    /// The file contains multiple tracks, but they are independent of each other
298    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            // Big endian 16 bit value: Only the LSB is used
308            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/// The division of a Standard Midi File, which specifies the meaning of the delta times in the file
328#[derive(Debug, Clone, Copy, PartialEq)]
329pub enum Division {
330    /// Metrical time. Number of "ticks" per quarter note.
331    TicksPerQuarterNote(u16),
332    /// Time code based time.
333    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    /// Convert a beat or frame to the number of "ticks" in a file with this division.
347    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    /// Convert a number of file "ticks" to a beat or frame in a file with this division.
357    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/// A track in a Standard Midi File
368#[derive(Debug, Clone, PartialEq)]
369pub enum Track {
370    /// A standard "MTrk" chunk
371    Midi(Vec<TrackEvent>),
372    /// Any other chunk data.
373    ///
374    /// This includes the entire chuck data, include whatever chunk type and length.
375    AlienChunk(Vec<u8>),
376}
377
378impl Default for Track {
379    fn default() -> Self {
380        Track::Midi(vec![])
381    }
382}
383
384impl Track {
385    /// Get the number of events in the track, or the length in bytes of an `AlienChunk`.
386    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    /// Get the [`TrackEvent`] events in the track. Will be empty for an `AlienChunk`.
399    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); // We will fill this in after we know the length
461
462                for event in events {
463                    event.extend_midi(v);
464                }
465                let e = v.len();
466                // Fill in the length
467                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/// An event occurring in a track in a Standard Midi File
477#[derive(Debug, Clone, PartialEq)]
478pub struct TrackEvent {
479    /// The time since the last event. The meaning of this value is determined by the file header's [`Division`].
480    pub delta_time: u32,
481    /// The actual midi event.
482    pub event: MidiMsg,
483    /// Given the file's [`Division`], the time in beats or frames at which this event occurs.
484    ///
485    /// When deserializing, this is derived from the `delta_time` and the previous event's `beat_or_frame`.
486    ///
487    /// 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.
488    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                                // len is not the length of the entire message, since we don't have the status byte
510                                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                                // len _is_ the length of the entire message
538                                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        // Compound channel-voice messages (14-bit CCs, `CCHighRes`, `Parameter`,
607        // and high-resolution note on/off) serialize to multiple individual
608        // MIDI messages' worth of bytes. In a live MIDI stream these are sent
609        // back-to-back using running status, but in an SMF every such sub-message
610        // must be its own track event with its own delta-time, or re-parsing
611        // will misinterpret the stream. Emit them split here, with the track
612        // event's delta-time preceding the first sub-message and 0-delta
613        // preceding each subsequent one.
614        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        // Any kind of system event
625        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            // We always use the 0xF7 format for system events, since it can be used for all system events, not just system exclusive
635            v.push(0xF7);
636            push_vlq(event.len() as u32, v);
637        }
638        v.extend_from_slice(&event);
639    }
640
641    /// If `msg` is a compound channel-voice message that must be split into
642    /// multiple SMF events, write all the sub-events (delta-time + status byte
643    /// + payload, with 0-delta between them) and return `true`. Otherwise
644    ///   return `false` so the caller can fall back to the single-event path.
645    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                // These messages are a note-on/off followed by a HighResVelocity
670                // CC (control 0x58). Emit them as two separate SMF events.
671                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); // HighResVelocity CC
684                v.push(lsb);
685                true
686            }
687            _ => false,
688        }
689    }
690}
691
692/// A meta event in a Standard Midi File
693#[derive(Debug, Clone, PartialEq)]
694pub enum Meta {
695    /// 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.
696    SequenceNumber(u16),
697    /// Any text, describing anything
698    Text(String),
699    /// A copyright notice
700    Copyright(String),
701    /// The name of the track
702    TrackName(String),
703    /// The name of the instrument used in the track
704    InstrumentName(String),
705    /// A lyric. See RP-017 for guidance on the use of this meta event.
706    Lyric(String),
707    /// Normally only used in a SingleTrack file, or the first track of a MultiTrack file. Used to mark significant points in the music.
708    Marker(String),
709    /// A description of something happening at a point in time
710    CuePoint(String),
711    /// The MIDI channel that the following track events are intended for. Effective until the next event that specifies a channel.
712    ChannelPrefix(Channel),
713    /// Marks the end of a track. This event is not optional. It must be the last event in every track.
714    EndOfTrack,
715    /// The tempo in microseconds per quarter note.
716    SetTempo(u32),
717    /// If present, the time at which the track is supposed to start. Should be present at the start of the track.
718    SmpteOffset(HighResTimeCode),
719    /// A time signature.
720    TimeSignature(FileTimeSignature),
721    /// A key signature.
722    KeySignature(KeySignature),
723    /// A chunk of data that is specific to the sequencer that created the file.
724    SequencerSpecific(Vec<u8>),
725    // TODO: RP-32
726    // TODO: RP-19
727    /// Any other meta event that is not recognized
728    Unknown { meta_type: u8, data: Vec<u8> },
729}
730
731impl Meta {
732    // We do not extend with 0xFF, as this is done in TrackEvent::extend_midi
733    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/// A time signature occurring in a Standard Midi File.
927#[derive(Debug, Clone, PartialEq)]
928pub struct FileTimeSignature {
929    /// The numerator of the time signature, as it would be notated.
930    pub numerator: u8,
931    /// The denominator of the time signature, as it would be notated.
932    ///
933    /// This is tranformed from the power-of-two representation used in the file.
934    pub denominator: u16,
935    /// The number of MIDI clocks per metronome tick.
936    pub clocks_per_metronome_tick: u8,
937    /// How many 32nd notes are in a MIDI quarter note, which should usually be 8.
938    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/// A key signature occurring in a Standard Midi File.
963#[derive(Debug, Clone, PartialEq)]
964pub struct KeySignature {
965    /// Negative for number of flats, positive for number of sharps
966    pub key: i8,
967    /// 0 for major, 1 for minor
968    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        // Create a simple MIDI file
1045        let mut file = MidiFile::default();
1046        // Set the division
1047        file.header.division = Division::TicksPerQuarterNote(480);
1048
1049        file.add_track(Track::default());
1050
1051        // Add some events to the track
1052        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        // Convert the file to bytes
1105        let bytes = file.to_midi();
1106
1107        // Assert that we've created a valid MIDI file
1108        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        // The system reset message should not be included in the track, since it is not a valid MIDI file message
1137        assert_eq!(deserialized_file.tracks[0].events().len(), 0);
1138    }
1139}