2 个版本
0.1.1 | 2022 年 7 月 27 日 |
---|---|
0.1.0 | 2022 年 7 月 19 日 |
在 解析实现 中排名 #1032
115KB
2.5K SLoC
该包旨在处理在 ALSA 控制接口中表示的 TLV (类型-长度-值) 风格数据。该包生成 TLV 风格数据的 u32 数组的编解码器以及用于表示内容的结构和枚举。
TLV 风格的数据用于多种目的。截至 Linux 内核 5.10,它包括 dB 表达式以及 ALSA PCM 子流的通道映射信息。这些定义在 Linux 内核源代码的 include/uapi/sound/tlv.h
中。
结构和枚举
Linux 内核有一系列宏来构建用于 TLV 数据的 u32 数组,而不是 C 语言中的结构定义。这方便将二进制数据嵌入到对象文件中,但对开发者和用户不够友好。该包提供了一些结构和枚举来表示 TLV 数据。结构和宏之间的关系如下:
DbScale
SNDRV_CTL_TLVT_DB_SCALE
DbInterval
SNDRV_CTL_TLVT_DB_LINEAR
SNDRV_CTL_TLVT_DB_MINMAX
SNDRV_CTL_TLVT_DB_MINMAX_MUTE
Chmap
/ChmapMode
/ChmapEntry
/ChmapPos
/ChmapGenericPos
SNDRV_CTL_TLVT_CHMAP_FIXED
SNDRV_CTL_TLVT_CHMAP_VAR
SNDRV_CTL_TLVT_CHMAP_PAIRED
DbRange
/DbRangeEntry
/DbRangeEntryData
SNDRV_CTL_TLVT_DB_RANGE
容器
SNDRV_CTL_TLVT_CONTAINER
该包提供了 TlvItem
枚举,用于将上述结构的数据分派到 TLV。
用法
将以下行添加到您的 Cargo.toml 文件中
[dependencies]
alsa-ctl-tlv-codec = "0.1"
TlvItem
枚举是使用该包的不错起点。
use alsa_ctl_tlv_codec::TlvItem;
use std::convert::TryFrom;
// Prepare raw data of TLV as array of u32 elements.
let raw = [2 as u32, 8, -100i32 as u32, 0]; // This is for SNDRV_CTL_TLVT_DB_LINEAR.
match TlvItem::try_from(&raw[..]) {
Ok(data) => {
let raw_generated: Vec<u32> = match &data {
TlvItem::Container(d) => d.into(),
TlvItem::DbRange(d) => d.into(),
TlvItem::DbScale(d) => d.into(),
TlvItem::DbInterval(d) => d.into(),
TlvItem::Chmap(d) => d.into(),
TlvItem::Unknown(d) => d.to_owned(),
};
assert_eq!(&raw[..], &raw_generated[..]);
}
Err(err) => println!("{}", err),
}
它实现了 TryFrom<&[u32]>
来解码 TLV 的原始数据,TLV 是 u32 元素数组。数据类型通过 Rust 枚举项的形状来检索。每个项都有关联的值。枚举本身及其关联值的结构都具有与 Vec::<u32>: From(&Self)
的 trait 边界,以生成 TLV 的原始数据。
关联值可以直接实例化,然后生成原始数据。
use alsa_ctl_tlv_codec::DbScale;
let scale = DbScale{
min: -100,
step: 10,
mute_avail: true,
};
let raw_generated: Vec<u32> = (&scale).into();
let raw_expected = [1 as u32, 8, -100i32 as u32, 10 | 0x00010000];
assert_eq!(&raw_generated[..], &raw_expected[..]);
一些关联值是容器类型,它聚集了其他项。在这种情况下,使用 TlvItem
对 Container
进行聚合。
use alsa_ctl_tlv_codec::*;
let cntr = Container{
entries: vec![
TlvItem::Chmap(Chmap{
mode: ChmapMode::Fixed,
entries: vec![
ChmapEntry{pos: ChmapPos::Generic(ChmapGenericPos::FrontLeft), ..Default::default()},
ChmapEntry{pos: ChmapPos::Generic(ChmapGenericPos::FrontRight), ..Default::default()},
ChmapEntry{pos: ChmapPos::Generic(ChmapGenericPos::LowFrequencyEffect), ..Default::default()},
],
}),
TlvItem::Chmap(Chmap{
mode: ChmapMode::ArbitraryExchangeable,
entries: vec![
ChmapEntry{pos: ChmapPos::Generic(ChmapGenericPos::FrontLeft), ..Default::default()},
ChmapEntry{pos: ChmapPos::Generic(ChmapGenericPos::FrontRight), ..Default::default()},
],
}),
TlvItem::Chmap(Chmap{
mode: ChmapMode::PairedExchangeable,
entries: vec![
ChmapEntry{pos: ChmapPos::Generic(ChmapGenericPos::FrontLeft), ..Default::default()},
ChmapEntry{pos: ChmapPos::Generic(ChmapGenericPos::FrontRight), ..Default::default()},
ChmapEntry{pos: ChmapPos::Generic(ChmapGenericPos::RearLeft), ..Default::default()},
ChmapEntry{pos: ChmapPos::Generic(ChmapGenericPos::RearRight), ..Default::default()},
],
}),
],
};
let raw_generated: Vec<u32> = (&cntr).into();
let raw_expected = [0 as u32, 60,
0x101, 12, 3, 4, 8,
0x102, 8, 3, 4,
0x103, 16, 3, 4, 5, 6];
assert_eq!(&raw_generated[..], &raw_expected[..]);
工具
一些程序位于 src/bin
目录下。
tlv-decode.rs
此程序从 stdin 解码 TLV 的原始数据,或作为命令行参数的数值字面量,然后打印解析的结构。
如果没有命令行参数,它将打印帮助信息并退出。
$ cargo run --bin tlv-decode
Usage:
tlv-decode MODE DATA | "-"
where:
MODE: The mode to process after parsing DATA:
"structure": prints data structures.
"macro": prints C macro expression
"literal": prints space-separated decimal array.
"raw": prints binary with host endian.
DATA: space-separated DECIMAL and HEXADECIMAL array for the data of TLV.
"-": use binary from STDIN to interpret DATA according to host endian.
DECIMAL: decimal number. It can be signed if needed.
HEXADECIMAL: hexadecimal number. It should have '0x' as prefix.
对于命令行参数中的 TLV 数据
$ cargo run --bin tlv-decode -- structure 5 8 0xfffffe00 128
...
DbInterval(DbInterval { min: -512, max: 128, linear: false, mute_avail: true })
对于来自 STDIN 的 TLV 数据,在机器架构为小端的情况下
$ echo -en "\x05\x00\x00\x00\x08\x00\x00\x00\x00\xfe\xff\xff\x80\x00\x00\x00" | \
cargo run --bin tlv-decode -- structure -
...
DbInterval(DbInterval { min: -512, max: 128, linear: false, mute_avail: true })
TLV 数据可以以 C 语言宏表达式形式打印
$ echo -en "\x05\x00\x00\x00\x08\x00\x00\x00\x00\xfe\xff\xff\x80\x00\x00\x00" | \
cargo run --bin tlv-decode -- macro -
...
SNDRV_CTL_TLVD_ITEM ( SNDRV_CTL_TLVT_DB_MINMAX_MUTE, 0xfffffe00, 0x80 )
TLV 数据可以以 u32 数值字面量数组或按主机端序对齐的 u8 二进制形式打印
$ echo -en "\x05\x00\x00\x00\x08\x00\x00\x00\x00\xfe\xff\xff\x80\x00\x00\x00" | \
cargo run --bin tlv-decode -- literal -
...
5 8 4294966784 128
$ echo -en "\x05\x00\x00\x00\x08\x00\x00\x00\x00\xfe\xff\xff\x80\x00\x00\x00" | \
cargo run --bin tlv-decode -- raw -
...
db-calculate.rs
此程序根据 STDIN 或命令行参数中的 TLV 数据,计算控制元素的 dB 值和原始值。它使用双精度浮点数进行 dB 计算。对于线性类型的 dB 计算,它使用指数和对数。
如果没有命令行参数,它将打印帮助信息并退出。
$ cargo run --bin db-calculate
Usage:
db-calculate "db" DECIMAL-FLOATING-POINT VALUE-RANGE DATA | "-"
db-calculate "value" DECIMAL | HEXADECIMAL VALUE-RANGE DATA | "-"
where:
"db": Use this program for db calculation.
"value": Use this program for value calculation.
DECIMAL-FLOATING-POINT: decimal floating point number. It can be signed if needed.
DECIMAL: decimal number. It can be signed if needed.
HEXADECIMAL: hexadecimal number. It should have '0x' as prefix.
VALUE-RANGE: space-separated triplet of MIN, MAX, and STEP comes from information of
control element. All of them are DECIMAL or HEXADECIMAL.
DATA: space-separated DECIMAL and HEXADECIMAL array for the data of TLV.
"-": use STDIN to interpret DATA according to host endian.
When data of TLV has information to support mute, "-9999999" for value and "-inf" for db are
available.
对于基于 STDIN 的 TLV 数据从 dB 到值的计算,在机器架构为小端的情况下
$ echo -en "\x05\x00\x00\x00\x08\x00\x00\x00\x00\xfe\xff\xff\x80\x00\x00\x00" | \
cargo run --bin db-calculate db 1.0 128 512 1 -
...
495
对于基于命令行参数的 TLV 数据从值到 dB 的计算
$ cargo run --bin db-calculate value 495 128 512 1 5 8 0xfffffe00 0
...
0.996666666666667
计算没有经过验证的数字。
许可证
alsa-ctl-tlv-codec crate 在 MIT 许可证 下发布。
支持
如果发现问题,请在 https://github.com/alsa-project/snd-firewire-ctl-services/ 中提交。