diff --git a/src/param.rs b/src/param.rs index 8a143fe3..901d9709 100644 --- a/src/param.rs +++ b/src/param.rs @@ -3,17 +3,18 @@ //! example. use std::fmt::Display; -use std::sync::Arc; use self::range::Range; // Parameter types +mod boolean; mod plain; pub mod internals; pub mod range; pub mod smoothing; +pub use boolean::BoolParam; pub use plain::{FloatParam, IntParam}; // Re-export for the [EnumParam] @@ -75,28 +76,6 @@ pub trait Param: Display { fn as_ptr(&self) -> internals::ParamPtr; } -/// A simple boolean parmaeter. -#[repr(C, align(4))] -pub struct BoolParam { - /// The field's current, normalized value. Should be initialized with the default value. - pub value: bool, - - /// Optional callback for listening to value changes. The argument passed to this function is - /// the parameter's new value. This should not do anything expensive as it may be called - /// multiple times in rapid succession, and it can be run from both the GUI and the audio - /// thread. - pub value_changed: Option>, - - /// The parameter's human readable display name. - pub name: &'static str, - /// Optional custom conversion function from a boolean value to a string. - pub value_to_string: Option String + Send + Sync>>, - /// Optional custom conversion function from a string to a boolean value. If the string cannot - /// be parsed, then this should return a `None`. If this happens while the parameter is being - /// updated then the update will be canceled. - pub string_to_value: Option Option + Send + Sync>>, -} - /// An [IntParam]-backed categorical parameter that allows convenient conversion to and from a /// simple enum. This enum must derive the re-exported [EnumIter] and [EnumMessage] and [Display] /// traits. You can use the `#[strum(message = "Foo Bar")]` to override the name of the variant. @@ -112,19 +91,6 @@ pub struct EnumParam { variants: Vec<(T, String)>, } -#[allow(clippy::derivable_impls)] -impl Default for BoolParam { - fn default() -> Self { - Self { - value: false, - value_changed: None, - name: "", - value_to_string: None, - string_to_value: None, - } - } -} - impl Default for EnumParam { fn default() -> Self { let variants: Vec<_> = Self::build_variants(); @@ -147,82 +113,6 @@ impl Default for Enum } } -impl Param for BoolParam { - type Plain = bool; - - fn update_smoother(&mut self, _sample_rate: f32, _init: bool) { - // Can't really smooth a binary parameter now can you - } - - fn set_from_string(&mut self, string: &str) -> bool { - let value = match &self.string_to_value { - Some(f) => f(string), - None => Some(string.eq_ignore_ascii_case("true") || string.eq_ignore_ascii_case("on")), - }; - - match value { - Some(plain) => { - self.set_plain_value(plain); - true - } - None => false, - } - } - - fn plain_value(&self) -> Self::Plain { - self.value - } - - fn set_plain_value(&mut self, plain: Self::Plain) { - self.value = plain; - if let Some(f) = &self.value_changed { - f(plain); - } - } - - fn normalized_value(&self) -> f32 { - self.preview_normalized(self.value) - } - - fn set_normalized_value(&mut self, normalized: f32) { - self.set_plain_value(self.preview_plain(normalized)); - } - - fn normalized_value_to_string(&self, normalized: f32, _include_unit: bool) -> String { - let value = self.preview_plain(normalized); - match (value, &self.value_to_string) { - (v, Some(f)) => f(v), - (true, None) => String::from("On"), - (false, None) => String::from("Off"), - } - } - - fn string_to_normalized_value(&self, string: &str) -> Option { - let value = match &self.string_to_value { - Some(f) => f(string), - None => Some(string.eq_ignore_ascii_case("true") || string.eq_ignore_ascii_case("on")), - }?; - - Some(self.preview_normalized(value)) - } - - fn preview_normalized(&self, plain: Self::Plain) -> f32 { - if plain { - 1.0 - } else { - 0.0 - } - } - - fn preview_plain(&self, normalized: f32) -> Self::Plain { - normalized > 0.5 - } - - fn as_ptr(&self) -> internals::ParamPtr { - internals::ParamPtr::BoolParam(self as *const BoolParam as *mut BoolParam) - } -} - impl Param for EnumParam { type Plain = T; @@ -287,63 +177,12 @@ impl Param for EnumParam { } } -impl Display for BoolParam { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match (self.value, &self.value_to_string) { - (v, Some(func)) => write!(f, "{}", func(v)), - (true, None) => write!(f, "On"), - (false, None) => write!(f, "Off"), - } - } -} - impl Display for EnumParam { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.variants[self.inner.plain_value() as usize].1) } } -impl BoolParam { - /// Build a new [Self]. Use the other associated functions to modify the behavior of the - /// parameter. - pub fn new(name: &'static str, default: bool) -> Self { - Self { - value: default, - name, - ..Default::default() - } - } - - /// Run a callback whenever this parameter's value changes. The argument passed to this function - /// is the parameter's new value. This should not do anything expensive as it may be called - /// multiple times in rapid succession, and it can be run from both the GUI and the audio - /// thread. - pub fn with_callback(mut self, callback: Arc) -> Self { - self.value_changed = Some(callback); - self - } - - /// Use a custom conversion function to convert the boolean value to a string. - pub fn with_value_to_string( - mut self, - callback: Arc String + Send + Sync>, - ) -> Self { - self.value_to_string = Some(callback); - self - } - - /// Use a custom conversion function to convert from a string to a boolean value. If the string - /// cannot be parsed, then this should return a `None`. If this happens while the parameter is - /// being updated then the update will be canceled. - pub fn with_string_to_value( - mut self, - callback: Arc Option + Send + Sync>, - ) -> Self { - self.string_to_value = Some(callback); - self - } -} - impl EnumParam { /// Build a new [Self]. Use the other associated functions to modify the behavior of the /// parameter. diff --git a/src/param/boolean.rs b/src/param/boolean.rs new file mode 100644 index 00000000..1626be30 --- /dev/null +++ b/src/param/boolean.rs @@ -0,0 +1,169 @@ +//! Simple boolean parameters. + +use std::fmt::Display; +use std::sync::Arc; + +use super::internals::ParamPtr; +use super::Param; + +/// A simple boolean parmaeter. +#[repr(C, align(4))] +pub struct BoolParam { + /// The field's current, normalized value. Should be initialized with the default value. + pub value: bool, + + /// Optional callback for listening to value changes. The argument passed to this function is + /// the parameter's new value. This should not do anything expensive as it may be called + /// multiple times in rapid succession, and it can be run from both the GUI and the audio + /// thread. + pub value_changed: Option>, + + /// The parameter's human readable display name. + pub name: &'static str, + /// Optional custom conversion function from a boolean value to a string. + pub value_to_string: Option String + Send + Sync>>, + /// Optional custom conversion function from a string to a boolean value. If the string cannot + /// be parsed, then this should return a `None`. If this happens while the parameter is being + /// updated then the update will be canceled. + pub string_to_value: Option Option + Send + Sync>>, +} + +#[allow(clippy::derivable_impls)] +impl Default for BoolParam { + fn default() -> Self { + Self { + value: false, + value_changed: None, + name: "", + value_to_string: None, + string_to_value: None, + } + } +} + +impl Display for BoolParam { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match (self.value, &self.value_to_string) { + (v, Some(func)) => write!(f, "{}", func(v)), + (true, None) => write!(f, "On"), + (false, None) => write!(f, "Off"), + } + } +} + +impl Param for BoolParam { + type Plain = bool; + + fn update_smoother(&mut self, _sample_rate: f32, _init: bool) { + // Can't really smooth a binary parameter now can you + } + + fn set_from_string(&mut self, string: &str) -> bool { + let value = match &self.string_to_value { + Some(f) => f(string), + None => Some(string.eq_ignore_ascii_case("true") || string.eq_ignore_ascii_case("on")), + }; + + match value { + Some(plain) => { + self.set_plain_value(plain); + true + } + None => false, + } + } + + fn plain_value(&self) -> Self::Plain { + self.value + } + + fn set_plain_value(&mut self, plain: Self::Plain) { + self.value = plain; + if let Some(f) = &self.value_changed { + f(plain); + } + } + + fn normalized_value(&self) -> f32 { + self.preview_normalized(self.value) + } + + fn set_normalized_value(&mut self, normalized: f32) { + self.set_plain_value(self.preview_plain(normalized)); + } + + fn normalized_value_to_string(&self, normalized: f32, _include_unit: bool) -> String { + let value = self.preview_plain(normalized); + match (value, &self.value_to_string) { + (v, Some(f)) => f(v), + (true, None) => String::from("On"), + (false, None) => String::from("Off"), + } + } + + fn string_to_normalized_value(&self, string: &str) -> Option { + let value = match &self.string_to_value { + Some(f) => f(string), + None => Some(string.eq_ignore_ascii_case("true") || string.eq_ignore_ascii_case("on")), + }?; + + Some(self.preview_normalized(value)) + } + + fn preview_normalized(&self, plain: Self::Plain) -> f32 { + if plain { + 1.0 + } else { + 0.0 + } + } + + fn preview_plain(&self, normalized: f32) -> Self::Plain { + normalized > 0.5 + } + + fn as_ptr(&self) -> ParamPtr { + ParamPtr::BoolParam(self as *const BoolParam as *mut BoolParam) + } +} + +impl BoolParam { + /// Build a new [Self]. Use the other associated functions to modify the behavior of the + /// parameter. + pub fn new(name: &'static str, default: bool) -> Self { + Self { + value: default, + name, + ..Default::default() + } + } + + /// Run a callback whenever this parameter's value changes. The argument passed to this function + /// is the parameter's new value. This should not do anything expensive as it may be called + /// multiple times in rapid succession, and it can be run from both the GUI and the audio + /// thread. + pub fn with_callback(mut self, callback: Arc) -> Self { + self.value_changed = Some(callback); + self + } + + /// Use a custom conversion function to convert the boolean value to a string. + pub fn with_value_to_string( + mut self, + callback: Arc String + Send + Sync>, + ) -> Self { + self.value_to_string = Some(callback); + self + } + + /// Use a custom conversion function to convert from a string to a boolean value. If the string + /// cannot be parsed, then this should return a `None`. If this happens while the parameter is + /// being updated then the update will be canceled. + pub fn with_string_to_value( + mut self, + callback: Arc Option + Send + Sync>, + ) -> Self { + self.string_to_value = Some(callback); + self + } +}