use crate::util::*;
#[derive(Debug, Clone, PartialEq)]
pub struct GlobalParameterControl {
pub slot_paths: Vec<SlotPath>,
pub param_id_width: u8,
pub value_width: u8,
pub params: Vec<GlobalParameter>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ReverbType {
SmallRoom = 0,
MediumRoom = 1,
LargeRoom = 2,
MediumHall = 3,
LargeHall = 4,
Plate = 8,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ChorusType {
Chorus1 = 0,
Chorus2 = 1,
Chorus3 = 2,
Chorus4 = 3,
FBChorus = 4,
Flanger = 5,
}
impl GlobalParameterControl {
pub fn reverb(reverb_type: Option<ReverbType>, reverb_time: Option<f32>) -> Self {
let mut params = vec![];
if let Some(reverb_type) = reverb_type {
params.push(GlobalParameter {
id: vec![0],
value: vec![reverb_type as u8],
});
}
if let Some(reverb_time) = reverb_time {
params.push(GlobalParameter {
id: vec![1],
value: vec![to_u7((reverb_time.ln() / 0.025 + 40.0) as u8)],
});
}
Self {
slot_paths: vec![SlotPath::Reverb],
param_id_width: 1,
value_width: 1,
params,
}
}
pub fn chorus(
chorus_type: Option<ChorusType>,
mod_rate: Option<f32>,
mod_depth: Option<f32>,
feedback: Option<f32>,
send_to_reverb: Option<f32>,
) -> Self {
let mut params = vec![];
if let Some(chorus_type) = chorus_type {
params.push(GlobalParameter {
id: vec![0],
value: vec![chorus_type as u8],
});
}
if let Some(mod_rate) = mod_rate {
params.push(GlobalParameter {
id: vec![1],
value: vec![to_u7((mod_rate / 0.122) as u8)],
});
}
if let Some(mod_depth) = mod_depth {
params.push(GlobalParameter {
id: vec![2],
value: vec![to_u7(((mod_depth * 3.2) - 1.0) as u8)],
});
}
if let Some(feedback) = feedback {
params.push(GlobalParameter {
id: vec![3],
value: vec![to_u7((feedback / 0.763) as u8)],
});
}
if let Some(send_to_reverb) = send_to_reverb {
params.push(GlobalParameter {
id: vec![4],
value: vec![to_u7((send_to_reverb / 0.787) as u8)],
});
}
Self {
slot_paths: vec![SlotPath::Chorus],
param_id_width: 1,
value_width: 1,
params,
}
}
pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
v.push(self.slot_paths.len().min(127) as u8);
push_u7(self.param_id_width, v);
push_u7(self.value_width, v);
for (i, sp) in self.slot_paths.iter().enumerate() {
if i > 127 {
break;
}
sp.extend_midi(v);
}
for p in self.params.iter() {
p.extend_midi_with_limits(v, self.param_id_width.max(1), self.value_width.max(1));
}
}
pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), &str> {
Err("TODO: not implemented")
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SlotPath {
Reverb,
Chorus,
Unregistered(u8, u8),
}
impl SlotPath {
pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
match self {
Self::Reverb => {
v.push(1);
v.push(1);
}
Self::Chorus => {
v.push(1);
v.push(2);
}
Self::Unregistered(a, b) => {
push_u7(*a, v);
push_u7(*b, v);
}
}
}
pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), &str> {
Err("TODO: not implemented")
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct GlobalParameter {
pub id: Vec<u8>,
pub value: Vec<u8>,
}
impl GlobalParameter {
pub(crate) fn extend_midi_with_limits(
&self,
v: &mut Vec<u8>,
param_id_width: u8,
value_width: u8,
) {
for i in 0..param_id_width {
if let Some(x) = self.id.get(i as usize) {
push_u7(*x, v);
} else {
v.push(0);
}
}
for i in (0..value_width).rev() {
if let Some(x) = self.value.get(i as usize) {
push_u7(*x, v);
} else {
v.push(0);
}
}
}
pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), &str> {
Err("TODO: not implemented")
}
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn serialize_global_parameter() {
assert_eq!(
MidiMsg::SystemExclusive {
msg: SystemExclusiveMsg::UniversalRealTime {
device: DeviceID::AllCall,
msg: UniversalRealTimeMsg::GlobalParameterControl(GlobalParameterControl {
slot_paths: vec![
SlotPath::Unregistered(1, 0x47),
SlotPath::Unregistered(2, 3)
],
param_id_width: 1,
value_width: 2,
params: vec![
GlobalParameter {
id: vec![4],
value: vec![5, 6, 7]
},
GlobalParameter {
id: vec![4],
value: vec![1]
}
]
}),
},
}
.to_midi(),
vec![
0xF0, 0x7F, 0x7F,
04, 05, 2,
1,
2,
1,
0x47,
2,
3,
4,
6,
5,
4,
0,
1,
0xF7
]
);
assert_eq!(
MidiMsg::SystemExclusive {
msg: SystemExclusiveMsg::UniversalRealTime {
device: DeviceID::AllCall,
msg: UniversalRealTimeMsg::GlobalParameterControl(
GlobalParameterControl::chorus(
Some(ChorusType::Flanger),
Some(1.1),
None,
None,
Some(100.0)
)
),
},
}
.to_midi(),
vec![
0xF0, 0x7F, 0x7F,
04, 05, 1,
1,
1,
1,
2,
0,
5,
1,
9,
4,
127,
0xF7
]
);
}
}