use std::fmt;
use std::convert::TryFrom;
use std::convert::TryInto;
use std::collections::HashMap;
use lazy_static::lazy_static;
use num_enum::TryFromPrimitive;
use crate::k4::{
Level,
SUBMIX_COUNT,
SmallEffectParameter,
BigEffectParameter
};
use crate::{
Ranged,
SystemExclusiveData,
ParseError,
Checksum
};
static EFFECT_NAMES: &[&str] = &[
"None", "Reverb 1",
"Reverb 2",
"Reverb 3",
"Reverb 4",
"Gate Reverb",
"Reverse Gate",
"Normal Delay",
"Stereo Panpot Delay",
"Chorus",
"Overdrive + Flanger",
"Overdrive + Normal Delay",
"Overdrive + Reverb",
"Normal Delay + Normal Delay",
"Normal Delay + Stereo Panpot Delay",
"Chorus + Normal Delay",
"Chorus + Stereo Panpot Delay",
];
#[derive(Debug, Eq, PartialEq, Copy, Clone, TryFromPrimitive, Hash)]
#[repr(u8)]
pub enum Effect {
None, Reverb1,
Reverb2,
Reverb3,
Reverb4,
GateReverb,
ReverseGate,
NormalDelay,
StereoPanpotDelay,
Chorus,
OverdrivePlusFlanger,
OverdrivePlusNormalDelay,
OverdrivePlusReverb,
NormalDelayPlusNormalDelay,
NormalDelayPlusStereoPanpotDelay,
ChorusPlusNormalDelay,
ChorusPlusStereoPanpotDelay,
}
impl fmt::Display for Effect {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", EFFECT_NAMES[*self as usize])
}
}
#[derive(Clone)]
pub struct EffectPatch {
pub effect: Effect,
pub param1: SmallEffectParameter,
pub param2: SmallEffectParameter,
pub param3: BigEffectParameter,
pub submixes: [SubmixSettings; SUBMIX_COUNT],
}
lazy_static! {
static ref EFFECT_PARAMETER_NAMES: HashMap<&'static Effect, Vec<&'static str>> = {
let mut map = HashMap::new();
map.insert(&Effect::Reverb1, vec!["Pre.delay", "Rev.Time", "Tone"]);
map.insert(&Effect::Reverb2, vec!["Pre.delay", "Rev.Time", "Tone"]);
map.insert(&Effect::Reverb3, vec!["Pre.delay", "Rev.Time", "Tone"]);
map.insert(&Effect::Reverb4, vec!["Pre.delay", "Rev.Time", "Tone"]);
map.insert(&Effect::GateReverb, vec!["Pre.delay", "Gate Time", "Tone"]);
map.insert(&Effect::ReverseGate, vec!["Pre.delay", "Gate Time", "Tone"]);
map.insert(&Effect::NormalDelay, vec!["Feedback", "Tone", "Delay"]);
map.insert(&Effect::StereoPanpotDelay, vec!["Feedback", "L/R Delay", "Delay"]);
map.insert(&Effect::Chorus, vec!["Width", "Feedback", "Rate"]);
map.insert(&Effect::OverdrivePlusFlanger, vec!["Drive", "Fl.Type", "1-2 Bal"]);
map.insert(&Effect::OverdrivePlusNormalDelay, vec!["Drive", "Delay Time", "1-2 Bal"]);
map.insert(&Effect::OverdrivePlusReverb, vec!["Drive", "Rev.Type", "1-2 Bal"]);
map.insert(&Effect::NormalDelayPlusNormalDelay, vec!["Delay1", "Delay2", "1-2 Bal"]);
map.insert(&Effect::NormalDelayPlusStereoPanpotDelay, vec!["Delay1", "Delay2", "1-2 Bal"]);
map.insert(&Effect::ChorusPlusNormalDelay, vec!["Chorus", "Delay", "1-2 Bal"]);
map.insert(&Effect::ChorusPlusStereoPanpotDelay, vec!["Chorus", "Delay", "1-2 Bal"]);
map
};
}
impl Default for EffectPatch {
fn default() -> Self {
EffectPatch {
effect: Effect::Reverb1,
param1: SmallEffectParameter::new(0),
param2: SmallEffectParameter::new(0),
param3: BigEffectParameter::new(0),
submixes: [Default::default(); SUBMIX_COUNT],
}
}
}
impl fmt::Display for EffectPatch {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}, {} = {}, {} = {}, {} = {}",
EFFECT_NAMES[self.effect as usize],
EFFECT_PARAMETER_NAMES.get(&self.effect).unwrap()[0], self.param1.value(),
EFFECT_PARAMETER_NAMES.get(&self.effect).unwrap()[1], self.param2.value(),
EFFECT_PARAMETER_NAMES.get(&self.effect).unwrap()[2], self.param3.value()
)
}
}
impl EffectPatch {
fn collect_data(&self) -> Vec<u8> {
let mut buf = vec![
self.effect as u8 - 1,
(self.param1.value() + 7) as u8,
(self.param2.value() + 7) as u8,
self.param3.value().try_into().unwrap()
];
buf.extend(vec![0, 0, 0, 0, 0, 0]);
for i in 0..SUBMIX_COUNT {
buf.extend(self.submixes[i].to_bytes());
}
buf
}
pub fn parameter_names(&self) -> Vec<String> {
vec![
EFFECT_PARAMETER_NAMES.get(&self.effect).unwrap()[0].to_string(),
EFFECT_PARAMETER_NAMES.get(&self.effect).unwrap()[1].to_string(),
EFFECT_PARAMETER_NAMES.get(&self.effect).unwrap()[2].to_string(),
]
}
}
impl SystemExclusiveData for EffectPatch {
fn from_bytes(data: &[u8]) -> Result<Self, ParseError> {
let mut submixes = [Default::default(); SUBMIX_COUNT];
let mut offset = 10;
let mut i = 0;
while i < SUBMIX_COUNT {
submixes[i] = SubmixSettings {
pan: data[offset] as i32 - 7,
send1: Level::new((data[offset + 1]).into()),
send2: Level::new((data[offset + 2]).into()),
};
offset += 3;
i += 1;
}
Ok(EffectPatch {
effect: Effect::try_from(data[0] + 1).unwrap(),
param1: SmallEffectParameter::new(((data[1] as i8) - 7).into()),
param2: SmallEffectParameter::new(((data[2] as i8) - 7).into()),
param3: BigEffectParameter::new(data[3].into()),
submixes,
})
}
fn to_bytes(&self) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
let data = self.collect_data();
buf.extend(data);
buf.push(self.checksum());
buf
}
fn data_size() -> usize { 35 }
}
impl Checksum for EffectPatch {
fn checksum(&self) -> u8 {
let data = self.collect_data();
let mut total = data.iter().fold(0, |acc, x| acc + ((*x as u32) & 0xFF));
total += 0xA5;
(total & 0x7F) as u8
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub struct SubmixSettings {
pub pan: i32,
pub send1: Level,
pub send2: Level,
}
impl Default for SubmixSettings {
fn default() -> Self {
SubmixSettings {
pan: 0,
send1: Level::new(0),
send2: Level::new(0),
}
}
}
impl SystemExclusiveData for SubmixSettings {
fn from_bytes(data: &[u8]) -> Result<Self, ParseError> {
Ok(SubmixSettings {
pan: data[0] as i32 - 7,
send1: Level::new(data[1].into()),
send2: Level::new(data[2].into()),
})
}
fn to_bytes(&self) -> Vec<u8> {
vec![
(self.pan + 7).try_into().unwrap(),
self.send1.value().try_into().unwrap(),
self.send2.value().try_into().unwrap()
]
}
fn data_size() -> usize { 3 }
}
#[derive(Debug, Eq, PartialEq, Copy, Clone, TryFromPrimitive)]
#[repr(u8)]
#[allow(dead_code)]
pub enum Submix {
A = 0,
B = 1,
C = 2,
D = 3,
E = 4,
F = 5,
G = 6,
H = 7,
}
impl Submix {
pub fn name(&self) -> String {
match self {
Submix::A => "A".to_string(),
Submix::B => "B".to_string(),
Submix::C => "C".to_string(),
Submix::D => "D".to_string(),
Submix::E => "E".to_string(),
Submix::F => "F".to_string(),
Submix::G => "G".to_string(),
Submix::H => "H".to_string(),
}
}
}
impl fmt::Display for Submix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[cfg(test)]
mod tests {
use crate::k4::{
bank,
sysex::Header,
single::SinglePatch,
multi::MultiPatch,
drum::DrumPatch
};
use super::{*};
static DATA: &'static [u8] = include_bytes!("A401.SYX");
#[test]
fn test_submix_name() {
let submix = Submix::A;
assert_eq!(submix.name(), "A");
}
#[test]
fn test_effect_patch_from_bytes() {
let start: usize = dbg!(
2 +
Header::data_size() +
bank::SINGLE_PATCH_COUNT * SinglePatch::data_size() +
bank::MULTI_PATCH_COUNT * MultiPatch::data_size() +
DrumPatch::data_size());
let patch = EffectPatch::from_bytes(&DATA[start..]);
assert_eq!(patch.unwrap().effect, Effect::Reverb1);
}
#[test]
fn test_effect_parameter_names() {
let effect = EffectPatch {
effect: Effect::Reverb1,
param1: SmallEffectParameter::new(7),
param2: SmallEffectParameter::new(5),
param3: BigEffectParameter::new(31),
submixes: [Default::default(); SUBMIX_COUNT],
};
if let Some(param_names) = EFFECT_PARAMETER_NAMES.get(&effect.effect) {
assert_eq!(param_names[0], "Pre.delay");
}
else {
assert_eq!(true, false);
}
}
#[test]
fn test_effect_get_parameter_names() {
let effect = EffectPatch {
effect: Effect::Reverb1,
param1: SmallEffectParameter::new(7),
param2: SmallEffectParameter::new(5),
param3: BigEffectParameter::new(31),
submixes: [Default::default(); SUBMIX_COUNT],
};
assert_eq!(effect.parameter_names(), vec!["Pre.delay", "Rev.Time", "Tone"]);
}
}