//! Mode-dependent glide values and firmware register codecs. use crate::parameter::{self, DecodeError, RegisterCodec, StoredValue}; use mfctl_derive::author; use serde::Deserialize; use std::fmt; #[author] #[schemars(extend("default" = Self::default()))] pub enum Glide { /// Fixed glide duration in milliseconds, from zero (Off) to 10000. #[serde(rename = "glideTimeMs")] Time(DurationMs), /// Tempo-synchronized glide division, or off while retaining Sync mode. #[serde(rename = "glideDivision")] Sync(Division), /// Glide duration per octave in milliseconds, from zero (Off) to 10000. #[serde(rename = "glideMsPerOctave")] Rate(DurationMs), } impl Default for Glide { fn default() -> Self { Self::Time(DurationMs::default()) } } impl Glide { pub(crate) fn from_mode_register(raw: StoredValue) -> Result { Ok(match parameter::decode_bin::(raw, 2)? { 0 => Self::default(), 1 => Self::Sync(Division::default()), 2 => Self::Rate(DurationMs::default()), _ => unreachable!(), }) } // As with LFO rate, omission defaults but explicit invalid/multiple choices do not. pub(super) fn deserialize_or_default<'de, D: serde::Deserializer<'de>>( deserializer: D, ) -> Result { struct GlideVisitor; impl<'de> serde::de::Visitor<'de> for GlideVisitor { type Value = Glide; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("glideTimeMs, glideDivision or glideMsPerOctave") } fn visit_map>(self, map: M) -> Result { let fields = serde_yaml::Mapping::deserialize( serde::de::value::MapAccessDeserializer::new(map), )?; if fields.is_empty() { Ok(Glide::default()) } else if fields.len() == 1 { Glide::deserialize(serde::de::value::MapAccessDeserializer::new( serde::de::value::MapDeserializer::new(fields.into_iter()), )) .map_err(serde::de::Error::custom) } else { Err(serde::de::Error::custom( "choose only one of glideTimeMs, glideDivision or glideMsPerOctave", )) } } } deserializer.deserialize_struct( "Glide", &["glideTimeMs", "glideDivision", "glideMsPerOctave"], GlideVisitor, ) } } #[author] #[derive(Default)] #[serde(transparent)] #[garde(transparent)] pub struct DurationMs(#[garde(range(min = 0.0, max = 10000.0))] pub f64); impl RegisterCodec for DurationMs { fn encode(&self) -> StoredValue { StoredValue((32767.0 * (self.0 / 10000.0).cbrt()).round() as u16) } fn decode(raw: StoredValue) -> Result { Ok(Self( 10000.0 * (f64::from(parameter::unsigned::(raw)?) / 32767.0).powi(3), )) } } #[author] #[derive(Default, strum_macros::FromRepr)] #[repr(u16)] #[schemars(extend("default" = Self::default()))] pub enum Division { #[default] #[serde(rename = "off")] Off, #[serde(rename = "1/32T")] ThirtySecondTriplet, #[serde(rename = "1/32")] ThirtySecond, #[serde(rename = "1/16T")] SixteenthTriplet, #[serde(rename = "1/16")] Sixteenth, #[serde(rename = "1/8T")] EighthTriplet, #[serde(rename = "1/8")] Eighth, #[serde(rename = "1/4T")] QuarterTriplet, #[serde(rename = "1/4")] Quarter, #[serde(rename = "1/2T")] HalfTriplet, #[serde(rename = "1/2")] Half, #[serde(rename = "1/1")] Whole, } impl RegisterCodec for Division { fn encode(&self) -> StoredValue { if matches!(self, Self::Off) { StoredValue(0) } else { // Use bin interiors, except Whole, whose bin is only 32760..32767. StoredValue(((*self as u16 - 1) * 3276 + 1638).min(32767)) } } fn decode(raw: StoredValue) -> Result { let raw = parameter::unsigned::(raw)?; Ok(if raw == 0 { Self::Off } else { Self::from_repr((raw / 3276).min(10) + 1).unwrap() }) } } #[cfg(test)] mod tests { use super::*; #[test] fn durations_follow_the_cubic_curve_and_preserve_registers() { for (raw, expected) in [ (0, 0.0), (8192, 156.2643059879054), (16384, 1250.1144479032432), (32767, 10000.0), ] { let duration = DurationMs::decode(StoredValue(raw)).unwrap(); assert!((duration.0 - expected).abs() < 1e-9); assert_eq!(duration.encode(), StoredValue(raw)); } for raw in 0..=32767 { assert_eq!( DurationMs::decode(StoredValue(raw)).unwrap().encode(), StoredValue(raw) ); } assert!(DurationMs::decode(StoredValue(32768)).is_err()); } #[test] fn divisions_use_firmware_boundaries_and_round_trip() { assert_eq!(Division::decode(StoredValue(0)).unwrap(), Division::Off); assert_eq!(Division::Off.encode(), StoredValue(0)); for index in 0..=10 { let division = Division::from_repr(index + 1).unwrap(); let lower = (index * 3276).max(1); let upper = ((index + 1) * 3276 - 1).min(32767); for raw in [lower, upper] { assert_eq!(Division::decode(StoredValue(raw)).unwrap(), division); } assert!((lower..=upper).contains(&division.encode().0)); assert_eq!(Division::decode(division.encode()).unwrap(), division); } assert_eq!(Division::Whole.encode(), StoredValue(32767)); assert!(Division::decode(StoredValue(32768)).is_err()); } }