use crate::error::{Error, ParseError};
use std::io::{Read, Write};
pub const SYSEX_START: u8 = 0xf0;
const SYSEX_END: u8 = 0xf7;
const CLAVIA_ID: u8 = 0x33;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Family {
Lead2Family,
Lead3,
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Sysex {
pub data: Vec<u8>,
}
impl Sysex {
pub fn read_from(reader: &mut impl Read) -> Result<Sysex, Error> {
let mut data = Vec::new();
reader.read_to_end(&mut data)?;
if data.first() != Some(&SYSEX_START) {
return Err(ParseError::UnknownFileType("not sysex".to_string()).into());
}
Ok(Sysex { data })
}
pub fn write_to(&self, writer: &mut impl Write) -> Result<(), Error> {
writer.write_all(&self.data)?;
Ok(())
}
pub fn family(&self) -> Family {
match self.data.as_slice() {
[SYSEX_START, CLAVIA_ID, _, 0x04, ..] => Family::Lead2Family,
[SYSEX_START, CLAVIA_ID, _, 0x09, ..] => Family::Lead3,
_ => Family::Unknown,
}
}
pub fn messages(&self) -> impl Iterator<Item = &[u8]> {
self.data
.split_inclusive(|&b| b == SYSEX_END)
.filter(|m| !m.is_empty())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_two_lead_envelopes_classify() {
let lead2 = Sysex {
data: vec![0xf0, 0x33, 0x0f, 0x04, 0x00, 0xf7],
};
assert_eq!(lead2.family(), Family::Lead2Family);
let lead3 = Sysex {
data: vec![0xf0, 0x33, 0x7f, 0x09, 0x00, 0xf7],
};
assert_eq!(lead3.family(), Family::Lead3);
assert_eq!(lead3.messages().count(), 1);
}
#[test]
fn messages_split_on_the_end_byte() {
let two = Sysex {
data: vec![0xf0, 0x33, 0xf7, 0xf0, 0x44, 0xf7],
};
let m: Vec<_> = two.messages().collect();
assert_eq!(m, [&[0xf0, 0x33, 0xf7][..], &[0xf0, 0x44, 0xf7][..]]);
}
}