use crate::consts::system_event;
use crate::message::{SysExEvent, SysExType};
use crate::MidiMessage;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ManufacturerId {
Id(u8),
ExtId(u8, u8),
}
impl ManufacturerId {
pub fn from_raw(raw: &[u8]) -> Option<ManufacturerId> {
if raw[0] != 0 {
Some(ManufacturerId::Id(raw[0]))
} else if raw.len() < 3 {
None
} else {
Some(ManufacturerId::ExtId(raw[1], raw[2]))
}
}
pub fn raw_len(self) -> usize {
match self {
ManufacturerId::Id(_) => 1,
ManufacturerId::ExtId(_, _) => 3,
}
}
pub fn push_to(self, vec: &mut Vec<u8>) {
match self {
ManufacturerId::Id(m) => vec.push(m),
ManufacturerId::ExtId(m1, m2) => {
vec.push(system_event::sysex::ID_EXTENSION);
vec.push(m1);
vec.push(m2);
}
}
}
}
pub struct SysExDecoder<'a>(&'a SysExEvent);
impl SysExDecoder<'_> {
pub fn decode(msg: &MidiMessage) -> Option<SysExDecoder> {
if let MidiMessage::SysEx(ref e) = msg {
Some(SysExDecoder(e))
} else {
None
}
}
pub fn is_universal_sysex(&self) -> bool {
match self.0.get_type() {
SysExType::NonRealTime(_, _) | SysExType::RealTime(_, _) => true,
_ => false,
}
}
}
pub struct USysExDecoder<'a>(&'a SysExEvent);
impl USysExDecoder<'_> {
pub fn decode(msg: &MidiMessage) -> Option<USysExDecoder> {
if let MidiMessage::SysEx(ref e) = msg {
match e.get_type() {
SysExType::NonRealTime(_, _) | SysExType::RealTime(_, _) => {
return Some(USysExDecoder(e))
}
_ => {}
}
}
None
}
pub fn is_non_realtime(&self) -> bool {
if let SysExType::NonRealTime(_, _) = self.0.get_type() {
true
} else {
false
}
}
pub fn target_device(&self) -> u8 {
match self.0.get_type() {
SysExType::NonRealTime(d, _) | SysExType::RealTime(d, _) => *d,
_ => unreachable!(),
}
}
pub fn subid(&self) -> [u8; 2] {
match self.0.get_type() {
SysExType::NonRealTime(_, subid) | SysExType::RealTime(_, subid) => *subid,
_ => unreachable!(),
}
}
pub fn general_info_reply_manufacturer_id(&self) -> Option<ManufacturerId> {
if self.is_non_realtime() && self.subid() == [6, 2] {
let data = self.0.get_data();
if data.len() >= 8 {
return ManufacturerId::from_raw(data);
}
}
None
}
pub fn general_info_reply_family(&self) -> Option<([u8; 2], [u8; 2])> {
if self.is_non_realtime() && self.subid() == [6, 2] {
let data = self.0.get_data();
if data.len() >= 8 {
return Some(([data[3], data[4]], [data[5], data[6]]));
}
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
pub fn test_sysex_decoder() {
let msg = MidiMessage::SysEx(SysExEvent::new_non_realtime(
0,
[6, 2],
&[0, 32, 107, 2, 0, 4, 2, 67, 7, 0, 1, 247],
));
let decoder = SysExDecoder::decode(&msg);
assert!(decoder.is_some());
let decoder = decoder.unwrap();
assert!(decoder.is_universal_sysex());
let decoder = USysExDecoder::decode(&msg);
assert!(decoder.is_some());
let decoder = decoder.unwrap();
assert!(decoder.is_non_realtime());
assert_eq!(decoder.target_device(), 0);
assert_eq!(decoder.subid(), [6, 2]);
assert_eq!(
decoder.general_info_reply_manufacturer_id(),
Some(ManufacturerId::ExtId(32, 107))
);
assert_eq!(decoder.general_info_reply_family(), Some(([2, 0], [4, 2])));
}
}