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}