pub const NOTE_OFF: u8 = 0b1000;
pub const NOTE_ON: u8 = 0b1001;
pub const POLYPHONIC_KEY_PRESSURE: u8 = 0b1010;
pub const CONTROL_CHANGE: u8 = 0b1011;
pub const PROGRAM_CHANGE: u8 = 0b1100;
pub const CHANNEL_PRESSURE: u8 = 0b1101;
pub const PITCH_BEND_CHANGE: u8 = 0b1110;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ChannelMessage {
channel: u8,
message_type: ChannelMessageType,
}
impl ChannelMessage {
pub fn from_raw_message(message: &[u8]) -> Option<ChannelMessage> {
let status_byte = *message.first()?;
let channel = status_byte & 0b0000_1111;
let action = status_byte >> 4;
let message_type = match action {
NOTE_OFF => ChannelMessageType::NoteOff {
key: *message.get(1)?,
velocity: *message.get(2)?,
},
NOTE_ON => ChannelMessageType::NoteOn {
key: *message.get(1)?,
velocity: *message.get(2)?,
},
POLYPHONIC_KEY_PRESSURE => ChannelMessageType::PolyphonicKeyPressure {
key: *message.get(1)?,
pressure: *message.get(2)?,
},
CONTROL_CHANGE => ChannelMessageType::ControlChange {
controller: *message.get(1)?,
value: *message.get(2)?,
},
PROGRAM_CHANGE => ChannelMessageType::ProgramChange {
program: *message.get(1)?,
},
CHANNEL_PRESSURE => ChannelMessageType::ChannelPressure {
pressure: *message.get(1)?,
},
PITCH_BEND_CHANGE => ChannelMessageType::PitchBendChange {
value: i16::from(*message.get(1)?) + i16::from(*message.get(2)?) * 128 - 8192,
},
_ => return None,
};
Some(ChannelMessage {
channel,
message_type,
})
}
#[allow(clippy::wrong_self_convention)] pub fn to_raw_message(&self) -> [u8; 3] {
match self.message_type {
ChannelMessageType::NoteOff { key, velocity } => {
channel_message(NOTE_OFF, self.channel, key, velocity)
}
ChannelMessageType::NoteOn { key, velocity } => {
channel_message(NOTE_ON, self.channel, key, velocity)
}
ChannelMessageType::PolyphonicKeyPressure { key, pressure } => {
channel_message(POLYPHONIC_KEY_PRESSURE, self.channel, key, pressure)
}
ChannelMessageType::ControlChange { controller, value } => {
channel_message(CONTROL_CHANGE, self.channel, controller, value)
}
ChannelMessageType::ProgramChange { program } => {
channel_message(PROGRAM_CHANGE, self.channel, program, 0)
}
ChannelMessageType::ChannelPressure { pressure } => {
channel_message(CHANNEL_PRESSURE, self.channel, pressure, 0)
}
ChannelMessageType::PitchBendChange { value } => channel_message(
PITCH_BEND_CHANGE,
self.channel,
(value.rem_euclid(128)) as u8,
(value.div_euclid(128) + 64) as u8,
),
}
}
pub fn channel(&self) -> u8 {
self.channel
}
pub fn message_type(&self) -> ChannelMessageType {
self.message_type
}
}
fn channel_message(prefix: u8, channel: u8, payload1: u8, payload2: u8) -> [u8; 3] {
[prefix << 4 | channel, payload1, payload2]
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ChannelMessageType {
NoteOff { key: u8, velocity: u8 },
NoteOn { key: u8, velocity: u8 },
PolyphonicKeyPressure { key: u8, pressure: u8 },
ControlChange { controller: u8, value: u8 },
ProgramChange { program: u8 },
ChannelPressure { pressure: u8 },
PitchBendChange { value: i16 },
}
impl ChannelMessageType {
pub fn in_channel(self, channel: u8) -> Option<ChannelMessage> {
match channel < 16 {
true => Some(ChannelMessage {
channel,
message_type: self,
}),
false => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_note_off() {
let message = ChannelMessage::from_raw_message(&[0b1000_0111, 88, 99]).unwrap();
assert_eq!(
message,
ChannelMessage {
channel: 7,
message_type: ChannelMessageType::NoteOff {
key: 88,
velocity: 99
}
}
);
}
#[test]
fn parse_note_on() {
let message = ChannelMessage::from_raw_message(&[0b1001_1000, 77, 88]).unwrap();
assert_eq!(
message,
ChannelMessage {
channel: 8,
message_type: ChannelMessageType::NoteOn {
key: 77,
velocity: 88
}
}
);
}
#[test]
fn parse_polyphonic_key_pressure() {
let message = ChannelMessage::from_raw_message(&[0b1010_1001, 66, 77]).unwrap();
assert_eq!(
message,
ChannelMessage {
channel: 9,
message_type: ChannelMessageType::PolyphonicKeyPressure {
key: 66,
pressure: 77
}
}
);
}
#[test]
fn parse_control_change() {
let message = ChannelMessage::from_raw_message(&[0b1011_1010, 55, 66]).unwrap();
assert_eq!(
message,
ChannelMessage {
channel: 10,
message_type: ChannelMessageType::ControlChange {
controller: 55,
value: 66
}
}
);
}
#[test]
fn parse_program_change() {
let message = ChannelMessage::from_raw_message(&[0b1100_1011, 44]).unwrap();
assert_eq!(
message,
ChannelMessage {
channel: 11,
message_type: ChannelMessageType::ProgramChange { program: 44 }
}
);
}
#[test]
fn parse_channel_pressure() {
let message = ChannelMessage::from_raw_message(&[0b1101_1100, 33]).unwrap();
assert_eq!(
message,
ChannelMessage {
channel: 12,
message_type: ChannelMessageType::ChannelPressure { pressure: 33 }
}
);
}
#[test]
fn parse_pitch_bend_change() {
let message = ChannelMessage::from_raw_message(&[0b1110_1101, 22, 33]).unwrap();
assert_eq!(
message,
ChannelMessage {
channel: 13,
message_type: ChannelMessageType::PitchBendChange { value: -3946 }
}
);
}
#[test]
fn serialize_note_off() {
let message = ChannelMessage {
channel: 7,
message_type: ChannelMessageType::NoteOff {
key: 88,
velocity: 99,
},
};
assert_eq!(message.to_raw_message(), [0b1000_0111, 88, 99]);
}
#[test]
fn serialize_note_on() {
let message = ChannelMessage {
channel: 8,
message_type: ChannelMessageType::NoteOn {
key: 77,
velocity: 88,
},
};
assert_eq!(message.to_raw_message(), [0b1001_1000, 77, 88]);
}
#[test]
fn serialize_polyphonic_key_pressure() {
let message = ChannelMessage {
channel: 9,
message_type: ChannelMessageType::PolyphonicKeyPressure {
key: 66,
pressure: 77,
},
};
assert_eq!(message.to_raw_message(), [0b1010_1001, 66, 77]);
}
#[test]
fn serialize_control_change() {
let message = ChannelMessage {
channel: 10,
message_type: ChannelMessageType::ControlChange {
controller: 55,
value: 66,
},
};
assert_eq!(message.to_raw_message(), [0b1011_1010, 55, 66]);
}
#[test]
fn serialize_program_change() {
let message = ChannelMessage {
channel: 11,
message_type: ChannelMessageType::ProgramChange { program: 44 },
};
assert_eq!(message.to_raw_message(), [0b1100_1011, 44, 0]);
}
#[test]
fn serialize_channel_pressure() {
let message = ChannelMessage {
channel: 12,
message_type: ChannelMessageType::ChannelPressure { pressure: 33 },
};
assert_eq!(message.to_raw_message(), [0b1101_1100, 33, 0]);
}
#[test]
fn serialize_pitch_bend_change() {
let message = ChannelMessage {
channel: 13,
message_type: ChannelMessageType::PitchBendChange { value: -3946 },
};
assert_eq!(message.to_raw_message(), [0b1110_1101, 22, 33]);
}
}