#[cfg(test)] pub(super) mod tests; use mfctl_derive::author; use crate::parameter::{DecodeError, RegisterCodec, StoredValue, unsigned}; macro_rules! integer_control { ($name:ident, $scalar:ty, $minimum:expr, $maximum:expr) => { #[author] #[serde(transparent)] #[garde(transparent)] pub struct $name(#[garde(range(min = $minimum, max = $maximum))] pub $scalar); impl crate::parameter::RegisterCodec for $name { fn encode(&self) -> crate::parameter::StoredValue { crate::parameter::encode_bin( (self.0 as i32 - $minimum as i32) as u16, ($maximum - $minimum) as u16, ) } fn decode( raw: crate::parameter::StoredValue, ) -> Result { let index = crate::parameter::decode_bin::(raw, ($maximum - $minimum) as u16)?; Ok(Self(($minimum as i32 + i32::from(index)) as $scalar)) } } }; } #[author] #[serde(transparent)] #[garde(transparent)] pub struct Percent(#[garde(range(min = 0.0, max = 100.0))] pub f64); impl Default for Percent { fn default() -> Self { Self(0.0) } } impl RegisterCodec for Percent { fn encode(&self) -> StoredValue { StoredValue((self.0 * 32767.0 / 100.0).round() as u16) } fn decode(raw: StoredValue) -> Result { Ok(Self(f64::from(unsigned::(raw)?) * 100.0 / 32767.0)) } } #[author] #[serde(transparent)] #[garde(transparent)] pub struct Bipolar(#[garde(range(min = -100.0, max = 100.0))] pub f64); impl Default for Bipolar { fn default() -> Self { Self(0.0) } } impl RegisterCodec for Bipolar { fn encode(&self) -> StoredValue { StoredValue(if self.0 == 100.0 { 32767 } else { (self.0 * 32768.0 / 100.0).round() as i32 as u16 }) } fn decode(raw: StoredValue) -> Result { Ok(Self(if raw.0 == 32767 { 100.0 } else { f64::from(raw.0 as i16) * 100.0 / 32768.0 })) } } // Attack, decay/release and cycling hold share the firmware's register knots. // Registers 32760..32767 decode to the final time entry. pub(super) const TIME_REGISTERS: [f64; 10] = [ 0.0, 3640.0, 7280.0, 10920.0, 14560.0, 18200.0, 21840.0, 25480.0, 29120.0, 32760.0, ]; pub(super) fn interpolate(value: f64, from: &[f64; 10], to: &[f64; 10]) -> f64 { let i = from.partition_point(|entry| *entry < value); match i { 0 => to[0], 10 => to[9], _ => to[i - 1] + (value - from[i - 1]) * (to[i] - to[i - 1]) / (from[i] - from[i - 1]), } } macro_rules! time_control { ($name:ident, $maximum:literal, [$($time:literal),+]) => { #[author] #[serde(transparent)] #[garde(transparent)] pub struct $name(#[garde(range(max = $maximum))] pub u16); impl $name { const TIMES: [f64; 10] = [$($time as f64),+]; } impl crate::parameter::RegisterCodec for $name { fn encode(&self) -> crate::parameter::StoredValue { crate::parameter::StoredValue( crate::patch::value::interpolate( f64::from(self.0), &Self::TIMES, &crate::patch::value::TIME_REGISTERS, ).round() as u16, ) } fn decode( raw: crate::parameter::StoredValue, ) -> Result { let register = f64::from(crate::parameter::unsigned::(raw)?); Ok(Self(crate::patch::value::interpolate( register, &crate::patch::value::TIME_REGISTERS, &Self::TIMES, ).round() as u16)) } } }; } pub(super) use {integer_control, time_control};