diff --git a/src/param.rs b/src/param.rs index 22902c82..b7ade65f 100644 --- a/src/param.rs +++ b/src/param.rs @@ -60,10 +60,7 @@ pub trait Param: Display { fn plain_value(&self) -> Self::Plain; /// Get the normalized `[0, 1]` value for this parameter. - #[inline] - fn normalized_value(&self) -> f32 { - self.preview_normalized(self.plain_value()) - } + fn normalized_value(&self) -> f32; /// Get the unnormalized default value for this parameter. fn default_plain_value(&self) -> Self::Plain; @@ -112,9 +109,7 @@ pub trait Param: Display { /// continuous parameters (i.e. [`FloatParam`]). /// /// This does **not** update the smoother. - fn set_normalized_value(&mut self, normalized: f32) { - self.set_plain_value(self.preview_plain(normalized)) - } + fn set_normalized_value(&mut self, normalized: f32); /// Get the string representation for a normalized value. Used as part of the wrappers. Most /// plugin formats already have support for units, in which case it shouldn't be part of this diff --git a/src/param/boolean.rs b/src/param/boolean.rs index 18ed1c7b..1c9fb096 100644 --- a/src/param/boolean.rs +++ b/src/param/boolean.rs @@ -9,8 +9,10 @@ use super::{Param, ParamFlags}; /// A simple boolean parameter. #[repr(C, align(4))] pub struct BoolParam { - /// The field's current value. Should be initialized with the default value. + /// The field's current value. pub value: bool, + /// The field's current value normalized to the `[0, 1]` range. + normalized_value: f32, /// The field's default value. default: bool, @@ -37,6 +39,7 @@ impl Default for BoolParam { fn default() -> Self { Self { value: false, + normalized_value: 0.0, default: false, flags: ParamFlags::default(), value_changed: None, @@ -73,6 +76,10 @@ impl Param for BoolParam { self.value } + fn normalized_value(&self) -> f32 { + self.normalized_value + } + #[inline] fn default_plain_value(&self) -> Self::Plain { self.default @@ -92,8 +99,17 @@ impl Param for BoolParam { fn set_plain_value(&mut self, plain: Self::Plain) { self.value = plain; + self.normalized_value = self.preview_normalized(plain); if let Some(f) = &self.value_changed { - f(plain); + f(self.value); + } + } + + fn set_normalized_value(&mut self, normalized: f32) { + self.value = self.preview_plain(normalized); + self.normalized_value = normalized; + if let Some(f) = &self.value_changed { + f(self.value); } } diff --git a/src/param/enums.rs b/src/param/enums.rs index c8438022..964e5a4c 100644 --- a/src/param/enums.rs +++ b/src/param/enums.rs @@ -71,15 +71,14 @@ impl Default for EnumParam { Self { inner: EnumParamInner { - inner: IntParam { - value: T::default().to_index() as i32, - default: T::default().to_index() as i32, - range: IntRange::Linear { + inner: IntParam::new( + "", + T::default().to_index() as i32, + IntRange::Linear { min: 0, max: variants.len() as i32 - 1, }, - ..Default::default() - }, + ), variants, }, _marker: PhantomData, @@ -115,6 +114,11 @@ impl Param for EnumParam { T::from_index(self.inner.plain_value() as usize) } + #[inline] + fn normalized_value(&self) -> f32 { + self.inner.normalized_value() + } + #[inline] fn default_plain_value(&self) -> Self::Plain { T::from_index(self.inner.default_plain_value() as usize) @@ -136,6 +140,10 @@ impl Param for EnumParam { self.inner.set_plain_value(T::to_index(plain) as i32) } + fn set_normalized_value(&mut self, normalized: f32) { + self.inner.set_normalized_value(normalized) + } + fn normalized_value_to_string(&self, normalized: f32, include_unit: bool) -> String { self.inner .normalized_value_to_string(normalized, include_unit) @@ -174,7 +182,7 @@ impl Param for EnumParamInner { type Plain = i32; fn name(&self) -> &str { - &self.inner.name + self.inner.name() } fn unit(&self) -> &'static str { @@ -186,6 +194,11 @@ impl Param for EnumParamInner { self.inner.plain_value() } + #[inline] + fn normalized_value(&self) -> f32 { + self.inner.normalized_value() + } + #[inline] fn default_plain_value(&self) -> Self::Plain { self.inner.default_plain_value() @@ -207,6 +220,10 @@ impl Param for EnumParamInner { self.inner.set_plain_value(plain) } + fn set_normalized_value(&mut self, normalized: f32) { + self.inner.set_normalized_value(normalized) + } + fn normalized_value_to_string(&self, normalized: f32, _include_unit: bool) -> String { let index = self.preview_plain(normalized); self.variants[index as usize].to_string() @@ -253,15 +270,14 @@ impl EnumParam { Self { inner: EnumParamInner { - inner: IntParam { - value: T::to_index(default) as i32, - range: IntRange::Linear { + inner: IntParam::new( + name, + T::to_index(default) as i32, + IntRange::Linear { min: 0, max: variants.len() as i32 - 1, }, - name: name.into(), - ..Default::default() - }, + ), variants, }, _marker: PhantomData, @@ -273,7 +289,7 @@ impl EnumParam { /// 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.inner.inner.value_changed = Some(Arc::new(move |value| { + self.inner.inner = self.inner.inner.with_callback(Arc::new(move |value| { callback(T::from_index(value as usize)) })); self diff --git a/src/param/float.rs b/src/param/float.rs index bb7e8877..680a60bd 100644 --- a/src/param/float.rs +++ b/src/param/float.rs @@ -21,10 +21,10 @@ use super::{Param, ParamFlags}; // a partially written to value here. We should probably reconsider this at some point though. #[repr(C, align(4))] pub struct FloatParam { - /// The field's current plain, unnormalized value. Should be initialized with the default value. - /// Storing parameter values like this instead of in a single contiguous array is bad for cache - /// locality, but it does allow for a much nicer declarative API. + /// The field's current plain, unnormalized value. pub value: f32, + /// The field's current value normalized to the `[0, 1]` range. + normalized_value: f32, /// The field's default plain, unnormalized value. default: f32, /// An optional smoother that will automatically interpolate between the new automation values @@ -69,6 +69,7 @@ impl Default for FloatParam { fn default() -> Self { Self { value: 0.0, + normalized_value: 0.0, default: 0.0, smoothed: Smoother::none(), flags: ParamFlags::default(), @@ -112,6 +113,11 @@ impl Param for FloatParam { self.value } + #[inline] + fn normalized_value(&self) -> Self::Plain { + self.normalized_value + } + #[inline] fn default_plain_value(&self) -> Self::Plain { self.default @@ -146,8 +152,17 @@ impl Param for FloatParam { fn set_plain_value(&mut self, plain: Self::Plain) { self.value = plain; + self.normalized_value = self.preview_normalized(plain); if let Some(f) = &self.value_changed { - f(plain); + f(self.value); + } + } + + fn set_normalized_value(&mut self, normalized: f32) { + self.value = self.preview_plain(normalized); + self.normalized_value = normalized; + if let Some(f) = &self.value_changed { + f(self.value); } } @@ -218,6 +233,7 @@ impl FloatParam { pub fn new(name: impl Into, default: f32, range: FloatRange) -> Self { Self { value: default, + normalized_value: range.normalize(default), default, range, name: name.into(), diff --git a/src/param/integer.rs b/src/param/integer.rs index aa8d2147..2d73382f 100644 --- a/src/param/integer.rs +++ b/src/param/integer.rs @@ -21,18 +21,18 @@ use super::{Param, ParamFlags}; // a partially written to value here. We should probably reconsider this at some point though. #[repr(C, align(4))] pub struct IntParam { - /// The field's current plain, unnormalized value. Should be initialized with the default value. - /// Storing parameter values like this instead of in a single contiguous array is bad for cache - /// locality, but it does allow for a much nicer declarative API. + /// The field's current plain, unnormalized value. pub value: i32, + /// The field's current value normalized to the `[0, 1]` range. + normalized_value: f32, /// The field's default plain, unnormalized value. - pub(crate) default: i32, + default: i32, /// An optional smoother that will automatically interpolate between the new automation values /// set by the host. pub smoothed: Smoother, /// Flags to control the parameter's behavior. See [`ParamFlags`]. - pub(crate) flags: ParamFlags, + flags: ParamFlags, /// Optional callback for listening to value changes. The argument passed to this function is /// the parameter's new **plain** value. This should not do anything expensive as it may be /// called multiple times in rapid succession. @@ -41,30 +41,31 @@ pub struct IntParam { /// parameters struct, move a clone of that `Arc` into this closure, and then modify that. /// /// TODO: We probably also want to pass the old value to this function. - pub(crate) value_changed: Option>, + value_changed: Option>, /// The distribution of the parameter's values. - pub(crate) range: IntRange, + range: IntRange, /// The parameter's human readable display name. - pub(crate) name: String, + name: String, /// The parameter value's unit, added after `value_to_string` if that is set. NIH-plug will not /// automatically add a space before the unit. - pub(crate) unit: &'static str, + unit: &'static str, /// Optional custom conversion function from a plain **unnormalized** value to a string. - pub(crate) value_to_string: Option String + Send + Sync>>, + value_to_string: Option String + Send + Sync>>, /// Optional custom conversion function from a string to a plain **unnormalized** 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. /// /// The input string may or may not contain the unit, so you will need to be able to handle /// that. - pub(crate) string_to_value: Option Option + Send + Sync>>, + string_to_value: Option Option + Send + Sync>>, } impl Default for IntParam { fn default() -> Self { Self { value: 0, + normalized_value: 0.0, default: 0, smoothed: Smoother::none(), flags: ParamFlags::default(), @@ -98,10 +99,17 @@ impl Param for IntParam { self.unit } + #[inline] fn plain_value(&self) -> Self::Plain { self.value } + #[inline] + fn normalized_value(&self) -> f32 { + self.normalized_value + } + + #[inline] fn default_plain_value(&self) -> Self::Plain { self.default } @@ -120,8 +128,17 @@ impl Param for IntParam { fn set_plain_value(&mut self, plain: Self::Plain) { self.value = plain; + self.normalized_value = self.preview_normalized(plain); if let Some(f) = &self.value_changed { - f(plain); + f(self.value); + } + } + + fn set_normalized_value(&mut self, normalized: f32) { + self.value = self.preview_plain(normalized); + self.normalized_value = normalized; + if let Some(f) = &self.value_changed { + f(self.value); } } @@ -180,6 +197,7 @@ impl IntParam { pub fn new(name: impl Into, default: i32, range: IntRange) -> Self { Self { value: default, + normalized_value: range.normalize(default), default, range, name: name.into(),