use super::util::*;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ChannelVoiceMsg {
NoteOn {
note: u8,
velocity: u8,
},
NoteOff {
note: u8,
velocity: u8,
},
ControlChange { control: ControlChange },
HighResNoteOn { note: u8, velocity: u16 },
HighResNoteOff { note: u8, velocity: u16 },
PolyPressure {
note: u8,
pressure: u8,
},
ChannelPressure { pressure: u8 },
ProgramChange { program: u8 },
PitchBend { bend: u16 },
}
impl ChannelVoiceMsg {
pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
match self {
ChannelVoiceMsg::NoteOff { .. } => v.push(0x80),
ChannelVoiceMsg::NoteOn { .. } => v.push(0x90),
ChannelVoiceMsg::HighResNoteOff { .. } => v.push(0x80),
ChannelVoiceMsg::HighResNoteOn { .. } => v.push(0x90),
ChannelVoiceMsg::PolyPressure { .. } => v.push(0xA0),
ChannelVoiceMsg::ControlChange { .. } => v.push(0xB0),
ChannelVoiceMsg::ProgramChange { .. } => v.push(0xC0),
ChannelVoiceMsg::ChannelPressure { .. } => v.push(0xD0),
ChannelVoiceMsg::PitchBend { .. } => v.push(0xE0),
}
self.extend_midi_running(v);
}
pub(crate) fn extend_midi_running(&self, v: &mut Vec<u8>) {
match *self {
ChannelVoiceMsg::NoteOff { note, velocity } => {
v.push(to_u7(note));
v.push(to_u7(velocity));
}
ChannelVoiceMsg::NoteOn { note, velocity } => {
v.push(to_u7(note));
v.push(to_u7(velocity));
}
ChannelVoiceMsg::HighResNoteOff { note, velocity } => {
let [msb, lsb] = to_u14(velocity);
push_u7(note, v);
v.push(msb);
v.push(0xB0);
v.push(0x58);
v.push(lsb);
}
ChannelVoiceMsg::HighResNoteOn { note, velocity } => {
let [msb, lsb] = to_u14(velocity);
push_u7(note, v);
v.push(msb);
v.push(0xB0);
v.push(0x58);
v.push(lsb);
}
ChannelVoiceMsg::PolyPressure { note, pressure } => {
v.push(to_u7(note));
v.push(to_u7(pressure));
}
ChannelVoiceMsg::ControlChange { control } => control.extend_midi_running(v),
ChannelVoiceMsg::ProgramChange { program } => v.push(to_u7(program)),
ChannelVoiceMsg::ChannelPressure { pressure } => v.push(to_u7(pressure)),
ChannelVoiceMsg::PitchBend { bend } => {
push_u14(bend, v);
}
}
}
pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), &str> {
Err("TODO: not implemented")
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ControlNumber {
BankSelect = 0,
BankSelectLSB = 32,
ModWheel = 1,
ModWheelLSB = 33,
Breath = 2,
BreathLSB = 34,
Foot = 4,
FootLSB = 36,
Portamento = 5,
PortamentoLSB = 37,
DataEntry = 6,
DataEntryLSB = 38,
Volume = 7,
VolumeLSB = 39,
Balance = 8,
BalanceLSB = 40,
Pan = 10,
PanLSB = 42,
Expression = 11,
ExpressionLSB = 43,
Effect1 = 12,
Effect1LSB = 44,
Effect2 = 13,
Effect2LSB = 45,
GeneralPurpose1 = 16,
GeneralPurpose1LSB = 48,
GeneralPurpose2 = 17,
GeneralPurpose2LSB = 49,
GeneralPurpose3 = 18,
GeneralPurpose3LSB = 50,
GeneralPurpose4 = 19,
GeneralPurpose4LSB = 51,
Hold = 64,
TogglePortamento = 65,
Sostenuto = 66,
SoftPedal = 67,
ToggleLegato = 68,
Hold2 = 69,
SoundControl1 = 70,
SoundControl2 = 71,
SoundControl3 = 72,
SoundControl4 = 73,
SoundControl5 = 74,
SoundControl6 = 75,
SoundControl7 = 76,
SoundControl8 = 77,
SoundControl9 = 78,
SoundControl10 = 79,
GeneralPurpose5 = 80,
GeneralPurpose6 = 81,
GeneralPurpose7 = 82,
GeneralPurpose8 = 83,
PortamentoControl = 84,
HighResVelocity = 88,
Effects1Depth = 91,
Effects2Depth = 92,
Effects3Depth = 93,
Effects4Depth = 94,
Effects5Depth = 95,
DataIncrement = 96,
DataDecrement = 97,
NonRegisteredParameterLSB = 98,
NonRegisteredParameter = 99,
RegisteredParameterLSB = 100,
RegisteredParameter = 101,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ControlChange {
BankSelect(u16),
ModWheel(u16),
Breath(u16),
Undefined {
control: u8,
value: u8,
},
UndefinedHighRes {
control1: u8,
control2: u8,
value: u16,
},
Foot(u16),
Portamento(u16),
Volume(u16),
Balance(u16),
Pan(u16),
Expression(u16),
Effect1(u16),
Effect2(u16),
GeneralPurpose1(u16),
GeneralPurpose2(u16),
GeneralPurpose3(u16),
GeneralPurpose4(u16),
GeneralPurpose5(u8),
GeneralPurpose6(u8),
GeneralPurpose7(u8),
GeneralPurpose8(u8),
Hold(u8),
Hold2(u8),
TogglePortamento(bool),
Sostenuto(u8),
SoftPedal(u8),
ToggleLegato(bool),
SoundVariation(u8),
Timbre(u8),
ReleaseTime(u8),
AttackTime(u8),
Brightness(u8),
DecayTime(u8),
VibratoRate(u8),
VibratoDepth(u8),
VibratoDelay(u8),
SoundControl1(u8),
SoundControl2(u8),
SoundControl3(u8),
SoundControl4(u8),
SoundControl5(u8),
SoundControl6(u8),
SoundControl7(u8),
SoundControl8(u8),
SoundControl9(u8),
SoundControl10(u8),
HighResVelocity(u8),
PortamentoControl(u8),
Effects1Depth(u8),
Effects2Depth(u8),
Effects3Depth(u8),
Effects4Depth(u8),
Effects5Depth(u8),
ReverbSendLevel(u8),
TremoloDepth(u8),
ChorusSendLevel(u8),
CelesteDepth(u8),
PhaserDepth(u8),
Parameter(Parameter),
DataEntry(u16),
DataEntry2(u8, u8),
DataIncrement(u8),
DataDecrement(u8),
}
impl ControlChange {
fn high_res_cc(v: &mut Vec<u8>, control: u8, value: u16) {
let [msb, lsb] = to_u14(value);
v.push(control);
v.push(msb);
v.push(control + 32);
v.push(lsb);
}
fn undefined(v: &mut Vec<u8>, control: u8, value: u8) {
v.push(control.min(119));
v.push(to_u7(value));
}
fn undefined_high_res(v: &mut Vec<u8>, control1: u8, control2: u8, value: u16) {
let [msb, lsb] = to_u14(value);
v.push(control1.min(119));
v.push(msb);
v.push(control2.min(119));
v.push(lsb);
}
}
impl ControlChange {
pub fn to_midi_running(&self) -> Vec<u8> {
let mut r: Vec<u8> = vec![];
self.extend_midi_running(&mut r);
r
}
pub fn extend_midi_running(&self, v: &mut Vec<u8>) {
match *self {
ControlChange::BankSelect(x) => ControlChange::high_res_cc(v, 0, x),
ControlChange::ModWheel(x) => ControlChange::high_res_cc(v, 1, x),
ControlChange::Breath(x) => ControlChange::high_res_cc(v, 2, x),
ControlChange::Undefined { control, value } => {
ControlChange::undefined(v, control, value)
}
ControlChange::UndefinedHighRes {
control1,
control2,
value,
} => ControlChange::undefined_high_res(v, control1, control2, value),
ControlChange::Foot(x) => ControlChange::high_res_cc(v, 4, x),
ControlChange::Portamento(x) => ControlChange::high_res_cc(v, 5, x),
ControlChange::Volume(x) => ControlChange::high_res_cc(v, 7, x),
ControlChange::Balance(x) => ControlChange::high_res_cc(v, 8, x),
ControlChange::Pan(x) => ControlChange::high_res_cc(v, 10, x),
ControlChange::Expression(x) => ControlChange::high_res_cc(v, 11, x),
ControlChange::Effect1(x) => ControlChange::high_res_cc(v, 12, x),
ControlChange::Effect2(x) => ControlChange::high_res_cc(v, 13, x),
ControlChange::GeneralPurpose1(x) => ControlChange::high_res_cc(v, 0, x),
ControlChange::GeneralPurpose2(x) => ControlChange::high_res_cc(v, 0, x),
ControlChange::GeneralPurpose3(x) => ControlChange::high_res_cc(v, 0, x),
ControlChange::GeneralPurpose4(x) => ControlChange::high_res_cc(v, 0, x),
ControlChange::GeneralPurpose5(x) => {
v.push(80);
v.push(to_u7(x));
}
ControlChange::GeneralPurpose6(x) => {
v.push(82);
v.push(to_u7(x));
}
ControlChange::GeneralPurpose7(x) => {
v.push(83);
v.push(to_u7(x));
}
ControlChange::GeneralPurpose8(x) => {
v.push(84);
v.push(to_u7(x));
}
ControlChange::Hold(x) => {
v.push(64);
v.push(to_u7(x));
}
ControlChange::Hold2(x) => {
v.push(69);
v.push(to_u7(x));
}
ControlChange::TogglePortamento(on) => {
v.push(65);
v.push(if on { 127 } else { 0 });
}
ControlChange::Sostenuto(x) => {
v.push(66);
v.push(to_u7(x));
}
ControlChange::SoftPedal(x) => {
v.push(67);
v.push(to_u7(x));
}
ControlChange::ToggleLegato(on) => {
v.push(68);
v.push(if on { 127 } else { 0 });
}
ControlChange::SoundVariation(x) | ControlChange::SoundControl1(x) => {
v.push(70);
v.push(to_u7(x));
}
ControlChange::Timbre(x) | ControlChange::SoundControl2(x) => {
v.push(71);
v.push(to_u7(x));
}
ControlChange::ReleaseTime(x) | ControlChange::SoundControl3(x) => {
v.push(72);
v.push(to_u7(x));
}
ControlChange::AttackTime(x) | ControlChange::SoundControl4(x) => {
v.push(73);
v.push(to_u7(x));
}
ControlChange::Brightness(x) | ControlChange::SoundControl5(x) => {
v.push(74);
v.push(to_u7(x));
}
ControlChange::DecayTime(x) | ControlChange::SoundControl6(x) => {
v.push(75);
v.push(to_u7(x));
}
ControlChange::VibratoRate(x) | ControlChange::SoundControl7(x) => {
v.push(76);
v.push(to_u7(x));
}
ControlChange::VibratoDepth(x) | ControlChange::SoundControl8(x) => {
v.push(77);
v.push(to_u7(x));
}
ControlChange::VibratoDelay(x) | ControlChange::SoundControl9(x) => {
v.push(78);
v.push(to_u7(x));
}
ControlChange::SoundControl10(x) => {
v.push(79);
v.push(to_u7(x));
}
ControlChange::PortamentoControl(x) => {
v.push(84);
v.push(to_u7(x));
}
ControlChange::HighResVelocity(x) => {
v.push(88);
v.push(to_u7(x));
}
ControlChange::Effects1Depth(x) | ControlChange::ReverbSendLevel(x) => {
v.push(91);
v.push(to_u7(x));
}
ControlChange::Effects2Depth(x) | ControlChange::TremoloDepth(x) => {
v.push(92);
v.push(to_u7(x));
}
ControlChange::Effects3Depth(x) | ControlChange::ChorusSendLevel(x) => {
v.push(93);
v.push(to_u7(x));
}
ControlChange::Effects4Depth(x) | ControlChange::CelesteDepth(x) => {
v.push(94);
v.push(to_u7(x));
}
ControlChange::Effects5Depth(x) | ControlChange::PhaserDepth(x) => {
v.push(95);
v.push(to_u7(x));
}
ControlChange::Parameter(p) => p.extend_midi_running(v),
ControlChange::DataEntry(x) => ControlChange::high_res_cc(v, 6, x),
ControlChange::DataEntry2(msb, lsb) => {
v.push(6);
v.push(msb);
v.push(6 + 32);
v.push(lsb);
}
ControlChange::DataIncrement(x) => {
v.push(96);
v.push(to_u7(x));
}
ControlChange::DataDecrement(x) => {
v.push(97);
v.push(to_u7(x));
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Parameter {
Unregistered(u16),
Null,
PitchBendSensitivity,
PitchBendSensitivityEntry(u8, u8),
FineTuning,
FineTuningEntry(i16),
CoarseTuning,
CoarseTuningEntry(i8),
TuningProgramSelect,
TuningProgramSelectEntry(u8),
TuningBankSelect,
TuningBankSelectEntry(u8),
ModulationDepthRange,
ModulationDepthRangeEntry(u16),
PolyphonicExpression,
PolyphonicExpressionEntry(u8),
AzimuthAngle3DSound,
AzimuthAngle3DSoundEntry(u16),
ElevationAngle3DSound,
ElevationAngle3DSoundEntry(u16),
Gain3DSound,
Gain3DSoundEntry(u16),
DistanceRatio3DSound,
DistanceRatio3DSoundEntry(u16),
MaxiumumDistance3DSound,
MaxiumumDistance3DSoundEntry(u16),
GainAtMaxiumumDistance3DSound,
GainAtMaxiumumDistance3DSoundEntry(u16),
ReferenceDistanceRatio3DSound,
ReferenceDistanceRatio3DSoundEntry(u16),
PanSpreadAngle3DSound,
PanSpreadAngle3DSoundEntry(u16),
RollAngle3DSound,
RollAngle3DSoundEntry(u16),
}
impl Parameter {
fn extend_midi_running(&self, v: &mut Vec<u8>) {
match self {
Self::Null => {
v.push(100);
v.push(0x7F);
v.push(101);
v.push(0x7F);
}
Self::PitchBendSensitivity => {
v.push(100);
v.push(0);
v.push(101);
v.push(0);
}
Self::PitchBendSensitivityEntry(c, f) => {
Self::PitchBendSensitivity.extend_midi_running(v);
v.push(6);
v.push(*c);
v.push(6 + 32);
v.push((*f).min(100));
}
Self::FineTuning => {
v.push(100);
v.push(1);
v.push(101);
v.push(0);
}
Self::FineTuningEntry(x) => {
Self::FineTuning.extend_midi_running(v);
let [msb, lsb] = i_to_u14(*x);
v.push(6);
v.push(msb);
v.push(6 + 32);
v.push(lsb);
}
Self::CoarseTuning => {
v.push(100);
v.push(2);
v.push(101);
v.push(0);
}
Self::CoarseTuningEntry(x) => {
Self::CoarseTuning.extend_midi_running(v);
let msb = i_to_u7(*x);
v.push(6);
v.push(msb);
v.push(6 + 32);
v.push(0);
}
Self::TuningProgramSelect => {
v.push(100);
v.push(3);
v.push(101);
v.push(0);
}
Self::TuningProgramSelectEntry(x) => {
Self::TuningProgramSelect.extend_midi_running(v);
v.push(6);
v.push(*x);
}
Self::TuningBankSelect => {
v.push(100);
v.push(4);
v.push(101);
v.push(0);
}
Self::TuningBankSelectEntry(x) => {
Self::TuningBankSelect.extend_midi_running(v);
v.push(6);
v.push(*x);
}
Self::ModulationDepthRange => {
v.push(100);
v.push(5);
v.push(101);
v.push(0);
}
Self::ModulationDepthRangeEntry(x) => {
Self::ModulationDepthRange.extend_midi_running(v);
ControlChange::high_res_cc(v, 6, *x);
}
Self::PolyphonicExpression => {
v.push(100);
v.push(6);
v.push(101);
v.push(0);
}
Self::PolyphonicExpressionEntry(x) => {
Self::PolyphonicExpression.extend_midi_running(v);
v.push(6);
v.push((*x).min(16));
}
Self::AzimuthAngle3DSound => {
v.push(100);
v.push(0);
v.push(101);
v.push(61);
}
Self::AzimuthAngle3DSoundEntry(x) => {
Self::AzimuthAngle3DSound.extend_midi_running(v);
ControlChange::high_res_cc(v, 6, *x);
}
Self::ElevationAngle3DSound => {
v.push(100);
v.push(1);
v.push(101);
v.push(61);
}
Self::ElevationAngle3DSoundEntry(x) => {
Self::ElevationAngle3DSound.extend_midi_running(v);
ControlChange::high_res_cc(v, 6, *x);
}
Self::Gain3DSound => {
v.push(100);
v.push(2);
v.push(101);
v.push(61);
}
Self::Gain3DSoundEntry(x) => {
Self::Gain3DSound.extend_midi_running(v);
ControlChange::high_res_cc(v, 6, *x);
}
Self::DistanceRatio3DSound => {
v.push(100);
v.push(3);
v.push(101);
v.push(61);
}
Self::DistanceRatio3DSoundEntry(x) => {
Self::DistanceRatio3DSound.extend_midi_running(v);
ControlChange::high_res_cc(v, 6, *x);
}
Self::MaxiumumDistance3DSound => {
v.push(100);
v.push(4);
v.push(101);
v.push(61);
}
Self::MaxiumumDistance3DSoundEntry(x) => {
Self::MaxiumumDistance3DSound.extend_midi_running(v);
ControlChange::high_res_cc(v, 6, *x);
}
Self::GainAtMaxiumumDistance3DSound => {
v.push(100);
v.push(5);
v.push(101);
v.push(61);
}
Self::GainAtMaxiumumDistance3DSoundEntry(x) => {
Self::GainAtMaxiumumDistance3DSound.extend_midi_running(v);
ControlChange::high_res_cc(v, 6, *x);
}
Self::ReferenceDistanceRatio3DSound => {
v.push(100);
v.push(6);
v.push(101);
v.push(61);
}
Self::ReferenceDistanceRatio3DSoundEntry(x) => {
Self::ReferenceDistanceRatio3DSound.extend_midi_running(v);
ControlChange::high_res_cc(v, 6, *x);
}
Self::PanSpreadAngle3DSound => {
v.push(100);
v.push(7);
v.push(101);
v.push(61);
}
Self::PanSpreadAngle3DSoundEntry(x) => {
Self::PanSpreadAngle3DSound.extend_midi_running(v);
ControlChange::high_res_cc(v, 6, *x);
}
Self::RollAngle3DSound => {
v.push(100);
v.push(8);
v.push(101);
v.push(61);
}
Self::RollAngle3DSoundEntry(x) => {
Self::RollAngle3DSound.extend_midi_running(v);
ControlChange::high_res_cc(v, 6, *x);
}
Self::Unregistered(x) => {
let [msb, lsb] = to_u14(*x);
v.push(98);
v.push(lsb);
v.push(99);
v.push(msb);
}
}
}
}
#[cfg(test)]
mod tests {
use super::super::*;
#[test]
fn serialize_channel_voice_msg() {
assert_eq!(
MidiMsg::ChannelVoice {
channel: Channel::Ch1,
msg: ChannelVoiceMsg::NoteOn {
note: 0x88,
velocity: 0xff
}
}
.to_midi(),
vec![0x90, 0x7f, 127]
);
assert_eq!(
MidiMsg::RunningChannelVoice {
channel: Channel::Ch10,
msg: ChannelVoiceMsg::PitchBend { bend: 0xff44 }
}
.to_midi(),
vec![0x7f, 0x7f]
);
assert_eq!(
MidiMsg::ChannelVoice {
channel: Channel::Ch10,
msg: ChannelVoiceMsg::PitchBend { bend: 1000 }
}
.to_midi(),
vec![0xE9, 0x68, 0x07]
);
assert_eq!(
MidiMsg::ChannelVoice {
channel: Channel::Ch2,
msg: ChannelVoiceMsg::ControlChange {
control: ControlChange::Volume(1000)
}
}
.to_midi(),
vec![0xB1, 7, 0x07, 39, 0x68]
);
assert_eq!(
MidiMsg::ChannelVoice {
channel: Channel::Ch4,
msg: ChannelVoiceMsg::ControlChange {
control: ControlChange::Undefined {
control: 85,
value: 77
}
}
}
.to_midi(),
vec![0xB3, 85, 77]
);
assert_eq!(
MidiMsg::ChannelVoice {
channel: Channel::Ch2,
msg: ChannelVoiceMsg::ControlChange {
control: ControlChange::UndefinedHighRes {
control1: 3,
control2: 35,
value: 1000
}
}
}
.to_midi(),
vec![0xB1, 3, 0x07, 35, 0x68]
);
assert_eq!(
MidiMsg::ChannelVoice {
channel: Channel::Ch3,
msg: ChannelVoiceMsg::ControlChange {
control: ControlChange::TogglePortamento(true)
}
}
.to_midi(),
vec![0xB2, 65, 0x7f]
);
assert_eq!(
MidiMsg::ChannelVoice {
channel: Channel::Ch2,
msg: ChannelVoiceMsg::ControlChange {
control: ControlChange::Parameter(Parameter::FineTuning)
}
}
.to_midi(),
vec![0xB1, 100, 0x01, 101, 0x00]
);
assert_eq!(
MidiMsg::ChannelVoice {
channel: Channel::Ch2,
msg: ChannelVoiceMsg::ControlChange {
control: ControlChange::Parameter(Parameter::Unregistered(1000))
}
}
.to_midi(),
vec![0xB1, 98, 0x68, 99, 0x07]
);
}
}