use io::ErrorKind;
use crate::prelude::*;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct ChannelVoiceMessage {
status: StatusByte,
message: VoiceEvent,
}
impl ChannelVoiceMessage {
pub fn new(channel: Channel, message: VoiceEvent) -> Self {
let status = channel.to_byte() | (message.status_nibble() << 4);
Self {
status: StatusByte::new_unchecked(status),
message,
}
}
pub(crate) fn read<'a, R>(status: StatusByte, reader: &mut Reader<R>) -> ReadResult<Self>
where
R: MidiSource<'a>,
{
let msg = match status.byte() >> 4 {
0x8 => VoiceEvent::NoteOff {
key: Key::from_databyte(reader.read_next()?)?,
velocity: Velocity::new(reader.read_next()?)?,
},
0x9 => {
let key = reader.read_next()?;
let velocity = reader.read_next()?;
VoiceEvent::NoteOn {
key: Key::from_databyte(key)?,
velocity: Velocity::new(velocity)?,
}
}
0xA => VoiceEvent::Aftertouch {
key: Key::from_databyte(reader.read_next()?)?,
velocity: Velocity::new(reader.read_next()?)?,
},
0xB => VoiceEvent::ControlChange {
controller: Controller::new(reader.read_next()?)?,
value: reader.read_next()?.try_into()?,
},
0xC => VoiceEvent::ProgramChange {
program: Program::new(reader.read_next()?)?,
},
0xD => VoiceEvent::ChannelPressureAfterTouch {
velocity: Velocity::new(reader.read_next()?)?,
},
0xE => {
let b = reader.read_exact(2)?;
let lsb = b[0];
let msb = b[1];
VoiceEvent::PitchBend(PitchBend::new_unchecked(lsb, msb))
}
b => {
return Err(inv_data(
reader,
format!("Invalid status byte for message: {}", b),
));
}
};
Ok(ChannelVoiceMessage {
status,
message: msg,
})
}
pub fn channel(&self) -> Channel {
Channel::from_status(self.status.byte())
}
pub fn is_note_on(&self) -> bool {
self.message.is_note_on()
}
pub fn is_note_off(&self) -> bool {
self.message.is_note_off()
}
pub fn key(&self) -> Option<&Key> {
match &self.message {
VoiceEvent::NoteOn { key, .. }
| VoiceEvent::NoteOff { key, .. }
| VoiceEvent::Aftertouch { key, .. } => Some(key),
_ => None,
}
}
pub fn velocity(&self) -> Option<&Velocity> {
match &self.message {
VoiceEvent::NoteOn { velocity, .. }
| VoiceEvent::NoteOff { velocity, .. }
| VoiceEvent::Aftertouch { velocity, .. }
| VoiceEvent::ChannelPressureAfterTouch { velocity } => Some(velocity),
_ => None,
}
}
pub fn data_1_byte(&self) -> u8 {
use VoiceEvent as V;
match &self.message {
V::NoteOn { key, .. } | V::NoteOff { key, .. } | V::Aftertouch { key, .. } => {
key.byte()
}
V::ControlChange { controller, .. } => controller.byte(),
V::ProgramChange { program } => program.byte(),
V::ChannelPressureAfterTouch { velocity } => velocity.byte(),
V::PitchBend(p) => p.lsb(),
}
}
pub fn data_2_byte(&self) -> Option<u8> {
match &self.message {
VoiceEvent::NoteOn { velocity, .. }
| VoiceEvent::NoteOff { velocity, .. }
| VoiceEvent::Aftertouch { velocity, .. }
| VoiceEvent::ChannelPressureAfterTouch { velocity } => Some(velocity.byte()),
VoiceEvent::ControlChange { value, .. } => Some(value.0),
VoiceEvent::PitchBend(p) => Some(p.msb()),
_ => None,
}
}
pub fn status(&self) -> u8 {
self.status.byte()
}
pub fn event(&self) -> &VoiceEvent {
&self.message
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut packet = Vec::with_capacity(3);
packet.push(self.status());
packet.extend(self.message.to_raw());
packet
}
}
impl FromLiveEventBytes for ChannelVoiceMessage {
const MIN_STATUS_BYTE: u8 = 0x80;
const MAX_STATUS_BYTE: u8 = 0xEF;
fn from_status_and_data(status: u8, data: &[u8]) -> Result<Self, std::io::Error>
where
Self: Sized,
{
let msg = match status >> 4 {
0x8 => VoiceEvent::NoteOff {
key: Key::from_databyte(
data.get_byte(0)
.ok_or(io::Error::new(ErrorKind::InvalidData, "byte not found"))?,
)?,
velocity: Velocity::new(
data.get_byte(1)
.ok_or(io::Error::new(ErrorKind::InvalidData, "byte not found"))?,
)?,
},
0x9 => VoiceEvent::NoteOn {
key: Key::from_databyte(
data.get_byte(0)
.ok_or(io::Error::new(ErrorKind::InvalidData, "byte not found"))?,
)?,
velocity: Velocity::new(
data.get_byte(1)
.ok_or(io::Error::new(ErrorKind::InvalidData, "byte not found"))?,
)?,
},
0xA => VoiceEvent::Aftertouch {
key: Key::from_databyte(
data.get_byte(0)
.ok_or(io::Error::new(ErrorKind::InvalidData, "byte not found"))?,
)?,
velocity: Velocity::new(
data.get_byte(1)
.ok_or(io::Error::new(ErrorKind::InvalidData, "byte not found"))?,
)?,
},
0xB => VoiceEvent::ControlChange {
controller: Controller::new(
data.get_byte(0)
.ok_or(io::Error::new(ErrorKind::InvalidData, "byte not found"))?,
)?,
value: (data
.get_byte(1)
.ok_or(io::Error::new(ErrorKind::InvalidData, "byte not found"))?)
.try_into()?,
},
0xC => VoiceEvent::ProgramChange {
program: Program::new(
data.get_byte(0)
.ok_or(io::Error::new(ErrorKind::InvalidData, "byte not found"))?,
)?,
},
0xD => VoiceEvent::ChannelPressureAfterTouch {
velocity: Velocity::new(
data.get_byte(0)
.ok_or(io::Error::new(ErrorKind::InvalidData, "byte not found"))?,
)?,
},
0xE => {
let lsb = data
.get_byte(0)
.ok_or(io::Error::new(ErrorKind::InvalidData, "byte not found"))?;
let msb = data
.get_byte(1)
.ok_or(io::Error::new(ErrorKind::InvalidData, "byte not found"))?;
VoiceEvent::PitchBend(PitchBend::new(lsb, msb)?)
}
_ => panic!("parsed midi message before checking that status is in range"),
};
Ok(ChannelVoiceMessage {
status: status.try_into()?,
message: msg,
})
}
}