From 06c5e4b04af16a90b02e9af934d8ec025259781c Mon Sep 17 00:00:00 2001 From: Robbert van der Helm <mail@robbertvanderhelm.nl> Date: Mon, 2 May 2022 15:46:57 +0200 Subject: [PATCH] Make parameter modulation stick after automation This is how it's supposed to be implemented in CLAP. --- src/param.rs | 15 ++++++++++----- src/param/boolean.rs | 33 +++++++++++++++++++++++---------- src/param/float.rs | 33 +++++++++++++++++++++++---------- src/param/integer.rs | 33 +++++++++++++++++++++++---------- src/wrapper/clap/wrapper.rs | 6 +++--- 5 files changed, 82 insertions(+), 38 deletions(-) diff --git a/src/param.rs b/src/param.rs index 48bcbb3f..3e04c909 100644 --- a/src/param.rs +++ b/src/param.rs @@ -145,20 +145,25 @@ pub trait Param: Display { /// Contains the setters for parameters. These should not be exposed to plugins to avoid confusion. pub(crate) trait ParamMut: Param { /// Set this parameter based on a plain, unnormalized value. This does not snap to step sizes - /// for continuous parameters (i.e. [`FloatParam`]). + /// for continuous parameters (i.e. [`FloatParam`]). If + /// [`modulate_value()`][Self::modulate_value()] has previously been called with a non zero + /// value then this offset is taken into account to form the effective value. /// /// This does **not** update the smoother. fn set_plain_value(&mut self, plain: Self::Plain); /// Set this parameter based on a normalized value. The normalized value will be snapped to the - /// step size for continuous parameters (i.e. [`FloatParam`]). + /// step size for continuous parameters (i.e. [`FloatParam`]). If + /// [`modulate_value()`][Self::modulate_value()] has previously been called with a non zero + /// value then this offset is taken into account to form the effective value. /// /// This does **not** update the smoother. fn set_normalized_value(&mut self, normalized: f32); - /// Add a modulation offset to the value's unmodulated value. Out of bound values will be - /// clamped to the parameter's range. The normalized value will be snapped to the step size for - /// continuous parameters (i.e. [`FloatParam`]). + /// Add a modulation offset to the value's unmodulated value. This value sticks until this + /// function is called again with a 0.0 value. Out of bound values will be clamped to the + /// parameter's range. The normalized value will be snapped to the step size for continuous + /// parameters (i.e. [`FloatParam`]). /// /// This does **not** update the smoother. fn modulate_value(&mut self, modulation_offset: f32); diff --git a/src/param/boolean.rs b/src/param/boolean.rs index 78875ebf..e6b3dcab 100644 --- a/src/param/boolean.rs +++ b/src/param/boolean.rs @@ -19,6 +19,10 @@ pub struct BoolParam { /// The field's value normalized to the `[0, 1]` range before any monophonic automation coming /// from the host has been applied. This will always be the same as `value` for VST3 plugins. unmodulated_normalized_value: f32, + /// A value in `[-1, 1]` indicating the amount of modulation applied to + /// `unmodulated_normalized_`. This needs to be stored separately since the normalied values are + /// clamped, and this value persists after new automation events. + modulation_offset: f32, /// The field's default value. default: bool, @@ -144,8 +148,14 @@ impl ParamMut for BoolParam { fn set_plain_value(&mut self, plain: Self::Plain) { self.unmodulated_value = plain; self.unmodulated_normalized_value = self.preview_normalized(plain); - self.value = self.unmodulated_value; - self.normalized_value = self.unmodulated_normalized_value; + if self.modulation_offset == 0.0 { + self.value = self.unmodulated_value; + self.normalized_value = self.unmodulated_normalized_value; + } else { + self.normalized_value = + (self.unmodulated_normalized_value + self.modulation_offset).clamp(0.0, 1.0); + self.value = self.preview_plain(self.normalized_value); + } if let Some(f) = &self.value_changed { f(self.value); } @@ -154,20 +164,22 @@ impl ParamMut for BoolParam { fn set_normalized_value(&mut self, normalized: f32) { self.unmodulated_value = self.preview_plain(normalized); self.unmodulated_normalized_value = normalized; - self.value = self.unmodulated_value; - self.normalized_value = self.unmodulated_normalized_value; + if self.modulation_offset == 0.0 { + self.value = self.unmodulated_value; + self.normalized_value = self.unmodulated_normalized_value; + } else { + self.normalized_value = + (self.unmodulated_normalized_value + self.modulation_offset).clamp(0.0, 1.0); + self.value = self.preview_plain(self.normalized_value); + } if let Some(f) = &self.value_changed { f(self.value); } } fn modulate_value(&mut self, modulation_offset: f32) { - self.normalized_value = - (self.unmodulated_normalized_value + modulation_offset).clamp(0.0, 1.0); - self.value = self.preview_plain(self.normalized_value); - if let Some(f) = &self.value_changed { - f(self.value); - } + self.modulation_offset = modulation_offset; + self.set_normalized_value(self.unmodulated_normalized_value); } fn update_smoother(&mut self, _sample_rate: f32, _init: bool) { @@ -184,6 +196,7 @@ impl BoolParam { normalized_value: if default { 1.0 } else { 0.0 }, unmodulated_value: default, unmodulated_normalized_value: if default { 1.0 } else { 0.0 }, + modulation_offset: 0.0, default, flags: ParamFlags::default(), diff --git a/src/param/float.rs b/src/param/float.rs index e066ded1..14fc874a 100644 --- a/src/param/float.rs +++ b/src/param/float.rs @@ -31,6 +31,10 @@ pub struct FloatParam { /// The field's value normalized to the `[0, 1]` range before any monophonic automation coming /// from the host has been applied. This will always be the same as `value` for VST3 plugins. unmodulated_normalized_value: f32, + /// A value in `[-1, 1]` indicating the amount of modulation applied to + /// `unmodulated_normalized_`. This needs to be stored separately since the normalied values are + /// clamped, and this value persists after new automation events. + modulation_offset: f32, /// The field's default plain, unnormalized value. default: f32, /// An optional smoother that will automatically interpolate between the new automation values @@ -204,8 +208,14 @@ impl ParamMut for FloatParam { fn set_plain_value(&mut self, plain: Self::Plain) { self.unmodulated_value = plain; self.unmodulated_normalized_value = self.preview_normalized(plain); - self.value = self.unmodulated_value; - self.normalized_value = self.unmodulated_normalized_value; + if self.modulation_offset == 0.0 { + self.value = self.unmodulated_value; + self.normalized_value = self.unmodulated_normalized_value; + } else { + self.normalized_value = + (self.unmodulated_normalized_value + self.modulation_offset).clamp(0.0, 1.0); + self.value = self.preview_plain(self.normalized_value); + } if let Some(f) = &self.value_changed { f(self.value); } @@ -214,20 +224,22 @@ impl ParamMut for FloatParam { fn set_normalized_value(&mut self, normalized: f32) { self.unmodulated_value = self.preview_plain(normalized); self.unmodulated_normalized_value = normalized; - self.value = self.unmodulated_value; - self.normalized_value = self.unmodulated_normalized_value; + if self.modulation_offset == 0.0 { + self.value = self.unmodulated_value; + self.normalized_value = self.unmodulated_normalized_value; + } else { + self.normalized_value = + (self.unmodulated_normalized_value + self.modulation_offset).clamp(0.0, 1.0); + self.value = self.preview_plain(self.normalized_value); + } if let Some(f) = &self.value_changed { f(self.value); } } fn modulate_value(&mut self, modulation_offset: f32) { - self.normalized_value = - (self.unmodulated_normalized_value + modulation_offset).clamp(0.0, 1.0); - self.value = self.preview_plain(self.normalized_value); - if let Some(f) = &self.value_changed { - f(self.value); - } + self.modulation_offset = modulation_offset; + self.set_normalized_value(self.unmodulated_normalized_value); } fn update_smoother(&mut self, sample_rate: f32, reset: bool) { @@ -248,6 +260,7 @@ impl FloatParam { normalized_value: range.normalize(default), unmodulated_value: default, unmodulated_normalized_value: range.normalize(default), + modulation_offset: 0.0, default, smoothed: Smoother::none(), diff --git a/src/param/integer.rs b/src/param/integer.rs index 1fae8592..61c0b332 100644 --- a/src/param/integer.rs +++ b/src/param/integer.rs @@ -31,6 +31,10 @@ pub struct IntParam { /// The field's value normalized to the `[0, 1]` range before any monophonic automation coming /// from the host has been applied. This will always be the same as `value` for VST3 plugins. unmodulated_normalized_value: f32, + /// A value in `[-1, 1]` indicating the amount of modulation applied to + /// `unmodulated_normalized_`. This needs to be stored separately since the normalied values are + /// clamped, and this value persists after new automation events. + modulation_offset: f32, /// The field's default plain, unnormalized value. default: i32, /// An optional smoother that will automatically interpolate between the new automation values @@ -169,8 +173,14 @@ impl ParamMut for IntParam { fn set_plain_value(&mut self, plain: Self::Plain) { self.unmodulated_value = plain; self.unmodulated_normalized_value = self.preview_normalized(plain); - self.value = self.unmodulated_value; - self.normalized_value = self.unmodulated_normalized_value; + if self.modulation_offset == 0.0 { + self.value = self.unmodulated_value; + self.normalized_value = self.unmodulated_normalized_value; + } else { + self.normalized_value = + (self.unmodulated_normalized_value + self.modulation_offset).clamp(0.0, 1.0); + self.value = self.preview_plain(self.normalized_value); + } if let Some(f) = &self.value_changed { f(self.value); } @@ -179,20 +189,22 @@ impl ParamMut for IntParam { fn set_normalized_value(&mut self, normalized: f32) { self.unmodulated_value = self.preview_plain(normalized); self.unmodulated_normalized_value = normalized; - self.value = self.unmodulated_value; - self.normalized_value = self.unmodulated_normalized_value; + if self.modulation_offset == 0.0 { + self.value = self.unmodulated_value; + self.normalized_value = self.unmodulated_normalized_value; + } else { + self.normalized_value = + (self.unmodulated_normalized_value + self.modulation_offset).clamp(0.0, 1.0); + self.value = self.preview_plain(self.normalized_value); + } if let Some(f) = &self.value_changed { f(self.value); } } fn modulate_value(&mut self, modulation_offset: f32) { - self.normalized_value = - (self.unmodulated_normalized_value + modulation_offset).clamp(0.0, 1.0); - self.value = self.preview_plain(self.normalized_value); - if let Some(f) = &self.value_changed { - f(self.value); - } + self.modulation_offset = modulation_offset; + self.set_normalized_value(self.unmodulated_normalized_value); } fn update_smoother(&mut self, sample_rate: f32, reset: bool) { @@ -213,6 +225,7 @@ impl IntParam { normalized_value: range.normalize(default), unmodulated_value: default, unmodulated_normalized_value: range.normalize(default), + modulation_offset: 0.0, default, smoothed: Smoother::none(), diff --git a/src/wrapper/clap/wrapper.rs b/src/wrapper/clap/wrapper.rs index 07e9af76..b369d817 100644 --- a/src/wrapper/clap/wrapper.rs +++ b/src/wrapper/clap/wrapper.rs @@ -249,9 +249,9 @@ pub enum ClapParamUpdate { /// Set the parameter to this plain value. In our wrapper the plain values are the normalized /// values multiplied by the step count for discrete parameters. PlainValueSet(f64), - /// Set a normalized offset for the parameter's plain value. A `PlainValueSet` clears out any - /// modulation, and subsequent modulation events replace the previous one. In other words, this - /// is not additive. These values should also be divided by the step size. + /// Set a normalized offset for the parameter's plain value. Subsequent modulation events + /// override the previous one, but `PlainValueSet`s do not override the existing modulation. + /// These values should also be divided by the step size. PlainValueMod(f64), }