use crate::consts;
use crate::note::MidiNote;
use crate::sysex;
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(u8)]
pub enum Channel {
Ch1,
Ch2,
Ch3,
Ch4,
Ch5,
Ch6,
Ch7,
Ch8,
Ch9,
Ch10,
Ch11,
Ch12,
Ch13,
Ch14,
Ch15,
Ch16,
Invalid,
}
impl Channel {
pub fn from_midi_cmd(v: u8) -> Channel {
Self::from(v & consts::MIDI_CHANNEL_MASK)
}
}
impl From<u8> for Channel {
fn from(v: u8) -> Channel {
use Channel::*;
match v {
0 => Ch1,
1 => Ch2,
2 => Ch3,
3 => Ch4,
4 => Ch5,
5 => Ch6,
6 => Ch7,
7 => Ch8,
8 => Ch9,
9 => Ch10,
10 => Ch11,
11 => Ch12,
12 => Ch13,
13 => Ch14,
14 => Ch15,
15 => Ch16,
_ => Invalid,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct KeyEvent {
pub key: MidiNote,
pub value: u8,
}
#[derive(Debug, PartialEq)]
pub struct ControlEvent {
pub control: u8,
pub value: u8,
}
#[derive(Debug, PartialEq)]
pub enum SysExType {
Manufacturer(sysex::ManufacturerId),
NonRealTime(u8, [u8; 2]),
RealTime(u8, [u8; 2]),
}
#[derive(Debug, PartialEq)]
pub struct SysExEvent {
r#type: SysExType,
data: Vec<u8>,
}
impl SysExEvent {
pub fn new_manufacturer(manufacturer: sysex::ManufacturerId, data: &[u8]) -> SysExEvent {
SysExEvent {
r#type: SysExType::Manufacturer(manufacturer),
data: Vec::from(data),
}
}
pub fn new_non_realtime(device: u8, subids: [u8; 2], data: &[u8]) -> SysExEvent {
SysExEvent {
r#type: SysExType::NonRealTime(device, subids),
data: Vec::from(data),
}
}
pub fn new_realtime(device: u8, subids: [u8; 2], data: &[u8]) -> SysExEvent {
SysExEvent {
r#type: SysExType::RealTime(device, subids),
data: Vec::from(data),
}
}
pub fn get_type(&self) -> &SysExType {
&self.r#type
}
pub fn get_data(&self) -> &Vec<u8> {
&self.data
}
}
#[derive(Debug, PartialEq)]
pub enum MidiMessage {
Invalid,
NoteOn(Channel, KeyEvent),
NoteOff(Channel, KeyEvent),
PolyKeyPressure(Channel, KeyEvent),
ControlChange(Channel, ControlEvent),
ProgramChange(Channel, u8),
ChannelPressure(Channel, u8),
PitchBend(Channel, u8, u8),
SysEx(SysExEvent),
}
impl MidiMessage {
pub fn get_channel(&self) -> Channel {
use MidiMessage::*;
match *self {
Invalid | SysEx(_) => Channel::Invalid,
NoteOn(ch, _)
| NoteOff(ch, _)
| PolyKeyPressure(ch, _)
| ControlChange(ch, _)
| ProgramChange(ch, _)
| ChannelPressure(ch, _)
| PitchBend(ch, _, _) => ch,
}
}
fn sysex_message_from(data: &[u8]) -> MidiMessage {
use consts::system_event::sysex::*;
if data[0] != consts::SYSEX {
return MidiMessage::Invalid;
}
let idx;
let manufacturer = data[1];
let r#type = match manufacturer {
NON_REAL_TIME => {
idx = 5;
SysExType::NonRealTime(data[2], [data[3], data[4]])
}
REAL_TIME => {
idx = 5;
SysExType::RealTime(data[2], [data[3], data[4]])
}
_ => {
let (_, d) = data.split_at(1);
let manufacturer = sysex::ManufacturerId::from_raw(d).unwrap();
idx = 1 + manufacturer.raw_len();
SysExType::Manufacturer(manufacturer)
}
};
MidiMessage::SysEx(SysExEvent {
r#type,
data: data.split_at(idx).1.to_vec(),
})
}
}
impl Into<Vec<u8>> for MidiMessage {
fn into(self) -> Vec<u8> {
use MidiMessage::*;
match self {
NoteOff(ch, e) => vec![consts::NOTE_OFF | ch as u8, e.key, e.value],
NoteOn(ch, e) => vec![consts::NOTE_ON | ch as u8, e.key, e.value],
PolyKeyPressure(ch, e) => {
vec![consts::POLYPHONIC_KEY_PRESSURE | ch as u8, e.key, e.value]
}
ControlChange(ch, e) => vec![consts::CONTROL_CHANGE | ch as u8, e.control, e.value],
ProgramChange(ch, p) => vec![consts::PROGRAM_CHANGE | ch as u8, p, 0],
ChannelPressure(ch, p) => vec![consts::CHANNEL_KEY_PRESSURE | ch as u8, p, 0],
PitchBend(ch, lsb, msb) => vec![consts::PITCH_BEND_CHANGE | ch as u8, lsb, msb],
SysEx(ref e) => {
let out_size = match e.r#type {
SysExType::Manufacturer(m) => 1 + m.raw_len() + e.data.len(),
SysExType::NonRealTime(_, _) | SysExType::RealTime(_, _) => 5 + e.data.len(),
};
let mut vec = Vec::with_capacity(out_size);
vec.push(consts::SYSEX);
use SysExType::*;
match e.r#type {
Manufacturer(ref b) => b.push_to(&mut vec),
NonRealTime(d, id) => {
vec.push(consts::system_event::sysex::NON_REAL_TIME);
vec.push(d);
vec.extend_from_slice(&id);
}
RealTime(d, id) => {
vec.push(consts::system_event::sysex::REAL_TIME);
vec.push(d);
vec.extend_from_slice(&id);
}
}
vec.extend_from_slice(&e.data);
vec
}
Invalid => vec![],
}
}
}
impl From<&[u8]> for MidiMessage {
fn from(data: &[u8]) -> MidiMessage {
if data.len() < 3 {
MidiMessage::Invalid
} else {
let (event, channel) = if data[0] < consts::SYSEX {
(
data[0] & consts::EVENT_TYPE_MASK,
data[0] & consts::MIDI_CHANNEL_MASK,
)
} else {
(data[0], 0u8)
};
match event {
consts::NOTE_OFF => {
MidiMessage::NoteOff(
Channel::from(channel),
KeyEvent {
key: data[1],
value: data[2],
},
)
}
consts::NOTE_ON => {
MidiMessage::NoteOn(
Channel::from(channel),
KeyEvent {
key: data[1],
value: data[2],
},
)
}
consts::POLYPHONIC_KEY_PRESSURE => MidiMessage::PolyKeyPressure(
Channel::from(channel),
KeyEvent {
key: data[1],
value: data[2],
},
),
consts::CONTROL_CHANGE => MidiMessage::ControlChange(
Channel::from(channel),
ControlEvent {
control: data[1],
value: data[2],
},
),
consts::PROGRAM_CHANGE => {
MidiMessage::ProgramChange(Channel::from(channel), data[1])
}
consts::CHANNEL_KEY_PRESSURE => {
MidiMessage::ChannelPressure(Channel::from(channel), data[1])
}
consts::PITCH_BEND_CHANGE => {
MidiMessage::PitchBend(Channel::from(channel), data[1], data[2])
}
consts::SYSEX => MidiMessage::sysex_message_from(data),
_ => MidiMessage::Invalid,
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
pub fn test_midi_channel() {
let ch = Channel::from_midi_cmd(128);
assert_eq!(ch, Channel::Ch1);
let ch = Channel::from_midi_cmd(137);
assert_eq!(ch, Channel::Ch10);
}
#[test]
pub fn test_midi_from_to_raw() {
let raw = vec![0u8];
let msg = MidiMessage::Invalid;
assert_eq!(MidiMessage::from(raw.as_slice()), msg);
let out: Vec<u8> = msg.into();
assert!(out.len() == 0);
let raw = vec![144u8, 59u8, 88u8];
let msg = MidiMessage::NoteOn(Channel::Ch1, KeyEvent { key: 59, value: 88 });
assert_eq!(MidiMessage::from(raw.as_slice()), msg);
let out: Vec<u8> = msg.into();
assert_eq!(out, raw);
let raw = vec![128u8, 60u8, 0u8];
let msg = MidiMessage::NoteOff(Channel::Ch1, KeyEvent { key: 60, value: 0 });
assert_eq!(MidiMessage::from(raw.as_slice()), msg);
let out: Vec<u8> = msg.into();
assert_eq!(out, raw);
let raw = vec![176, 114, 65];
let msg = MidiMessage::ControlChange(
Channel::Ch1,
ControlEvent {
control: 114,
value: 65,
},
);
assert_eq!(MidiMessage::from(raw.as_slice()), msg);
let out: Vec<u8> = msg.into();
assert_eq!(out, raw);
let raw = vec![224, 0, 76];
let msg = MidiMessage::PitchBend(Channel::Ch1, 0, 76);
assert_eq!(MidiMessage::from(raw.as_slice()), msg);
let out: Vec<u8> = msg.into();
assert_eq!(out, raw);
let raw = vec![240, 30, 127, 66, 2, 0, 0, 16, 127, 247];
let msg = MidiMessage::SysEx(SysExEvent::new_manufacturer(
sysex::ManufacturerId::Id(30),
&[127, 66, 2, 0, 0, 16, 127, 247],
));
assert_eq!(MidiMessage::from(raw.as_slice()), msg);
let out: Vec<u8> = msg.into();
assert_eq!(out, raw);
let raw = vec![240, 0, 32, 107, 127, 66, 2, 0, 0, 16, 127, 247];
let msg = MidiMessage::SysEx(SysExEvent::new_manufacturer(
sysex::ManufacturerId::ExtId(32, 107),
&[127, 66, 2, 0, 0, 16, 127, 247],
));
assert_eq!(MidiMessage::from(raw.as_slice()), msg);
let out: Vec<u8> = msg.into();
assert_eq!(out, raw);
let raw = vec![240, 126, 0, 6, 2, 0, 32, 107, 2, 0, 4, 2, 67, 7, 0, 1, 247];
let msg = MidiMessage::SysEx(SysExEvent::new_non_realtime(
0,
[6, 2],
&[0, 32, 107, 2, 0, 4, 2, 67, 7, 0, 1, 247],
));
assert_eq!(MidiMessage::from(raw.as_slice()), msg);
let out: Vec<u8> = msg.into();
assert_eq!(out, raw);
}
}