rusty_daw_core/
parameter.rs

1// Some modified code from baseplug:
2//
3// https://github.com/wrl/baseplug/blob/trunk/src/parameter.rs
4// https://github.com/wrl/baseplug/blob/trunk/LICENSE-APACHE
5// https://github.com/wrl/baseplug/blob/trunk/LICENSE-MIT
6//
7//  Thanks wrl! :)
8
9use std::sync::Arc;
10
11use super::atomic::{AtomicF32, AtomicF64};
12use super::decibel::{
13    coeff_to_db_clamped_neg_90_db_f32, coeff_to_db_clamped_neg_90_db_f64,
14    db_to_coeff_clamped_neg_90_db_f32, db_to_coeff_clamped_neg_90_db_f64,
15};
16use super::{SampleRate, Seconds, SmoothF32, SmoothF64, SmoothOutputF32, SmoothOutputF64};
17
18/// A good default value to use as `smooth_secs` parameter when creating a [`ParamF32`]/[`ParamF64`].
19///
20/// This specifies that the low-pass parameter smoothing filter should use a period of `5 ms`.
21///
22/// [`ParamF32`]: struct.ParamF32.html
23/// [`ParamF64`]: struct.ParamF64.html
24pub const DEFAULT_SMOOTH_SECS: Seconds = Seconds(5.0 / 1_000.0);
25
26/// A good default value to use as `gradient` parameter when creating a [`ParamF32`]/[`ParamF64`] that
27/// deals with decibels.
28pub const DEFAULT_DB_GRADIENT: Gradient = Gradient::Power(0.15);
29
30/// The gradient used when mapping the normalized value in the range `[0.0, 1.0]` to the
31/// desired value.
32///
33/// For example, it is useful for parameters dealing with decibels to have a mapping
34/// gradient around `Power(0.15)`. This is so one tick near the top of the slider/knob
35/// controlling this parameter causes a small change in dB around `0.0 dB` and one tick
36/// on the other end causes a large change in dB around `-90.0 dB`.
37#[derive(Debug, Clone, Copy, PartialEq)]
38pub enum Gradient {
39    /// Linear mapping
40    Linear,
41    /// Power mapping
42    ///
43    /// For example, it is useful for parameters dealing with decibels to have a mapping
44    /// gradient around `Power(0.15)`. This is so one tick near the top of the slider/knob
45    /// controlling this parameter causes a small change in dB around `0.0 dB` and one tick
46    /// on the other end causes a large change in dB around `-90.0 dB`.
47    Power(f32),
48    /// Exponential (logarithmic) mapping
49    ///
50    /// This is useful for parameters dealing with frequency in Hz.
51    Exponential,
52}
53
54/// The unit of this parameter. This signifies how the value displayed to the end user should
55/// differ from the actual value used in DSP.
56#[derive(Debug, Clone, Copy, PartialEq)]
57pub enum Unit {
58    /// Any kind of unit where the value displayed to the end user is the same value used
59    /// in the DSP.
60    Generic,
61    /// Signifies that the value displayed to the end user should be in decibels and the
62    /// value used in the DSP should be in raw amplitude.
63    ///
64    /// In addition, whenever the dB value is less than or equal to `-90.0 dB`, then the
65    /// resulting raw DSP ampilitude value will be clamped to `0.0` (essentially equaling
66    /// `-infinity dB`).
67    Decibels,
68}
69
70impl Unit {
71    /// Convert the given unit value to the corresponding raw value used in DSP.
72    ///
73    /// This is only effective when this unit is not of type `Unit::Generic`.
74    pub fn unit_to_dsp_f32(&self, value: f32) -> f32 {
75        match self {
76            Unit::Decibels => db_to_coeff_clamped_neg_90_db_f32(value),
77            _ => value,
78        }
79    }
80
81    /// Convert the given raw DSP value to the corresponding unit value.
82    ///
83    /// This is only effective when this unit is not of type `Unit::Generic`.
84    pub fn dsp_to_unit_f32(&self, dsp_value: f32) -> f32 {
85        match self {
86            Unit::Decibels => coeff_to_db_clamped_neg_90_db_f32(dsp_value),
87            _ => dsp_value,
88        }
89    }
90
91    /// Convert the given unit value to the corresponding raw value used in DSP.
92    ///
93    /// This is only effective when this unit is not of type `Unit::Generic`.
94    pub fn unit_to_dsp_f64(&self, value: f64) -> f64 {
95        match self {
96            Unit::Decibels => db_to_coeff_clamped_neg_90_db_f64(value),
97            _ => value,
98        }
99    }
100
101    /// Convert the given raw DSP value to the corresponding unit value.
102    ///
103    /// This is only effective when this unit is not of type `Unit::Generic`.
104    pub fn dsp_to_unit_f64(&self, dsp_value: f64) -> f64 {
105        match self {
106            Unit::Decibels => coeff_to_db_clamped_neg_90_db_f64(dsp_value),
107            _ => dsp_value,
108        }
109    }
110}
111
112/// An auto-smoothed parameter with an `f32` value.
113pub struct ParamF32<const MAX_BLOCKSIZE: usize> {
114    min: f32,
115    max: f32,
116    gradient: Gradient,
117    unit: Unit,
118
119    shared_normalized: Arc<AtomicF32>,
120    normalized: f32,
121
122    value: f32,
123
124    smoothed: SmoothF32<MAX_BLOCKSIZE>,
125    smooth_secs: Seconds,
126}
127
128impl<const MAX_BLOCKSIZE: usize> ParamF32<MAX_BLOCKSIZE> {
129    /// Create a Parameter/Handle pair from its (de-normalized) value.
130    ///
131    /// * value - The initial (de-normalized) value of the parameter.
132    /// * min - The minimum (de-normalized) value of the parameter.
133    /// * max - The maximum (de-normalized) value of the parameter.
134    /// * gradient - The [`Gradient`] mapping used when converting from the normalized value
135    /// in the range `[0.0, 1.0]` to the desired value. If this parameter deals with decibels,
136    /// you may use `ParamF32::DEFAULT_SMOOTH_SECS` as a good default.
137    /// * unit - The [`Unit`] that signifies how the value displayed to the end user should
138    /// differ from the actual value used in DSP.
139    /// * smooth_secs: The period of the low-pass parameter smoothing filter (for declicking). You
140    /// may use `ParamF32::DEFAULT_SMOOTH_SECS` as a good default.
141    /// * sample_rate: The sample rate of this process. This is used for the low-pass parameter
142    /// smoothing filter.
143    ///
144    /// [`Gradient`]: enum.Gradient.html
145    /// [`Unit`]: enum.Unit.html
146    pub fn from_value(
147        value: f32,
148        min: f32,
149        max: f32,
150        gradient: Gradient,
151        unit: Unit,
152        smooth_secs: Seconds,
153        sample_rate: SampleRate,
154    ) -> (Self, ParamF32Handle) {
155        let normalized = value_to_normalized_f32(value, min, max, gradient);
156
157        let handle_value = normalized_to_value_f32(normalized, min, max, gradient);
158        let rt_value = match unit {
159            Unit::Decibels => db_to_coeff_clamped_neg_90_db_f32(handle_value),
160            _ => handle_value,
161        };
162
163        let shared_normalized = Arc::new(AtomicF32::new(normalized));
164
165        let mut smoothed = SmoothF32::new(rt_value);
166        smoothed.set_speed(sample_rate, smooth_secs);
167
168        (
169            Self {
170                min,
171                max,
172                gradient,
173                unit,
174                shared_normalized: Arc::clone(&shared_normalized),
175                normalized,
176                value: rt_value,
177                smoothed,
178                smooth_secs,
179            },
180            ParamF32Handle {
181                min,
182                max,
183                gradient,
184                unit,
185                shared_normalized,
186            },
187        )
188    }
189
190    /// Create a Parameter/Handle pair from its normalized value in the range `[0.0, 1.0]`.
191    ///
192    /// * value - The initial normalized value of the parameter in the range `[0.0, 1.0]`.
193    /// * min - The minimum (de-normalized) value of the parameter.
194    /// * max - The maximum (de-normalized) value of the parameter.
195    /// * gradient - The [`Gradient`] mapping used when converting from the normalized value
196    /// in the range `[0.0, 1.0]` to the desired value. If this parameter deals with decibels,
197    /// you may use `ParamF32::DEFAULT_SMOOTH_SECS` as a good default.
198    /// * unit - The [`Unit`] that signifies how the value displayed to the end user should
199    /// differ from the actual value used in DSP.
200    /// * smooth_secs: The period of the low-pass parameter smoothing filter (for declicking). You
201    /// may use `ParamF32::DEFAULT_SMOOTH_SECS` as a good default.
202    /// * sample_rate: The sample rate of this process. This is used for the low-pass parameter
203    /// smoothing filter.
204    ///
205    /// [`Gradient`]: enum.Gradient.html
206    /// [`Unit`]: enum.Unit.html
207    pub fn from_normalized(
208        normalized: f32,
209        min_value: f32,
210        max_value: f32,
211        gradient: Gradient,
212        unit: Unit,
213        smooth_secs: Seconds,
214        sample_rate: SampleRate,
215    ) -> (Self, ParamF32Handle) {
216        let normalized = normalized.clamp(0.0, 1.0);
217
218        let shared_normalized = Arc::new(AtomicF32::new(normalized));
219
220        let handle_value = normalized_to_value_f32(normalized, min_value, max_value, gradient);
221        let rt_value = match unit {
222            Unit::Decibels => db_to_coeff_clamped_neg_90_db_f32(handle_value),
223            _ => handle_value,
224        };
225
226        let mut smoothed = SmoothF32::new(rt_value);
227        smoothed.set_speed(sample_rate, smooth_secs);
228
229        (
230            Self {
231                min: min_value,
232                max: max_value,
233                gradient,
234                unit,
235                shared_normalized: Arc::clone(&shared_normalized),
236                normalized,
237                value: rt_value,
238                smoothed,
239                smooth_secs,
240            },
241            ParamF32Handle {
242                min: min_value,
243                max: max_value,
244                gradient,
245                unit,
246                shared_normalized,
247            },
248        )
249    }
250
251    /// Set the (de-normalized) value of this parameter.
252    pub fn set_value(&mut self, value: f32) {
253        if self.value != value {
254            self.normalized = value_to_normalized_f32(value, self.min, self.max, self.gradient);
255            self.shared_normalized.set(self.normalized);
256
257            let v = normalized_to_value_f32(self.normalized, self.min, self.max, self.gradient);
258            self.value = match self.unit {
259                Unit::Decibels => db_to_coeff_clamped_neg_90_db_f32(v),
260                _ => v,
261            };
262
263            self.smoothed.set(self.value);
264        }
265    }
266
267    /// Set the normalized value of this parameter in the range `[0.0, 1.0]`.
268    pub fn set_normalized(&mut self, normalized: f32) {
269        if self.normalized != normalized {
270            self.normalized = normalized.clamp(0.0, 1.0);
271            self.shared_normalized.set(self.normalized);
272
273            let v = normalized_to_value_f32(self.normalized, self.min, self.max, self.gradient);
274            self.value = match self.unit {
275                Unit::Decibels => db_to_coeff_clamped_neg_90_db_f32(v),
276                _ => v,
277            };
278
279            self.smoothed.set(self.value);
280        }
281    }
282
283    /// Reset this parameter (without any smoothing) to the given (de-normalized) value.
284    pub fn reset_from_value(&mut self, value: f32) {
285        self.normalized = value_to_normalized_f32(value, self.min, self.max, self.gradient);
286        self.shared_normalized.set(self.normalized);
287
288        let v = normalized_to_value_f32(self.normalized, self.min, self.max, self.gradient);
289        self.value = match self.unit {
290            Unit::Decibels => db_to_coeff_clamped_neg_90_db_f32(v),
291            _ => v,
292        };
293
294        self.smoothed.reset(self.value);
295    }
296
297    /// Reset this parameter (without any smoothing) to the given normalized value in the range `[0.0, 1.0]`.
298    pub fn reset_from_normalized(&mut self, normalized: f32) {
299        self.normalized = normalized.clamp(0.0, 1.0);
300        self.shared_normalized.set(self.normalized);
301
302        let v = normalized_to_value_f32(self.normalized, self.min, self.max, self.gradient);
303        self.value = match self.unit {
304            Unit::Decibels => db_to_coeff_clamped_neg_90_db_f32(v),
305            _ => v,
306        };
307
308        self.smoothed.reset(self.value);
309    }
310
311    /// Reset the internal smoothing buffer.
312    pub fn reset(&mut self) {
313        self.smoothed.reset(self.value);
314    }
315
316    /// Get the smoothed buffer of values for use in DSP.
317    pub fn smoothed(&mut self, frames: usize) -> SmoothOutputF32<MAX_BLOCKSIZE> {
318        let new_normalized = self.shared_normalized.get();
319        if self.normalized != new_normalized {
320            self.normalized = new_normalized;
321
322            let v = normalized_to_value_f32(self.normalized, self.min, self.max, self.gradient);
323            self.value = match self.unit {
324                Unit::Decibels => db_to_coeff_clamped_neg_90_db_f32(v),
325                _ => v,
326            };
327
328            self.smoothed.set(self.value);
329        }
330
331        self.smoothed.process(frames);
332        self.smoothed.update_status();
333
334        self.smoothed.output()
335    }
336
337    /// Update the sample rate (used for the parameter smoothing LPF).
338    pub fn set_sample_rate(&mut self, sample_rate: SampleRate) {
339        self.smoothed.set_speed(sample_rate, self.smooth_secs);
340    }
341
342    /// The minimum value of this parameter.
343    pub fn min(&self) -> f32 {
344        self.min
345    }
346
347    /// The maximum value of this parameter.
348    pub fn max(&self) -> f32 {
349        self.max
350    }
351
352    /// The [`Gradient`] mapping used when converting from the normalized value
353    /// in the range `[0.0, 1.0]` to the desired value.
354    ///
355    /// [`Gradient`]: enum.Gradient.html
356    pub fn gradient(&self) -> Gradient {
357        self.gradient
358    }
359
360    /// The [`Unit`] that signifies how the value displayed to the end user should
361    /// differ from the actual value used in DSP.
362    ///
363    /// [`Unit`]: enum.Unit.html
364    pub fn unit(&self) -> Unit {
365        self.unit
366    }
367
368    /// Convert the given value to the corresponding normalized range `[0.0, 1.0]`
369    /// of this parameter.
370    pub fn value_to_normalized(&self, value: f32) -> f32 {
371        value_to_normalized_f32(value, self.min, self.max, self.gradient)
372    }
373
374    /// Convert the given normalized value in the range `[0.0, 1.0]` into the
375    /// corresponding value of this parameter.
376    pub fn normalized_to_value(&self, normalized: f32) -> f32 {
377        normalized_to_value_f32(normalized, self.min, self.max, self.gradient)
378    }
379
380    /// The current normalized value in the range `[0.0, 1.0]`. This is only meant for
381    /// communicating with the host. This is not meant to be used to retrieve the latest
382    /// value for DSP. To get the latest value for DSP please use `ParamF32::smoothed()`
383    /// instead.
384    ///
385    /// Please note that this should be called *after* calling `ParamF32::smoothed()`
386    /// if you need the latest value from the corresponding [`ParamF32Handle`],
387    /// otherwise this may not return the latest value.
388    ///
389    /// [`ParamF32Handle`]: struct.ParamF32Handle.html
390    pub fn normalized(&self) -> f32 {
391        self.normalized
392    }
393
394    /// Get the shared normalized float value.
395    ///
396    /// This can be useful to integrate with various plugin APIs.
397    pub fn shared_normalized(&self) -> Arc<AtomicF32> {
398        Arc::clone(&self.shared_normalized)
399    }
400}
401
402/// A handle to get and update the value of an auto-smoothed [`ParamF32`] from a UI.
403///
404/// [`ParamF32`]: struct.ParamF32.html
405pub struct ParamF32Handle {
406    min: f32,
407    max: f32,
408    gradient: Gradient,
409    unit: Unit,
410
411    shared_normalized: Arc<AtomicF32>,
412}
413
414impl ParamF32Handle {
415    /// The normalized value in the range `[0.0, 1.0]`.
416    pub fn normalized(&self) -> f32 {
417        self.shared_normalized.get()
418    }
419
420    /// The (un-normalized) value of this parameter.
421    ///
422    /// Please note that this is calculated from the shared normalized value every time, so
423    /// avoid calling this every frame if you can.
424    pub fn value(&self) -> f32 {
425        normalized_to_value_f32(
426            self.shared_normalized.get(),
427            self.min,
428            self.max,
429            self.gradient,
430        )
431    }
432
433    /// Set the normalized value of this parameter in the range `[0.0, 1.0]`.
434    ///
435    /// Please note that this will ***NOT*** automatically notify the host of the value change
436    /// if you are using this inside a plugin spec such as VST. It is intended for you use your
437    /// own method for achieving this.
438    pub fn set_normalized(&self, normalized: f32) {
439        self.shared_normalized.set(normalized.clamp(0.0, 1.0));
440    }
441
442    /// Set the (un-normalized) value of this parameter.
443    ///
444    /// Please note that this will ***NOT*** automatically notify the host of the value change
445    /// if you are using this inside a plugin spec such as VST. It is intended for you use your
446    /// own method for achieving this.
447    pub fn set_value(&self, value: f32) {
448        let normalized = value_to_normalized_f32(value, self.min, self.max, self.gradient);
449        self.set_normalized(normalized);
450    }
451
452    /// The minimum value of this parameter.
453    pub fn min(&self) -> f32 {
454        self.min
455    }
456
457    /// The maximum value of this parameter.
458    pub fn max(&self) -> f32 {
459        self.max
460    }
461
462    /// The [`Gradient`] mapping used when converting from the normalized value
463    /// in the range `[0.0, 1.0]` to the desired value.
464    ///
465    /// [`Gradient`]: enum.Gradient.html
466    pub fn gradient(&self) -> Gradient {
467        self.gradient
468    }
469
470    /// The [`Unit`] that signifies how the value displayed to the end user should
471    /// differ from the actual value used in DSP.
472    ///
473    /// [`Unit`]: enum.Unit.html
474    pub fn unit(&self) -> Unit {
475        self.unit
476    }
477
478    /// Convert the given value to the corresponding normalized range `[0.0, 1.0]`
479    /// of this parameter.
480    pub fn value_to_normalized(&self, value: f32) -> f32 {
481        value_to_normalized_f32(value, self.min, self.max, self.gradient)
482    }
483
484    /// Convert the given normalized value in the range `[0.0, 1.0]` into the
485    /// corresponding value of this parameter.
486    pub fn normalized_to_value(&self, normalized: f32) -> f32 {
487        normalized_to_value_f32(normalized, self.min, self.max, self.gradient)
488    }
489
490    /// Get the shared normalized float value.
491    ///
492    /// This can be useful to integrate with various plugin APIs.
493    pub fn shared_normalized(&self) -> Arc<AtomicF32> {
494        Arc::clone(&self.shared_normalized)
495    }
496}
497
498impl Clone for ParamF32Handle {
499    fn clone(&self) -> Self {
500        Self {
501            min: self.min,
502            max: self.max,
503            gradient: self.gradient,
504            unit: self.unit,
505
506            shared_normalized: Arc::clone(&self.shared_normalized),
507        }
508    }
509}
510
511fn normalized_to_value_f32(normalized: f32, min: f32, max: f32, gradient: Gradient) -> f32 {
512    let normalized = normalized.clamp(0.0, 1.0);
513
514    let map = |x: f32| -> f32 {
515        let range = max - min;
516        (x * range) + min
517    };
518
519    match gradient {
520        Gradient::Linear => map(normalized),
521
522        Gradient::Power(exponent) => map(normalized.powf(exponent)),
523
524        Gradient::Exponential => {
525            if normalized == 0.0 {
526                return min;
527            }
528
529            if normalized == 1.0 {
530                return max;
531            }
532
533            let minl = min.log2();
534            let range = max.log2() - minl;
535            2.0f32.powf((normalized * range) + minl)
536        }
537    }
538}
539
540fn value_to_normalized_f32(value: f32, min: f32, max: f32, gradient: Gradient) -> f32 {
541    if value <= min {
542        return 0.0;
543    }
544
545    if value >= max {
546        return 1.0;
547    }
548
549    let unmap = |x: f32| -> f32 {
550        let range = max - min;
551        (x - min) / range
552    };
553
554    match gradient {
555        Gradient::Linear => unmap(value),
556
557        Gradient::Power(exponent) => unmap(value).powf(1.0 / exponent),
558
559        Gradient::Exponential => {
560            let minl = min.log2();
561            let range = max.log2() - minl;
562            (value.log2() - minl) / range
563        }
564    }
565}
566
567// ------  F64  -------------------------------------------------------------------------
568
569/// An auto-smoothed parameter with an `f64` value.
570pub struct ParamF64<const MAX_BLOCKSIZE: usize> {
571    min: f64,
572    max: f64,
573    gradient: Gradient,
574    unit: Unit,
575
576    shared_normalized: Arc<AtomicF64>,
577    normalized: f64,
578
579    value: f64,
580
581    smoothed: SmoothF64<MAX_BLOCKSIZE>,
582    smooth_secs: Seconds,
583}
584
585impl<const MAX_BLOCKSIZE: usize> ParamF64<MAX_BLOCKSIZE> {
586    /// Create a Parameter/Handle pair from its (de-normalized) value.
587    ///
588    /// * value - The initial (de-normalized) value of the parameter.
589    /// * min - The minimum (de-normalized) value of the parameter.
590    /// * max - The maximum (de-normalized) value of the parameter.
591    /// * gradient - The [`Gradient`] mapping used when converting from the normalized value
592    /// in the range `[0.0, 1.0]` to the desired value. If this parameter deals with decibels,
593    /// you may use `ParamF64::DEFAULT_SMOOTH_SECS` as a good default.
594    /// * unit - The [`Unit`] that signifies how the value displayed to the end user should
595    /// differ from the actual value used in DSP.
596    /// * smooth_secs: The period of the low-pass parameter smoothing filter (for declicking). You
597    /// may use `ParamF64::DEFAULT_SMOOTH_SECS` as a good default.
598    /// * sample_rate: The sample rate of this process. This is used for the low-pass parameter
599    /// smoothing filter.
600    ///
601    /// [`Gradient`]: enum.Gradient.html
602    /// [`Unit`]: enum.Unit.html
603    pub fn from_value(
604        value: f64,
605        min: f64,
606        max: f64,
607        gradient: Gradient,
608        unit: Unit,
609        smooth_secs: Seconds,
610        sample_rate: SampleRate,
611    ) -> (Self, ParamF64Handle) {
612        let normalized = value_to_normalized_f64(value, min, max, gradient);
613
614        let handle_value = normalized_to_value_f64(normalized, min, max, gradient);
615        let rt_value = match unit {
616            Unit::Decibels => db_to_coeff_clamped_neg_90_db_f64(handle_value),
617            _ => handle_value,
618        };
619
620        let shared_normalized = Arc::new(AtomicF64::new(normalized));
621
622        let mut smoothed = SmoothF64::new(rt_value);
623        smoothed.set_speed(sample_rate, smooth_secs);
624
625        (
626            Self {
627                min,
628                max,
629                gradient,
630                unit,
631                shared_normalized: Arc::clone(&shared_normalized),
632                normalized,
633                value: rt_value,
634                smoothed,
635                smooth_secs,
636            },
637            ParamF64Handle {
638                min,
639                max,
640                gradient,
641                unit,
642                shared_normalized,
643            },
644        )
645    }
646
647    /// Create a Parameter/Handle pair from its normalized value in the range `[0.0, 1.0]`.
648    ///
649    /// * value - The initial normalized value of the parameter in the range `[0.0, 1.0]`.
650    /// * min - The minimum (de-normalized) value of the parameter.
651    /// * max - The maximum (de-normalized) value of the parameter.
652    /// * gradient - The [`Gradient`] mapping used when converting from the normalized value
653    /// in the range `[0.0, 1.0]` to the desired value. If this parameter deals with decibels,
654    /// you may use `ParamF64::DEFAULT_SMOOTH_SECS` as a good default.
655    /// * unit - The [`Unit`] that signifies how the value displayed to the end user should
656    /// differ from the actual value used in DSP.
657    /// * smooth_secs: The period of the low-pass parameter smoothing filter (for declicking). You
658    /// may use `ParamF64::DEFAULT_SMOOTH_SECS` as a good default.
659    /// * sample_rate: The sample rate of this process. This is used for the low-pass parameter
660    /// smoothing filter.
661    ///
662    /// [`Gradient`]: enum.Gradient.html
663    /// [`Unit`]: enum.Unit.html
664    pub fn from_normalized(
665        normalized: f64,
666        min_value: f64,
667        max_value: f64,
668        gradient: Gradient,
669        unit: Unit,
670        smooth_secs: Seconds,
671        sample_rate: SampleRate,
672    ) -> (Self, ParamF64Handle) {
673        let normalized = normalized.clamp(0.0, 1.0);
674
675        let shared_normalized = Arc::new(AtomicF64::new(normalized));
676
677        let handle_value = normalized_to_value_f64(normalized, min_value, max_value, gradient);
678        let rt_value = match unit {
679            Unit::Decibels => db_to_coeff_clamped_neg_90_db_f64(handle_value),
680            _ => handle_value,
681        };
682
683        let mut smoothed = SmoothF64::new(rt_value);
684        smoothed.set_speed(sample_rate, smooth_secs);
685
686        (
687            Self {
688                min: min_value,
689                max: max_value,
690                gradient,
691                unit,
692                shared_normalized: Arc::clone(&shared_normalized),
693                normalized,
694                value: rt_value,
695                smoothed,
696                smooth_secs,
697            },
698            ParamF64Handle {
699                min: min_value,
700                max: max_value,
701                gradient,
702                unit,
703                shared_normalized,
704            },
705        )
706    }
707
708    /// Set the (de-normalized) value of this parameter.
709    pub fn set_value(&mut self, value: f64) {
710        if self.value != value {
711            self.normalized = value_to_normalized_f64(value, self.min, self.max, self.gradient);
712            self.shared_normalized.set(self.normalized);
713
714            let v = normalized_to_value_f64(self.normalized, self.min, self.max, self.gradient);
715            self.value = match self.unit {
716                Unit::Decibels => db_to_coeff_clamped_neg_90_db_f64(v),
717                _ => v,
718            };
719
720            self.smoothed.set(self.value);
721        }
722    }
723
724    /// Set the normalized value of this parameter in the range `[0.0, 1.0]`.
725    pub fn set_normalized(&mut self, normalized: f64) {
726        if self.normalized != normalized {
727            self.normalized = normalized.clamp(0.0, 1.0);
728            self.shared_normalized.set(self.normalized);
729
730            let v = normalized_to_value_f64(self.normalized, self.min, self.max, self.gradient);
731            self.value = match self.unit {
732                Unit::Decibels => db_to_coeff_clamped_neg_90_db_f64(v),
733                _ => v,
734            };
735
736            self.smoothed.set(self.value);
737        }
738    }
739
740    /// Reset this parameter (without any smoothing) to the given (de-normalized) value.
741    pub fn reset_from_value(&mut self, value: f64) {
742        self.normalized = value_to_normalized_f64(value, self.min, self.max, self.gradient);
743        self.shared_normalized.set(self.normalized);
744
745        let v = normalized_to_value_f64(self.normalized, self.min, self.max, self.gradient);
746        self.value = match self.unit {
747            Unit::Decibels => db_to_coeff_clamped_neg_90_db_f64(v),
748            _ => v,
749        };
750
751        self.smoothed.reset(self.value);
752    }
753
754    /// Reset this parameter (without any smoothing) to the given normalized value in the range `[0.0, 1.0]`.
755    pub fn reset_from_normalized(&mut self, normalized: f64) {
756        self.normalized = normalized.clamp(0.0, 1.0);
757        self.shared_normalized.set(self.normalized);
758
759        let v = normalized_to_value_f64(self.normalized, self.min, self.max, self.gradient);
760        self.value = match self.unit {
761            Unit::Decibels => db_to_coeff_clamped_neg_90_db_f64(v),
762            _ => v,
763        };
764
765        self.smoothed.reset(self.value);
766    }
767
768    /// Reset the internal smoothing buffer.
769    pub fn reset(&mut self) {
770        self.smoothed.reset(self.value);
771    }
772
773    /// Get the smoothed buffer of values for use in DSP.
774    pub fn smoothed(&mut self, frames: usize) -> SmoothOutputF64<MAX_BLOCKSIZE> {
775        let new_normalized = self.shared_normalized.get();
776        if self.normalized != new_normalized {
777            self.normalized = new_normalized;
778
779            let v = normalized_to_value_f64(self.normalized, self.min, self.max, self.gradient);
780            self.value = match self.unit {
781                Unit::Decibels => db_to_coeff_clamped_neg_90_db_f64(v),
782                _ => v,
783            };
784
785            self.smoothed.set(self.value);
786        }
787
788        self.smoothed.process(frames);
789        self.smoothed.update_status();
790
791        self.smoothed.output()
792    }
793
794    /// Update the sample rate (used for the parameter smoothing LPF).
795    pub fn set_sample_rate(&mut self, sample_rate: SampleRate) {
796        self.smoothed.set_speed(sample_rate, self.smooth_secs);
797    }
798
799    /// The minimum value of this parameter.
800    pub fn min(&self) -> f64 {
801        self.min
802    }
803
804    /// The maximum value of this parameter.
805    pub fn max(&self) -> f64 {
806        self.max
807    }
808
809    /// The [`Gradient`] mapping used when converting from the normalized value
810    /// in the range `[0.0, 1.0]` to the desired value.
811    ///
812    /// [`Gradient`]: enum.Gradient.html
813    pub fn gradient(&self) -> Gradient {
814        self.gradient
815    }
816
817    /// The [`Unit`] that signifies how the value displayed to the end user should
818    /// differ from the actual value used in DSP.
819    ///
820    /// [`Unit`]: enum.Unit.html
821    pub fn unit(&self) -> Unit {
822        self.unit
823    }
824
825    /// Convert the given value to the corresponding normalized range `[0.0, 1.0]`
826    /// of this parameter.
827    pub fn value_to_normalized(&self, value: f64) -> f64 {
828        value_to_normalized_f64(value, self.min, self.max, self.gradient)
829    }
830
831    /// Convert the given normalized value in the range `[0.0, 1.0]` into the
832    /// corresponding value of this parameter.
833    pub fn normalized_to_value(&self, normalized: f64) -> f64 {
834        normalized_to_value_f64(normalized, self.min, self.max, self.gradient)
835    }
836
837    /// The current normalized value in the range `[0.0, 1.0]`. This is only meant for
838    /// communicating with the host. This is not meant to be used to retrieve the latest
839    /// value for DSP. To get the latest value for DSP please use `ParamF64::smoothed()`
840    /// instead.
841    ///
842    /// Please note that this should be called *after* calling `ParamF64::smoothed()`
843    /// if you need the latest value from the corresponding [`ParamF64Handle`],
844    /// otherwise this may not return the latest value.
845    ///
846    /// [`ParamF64Handle`]: struct.ParamF64Handle.html
847    pub fn normalized(&self) -> f64 {
848        self.normalized
849    }
850
851    /// Get the shared normalized float value.
852    ///
853    /// This can be useful to integrate with various plugin APIs.
854    pub fn shared_normalized(&self) -> Arc<AtomicF64> {
855        Arc::clone(&self.shared_normalized)
856    }
857}
858
859/// A handle to get and update the value of an auto-smoothed [`ParamF64`] from a UI.
860///
861/// [`ParamF64`]: struct.ParamF64.html
862pub struct ParamF64Handle {
863    min: f64,
864    max: f64,
865    gradient: Gradient,
866    unit: Unit,
867
868    shared_normalized: Arc<AtomicF64>,
869}
870
871impl ParamF64Handle {
872    /// The normalized value in the range `[0.0, 1.0]`.
873    pub fn normalized(&self) -> f64 {
874        self.shared_normalized.get()
875    }
876
877    /// The (un-normalized) value of this parameter.
878    ///
879    /// Please note that this is calculated from the shared normalized value every time, so
880    /// avoid calling this every frame if you can.
881    pub fn value(&self) -> f64 {
882        normalized_to_value_f64(
883            self.shared_normalized.get(),
884            self.min,
885            self.max,
886            self.gradient,
887        )
888    }
889
890    /// Set the normalized value of this parameter in the range `[0.0, 1.0]`.
891    ///
892    /// Please note that this will ***NOT*** automatically notify the host of the value change
893    /// if you are using this inside a plugin spec such as VST. It is intended for you use your
894    /// own method for achieving this.
895    pub fn set_normalized(&self, normalized: f64) {
896        self.shared_normalized.set(normalized.clamp(0.0, 1.0));
897    }
898
899    /// Set the (un-normalized) value of this parameter.
900    ///
901    /// Please note that this will ***NOT*** automatically notify the host of the value change
902    /// if you are using this inside a plugin spec such as VST. It is intended for you use your
903    /// own method for achieving this.
904    pub fn set_value(&self, value: f64) {
905        let normalized = value_to_normalized_f64(value, self.min, self.max, self.gradient);
906        self.set_normalized(normalized);
907    }
908
909    /// The minimum value of this parameter.
910    pub fn min(&self) -> f64 {
911        self.min
912    }
913
914    /// The maximum value of this parameter.
915    pub fn max(&self) -> f64 {
916        self.max
917    }
918
919    /// The [`Gradient`] mapping used when converting from the normalized value
920    /// in the range `[0.0, 1.0]` to the desired value.
921    ///
922    /// [`Gradient`]: enum.Gradient.html
923    pub fn gradient(&self) -> Gradient {
924        self.gradient
925    }
926
927    /// The [`Unit`] that signifies how the value displayed to the end user should
928    /// differ from the actual value used in DSP.
929    ///
930    /// [`Unit`]: enum.Unit.html
931    pub fn unit(&self) -> Unit {
932        self.unit
933    }
934
935    /// Convert the given value to the corresponding normalized range `[0.0, 1.0]`
936    /// of this parameter.
937    pub fn value_to_normalized(&self, value: f64) -> f64 {
938        value_to_normalized_f64(value, self.min, self.max, self.gradient)
939    }
940
941    /// Convert the given normalized value in the range `[0.0, 1.0]` into the
942    /// corresponding value of this parameter.
943    pub fn normalized_to_value(&self, normalized: f64) -> f64 {
944        normalized_to_value_f64(normalized, self.min, self.max, self.gradient)
945    }
946
947    /// Get the shared normalized float value.
948    ///
949    /// This can be useful to integrate with various plugin APIs.
950    pub fn shared_normalized(&self) -> Arc<AtomicF64> {
951        Arc::clone(&self.shared_normalized)
952    }
953}
954
955impl Clone for ParamF64Handle {
956    fn clone(&self) -> Self {
957        Self {
958            min: self.min,
959            max: self.max,
960            gradient: self.gradient,
961            unit: self.unit,
962
963            shared_normalized: Arc::clone(&self.shared_normalized),
964        }
965    }
966}
967
968fn normalized_to_value_f64(normalized: f64, min: f64, max: f64, gradient: Gradient) -> f64 {
969    let normalized = normalized.clamp(0.0, 1.0);
970
971    let map = |x: f64| -> f64 {
972        let range = max - min;
973        (x * range) + min
974    };
975
976    match gradient {
977        Gradient::Linear => map(normalized),
978
979        Gradient::Power(exponent) => map(normalized.powf(f64::from(exponent))),
980
981        Gradient::Exponential => {
982            if normalized == 0.0 {
983                return min;
984            }
985
986            if normalized == 1.0 {
987                return max;
988            }
989
990            let minl = min.log2();
991            let range = max.log2() - minl;
992            2.0f64.powf((normalized * range) + minl)
993        }
994    }
995}
996
997fn value_to_normalized_f64(value: f64, min: f64, max: f64, gradient: Gradient) -> f64 {
998    if value <= min {
999        return 0.0;
1000    }
1001
1002    if value >= max {
1003        return 1.0;
1004    }
1005
1006    let unmap = |x: f64| -> f64 {
1007        let range = max - min;
1008        (x - min) / range
1009    };
1010
1011    match gradient {
1012        Gradient::Linear => unmap(value),
1013
1014        Gradient::Power(exponent) => unmap(value).powf(1.0 / f64::from(exponent)),
1015
1016        Gradient::Exponential => {
1017            let minl = min.log2();
1018            let range = max.log2() - minl;
1019            (value.log2() - minl) / range
1020        }
1021    }
1022}