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),
 }