#![doc = include_str!("../README.md")]
#![cfg_attr(not(test), no_std)]
#[derive(Debug)]
pub struct MidiStreamParser<const SYSEX_MAX_LEN: usize> {
message: [u8; 3],
message_length: usize,
realtime_message: [u8; 1],
sysex_running: bool,
sysex_message: [u8; SYSEX_MAX_LEN],
sysex_message_length: usize,
}
#[derive(Debug)]
pub enum ParserError {
InvalidStatus,
SysexOverflow,
}
impl<const SYSEX_MAX_LEN: usize> Default for MidiStreamParser<SYSEX_MAX_LEN> {
fn default() -> Self {
Self::new()
}
}
impl<const SYSEX_MAX_LEN: usize> MidiStreamParser<SYSEX_MAX_LEN> {
pub fn new() -> Self {
Self {
message: [0; 3],
message_length: 0,
realtime_message: [0; 1],
sysex_running: false,
sysex_message: [0; SYSEX_MAX_LEN],
sysex_message_length: 0,
}
}
pub fn parse(&mut self, byte: u8) -> Result<Option<&[u8]>, ParserError> {
match byte {
0x00..=0x7F => {
if self.sysex_running {
if self.sysex_message_length >= SYSEX_MAX_LEN {
return Err(ParserError::SysexOverflow);
}
self.sysex_message[self.sysex_message_length] = byte;
self.sysex_message_length += 1;
} else {
if self.message_length == 0 {
return Err(ParserError::InvalidStatus);
}
self.message[self.message_length] = byte;
self.message_length += 1;
if self.message_length == 3 {
self.message_length = 1;
return Ok(Some(&self.message));
} else if matches!(self.message[0] & 0xF0, 0xC0 | 0xD0)
|| matches!(self.message[0], 0xF1 | 0xF3)
{
self.message_length = 1;
return Ok(Some(&self.message[0..2]));
}
}
}
0x80..=0xEF => {
self.message[0] = byte;
self.message_length = 1;
}
0xF0..=0xF7 => {
match byte {
0xF0 => {
self.message[0] = 0;
self.message_length = 0;
self.sysex_running = true;
self.sysex_message[0] = byte;
self.sysex_message_length = 1;
}
0xF7 => {
self.sysex_running = false;
if self.sysex_message_length >= SYSEX_MAX_LEN {
return Err(ParserError::SysexOverflow);
}
self.sysex_message[self.sysex_message_length] = byte;
self.sysex_message_length += 1;
return Ok(Some(&self.sysex_message[0..self.sysex_message_length]));
}
_ => {
self.message[0] = byte;
self.message_length = 1;
}
}
}
0xF8..=0xFF => {
self.realtime_message[0] = byte;
return Ok(Some(&self.realtime_message));
}
}
Ok(None)
}
}
#[cfg(test)]
mod tests;