alsa_ctl_tlv_codec/
range_utils.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2020 Takashi Sakamoto
3//! A set of trait and implementation to retrieve range of dB value and raw value from data of TLV.
4use super::*;
5
6/// The structure expresses the range of available value in the state of control element with
7/// step to change it.
8#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
9pub struct ValueRange {
10    /// The minimum value in the state of control element.
11    pub min: i32,
12    /// The maximum value in the state of control element.
13    pub max: i32,
14    /// The step of value in the state of control element.
15    pub step: i32,
16}
17
18/// The trait for utilities about range information.
19pub trait RangeUtil<T> {
20    /// The length from the minimum to maximum.
21    fn length(&self) -> T;
22
23    /// Whether the val is between the minimum and maximum.
24    fn contains(&self, val: T) -> bool;
25}
26
27impl RangeUtil<i32> for ValueRange {
28    fn length(&self) -> i32 {
29        (self.max - self.min).abs()
30    }
31
32    fn contains(&self, val: i32) -> bool {
33        val >= self.min && val <= self.max
34    }
35}
36
37// NOTE: in 0.1 dB unit.
38impl RangeUtil<i32> for DbInterval {
39    fn length(&self) -> i32 {
40        (self.max - self.min).abs()
41    }
42
43    fn contains(&self, db: i32) -> bool {
44        db >= self.min && db <= self.max
45    }
46}
47
48/// The trait for conversion into range of raw value on control element.
49pub trait ToValueRange {
50    fn to_valuerange(&self, range: &ValueRange) -> Option<ValueRange>;
51}
52
53impl ToValueRange for DbScale {
54    fn to_valuerange(&self, range: &ValueRange) -> Option<ValueRange> {
55        Some(*range)
56    }
57}
58
59impl ToValueRange for DbInterval {
60    fn to_valuerange(&self, range: &ValueRange) -> Option<ValueRange> {
61        Some(*range)
62    }
63}
64
65impl ToValueRange for DbRangeEntry {
66    fn to_valuerange(&self, range: &ValueRange) -> Option<ValueRange> {
67        Some(ValueRange {
68            min: self.min_val,
69            max: self.max_val,
70            step: range.step,
71        })
72    }
73}
74
75impl ToValueRange for DbRange {
76    fn to_valuerange(&self, range: &ValueRange) -> Option<ValueRange> {
77        let mut r = ValueRange {
78            min: i32::MAX,
79            max: i32::MIN,
80            step: range.step,
81        };
82        self.entries.iter().for_each(|entry| {
83            if !r.contains(entry.min_val) {
84                r.min = entry.min_val;
85            }
86            if !r.contains(entry.max_val) {
87                r.max = entry.max_val;
88            }
89        });
90        if r.min != i32::MAX && r.max != i32::MIN {
91            Some(r)
92        } else {
93            None
94        }
95    }
96}
97
98impl ToValueRange for Container {
99    fn to_valuerange(&self, range: &ValueRange) -> Option<ValueRange> {
100        let mut r = ValueRange {
101            min: i32::MAX,
102            max: i32::MIN,
103            step: range.step,
104        };
105        self.entries.iter().for_each(|entry| {
106            if let Some(range) = entry.to_valuerange(&range) {
107                if !r.contains(range.min) {
108                    r.min = range.min;
109                }
110                if !r.contains(range.max) {
111                    r.max = range.max;
112                }
113            }
114        });
115        if r.min != i32::MAX && r.max != i32::MIN {
116            Some(r)
117        } else {
118            None
119        }
120    }
121}
122
123impl ToValueRange for TlvItem {
124    fn to_valuerange(&self, range: &ValueRange) -> Option<ValueRange> {
125        match self {
126            TlvItem::DbRange(d) => d.to_valuerange(&range),
127            TlvItem::Container(d) => d.to_valuerange(&range),
128            TlvItem::DbScale(_) | TlvItem::DbInterval(_) => Some(*range),
129            _ => None,
130        }
131    }
132}
133
134/// The enumeration to express conversion error into interval of dB.
135#[derive(Debug, Clone, PartialEq, Eq)]
136pub enum ToDbIntervalError {
137    /// Including any entry in which both minimum and maximum value are out of range, or either
138    /// minimum or maximum value is out of range.
139    Range(
140        /// Minimum value of the entry.
141        i32,
142        /// Maximum value of the entry.
143        i32,
144    ),
145    /// Including entries for both of non-linear and linear value.
146    Linearity,
147    /// Including no entry.
148    Empty,
149    /// Including any entry without dB information.
150    Unsupported,
151}
152
153impl std::fmt::Display for ToDbIntervalError {
154    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155        match self {
156            Self::Range(min_val, max_val) => write!(
157                f,
158                "Any entry out of range, min: {}, max: {}",
159                min_val, max_val
160            ),
161            Self::Linearity => write!(f, "Some entries for both of non-linear and linear value"),
162            Self::Empty => write!(f, "No entry for dB information"),
163            Self::Unsupported => write!(f, "Any entry without dB information"),
164        }
165    }
166}
167
168/// The trait for conversion into interval of dB.
169pub trait ToDbInterval {
170    fn to_dbinterval(&self, range: &ValueRange) -> Result<DbInterval, ToDbIntervalError>;
171}
172
173impl ToDbInterval for DbScale {
174    fn to_dbinterval(&self, range: &ValueRange) -> Result<DbInterval, ToDbIntervalError> {
175        Ok(DbInterval {
176            min: self.min,
177            max: self.min + range.length() * self.step as i32,
178            linear: false,
179            mute_avail: self.mute_avail,
180        })
181    }
182}
183
184impl ToDbInterval for DbInterval {
185    fn to_dbinterval(&self, _: &ValueRange) -> Result<DbInterval, ToDbIntervalError> {
186        Ok(*self)
187    }
188}
189
190impl ToDbInterval for DbRangeEntry {
191    fn to_dbinterval(&self, range: &ValueRange) -> Result<DbInterval, ToDbIntervalError> {
192        let r = ValueRange {
193            min: self.min_val,
194            max: self.max_val,
195            step: range.step,
196        };
197        match &self.data {
198            DbRangeEntryData::DbScale(d) => d.to_dbinterval(&r),
199            DbRangeEntryData::DbInterval(d) => Ok(*d),
200            DbRangeEntryData::DbRange(d) => d.to_dbinterval(&r),
201        }
202    }
203}
204
205impl ToDbInterval for DbRange {
206    fn to_dbinterval(&self, range: &ValueRange) -> Result<DbInterval, ToDbIntervalError> {
207        let entries = self
208            .entries
209            .iter()
210            .map(|entry| {
211                if range.contains(entry.min_val) && range.contains(entry.max_val) {
212                    let r = ValueRange {
213                        min: entry.min_val,
214                        max: entry.max_val,
215                        step: range.step,
216                    };
217                    entry.to_dbinterval(&r).and_then(|i| Ok((r, i)))
218                } else {
219                    Err(ToDbIntervalError::Range(entry.min_val, entry.max_val))
220                }
221            })
222            .collect::<Result<Vec<_>, _>>()?;
223
224        if entries.len() > 0 {
225            let mut interval = entries[0].1;
226            entries[1..].iter().try_for_each(|entry| {
227                let i = entry.1;
228                if i.linear != interval.linear {
229                    Err(ToDbIntervalError::Linearity)
230                } else {
231                    if !interval.contains(i.min) {
232                        interval.min = i.min;
233                        interval.mute_avail = i.mute_avail;
234                    }
235                    if !interval.contains(i.max) {
236                        interval.max = i.max;
237                    }
238                    Ok(())
239                }
240            })?;
241            Ok(interval)
242        } else {
243            Err(ToDbIntervalError::Empty)
244        }
245    }
246}
247
248impl ToDbInterval for Container {
249    fn to_dbinterval(&self, range: &ValueRange) -> Result<DbInterval, ToDbIntervalError> {
250        let intervals = self
251            .entries
252            .iter()
253            .map(|entry| entry.to_dbinterval(&range))
254            .collect::<Result<Vec<_>, _>>()?;
255
256        if intervals.len() > 0 {
257            let mut interval = intervals[0];
258            intervals[1..].iter().try_for_each(|i| {
259                if i.linear != interval.linear {
260                    Err(ToDbIntervalError::Linearity)
261                } else {
262                    if !interval.contains(i.min) {
263                        interval.min = i.min;
264                        interval.mute_avail = i.mute_avail;
265                    }
266                    if !interval.contains(i.max) {
267                        interval.max = i.max;
268                    }
269                    Ok(())
270                }
271            })?;
272            Ok(interval)
273        } else {
274            Err(ToDbIntervalError::Empty)
275        }
276    }
277}
278
279impl ToDbInterval for TlvItem {
280    fn to_dbinterval(&self, range: &ValueRange) -> Result<DbInterval, ToDbIntervalError> {
281        match self {
282            TlvItem::Container(d) => d.to_dbinterval(&range),
283            TlvItem::DbRange(d) => d.to_dbinterval(&range),
284            TlvItem::DbScale(d) => d.to_dbinterval(&range),
285            TlvItem::DbInterval(d) => Ok(*d),
286            _ => Err(ToDbIntervalError::Unsupported),
287        }
288    }
289}
290
291#[cfg(test)]
292mod test {
293    use super::*;
294
295    #[test]
296    fn to_dbinterval_dbscale() {
297        let scale = &DbScale {
298            min: 100,
299            step: 10,
300            mute_avail: true,
301        };
302        let range = ValueRange {
303            min: 33,
304            max: 333,
305            step: 1,
306        };
307        let interval = scale.to_dbinterval(&range).unwrap();
308        assert_eq!(
309            interval,
310            DbInterval {
311                min: 100,
312                max: 3100,
313                linear: false,
314                mute_avail: true
315            }
316        );
317    }
318
319    #[test]
320    fn to_valuerange_dbrange() {
321        let first_data = DbInterval {
322            min: 1,
323            max: 5,
324            linear: false,
325            mute_avail: true,
326        };
327        let second_data = DbInterval {
328            min: 5,
329            max: 10,
330            linear: false,
331            mute_avail: false,
332        };
333        let third_data = DbInterval {
334            min: 10,
335            max: 20,
336            linear: false,
337            mute_avail: false,
338        };
339
340        let dbrange = DbRange {
341            entries: vec![
342                DbRangeEntry {
343                    min_val: 0,
344                    max_val: 10,
345                    data: DbRangeEntryData::DbInterval(first_data),
346                },
347                DbRangeEntry {
348                    min_val: 10,
349                    max_val: 20,
350                    data: DbRangeEntryData::DbInterval(second_data),
351                },
352                DbRangeEntry {
353                    min_val: 20,
354                    max_val: 40,
355                    data: DbRangeEntryData::DbInterval(third_data),
356                },
357            ],
358        };
359        let r = ValueRange {
360            min: 0,
361            max: 40,
362            step: 1,
363        };
364        let range = dbrange.to_valuerange(&r).unwrap();
365        assert_eq!(range.min, 0);
366        assert_eq!(range.max, 40);
367        assert_eq!(range.step, 1);
368    }
369
370    #[test]
371    fn to_dbinterval_dbrange() {
372        let first_data = DbInterval {
373            min: 1,
374            max: 5,
375            linear: false,
376            mute_avail: true,
377        };
378        let second_data = DbInterval {
379            min: 5,
380            max: 10,
381            linear: false,
382            mute_avail: false,
383        };
384        let third_data = DbInterval {
385            min: 10,
386            max: 20,
387            linear: false,
388            mute_avail: false,
389        };
390        let range = DbRange {
391            entries: vec![
392                DbRangeEntry {
393                    min_val: 0,
394                    max_val: 10,
395                    data: DbRangeEntryData::DbInterval(first_data),
396                },
397                DbRangeEntry {
398                    min_val: 10,
399                    max_val: 20,
400                    data: DbRangeEntryData::DbInterval(second_data),
401                },
402                DbRangeEntry {
403                    min_val: 20,
404                    max_val: 40,
405                    data: DbRangeEntryData::DbInterval(third_data),
406                },
407            ],
408        };
409        let r = ValueRange {
410            min: 0,
411            max: 40,
412            step: 1,
413        };
414        let interval = range.to_dbinterval(&r).unwrap();
415        assert_eq!(
416            interval,
417            DbInterval {
418                min: 1,
419                max: 20,
420                linear: false,
421                mute_avail: true
422            }
423        );
424    }
425}