tuix_widgets/audio_widgets/
normalized_map.rs

1pub trait NormalizedMap: 'static {
2    fn normalized_to_display(&self, normalized: f32) -> String;
3
4    fn snap(&self, normalized: f32) -> f32 {
5        normalized
6    }
7}
8
9#[derive(Debug, Clone, Copy, PartialEq)]
10pub enum DisplayDecimals {
11    Zero,
12    One,
13    Two,
14    Three,
15    Four,
16    Five
17}
18
19impl DisplayDecimals {
20    pub fn display_value(&self, value: f32) -> String {
21        match self {
22            DisplayDecimals::Zero => format!("{:.0}", value),
23            DisplayDecimals::One => format!("{:.1}", value),
24            DisplayDecimals::Two => format!("{:.2}", value),
25            DisplayDecimals::Three => format!("{:.3}", value),
26            DisplayDecimals::Four => format!("{:.4}", value),
27            DisplayDecimals::Five => format!("{:.5}", value),
28        }
29    }
30}
31
32impl Default for DisplayDecimals {
33    fn default() -> Self {
34        DisplayDecimals::One
35    }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq)]
39pub enum ValueScaling {
40    Linear,
41    Power(f32),
42    Frequency,
43}
44
45impl ValueScaling {
46    pub fn normalized_to_value(&self, normalized: f32, min: f32, max: f32) -> f32 {
47        if normalized <= 0.0 {
48            return min;
49        } else if normalized >= 1.0 {
50            return max;
51        }
52    
53        let map = |x: f32| -> f32 {
54            (x * (max - min)) + min
55        };
56    
57        match self {
58            ValueScaling::Linear => map(normalized),
59    
60            ValueScaling::Power(exponent) => map(normalized.powf(*exponent)),
61    
62            ValueScaling::Frequency => {
63                let minl = min.log2();
64                let range = max.log2() - minl;
65                2.0f32.powf((normalized * range) + minl)
66            }
67        }
68    }
69    
70    pub fn value_to_normalized(&self, value: f32, min: f32, max: f32) -> f32 {
71        if value <= min {
72            return 0.0;
73        } else if value >= max {
74            return 1.0;
75        }
76    
77        let unmap = |x: f32| -> f32 {
78            (x - min) / (max - min)
79        };
80    
81        match self {
82            ValueScaling::Linear => unmap(value),
83    
84            ValueScaling::Power(exponent) => unmap(value).powf(1.0 / *exponent),
85    
86            ValueScaling::Frequency => {
87                let minl = min.log2();
88                let range = max.log2() - minl;
89                (value.log2() - minl) / range
90            }
91        }
92    }
93}
94
95#[derive(Debug, Clone)]
96pub struct GenericMap {
97    min: f32,
98    max: f32,
99    span_recip: f32, // Small optimization to avoid division operations.
100
101    value_scaling: ValueScaling,
102
103    display_decimals: DisplayDecimals,
104    units: Option<String>,
105}
106
107impl GenericMap {
108    pub fn new(min: f32, max: f32, value_scaling: ValueScaling, display_decimals: DisplayDecimals, units: Option<String>) -> Self {
109        assert!(min < max);
110
111        Self {
112            min,
113            max,
114            span_recip: 1.0 / (max - min),
115            value_scaling,
116            display_decimals,
117            units,
118        }
119    }
120
121    pub fn min(&self) -> f32 {
122        self.min
123    }
124    pub fn max(&self) -> f32 {
125        self.max
126    }
127
128    pub fn value_scaling(&self) -> &ValueScaling {
129        &self.value_scaling
130    }
131
132    #[inline]
133    pub fn normalized_to_value(&self, normalized: f32) -> f32 {
134        self.value_scaling.normalized_to_value(normalized, self.min, self.max)
135    }
136
137    #[inline]
138    pub fn value_to_normalized(&self, value: f32) -> f32 {
139        self.value_scaling.value_to_normalized(value, self.min, self.max)
140    }
141
142    #[inline]
143    pub fn clamp_value(&self, value: f32) -> f32 {
144        value.min(self.max).max(self.min)
145    }
146}
147
148impl NormalizedMap for GenericMap {
149    fn normalized_to_display(&self, normalized: f32) -> String {
150        let mut s = self.display_decimals.display_value(self.normalized_to_value(normalized));
151        if let Some(units) = &self.units {
152            s += units;
153        }
154        s
155    }
156}
157
158#[derive(Debug, Clone)]
159pub struct DecibelMap {
160    min: f32,
161    max: f32,
162
163    value_scaling: ValueScaling,
164
165    display_decimals: DisplayDecimals,
166    display_units: bool,
167}
168
169impl DecibelMap {
170    pub fn new(min_db: f32, max_db: f32, value_scaling: ValueScaling, display_decimals: DisplayDecimals, display_units: bool) -> Self {
171        assert!(min_db < max_db);
172
173        Self {
174            min: min_db,
175            max: max_db,
176            value_scaling,
177            display_decimals,
178            display_units,
179        }
180    }
181
182    pub fn min_db(&self) -> f32 {
183        self.min
184    }
185    pub fn max_db(&self) -> f32 {
186        self.max
187    }
188
189    pub fn value_scaling(&self) -> &ValueScaling {
190        &self.value_scaling
191    }
192
193    #[inline]
194    pub fn normalized_to_db(&self, normalized: f32) -> f32 {
195        self.value_scaling.normalized_to_value(normalized, self.min, self.max)
196    }
197
198    #[inline]
199    pub fn normalized_to_amplitude(&self, normalized: f32) -> f32 {
200        db_to_amplitude(self.value_scaling.normalized_to_value(normalized, self.min, self.max))
201    }
202
203    #[inline]
204    pub fn db_to_normalized(&self, db: f32) -> f32 {
205        self.value_scaling.value_to_normalized(db, self.min, self.max)
206    }
207
208    #[inline]
209    pub fn amplitude_to_normalized(&self, amplitude: f32) -> f32 {
210        let db = amplitude_to_db(amplitude);
211        self.db_to_normalized(db)
212    }
213
214    #[inline]
215    pub fn clamp_db(&self, db: f32) -> f32 {
216        db.min(self.max).max(self.min)
217    }
218}
219
220impl NormalizedMap for DecibelMap {
221    fn normalized_to_display(&self, normalized: f32) -> String {
222        let mut s = self.display_decimals.display_value(self.normalized_to_db(normalized));
223        if self.display_units {
224            s += " dB"
225        }
226        s
227    }
228}
229
230#[derive(Debug, Clone, Copy, PartialEq)]
231pub enum FrequencyDisplayMode {
232    OnlyHz(DisplayDecimals),
233    HzThenKHz {
234        under_1k: DisplayDecimals,
235        over_1k: DisplayDecimals,
236    }
237}
238
239impl Default for FrequencyDisplayMode {
240    fn default() -> Self {
241        FrequencyDisplayMode::HzThenKHz {
242            under_1k: DisplayDecimals::One,
243            over_1k: DisplayDecimals::Two,
244        }
245    }
246}
247
248#[derive(Debug, Clone)]
249pub struct FrequencyMap {
250    min: f32,
251    max: f32,
252
253    value_scaling: ValueScaling,
254
255    display_mode: FrequencyDisplayMode,
256    display_units: bool,
257}
258
259impl FrequencyMap {
260    pub fn new(
261        min_hz: f32,
262        max_hz: f32,
263        value_scaling: ValueScaling,
264        display_mode: FrequencyDisplayMode,
265        display_units: bool,
266    ) -> Self {
267        assert!(min_hz < max_hz);
268
269        Self {
270            min: min_hz,
271            max: max_hz,
272            value_scaling,
273            display_mode,
274            display_units,
275        }
276    }
277
278    pub fn min_hz(&self) -> f32 {
279        self.min
280    }
281    pub fn max_hz(&self) -> f32 {
282        self.max
283    }
284
285    pub fn value_scaling(&self) -> &ValueScaling {
286        &self.value_scaling
287    }
288
289    #[inline]
290    pub fn normalized_to_hz(&self, normalized: f32) -> f32 {
291        self.value_scaling.normalized_to_value(normalized, self.min, self.max)
292    }
293
294    #[inline]
295    pub fn hz_to_normalized(&self, hz: f32) -> f32 {
296        self.value_scaling.value_to_normalized(hz, self.min, self.max)
297    }
298
299    #[inline]
300    pub fn clamp_hz(&self, hz: f32) -> f32 {
301        hz.min(self.max).max(self.min)
302    }
303}
304
305impl NormalizedMap for FrequencyMap {
306    fn normalized_to_display(&self, normalized: f32) -> String {
307        let hz = self.normalized_to_hz(normalized);
308
309        match self.display_mode {
310            FrequencyDisplayMode::OnlyHz(display_decimals) => {
311                let mut s = display_decimals.display_value(hz);
312                if self.display_units {
313                    s += " Hz"
314                }
315                s
316            }
317            FrequencyDisplayMode::HzThenKHz { under_1k, over_1k } => {
318                if hz < 1_000.0 {
319                    let mut s = under_1k.display_value(hz);
320                    if self.display_units {
321                        s += " Hz"
322                    }
323                    s
324                } else {
325                    let mut s = over_1k.display_value(hz / 1_000.0);
326                    if self.display_units {
327                        s += " kHz"
328                    }
329                    s
330                }
331            }
332        }
333    }
334}
335
336#[derive(Clone)]
337pub struct IntMap {
338    min: i32,
339    max: i32,
340    span: f32,  // Small optimization.
341
342    display_map: Option<&'static dyn Fn(i32) -> String>,
343}
344
345impl IntMap {
346    pub fn new(min: i32, max: i32, display_map: Option<&'static dyn Fn(i32) -> String>) -> Self {
347        assert!(min <= max);
348
349        Self {
350            min,
351            max,
352            span: (max - min) as f32,
353            display_map,
354        }
355    }
356
357    pub fn min(&self) -> i32 {
358        self.min
359    }
360    pub fn max(&self) -> i32 {
361        self.max
362    }
363
364    #[inline]
365    pub fn normalized_to_int(&self, normalized: f32) -> i32 {
366        if normalized <= 0.0 {
367            return self.min;
368        } else if normalized >= 1.0 {
369            return self.max;
370        }
371
372        (normalized * self.span).round() as i32
373    }
374
375    #[inline]
376    pub fn int_to_normalized(&self, int: i32) -> f32 {
377        if self.min == self.max {
378            // Value will always be the same, so avoid a divide
379            // by zero.
380            0.0
381        } else {
382            if int <= self.min {
383                return 0.0;
384            } else if int >= self.max {
385                return 1.0;
386            }
387
388            (int as f32 - self.min as f32) / self.span
389        }
390    }
391
392    #[inline]
393    pub fn clamp_int(&self, int: i32) -> i32 {
394        int.min(self.max).max(self.min)
395    }
396}
397
398impl NormalizedMap for IntMap {
399    fn normalized_to_display(&self, normalized: f32) -> String {
400        let int = self.normalized_to_int(normalized);
401
402        if let Some(display_map) = self.display_map {
403            (display_map)(int)
404        } else {
405            // Display the plain integer instead.
406            format!("{}", int)
407        }
408    }
409
410    fn snap(&self, normalized: f32) -> f32 {
411        let int = self.normalized_to_int(normalized);
412        self.int_to_normalized(int)
413    }
414}
415
416#[inline]
417pub fn db_to_amplitude(db: f32) -> f32 {
418    10.0f32.powf(db * 1.0 / 20.0)
419}
420
421#[inline]
422pub fn amplitude_to_db(amp: f32) -> f32 {
423    20.0f32 * amp.log10()
424}