1use 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))]
8pub enum MidiParseError {
10 BufferTooShort,
12
13 MessageNotFound,
15}
16
17pub trait MidiTryParseSlice: Sized {
19 fn try_parse_slice(buf: &[u8]) -> Result<Self, MidiParseError>;
21}
22
23#[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
61fn is_status_byte(byte: u8) -> bool {
63 byte & 0x80 == 0x80
64}
65
66fn is_system_message(byte: u8) -> bool {
68 byte & 0xf0 == 0xf0
69}
70
71fn split_message_and_channel(byte: u8) -> (u8, Channel) {
73 (byte & 0xf0u8, (byte & 0x0fu8).into())
74}
75
76impl MidiParser {
80 pub fn new() -> Self {
82 Self::default()
83 }
84
85 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 0xf0 => {
94 self.state = MidiParserState::Idle;
96 None
97 }
98 0xf1 => {
99 self.state = MidiParserState::QuarterFrameRecvd;
101 None
102 }
103 0xf2 => {
104 self.state = MidiParserState::SongPositionRecvd;
106 None
107 }
108 0xf3 => {
109 self.state = MidiParserState::SongSelectRecvd;
111 None
112 }
113 0xf6 => {
114 self.state = MidiParserState::Idle;
116 Some(MidiMessage::TuneRequest)
117 }
118 0xf7 => {
119 self.state = MidiParserState::Idle;
121 None
122 }
124
125 0xf8 => Some(MidiMessage::TimingClock),
127 0xf9 => None, 0xfa => Some(MidiMessage::Start),
129 0xfb => Some(MidiMessage::Continue),
130 0xfc => Some(MidiMessage::Stop),
131 0xfd => None, 0xfe => Some(MidiMessage::ActiveSensing),
133 0xff => Some(MidiMessage::Reset),
134
135 _ => {
136 self.state = MidiParserState::Idle;
138 None
139 }
140 }
141 } else {
142 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
264fn 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
277impl 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 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 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 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, 0x33, 0x65, ],
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, 0x33, 0x65, ],
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, 0x14, 0x56, ],
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, 0x43, 0x01, ],
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, 0x01, ],
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, 0x43, ],
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, 0x43, 0x01, ],
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, 0x56, ],
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, 0x23, 0x7b, ],
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, 0x00, ],
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, 0xf6, 0x34, ],
619 &[MidiMessage::TuneRequest],
620 );
621 }
622
623 #[test]
641 fn should_interrupt_parsing_for_undefined_message() {
642 MidiParser::new().assert_result(
643 &[
644 0x92, 0x76, 0xf5, 0x34, ],
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, 0xf8, 0x77, ],
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, 0xfa, 0x77, ],
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, 0xfb, 0x77, ],
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, 0xfc, 0x77, ],
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, 0xfe, 0x77, ],
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, 0xff, 0x77, ],
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, 0x82, 0x76, 0x34, ],
779 &[MidiMessage::NoteOff(2.into(), 0x76.into(), 0x34.into())],
780 );
781 }
782
783 impl MidiParser {
784 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}