alsa_ctl_tlv_codec/
containers.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2020 Takashi Sakamoto
3
4//! A set of containers to aggregate items in TLV (Type-Length-Value) of ALSA control interface.
5
6use super::*;
7
8trait DataEntry<'a>: std::convert::TryFrom<&'a [u32]> {
9    fn raw_length(&self) -> usize;
10}
11
12/// The enumeration to dispatch each type of data for entry of dB range.
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum DbRangeEntryData {
15    DbScale(DbScale),
16    DbInterval(DbInterval),
17    DbRange(DbRange),
18}
19
20/// The entry to express information of each entry of dB range.
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct DbRangeEntry {
23    pub min_val: i32,
24    pub max_val: i32,
25    /// The data of dB expression for the minimum/maximum range in the state of control element.
26    pub data: DbRangeEntryData,
27}
28
29impl<'a> DataEntry<'a> for DbRangeEntry {
30    fn raw_length(&self) -> usize {
31        let data_value_length = match &self.data {
32            DbRangeEntryData::DbScale(d) => d.value_length(),
33            DbRangeEntryData::DbInterval(d) => d.value_length(),
34            DbRangeEntryData::DbRange(d) => d.value_length(),
35        };
36        let data_length = 2 + data_value_length;
37        2 + data_length
38    }
39}
40
41const TYPES_FOR_DB_RANGE_ENTRY: &'static [u32] = &[
42    SNDRV_CTL_TLVT_DB_SCALE,
43    SNDRV_CTL_TLVT_DB_RANGE,
44    SNDRV_CTL_TLVT_DB_LINEAR,
45    SNDRV_CTL_TLVT_DB_MINMAX,
46    SNDRV_CTL_TLVT_DB_MINMAX_MUTE,
47];
48
49impl std::convert::TryFrom<&[u32]> for DbRangeEntry {
50    type Error = TlvDecodeError;
51
52    fn try_from(raw: &[u32]) -> Result<Self, Self::Error> {
53        if raw.len() < 4 {
54            Err(Self::Error::new(TlvDecodeErrorCtx::Length(raw.len(), 4), 0))
55        } else {
56            let min_val = raw[0] as i32;
57            let max_val = raw[1] as i32;
58
59            let entry_data = &raw[2..];
60            match entry_data[0] {
61                SNDRV_CTL_TLVT_DB_SCALE => DbScale::try_from(entry_data).map(|d| Self {
62                    min_val,
63                    max_val,
64                    data: DbRangeEntryData::DbScale(d),
65                }),
66                SNDRV_CTL_TLVT_DB_RANGE => DbRange::try_from(entry_data).map(|d| Self {
67                    min_val,
68                    max_val,
69                    data: DbRangeEntryData::DbRange(d),
70                }),
71                SNDRV_CTL_TLVT_DB_LINEAR
72                | SNDRV_CTL_TLVT_DB_MINMAX
73                | SNDRV_CTL_TLVT_DB_MINMAX_MUTE => DbInterval::try_from(entry_data).map(|d| Self {
74                    min_val,
75                    max_val,
76                    data: DbRangeEntryData::DbInterval(d),
77                }),
78                _ => Err(Self::Error::new(
79                    TlvDecodeErrorCtx::ValueType(entry_data[0], TYPES_FOR_DB_RANGE_ENTRY),
80                    0,
81                )),
82            }
83            .map_err(|e| Self::Error::new(TlvDecodeErrorCtx::ValueContent(Box::new(e)), 2))
84        }
85    }
86}
87
88impl From<&DbRangeEntry> for Vec<u32> {
89    fn from(entry: &DbRangeEntry) -> Self {
90        let mut raw = Vec::new();
91        raw.push(entry.min_val as u32);
92        raw.push(entry.max_val as u32);
93        let mut data_raw = match &entry.data {
94            DbRangeEntryData::DbScale(d) => Vec::<u32>::from(d),
95            DbRangeEntryData::DbRange(d) => Vec::<u32>::from(d),
96            DbRangeEntryData::DbInterval(d) => Vec::<u32>::from(d),
97        };
98        raw.append(&mut data_raw);
99        raw
100    }
101}
102
103impl From<DbRangeEntry> for Vec<u32> {
104    fn from(entry: DbRangeEntry) -> Self {
105        (&entry).into()
106    }
107}
108
109/// The data to express multiple ranges in the state of control element for dB expression.
110/// It has `SNDRV_CTL_TLVT_DB_RANGE` (=3) in its type field and has variable number of elements in
111/// value field.
112#[derive(Debug, Clone, PartialEq, Eq)]
113pub struct DbRange {
114    /// The entries of ranges for dB expression.
115    pub entries: Vec<DbRangeEntry>,
116}
117
118impl<'a> TlvData<'a> for DbRange {
119    fn value_type(&self) -> u32 {
120        SNDRV_CTL_TLVT_DB_RANGE
121    }
122
123    fn value_length(&self) -> usize {
124        self.entries
125            .iter()
126            .fold(0, |length, entry| length + entry.raw_length())
127    }
128
129    fn value(&self) -> Vec<u32> {
130        let mut raw = Vec::new();
131        self.entries.iter().for_each(|entry| {
132            let mut entry_raw = Vec::<u32>::from(entry);
133            raw.append(&mut entry_raw);
134        });
135        raw
136    }
137}
138
139const TYPES_FOR_DB_RANGE: &'static [u32] = &[SNDRV_CTL_TLVT_DB_RANGE];
140
141impl std::convert::TryFrom<&[u32]> for DbRange {
142    type Error = TlvDecodeError;
143
144    fn try_from(raw: &[u32]) -> Result<Self, Self::Error> {
145        // At least, type and length field should be included.
146        if raw.len() < 2 {
147            Err(Self::Error::new(TlvDecodeErrorCtx::Length(raw.len(), 2), 0))
148        // Check type field.
149        } else if raw[0] != SNDRV_CTL_TLVT_DB_RANGE {
150            Err(Self::Error::new(
151                TlvDecodeErrorCtx::ValueType(raw[0], TYPES_FOR_DB_RANGE),
152                0,
153            ))
154        } else {
155            // Check length field against length of value field.
156            let value_length = (raw[1] / 4) as usize;
157            let value = &raw[2..];
158            if value.len() < value_length {
159                Err(Self::Error::new(
160                    TlvDecodeErrorCtx::ValueLength(value_length, value.len()),
161                    1,
162                ))
163            } else {
164                // Decode value field.
165                let mut pos = 0;
166
167                let mut entries = Vec::new();
168                while pos < value_length {
169                    DbRangeEntry::try_from(&value[pos..])
170                        .map(|entry| {
171                            entries.push(entry);
172                            pos += 4 + (value[pos + 3] / 4) as usize;
173                        })
174                        .map_err(|e| {
175                            Self::Error::new(TlvDecodeErrorCtx::ValueContent(Box::new(e)), pos + 2)
176                        })?;
177                }
178
179                Ok(Self { entries })
180            }
181        }
182    }
183}
184
185impl From<&DbRange> for Vec<u32> {
186    fn from(data: &DbRange) -> Self {
187        let mut raw = Vec::new();
188        raw.push(data.value_type());
189        raw.push(4 * data.value_length() as u32);
190        raw.append(&mut data.value());
191        raw
192    }
193}
194
195impl From<DbRange> for Vec<u32> {
196    fn from(data: DbRange) -> Self {
197        (&data).into()
198    }
199}
200
201impl<'a> DataEntry<'a> for TlvItem {
202    fn raw_length(&self) -> usize {
203        let entry_value_length = match self {
204            TlvItem::Container(d) => d.value_length(),
205            TlvItem::DbRange(d) => d.value_length(),
206            TlvItem::DbScale(d) => d.value_length(),
207            TlvItem::DbInterval(d) => d.value_length(),
208            TlvItem::Chmap(d) => d.value_length(),
209            TlvItem::Unknown(d) => d.len(),
210        };
211        2 + entry_value_length
212    }
213}
214
215/// The data to express container to aggregate multiple data for TLV (Type-Length-Value) of ALSA
216/// control interface.
217///
218/// It has `SNDRV_CTL_TLVT_CONTAINER` (=0) in its type field and has variable number of elements in
219/// value field.
220#[derive(Debug, Clone, PartialEq, Eq)]
221pub struct Container {
222    /// The entries of data for TLV.
223    pub entries: Vec<TlvItem>,
224}
225
226impl<'a> TlvData<'a> for Container {
227    fn value_type(&self) -> u32 {
228        SNDRV_CTL_TLVT_CONTAINER
229    }
230
231    fn value_length(&self) -> usize {
232        self.entries
233            .iter()
234            .fold(0, |length, entry| length + entry.raw_length())
235    }
236
237    fn value(&self) -> Vec<u32> {
238        let mut raw = Vec::new();
239        self.entries.iter().for_each(|entry| {
240            raw.append(&mut entry.into());
241        });
242        raw
243    }
244}
245
246const TYPES_FOR_CONTAINER: &'static [u32] = &[SNDRV_CTL_TLVT_CONTAINER];
247
248impl std::convert::TryFrom<&[u32]> for Container {
249    type Error = TlvDecodeError;
250
251    fn try_from(raw: &[u32]) -> Result<Self, Self::Error> {
252        // At least, type and length field should be included.
253        if raw.len() < 2 {
254            Err(Self::Error::new(TlvDecodeErrorCtx::Length(raw.len(), 2), 0))
255        // Check type field.
256        } else if raw[0] != SNDRV_CTL_TLVT_CONTAINER {
257            Err(Self::Error::new(
258                TlvDecodeErrorCtx::ValueType(raw[0], TYPES_FOR_CONTAINER),
259                0,
260            ))
261        } else {
262            // Check length field against length of value field.
263            let value_length = (raw[1] / 4) as usize;
264            let value = &raw[2..];
265            if value.len() < value_length {
266                Err(Self::Error::new(
267                    TlvDecodeErrorCtx::ValueLength(value_length, value.len()),
268                    1,
269                ))
270            } else {
271                // Decode value field.
272                let mut pos = 0;
273
274                let mut entries = Vec::new();
275                while pos < value_length {
276                    TlvItem::try_from(&value[pos..])
277                        .map(|entry| {
278                            entries.push(entry);
279                            pos += 2 + (value[pos + 1] / 4) as usize;
280                        })
281                        .map_err(|e| {
282                            Self::Error::new(TlvDecodeErrorCtx::ValueContent(Box::new(e)), pos + 2)
283                        })?;
284                }
285
286                Ok(Self { entries })
287            }
288        }
289    }
290}
291
292impl From<&Container> for Vec<u32> {
293    fn from(data: &Container) -> Self {
294        let mut raw = Vec::new();
295        raw.push(data.value_type());
296        raw.push(4 * data.value_length() as u32);
297        raw.append(&mut data.value());
298        raw
299    }
300}
301
302impl From<Container> for Vec<u32> {
303    fn from(data: Container) -> Self {
304        (&data).into()
305    }
306}
307
308#[cfg(test)]
309mod test {
310    use super::{Container, TlvItem};
311    use super::{DbInterval, DbScale};
312    use super::{DbRange, DbRangeEntry, DbRangeEntryData};
313    use std::convert::TryFrom;
314
315    #[test]
316    fn test_dbrangeentry_dbscale() {
317        let raw = [-9i32 as u32, 100, 1, 8, 0, 10];
318        let entry = DbRangeEntry::try_from(&raw[..]).unwrap();
319        assert_eq!(entry.min_val, -9);
320        assert_eq!(entry.max_val, 100);
321        assert_eq!(
322            entry.data,
323            DbRangeEntryData::DbScale(DbScale {
324                min: 0,
325                step: 10,
326                mute_avail: false
327            })
328        );
329        assert_eq!(&Vec::<u32>::from(entry)[..], &raw[..]);
330    }
331
332    #[test]
333    fn test_dbrangeentry_dbinterval_linear() {
334        let raw = [-9i32 as u32, 100, 2, 8, 0, 10];
335        let entry = DbRangeEntry::try_from(&raw[..]).unwrap();
336        assert_eq!(entry.min_val, -9);
337        assert_eq!(entry.max_val, 100);
338        assert_eq!(
339            entry.data,
340            DbRangeEntryData::DbInterval(DbInterval {
341                min: 0,
342                max: 10,
343                linear: true,
344                mute_avail: true
345            })
346        );
347        assert_eq!(&Vec::<u32>::from(entry)[..], &raw[..]);
348    }
349
350    #[test]
351    fn test_dbrange() {
352        let raw = [
353            3u32,
354            72,
355            0,
356            10,
357            2,
358            8,
359            -110i32 as u32,
360            10,
361            10,
362            20,
363            4,
364            8,
365            -10i32 as u32,
366            0,
367            20,
368            30,
369            5,
370            8,
371            0,
372            20,
373        ];
374        let range = DbRange::try_from(&raw[..]).unwrap();
375        assert_eq!(
376            range.entries[0],
377            DbRangeEntry {
378                min_val: 0,
379                max_val: 10,
380                data: DbRangeEntryData::DbInterval(DbInterval {
381                    min: -110,
382                    max: 10,
383                    linear: true,
384                    mute_avail: true
385                }),
386            }
387        );
388        assert_eq!(
389            range.entries[1],
390            DbRangeEntry {
391                min_val: 10,
392                max_val: 20,
393                data: DbRangeEntryData::DbInterval(DbInterval {
394                    min: -10,
395                    max: 0,
396                    linear: false,
397                    mute_avail: false
398                }),
399            }
400        );
401        assert_eq!(
402            range.entries[2],
403            DbRangeEntry {
404                min_val: 20,
405                max_val: 30,
406                data: DbRangeEntryData::DbInterval(DbInterval {
407                    min: 0,
408                    max: 20,
409                    linear: false,
410                    mute_avail: true
411                }),
412            }
413        );
414        assert_eq!(&Vec::<u32>::from(range)[..], &raw[..]);
415    }
416
417    #[test]
418    fn test_containerentry_dbscale() {
419        let raw = [0u32, 32, 1, 8, 0, 5, 1, 8, 5, 5];
420        let cntr = Container::try_from(&raw[..]).unwrap();
421        assert_eq!(
422            cntr.entries[0],
423            TlvItem::DbScale(DbScale {
424                min: 0,
425                step: 5,
426                mute_avail: false
427            })
428        );
429        assert_eq!(
430            cntr.entries[1],
431            TlvItem::DbScale(DbScale {
432                min: 5,
433                step: 5,
434                mute_avail: false
435            })
436        );
437        assert_eq!(&Vec::<u32>::from(cntr)[..], &raw);
438    }
439
440    #[test]
441    fn test_containerentry_dbrange() {
442        let raw = [
443            0u32, 136, 3, 48, 0, 10, 4, 8, 0, 5, 10, 20, 4, 8, 0, 10, 3, 72, 0, 10, 4, 8, 0, 5, 10,
444            20, 4, 8, 5, 10, 20, 40, 4, 8, 10, 20,
445        ];
446        let cntr = Container::try_from(&raw[..]).unwrap();
447        assert_eq!(
448            cntr.entries[0],
449            TlvItem::DbRange(DbRange {
450                entries: vec![
451                    DbRangeEntry {
452                        min_val: 0,
453                        max_val: 10,
454                        data: DbRangeEntryData::DbInterval(DbInterval {
455                            min: 0,
456                            max: 5,
457                            linear: false,
458                            mute_avail: false
459                        }),
460                    },
461                    DbRangeEntry {
462                        min_val: 10,
463                        max_val: 20,
464                        data: DbRangeEntryData::DbInterval(DbInterval {
465                            min: 0,
466                            max: 10,
467                            linear: false,
468                            mute_avail: false
469                        }),
470                    },
471                ],
472            })
473        );
474        assert_eq!(
475            cntr.entries[1],
476            TlvItem::DbRange(DbRange {
477                entries: vec![
478                    DbRangeEntry {
479                        min_val: 0,
480                        max_val: 10,
481                        data: DbRangeEntryData::DbInterval(DbInterval {
482                            min: 0,
483                            max: 5,
484                            linear: false,
485                            mute_avail: false
486                        }),
487                    },
488                    DbRangeEntry {
489                        min_val: 10,
490                        max_val: 20,
491                        data: DbRangeEntryData::DbInterval(DbInterval {
492                            min: 5,
493                            max: 10,
494                            linear: false,
495                            mute_avail: false
496                        }),
497                    },
498                    DbRangeEntry {
499                        min_val: 20,
500                        max_val: 40,
501                        data: DbRangeEntryData::DbInterval(DbInterval {
502                            min: 10,
503                            max: 20,
504                            linear: false,
505                            mute_avail: false
506                        }),
507                    },
508                ],
509            })
510        );
511        assert_eq!(&Vec::<u32>::from(cntr)[..], &raw[..]);
512    }
513}