midi_convert/
parse.rs

1//! Parse midi messages
2use midi_types::{
3    status::*, Channel, Control, MidiMessage, Note, Program, QuarterFrame, Value14, Value7,
4};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8/// Errors parsing.
9pub enum MidiParseError {
10    /// Input buffer wasn't long enough to parse anything
11    BufferTooShort,
12
13    /// Couldn't find a valid message
14    MessageNotFound,
15}
16
17/// Trait for parsing a byte slice into a MidiMessage
18pub trait MidiTryParseSlice: Sized {
19    /// try to parse
20    fn try_parse_slice(buf: &[u8]) -> Result<Self, MidiParseError>;
21}
22
23/// A parser that parses a byte at a time.
24#[derive(Debug, Clone, PartialEq)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub struct MidiParser {
27    state: MidiParserState,
28}
29
30#[derive(Debug, Clone, PartialEq)]
31#[cfg_attr(feature = "defmt", derive(defmt::Format))]
32enum MidiParserState {
33    Idle,
34    NoteOnRecvd(Channel),
35    NoteOnNoteRecvd(Channel, Note),
36
37    NoteOffRecvd(Channel),
38    NoteOffNoteRecvd(Channel, Note),
39
40    KeyPressureRecvd(Channel),
41    KeyPressureNoteRecvd(Channel, Note),
42
43    ControlChangeRecvd(Channel),
44    ControlChangeControlRecvd(Channel, Control),
45
46    ProgramChangeRecvd(Channel),
47
48    ChannelPressureRecvd(Channel),
49
50    PitchBendRecvd(Channel),
51    PitchBendLsbRecvd(Channel, u8),
52
53    QuarterFrameRecvd,
54
55    SongPositionRecvd,
56    SongPositionLsbRecvd(u8),
57
58    SongSelectRecvd,
59}
60
61/// Check if most significant bit is set which signifies a Midi status byte
62fn is_status_byte(byte: u8) -> bool {
63    byte & 0x80 == 0x80
64}
65
66/// Check if a byte corresponds to 0x1111xxxx which signifies either a system common or realtime message
67fn is_system_message(byte: u8) -> bool {
68    byte & 0xf0 == 0xf0
69}
70
71/// Split the message and channel part of a channel voice message
72fn split_message_and_channel(byte: u8) -> (u8, Channel) {
73    (byte & 0xf0u8, (byte & 0x0fu8).into())
74}
75
76/// Parse Midi messages byte at a time.
77///
78/// Returns parsed Midi messages whenever one is completed.
79impl MidiParser {
80    /// Initialize midiparser state
81    pub fn new() -> Self {
82        Self::default()
83    }
84
85    /// Parse midi event byte by byte. Call this whenever a byte is received. When a midi-event is
86    /// completed it is returned, otherwise this method updates the internal midiparser state and
87    /// and returns none.
88    pub fn parse(&mut self, byte: u8) -> Option<MidiMessage> {
89        if is_status_byte(byte) {
90            if is_system_message(byte) {
91                match byte {
92                    // System common messages, these should reset parsing other messages
93                    0xf0 => {
94                        // System exclusive
95                        self.state = MidiParserState::Idle;
96                        None
97                    }
98                    0xf1 => {
99                        // Midi time code quarter frame
100                        self.state = MidiParserState::QuarterFrameRecvd;
101                        None
102                    }
103                    0xf2 => {
104                        // Song position pointer
105                        self.state = MidiParserState::SongPositionRecvd;
106                        None
107                    }
108                    0xf3 => {
109                        // Song select
110                        self.state = MidiParserState::SongSelectRecvd;
111                        None
112                    }
113                    0xf6 => {
114                        // Tune request
115                        self.state = MidiParserState::Idle;
116                        Some(MidiMessage::TuneRequest)
117                    }
118                    0xf7 => {
119                        // End of exclusive
120                        self.state = MidiParserState::Idle;
121                        None
122                        // Some(MidiMessage::EndOfExclusive)
123                    }
124
125                    // System realtime messages
126                    0xf8 => Some(MidiMessage::TimingClock),
127                    0xf9 => None, // Reserved
128                    0xfa => Some(MidiMessage::Start),
129                    0xfb => Some(MidiMessage::Continue),
130                    0xfc => Some(MidiMessage::Stop),
131                    0xfd => None, // Reserved
132                    0xfe => Some(MidiMessage::ActiveSensing),
133                    0xff => Some(MidiMessage::Reset),
134
135                    _ => {
136                        // Undefined messages like 0xf4 and should end up here
137                        self.state = MidiParserState::Idle;
138                        None
139                    }
140                }
141            } else {
142                // Channel voice message
143
144                let (message, channel) = split_message_and_channel(byte);
145
146                match message {
147                    0x80 => {
148                        self.state = MidiParserState::NoteOffRecvd(channel);
149                        None
150                    }
151                    0x90 => {
152                        self.state = MidiParserState::NoteOnRecvd(channel);
153                        None
154                    }
155                    0xA0 => {
156                        self.state = MidiParserState::KeyPressureRecvd(channel);
157                        None
158                    }
159                    0xB0 => {
160                        self.state = MidiParserState::ControlChangeRecvd(channel);
161                        None
162                    }
163                    0xC0 => {
164                        self.state = MidiParserState::ProgramChangeRecvd(channel);
165                        None
166                    }
167                    0xD0 => {
168                        self.state = MidiParserState::ChannelPressureRecvd(channel);
169                        None
170                    }
171                    0xE0 => {
172                        self.state = MidiParserState::PitchBendRecvd(channel);
173                        None
174                    }
175                    _ => None,
176                }
177            }
178        } else {
179            match self.state {
180                MidiParserState::NoteOffRecvd(channel) => {
181                    self.state = MidiParserState::NoteOffNoteRecvd(channel, byte.into());
182                    None
183                }
184                MidiParserState::NoteOffNoteRecvd(channel, note) => {
185                    self.state = MidiParserState::NoteOffRecvd(channel);
186                    Some(MidiMessage::NoteOff(channel, note, byte.into()))
187                }
188
189                MidiParserState::NoteOnRecvd(channel) => {
190                    self.state = MidiParserState::NoteOnNoteRecvd(channel, byte.into());
191                    None
192                }
193                MidiParserState::NoteOnNoteRecvd(channel, note) => {
194                    self.state = MidiParserState::NoteOnRecvd(channel);
195                    Some(MidiMessage::NoteOn(channel, note, byte.into()))
196                }
197
198                MidiParserState::KeyPressureRecvd(channel) => {
199                    self.state = MidiParserState::KeyPressureNoteRecvd(channel, byte.into());
200                    None
201                }
202                MidiParserState::KeyPressureNoteRecvd(channel, note) => {
203                    self.state = MidiParserState::KeyPressureRecvd(channel);
204                    Some(MidiMessage::KeyPressure(channel, note, byte.into()))
205                }
206
207                MidiParserState::ControlChangeRecvd(channel) => {
208                    self.state = MidiParserState::ControlChangeControlRecvd(channel, byte.into());
209                    None
210                }
211                MidiParserState::ControlChangeControlRecvd(channel, control) => {
212                    self.state = MidiParserState::ControlChangeRecvd(channel);
213                    Some(MidiMessage::ControlChange(channel, control, byte.into()))
214                }
215
216                MidiParserState::ProgramChangeRecvd(channel) => {
217                    Some(MidiMessage::ProgramChange(channel, byte.into()))
218                }
219
220                MidiParserState::ChannelPressureRecvd(channel) => {
221                    Some(MidiMessage::ChannelPressure(channel, byte.into()))
222                }
223
224                MidiParserState::PitchBendRecvd(channel) => {
225                    self.state = MidiParserState::PitchBendLsbRecvd(channel, byte);
226                    None
227                }
228                MidiParserState::PitchBendLsbRecvd(channel, lsb) => {
229                    self.state = MidiParserState::PitchBendRecvd(channel);
230                    Some(MidiMessage::PitchBendChange(channel, (byte, lsb).into()))
231                }
232                MidiParserState::QuarterFrameRecvd => Some(MidiMessage::QuarterFrame(byte.into())),
233                MidiParserState::SongPositionRecvd => {
234                    self.state = MidiParserState::SongPositionLsbRecvd(byte);
235                    None
236                }
237                MidiParserState::SongPositionLsbRecvd(lsb) => {
238                    self.state = MidiParserState::SongPositionRecvd;
239                    Some(MidiMessage::SongPositionPointer((byte, lsb).into()))
240                }
241                MidiParserState::SongSelectRecvd => Some(MidiMessage::SongSelect(byte.into())),
242                _ => None,
243            }
244        }
245    }
246}
247
248impl Default for MidiParser {
249    fn default() -> Self {
250        MidiParser {
251            state: MidiParserState::Idle,
252        }
253    }
254}
255
256const NOTE_OFF_END: u8 = NOTE_OFF + 0x0F;
257const NOTE_ON_END: u8 = NOTE_ON + 0x0F;
258const KEY_PRESSURE_END: u8 = KEY_PRESSURE + 0x0F;
259const CONTROL_CHANGE_END: u8 = CONTROL_CHANGE + 0x0F;
260const PITCH_BEND_CHANGE_END: u8 = PITCH_BEND_CHANGE + 0x0F;
261const PROGRAM_CHANGE_END: u8 = PROGRAM_CHANGE + 0x0F;
262const CHANNEL_PRESSURE_END: u8 = CHANNEL_PRESSURE + 0x0F;
263
264//parse helper guard
265fn check_len<F: Fn() -> Result<MidiMessage, MidiParseError>>(
266    buf: &[u8],
267    len: usize,
268    func: F,
269) -> Result<MidiMessage, MidiParseError> {
270    if buf.len() >= len {
271        func()
272    } else {
273        Err(MidiParseError::BufferTooShort)
274    }
275}
276
277/// Parse a byte slice for a MidiMessage
278impl MidiTryParseSlice for MidiMessage {
279    fn try_parse_slice(buf: &[u8]) -> Result<MidiMessage, MidiParseError> {
280        if buf.is_empty() {
281            Err(MidiParseError::BufferTooShort)
282        } else {
283            let chan = |status: u8| -> Channel { Channel::from(status & 0x0F) };
284            match buf[0] {
285                //1 byte
286                TUNE_REQUEST => Ok(MidiMessage::TuneRequest),
287                TIMING_CLOCK => Ok(MidiMessage::TimingClock),
288                START => Ok(MidiMessage::Start),
289                CONTINUE => Ok(MidiMessage::Continue),
290                STOP => Ok(MidiMessage::Stop),
291                ACTIVE_SENSING => Ok(MidiMessage::ActiveSensing),
292                RESET => Ok(MidiMessage::Reset),
293
294                //2 byte
295                s @ PROGRAM_CHANGE..=PROGRAM_CHANGE_END => check_len(buf, 2, || {
296                    Ok(MidiMessage::ProgramChange(chan(s), Program::from(buf[1])))
297                }),
298                s @ CHANNEL_PRESSURE..=CHANNEL_PRESSURE_END => check_len(buf, 2, || {
299                    Ok(MidiMessage::ChannelPressure(chan(s), Value7::from(buf[1])))
300                }),
301                QUARTER_FRAME => check_len(buf, 2, || {
302                    Ok(MidiMessage::QuarterFrame(QuarterFrame::from(buf[1])))
303                }),
304                SONG_SELECT => {
305                    check_len(buf, 2, || Ok(MidiMessage::SongSelect(Value7::from(buf[1]))))
306                }
307
308                //3 byte
309                s @ NOTE_OFF..=NOTE_OFF_END => check_len(buf, 3, || {
310                    Ok(MidiMessage::NoteOff(
311                        chan(s),
312                        Note::from(buf[1]),
313                        Value7::from(buf[2]),
314                    ))
315                }),
316                s @ NOTE_ON..=NOTE_ON_END => check_len(buf, 3, || {
317                    Ok(MidiMessage::NoteOn(
318                        chan(s),
319                        Note::from(buf[1]),
320                        Value7::from(buf[2]),
321                    ))
322                }),
323                s @ KEY_PRESSURE..=KEY_PRESSURE_END => check_len(buf, 3, || {
324                    Ok(MidiMessage::KeyPressure(
325                        chan(s),
326                        Note::from(buf[1]),
327                        Value7::from(buf[2]),
328                    ))
329                }),
330                s @ CONTROL_CHANGE..=CONTROL_CHANGE_END => check_len(buf, 3, || {
331                    Ok(MidiMessage::ControlChange(
332                        chan(s),
333                        Control::from(buf[1]),
334                        Value7::from(buf[2]),
335                    ))
336                }),
337                s @ PITCH_BEND_CHANGE..=PITCH_BEND_CHANGE_END => check_len(buf, 3, || {
338                    Ok(MidiMessage::PitchBendChange(
339                        chan(s),
340                        Value14::from((buf[1], buf[2])),
341                    ))
342                }),
343                SONG_POSITION_POINTER => check_len(buf, 3, || {
344                    Ok(MidiMessage::SongPositionPointer(Value14::from((
345                        buf[1], buf[2],
346                    ))))
347                }),
348
349                _ => Err(MidiParseError::MessageNotFound),
350            }
351        }
352    }
353}
354
355#[cfg(test)]
356mod tests {
357    extern crate std;
358    use super::*;
359    use std::vec::Vec;
360
361    #[test]
362    fn should_parse_status_byte() {
363        assert!(is_status_byte(0x80u8));
364        assert!(is_status_byte(0x94u8));
365        assert!(!is_status_byte(0x00u8));
366        assert!(!is_status_byte(0x78u8));
367    }
368
369    #[test]
370    fn should_parse_system_message() {
371        assert!(is_system_message(0xf0));
372        assert!(is_system_message(0xf4));
373        assert!(!is_system_message(0x0f));
374        assert!(!is_system_message(0x77));
375    }
376
377    #[test]
378    fn should_split_message_and_channel() {
379        let (message, channel) = split_message_and_channel(0x91u8);
380        assert_eq!(message, 0x90u8);
381        assert_eq!(channel, 1.into());
382    }
383
384    #[test]
385    fn should_parse_note_off() {
386        MidiParser::new().assert_result(
387            &[0x82, 0x76, 0x34],
388            &[MidiMessage::NoteOff(2.into(), 0x76.into(), 0x34.into())],
389        );
390    }
391
392    #[test]
393    fn should_handle_note_off_running_state() {
394        MidiParser::new().assert_result(
395            &[
396                0x82, 0x76, 0x34, // First note_off
397                0x33, 0x65, // Second note_off without status byte
398            ],
399            &[
400                MidiMessage::NoteOff(2.into(), 0x76.into(), 0x34.into()),
401                MidiMessage::NoteOff(2.into(), 0x33.into(), 0x65.into()),
402            ],
403        );
404    }
405
406    #[test]
407    fn should_parse_note_on() {
408        MidiParser::new().assert_result(
409            &[0x91, 0x04, 0x34],
410            &[MidiMessage::NoteOn(1.into(), 4.into(), 0x34.into())],
411        );
412    }
413
414    #[test]
415    fn should_handle_note_on_running_state() {
416        MidiParser::new().assert_result(
417            &[
418                0x92, 0x76, 0x34, // First note_on
419                0x33, 0x65, // Second note on without status byte
420            ],
421            &[
422                MidiMessage::NoteOn(2.into(), 0x76.into(), 0x34.into()),
423                MidiMessage::NoteOn(2.into(), 0x33.into(), 0x65.into()),
424            ],
425        );
426    }
427
428    #[test]
429    fn should_parse_keypressure() {
430        MidiParser::new().assert_result(
431            &[0xAA, 0x13, 0x34],
432            &[MidiMessage::KeyPressure(
433                10.into(),
434                0x13.into(),
435                0x34.into(),
436            )],
437        );
438    }
439
440    #[test]
441    fn should_handle_keypressure_running_state() {
442        MidiParser::new().assert_result(
443            &[
444                0xA8, 0x77, 0x03, // First key_pressure
445                0x14, 0x56, // Second key_pressure without status byte
446            ],
447            &[
448                MidiMessage::KeyPressure(8.into(), 0x77.into(), 0x03.into()),
449                MidiMessage::KeyPressure(8.into(), 0x14.into(), 0x56.into()),
450            ],
451        );
452    }
453
454    #[test]
455    fn should_parse_control_change() {
456        MidiParser::new().assert_result(
457            &[0xB2, 0x76, 0x34],
458            &[MidiMessage::ControlChange(
459                2.into(),
460                0x76.into(),
461                0x34.into(),
462            )],
463        );
464    }
465
466    #[test]
467    fn should_parse_control_change_running_state() {
468        MidiParser::new().assert_result(
469            &[
470                0xb3, 0x3C, 0x18, // First control change
471                0x43, 0x01, // Second control change without status byte
472            ],
473            &[
474                MidiMessage::ControlChange(3.into(), 0x3c.into(), 0x18.into()),
475                MidiMessage::ControlChange(3.into(), 0x43.into(), 0x01.into()),
476            ],
477        );
478    }
479
480    #[test]
481    fn should_parse_program_change() {
482        MidiParser::new().assert_result(
483            &[0xC9, 0x15],
484            &[MidiMessage::ProgramChange(9.into(), 0x15.into())],
485        );
486    }
487
488    #[test]
489    fn should_parse_program_change_running_state() {
490        MidiParser::new().assert_result(
491            &[
492                0xC3, 0x67, // First program change
493                0x01, // Second program change without status byte
494            ],
495            &[
496                MidiMessage::ProgramChange(3.into(), 0x67.into()),
497                MidiMessage::ProgramChange(3.into(), 0x01.into()),
498            ],
499        );
500    }
501
502    #[test]
503    fn should_parse_channel_pressure() {
504        MidiParser::new().assert_result(
505            &[0xDD, 0x37],
506            &[MidiMessage::ChannelPressure(13.into(), 0x37.into())],
507        );
508    }
509
510    #[test]
511    fn should_parse_channel_pressure_running_state() {
512        MidiParser::new().assert_result(
513            &[
514                0xD6, 0x77, // First channel pressure
515                0x43, // Second channel pressure without status byte
516            ],
517            &[
518                MidiMessage::ChannelPressure(6.into(), 0x77.into()),
519                MidiMessage::ChannelPressure(6.into(), 0x43.into()),
520            ],
521        );
522    }
523
524    #[test]
525    fn should_parse_pitchbend() {
526        MidiParser::new().assert_result(
527            &[0xE8, 0x14, 0x56],
528            &[MidiMessage::PitchBendChange(8.into(), (0x56, 0x14).into())],
529        );
530    }
531
532    #[test]
533    fn should_parse_pitchbend_running_state() {
534        MidiParser::new().assert_result(
535            &[
536                0xE3, 0x3C, 0x18, // First pitchbend
537                0x43, 0x01, // Second pitchbend without status byte
538            ],
539            &[
540                MidiMessage::PitchBendChange(3.into(), (0x18, 0x3c).into()),
541                MidiMessage::PitchBendChange(3.into(), (0x01, 0x43).into()),
542            ],
543        );
544    }
545
546    #[test]
547    fn should_parse_quarter_frame() {
548        MidiParser::new().assert_result(&[0xf1, 0x7f], &[MidiMessage::QuarterFrame(0x7f.into())]);
549    }
550
551    #[test]
552    fn should_handle_quarter_frame_running_state() {
553        MidiParser::new().assert_result(
554            &[
555                0xf1, 0x7f, // Send quarter frame
556                0x56, // Only send data of next quarter frame
557            ],
558            &[
559                MidiMessage::QuarterFrame(0x7f.into()),
560                MidiMessage::QuarterFrame(0x56.into()),
561            ],
562        );
563    }
564
565    #[test]
566    fn should_parse_song_position_pointer() {
567        MidiParser::new().assert_result(
568            &[0xf2, 0x7f, 0x68],
569            &[MidiMessage::SongPositionPointer((0x68, 0x7f).into())],
570        );
571    }
572
573    #[test]
574    fn should_handle_song_position_pointer_running_state() {
575        MidiParser::new().assert_result(
576            &[
577                0xf2, 0x7f, 0x68, // Send song position pointer
578                0x23, 0x7b, // Only send data of next song position pointer
579            ],
580            &[
581                MidiMessage::SongPositionPointer((0x68, 0x7f).into()),
582                MidiMessage::SongPositionPointer((0x7b, 0x23).into()),
583            ],
584        );
585    }
586
587    #[test]
588    fn should_parse_song_select() {
589        MidiParser::new().assert_result(&[0xf3, 0x3f], &[MidiMessage::SongSelect(0x3f.into())]);
590    }
591
592    #[test]
593    fn should_handle_song_select_running_state() {
594        MidiParser::new().assert_result(
595            &[
596                0xf3, 0x3f, // Send song select
597                0x00, // Only send data for next song select
598            ],
599            &[
600                MidiMessage::SongSelect(0x3f.into()),
601                MidiMessage::SongSelect(0x00.into()),
602            ],
603        );
604    }
605
606    #[test]
607    fn should_parse_tune_request() {
608        MidiParser::new().assert_result(&[0xf6], &[MidiMessage::TuneRequest]);
609    }
610
611    #[test]
612    fn should_interrupt_parsing_for_tune_request() {
613        MidiParser::new().assert_result(
614            &[
615                0x92, 0x76, // start note_on message
616                0xf6, // interrupt with tune request
617                0x34, // finish note on, this should be ignored
618            ],
619            &[MidiMessage::TuneRequest],
620        );
621    }
622
623    // #[test]
624    // fn should_parse_end_exclusive() {
625    //     MidiByteStreamParser::new().assert_result(&[0xf7], &[MidiMessage::EndOfExclusive]);
626    // }
627
628    // #[test]
629    // fn should_interrupt_parsing_for_end_of_exclusive() {
630    //     MidiByteStreamParser::new().assert_result(
631    //         &[
632    //             0x92, 0x76, // start note_on message
633    //             0xf7, // interrupt with end of exclusive
634    //             0x34, // finish note on, this should be ignored
635    //         ],
636    //         &[MidiMessage::EndOfExclusive],
637    //     );
638    // }
639
640    #[test]
641    fn should_interrupt_parsing_for_undefined_message() {
642        MidiParser::new().assert_result(
643            &[
644                0x92, 0x76, // start note_on message
645                0xf5, // interrupt with undefined message
646                0x34, // finish note on, this should be ignored
647            ],
648            &[],
649        );
650    }
651
652    #[test]
653    fn should_parse_timingclock_message() {
654        MidiParser::new().assert_result(&[0xf8], &[MidiMessage::TimingClock]);
655    }
656
657    #[test]
658    fn should_parse_timingclock_message_as_realtime() {
659        MidiParser::new().assert_result(
660            &[
661                0xD6, // Start channel pressure event
662                0xf8, // interupt with midi timing clock
663                0x77, // Finish channel pressure
664            ],
665            &[
666                MidiMessage::TimingClock,
667                MidiMessage::ChannelPressure(6.into(), 0x77.into()),
668            ],
669        );
670    }
671
672    #[test]
673    fn should_parse_start_message() {
674        MidiParser::new().assert_result(&[0xfa], &[MidiMessage::Start]);
675    }
676
677    #[test]
678    fn should_parse_start_message_as_realtime() {
679        MidiParser::new().assert_result(
680            &[
681                0xD6, // Start channel pressure event
682                0xfa, // interupt with start
683                0x77, // Finish channel pressure
684            ],
685            &[
686                MidiMessage::Start,
687                MidiMessage::ChannelPressure(6.into(), 0x77.into()),
688            ],
689        );
690    }
691
692    #[test]
693    fn should_parse_continue_message() {
694        MidiParser::new().assert_result(&[0xfb], &[MidiMessage::Continue]);
695    }
696
697    #[test]
698    fn should_parse_continue_message_as_realtime() {
699        MidiParser::new().assert_result(
700            &[
701                0xD6, // Start channel pressure event
702                0xfb, // interupt with continue
703                0x77, // Finish channel pressure
704            ],
705            &[
706                MidiMessage::Continue,
707                MidiMessage::ChannelPressure(6.into(), 0x77.into()),
708            ],
709        );
710    }
711
712    #[test]
713    fn should_parse_stop_message() {
714        MidiParser::new().assert_result(&[0xfc], &[MidiMessage::Stop]);
715    }
716
717    #[test]
718    fn should_parse_stop_message_as_realtime() {
719        MidiParser::new().assert_result(
720            &[
721                0xD6, // Start channel pressure event
722                0xfc, // interupt with stop
723                0x77, // Finish channel pressure
724            ],
725            &[
726                MidiMessage::Stop,
727                MidiMessage::ChannelPressure(6.into(), 0x77.into()),
728            ],
729        );
730    }
731
732    #[test]
733    fn should_parse_activesensing_message() {
734        MidiParser::new().assert_result(&[0xfe], &[MidiMessage::ActiveSensing]);
735    }
736
737    #[test]
738    fn should_parse_activesensing_message_as_realtime() {
739        MidiParser::new().assert_result(
740            &[
741                0xD6, // Start channel pressure event
742                0xfe, // interupt with activesensing
743                0x77, // Finish channel pressure
744            ],
745            &[
746                MidiMessage::ActiveSensing,
747                MidiMessage::ChannelPressure(6.into(), 0x77.into()),
748            ],
749        );
750    }
751
752    #[test]
753    fn should_parse_reset_message() {
754        MidiParser::new().assert_result(&[0xff], &[MidiMessage::Reset]);
755    }
756
757    #[test]
758    fn should_parse_reset_message_as_realtime() {
759        MidiParser::new().assert_result(
760            &[
761                0xD6, // Start channel pressure event
762                0xff, // interupt with reset
763                0x77, // Finish channel pressure
764            ],
765            &[
766                MidiMessage::Reset,
767                MidiMessage::ChannelPressure(6.into(), 0x77.into()),
768            ],
769        );
770    }
771
772    #[test]
773    fn should_ignore_incomplete_messages() {
774        MidiParser::new().assert_result(
775            &[
776                0x92, 0x1b, // Start note off message
777                0x82, 0x76, 0x34, // continue with a complete note on message
778            ],
779            &[MidiMessage::NoteOff(2.into(), 0x76.into(), 0x34.into())],
780        );
781    }
782
783    impl MidiParser {
784        /// Test helper function, asserts if a slice of bytes parses to some set of midi events
785        fn assert_result(&mut self, bytes: &[u8], expected_events: &[MidiMessage]) {
786            let events: Vec<MidiMessage> = bytes
787                .into_iter()
788                .filter_map(|byte| self.parse(*byte))
789                .collect();
790
791            assert_eq!(expected_events, events.as_slice());
792        }
793    }
794}