use crate::prelude::*;
use std::io::ErrorKind;
pub trait FromLiveEventBytes {
const MIN_STATUS_BYTE: u8;
const MAX_STATUS_BYTE: u8;
fn from_bytes(bytes: &[u8]) -> Result<Self, std::io::Error>
where
Self: Sized,
{
if bytes.is_empty() {
return Err(io_error!(
ErrorKind::InvalidInput,
"Invalid live event (no byte data!)"
));
}
let (status, data) = bytes.split_at(1);
let status = status[0];
if !(Self::MIN_STATUS_BYTE..=Self::MAX_STATUS_BYTE).contains(&status) {
return Err(io_error!(
ErrorKind::InvalidData,
"Invalid status message for type!)"
));
}
Self::from_status_and_data(status, data)
}
fn from_status_and_data(status: u8, data: &[u8]) -> Result<Self, std::io::Error>
where
Self: Sized;
}
#[doc = r"
An emittable message to/from a streaming MIDI device.
There is currently no `StreamReader` type, so this type is most often manually constructed.
"]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum LiveEvent<'a> {
ChannelVoice(ChannelVoiceMessage),
SysCommon(SystemCommonMessage<'a>),
SysRealTime(SystemRealTimeMessage),
}
impl LiveEvent<'_> {
pub fn channel_voice(&self) -> Option<&ChannelVoiceMessage> {
match self {
LiveEvent::ChannelVoice(c) => Some(c),
_ => None,
}
}
pub fn to_bytes(&self) -> Vec<u8> {
match self {
LiveEvent::ChannelVoice(c) => c.to_bytes(),
LiveEvent::SysCommon(s) => s.to_bytes(),
LiveEvent::SysRealTime(r) => vec![r.byte()],
}
}
}
impl From<ChannelVoiceMessage> for LiveEvent<'_> {
fn from(value: ChannelVoiceMessage) -> Self {
Self::ChannelVoice(value)
}
}
impl<'a> From<SystemCommonMessage<'a>> for LiveEvent<'a> {
fn from(value: SystemCommonMessage<'a>) -> Self {
Self::SysCommon(value)
}
}
impl<'a> From<SystemExclusiveMessage<'a>> for LiveEvent<'a> {
fn from(value: SystemExclusiveMessage<'a>) -> Self {
Self::SysCommon(SystemCommonMessage::SystemExclusive(value))
}
}
impl From<SystemRealTimeMessage> for LiveEvent<'_> {
fn from(value: SystemRealTimeMessage) -> Self {
Self::SysRealTime(value)
}
}
impl FromLiveEventBytes for LiveEvent<'_> {
const MIN_STATUS_BYTE: u8 = 0x80;
const MAX_STATUS_BYTE: u8 = 0xFF;
fn from_status_and_data(status: u8, data: &[u8]) -> Result<Self, std::io::Error>
where
Self: Sized,
{
match status {
0x80..=0xEF => Ok(Self::ChannelVoice(
ChannelVoiceMessage::from_status_and_data(status, data)?,
)),
0xF0..=0xF7 => Ok(Self::SysCommon(SystemCommonMessage::from_status_and_data(
status, data,
)?)),
0xF8..=0xFF => Ok(Self::SysRealTime(
SystemRealTimeMessage::from_status_and_data(status, data)?,
)),
_ => Err(io_error!(
ErrorKind::InvalidData,
"Received a status that is not a midi message"
)),
}
}
}
#[test]
fn parse_note_on() {
use crate::prelude::*;
let message = [0b1001_0001, 0b0100_1000, 0b001_00001];
let parsed = LiveEvent::from_bytes(&message).unwrap();
assert_eq!(
parsed,
LiveEvent::ChannelVoice(ChannelVoiceMessage::new(
Channel::Two,
VoiceEvent::NoteOn {
key: Key::from_databyte(72).unwrap(),
velocity: Velocity::new(33).unwrap()
}
))
);
}