From 4f501f539ed181e06d7f317575635de470b65429 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Mon, 24 Jan 2022 20:59:46 +0100 Subject: [PATCH] Implement the rest of the basic parameter UI --- src/params.rs | 103 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 3 deletions(-) diff --git a/src/params.rs b/src/params.rs index 2fe3c84c..a29cddda 100644 --- a/src/params.rs +++ b/src/params.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use std::{fmt::Display, sync::atomic::Ordering}; + use crate::atomic::AtomicType; /// Describes a single normalized parameter and also stores its value. @@ -45,7 +47,9 @@ trait NormalizebleRange { /// process. pub struct PlainParam { /// The field's current, normalized value. Should be initialized with the default value using - /// `T::new_atomic(...)` ([AtomicType::new_atomic]). + /// `T::new_atomic(...)` ([AtomicType::new_atomic]). 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. pub value: ::AtomicType, /// The distribution of the parameter's values. @@ -56,10 +60,103 @@ pub struct PlainParam { pub unit: &'static str, /// Optional custom conversion function from an **unnormalized** value to a string. pub value_to_string: Option String>>, - /// Optional custom conversion function from a string to an **unnormalized** value. - pub string_to_value: Option T>>, + /// Optional custom conversion function from a string to an **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. + pub string_to_value: Option Option>>, } +impl Param { + /// Get the human readable name for this parameter. + pub fn name(&self) -> &'static str { + match &self { + Param::FloatParam(p) => p.name, + Param::IntParam(p) => p.name, + } + } + + /// Set this parameter based on a string. Returns whether the updating succeeded. That can fail + /// if the string cannot be parsed. + /// + /// TODO: After implementing VST3, check if we handle parsing failures correctly + pub fn from_string(&self, string: &str) -> bool { + // TODO: Debug asserts on failures + match &self { + Param::FloatParam(p) => { + let value = match &p.string_to_value { + Some(f) => f(string), + // TODO: Check how Rust's parse function handles trailing garbage + None => string.parse().ok(), + }; + + match value { + Some(unnormalized) => { + p.value.store(unnormalized, Ordering::Relaxed); + true + } + None => false, + } + } + Param::IntParam(p) => { + let value = match &p.string_to_value { + Some(f) => f(string), + None => string.parse().ok(), + }; + + match value { + Some(unnormalized) => { + p.value.store(unnormalized, Ordering::Relaxed); + true + } + None => false, + } + } + } + } + + /// Get the normalized `[0, 1]` value for this parameter. + pub fn normalized_value(&self) -> f32 { + match &self { + Param::FloatParam(p) => p.range.normalize(p.value.load(Ordering::Relaxed)), + Param::IntParam(p) => p.range.normalize(p.value.load(Ordering::Relaxed)), + } + } + + /// Set this parameter based on a normalized value. + pub fn set_normalized_value(&self, normalized: f32) { + match &self { + Param::FloatParam(p) => p + .value + .store(p.range.unnormalize(normalized), Ordering::Relaxed), + Param::IntParam(p) => p + .value + .store(p.range.unnormalize(normalized), Ordering::Relaxed), + } + } +} + +impl Display for Param { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Param::FloatParam(p) => { + let unnormalized = p.value.load(Ordering::Relaxed); + match &p.value_to_string { + Some(func) => write!(f, "{}{}", func(unnormalized), p.unit), + None => write!(f, "{}{}", unnormalized, p.unit), + } + } + Param::IntParam(p) => { + let unnormalized = p.value.load(Ordering::Relaxed); + match &p.value_to_string { + Some(func) => write!(f, "{}{}", func(unnormalized), p.unit), + None => write!(f, "{}{}", unnormalized, p.unit), + } + } + } + } +} + +// TODO: Clamping impl NormalizebleRange for Range { fn normalize(&self, unnormalized: f32) -> f32 { match &self {