# ALSA UCM Configurations 此文档介绍了 alsa-ucm-aw-configs 中 UCM 配置文件的语法及约定 ## 目录结构 UCM 配置文件放置在 ucm_configs 目录下, 按芯片级方案名字分类 (astar, azalea, ...); 在芯片级方案目录下, 按板级方案分类 (astar-dm, astar-parrot, ...), 并有一个 Default 目录用于放置该芯片级方案默认的配置文件 alsa-ucm-aw-configs 在编译时, 会根据当前芯片级方案的名字将对应目录下的配置文件拷 贝到小机端的 /usr/share/alsa/ucm 目录; 若当前芯片级方案的配置文件不存在, 则会拷贝 Default 目录下的默认配置文件 ``` ucm_configs ├── astar │   ├── astar-dm │   │   ├── AC108 │   │   │   ├── AC108.conf │   │   │   └── Record.conf │   │   └── audiocodec │   │   ├── audiocodec.conf │   │   └── Play.conf │   ├── astar-parrot │   │   └── audiocodec │   │   ├── audiocodec.conf │   │   ├── Play.conf │   │   └── Record.conf │   └── Default │   └── audiocodec │   ├── audiocodec.conf │   ├── Play.conf │   └── Record.conf └── azalea    ├── azalea-m2ultra    │   └── audiocodec    │   ├── audiocodec.conf    │   ├── Play.conf    │   └── Record.conf    └── Default    └── audiocodec    ├── audiocodec.conf    ├── Play.conf    └── Record.conf ``` ## UCM 配置文件 一份完整的 UCM 配置文件包括以下内容: card, verb, device, modifier (可选). 想要使 用某个 ALSA 设备并配置它的控件, 需要指定 1 个 card + 1 个 verb + 1 个或以上的 device + 0 个或以上的 modifier. ### card 表示声卡, 每个 card 单独一个目录, 每个方案需要包含一个或以上的 card, 如 astar-dm 下的 "AC108" 和 "audiocodec" 分别为两个不同的 card ### verb 表示使用案例 (use case), 表明某个特定的使用场景, 每个 card 需要包含一个或以上的 verb, 如 astar-parrot 的 audiocodec 下有 "Play" 和 "Record" 两个 verb ALSA 官方默认定义有一些 verb 的名字, 如 "HiFi", "Voice", 但为了 Tina 平台下不同 方案的统一, **不推荐** 使用这些名字, 而应遵循后面的 verb 的命名约定 ### device 表示设备, 如 "Headphone", "Speaker" 等, 每个 verb 需要包含一个或以上的 device, 其可用的命名请参考后面的 device 的命名约定 ### modifier 表示修饰, 是对某个设备使用时附加的一些设置, 使同一个 verb 下的同一个 device 可以 有不同的使用效果, 如 "PlayTone", "PlayMusic" 等. modifier 并不是必须的, 每一个 verb 可以包含零至多个 modifier. 其可用的命名并无限制, 只要遵循通用命名约定 即可 ## 语法 以 ucm_configs/DEMO/Default 目录下的配置文件为例 ### 定义 card 新建一个目录, 并在该目录中新建一个与该目录同名的 .conf 文件, 如目录 *democard* 及其中的 *democard.conf* 就定义了一个名为 `democard` 的 card *democard.conf* : ``` Comment "Comment on democard" SectionUseCase."Play" { File "Play.conf" } ValueDefaults { Card "hw:demo" Foobar "value of Foobar in democard" } ``` - 开头的 `Comment` 关键字是对这个 card 的注释 - `SectionUseCase` 关键字用于定义 verb, 上述例子中定义了名为 `Play` 的 verb, 其 详细内容位于文件 *Play.conf* 中 - `ValueDefaults` 关键字用于定义 card 级别的变量值, 上述例子中定义的 `Foobar` 和 `Card` 变量的有效作用域为该 card 及其以下的 verb, device 和 modifier. 可在 verb, device 或 modifier 中定义同名变量来覆盖这些变量的值 ### 定义 verb 如上所述, 在 democard 这个 card 的定义中, 通过 *democard.conf* 定义了名为 `Play` 的 verb, 此时需要创建 *Play.conf* 文件以描述 `Play` 的详细内容 *Play.conf* 中 verb 相关的部分: ``` SectionVerb { Commnent "Comment on verb Play" EnableSequence [ cdev "hw:demo" cset "name='Play Switch' 1" ] DisableSequence [ cdev "hw:foobar" cset "name='Play Switch' 0" ] Value { Foobar "Value of Foobar in verb Play" } } ``` - `SectionVerb` 关键字中的就是 `Play` 这个 verb 的内容 - `Comment` 关键字是对这个 verb 的注释 - `EnableSequence` 指使能这个 verb 时的操作, `cdev` 指定控件对应的 ALSA 虚拟设备 名字, `cset` 指定需要操作的控件名字和它的值; `DisableSequence` 与 `EnableSequence` 类似, 指失能这个 verb 时的操作 (**注意** : 由于 ALSA UCM 自身的缺陷, `DisableSequence` 中的内容实际无法执行, 因此 **不推荐** 在 `SectionVerb` 中使用 `EnableSequence` 和 `DisableSequence` 关键字, 而应该把相关的使能和失能操作放到具体的 device 或 modifier 内容中; 另外, `EnableSequence` 和 `DisableSequence` 使用的是 **方括号 [ ]** 而不是大括号 { }) - `Value` 关键字定义了 verb 级别的变量值, 其有效作用域为该 verb 以及其下的 device 和 modifier. 此处中的 `Foobar` 变量的值覆盖了 *democard.conf* 中 `ValueDefaults` 处定义的同名变量的值 ### 定义 device device 的定义也在 *Play.conf* 文件中, 通过 `SectionDevice` 关键字指定 *Play.conf* 中 device 相关的内容: ``` SectionDevice."Headphone" { Comment "Comment on device Headphone" EnableSequence [ cdev "hw:demo" cset "name='Headphone Switch' 1" ] DisableSequence [ cdev "hw:foobar" cset "name='Headphone Switch' 0" ] Value { Foobar "Value of Foobar in device Headphone" } } ``` - `SectionDevice` 关键字用于定义 device, 此处定义了一个名为 `Headphone` 的 device - `Comment`, `EnableSequence` 和 `DisableSequence` 关键字与 `SectionVerb` 中含义 相同 (不同之处在于 `SectionDevice` 中的 `EnableSequence` 和 `DisableSequence` 都 是可被实际执行的) - `Value` 关键字定义 device 级别的变量值, 其有效作用域为该 device. 此处定义的 `Foobar` 变量的值覆盖了 `SectionVerb` 中同名变量的值 ### 定义 modifier 除了使用的是 `SectionModifier` 关键字, modifier 的定义与 device 的类似. `Value` 关键字定义的变量值作用域为该 modifier, 会覆盖 `SectionVerb` 中同名变量的值 *Play.conf* 中 modifier 相关的内容: ``` SectionModifier."PlayTone" { Comment "Comment on modifier PlayTone" EnableSequence [ cdev "hw:demo" cset "name='PlayTone Switch' 1" ] DisableSequence [ cdev "hw:foobar" cset "name='PlayTone Switch' 0" ] Value { Foobar "Value of Foobar in modifier PlayTone" } } ``` ### 注释 UCM 配置文件中 `#` 符号后的内容会被认为是注释 ``` conf # This is a comment SectionUseCase."Play" { File "Play.conf" } ``` ## 约定 为了保持 Tina 中不同方案下配置文件的统一, 有如下的约定: ### 通用命名约定 除了 card 和某些特定的 Value 的名字外, 其余自定义的名字均采用 **驼峰命名法**, 如 `LineIn`, `PlayTone`, `MicToHeadphone`, ... 名字中不包含空格, 下划线, 短横线等字 符 对于某些原本是全字母大写或部分字母大写的单词, 更倾向于将它们视作一个单词而仅 大写其首字母, 如: 使用 `Dmic` 而非 `DMIC`, `I2s` 而非 `I2S`, `AdcChannels` 而非 `ADCChannels` ### card 1. 全志 SoC 内置 codec 的名字统一使用 `audiocodec` 2. 内置 codec 以外的其他 card 的名字按照实际情况而定, 除了不能包含空格外并无额外 限制 3. 在 `ValueDefaults` 中需要定义变量 `Card` 指定对应的 ALSA 虚拟设备声卡名字 (不能使用数字编号, 而应该把声卡的名字写出来, 如 `hw:audiocodec`, `hw:sndcodec`) ### verb 1. verb 的命名遵循通用命名约定 2. 如无特殊情况, 推荐 verb 统一使用 `Play` , `Record` 和 `Call` 三个名字. `Play` 包含用于播放的设备, 如 `Headphone`, `Speaker` 等; `Record` 包含用于录 制的设备, 如 `Mic`, `LineIn` 等; `Call` 则包含兼有播放和录制功能的设备, 如 `Bluetooth`, `Headset` 等 3. **不推荐** 在 `SectionVerb` 中定义 `EnableSequence` 和 `DisableSequence`, 除非 只存在使能操作而不存在失能操作 ### device 1. device 的命名遵循通用命名约定 2. 在一般情况下, 推荐使用以下的名字, 若无合适的再自行定义 > - Headphone > - Headset > - Speaker > - Mic > - LineIn > - LineOut > - PhoneIn > - PhoneOut > - Bluetooth > - Spdif > - Hdmi 3. 若某一个 verb 中存在多个同类型的设备, 可在 device 名字后面加编号区分 (编号不 一定需要从 1 开始, 可以根据实际情况而定), 然后在 `SectionVerb` 的 `Value` 中定义 `xxxNumber` 和 `xxxNames` 分别说明这种同类型设备的数量和名字, 名字之间用空格隔开 (如实际存在两个麦克风 MIC2 和 MIC3, 可以定义 `Mic2` 和 `Mic3` 两个 device, 并在 `SectionVerb` 的 `Value` 中定义 `MicNumber "2"` 和 `MicNames "Mic2 Mic3"`) 4. 有时候会有多个设备联合使用的情况, 对此没有硬性的命名约定, 对于某些情况推荐以下 命名: > - 普通情况下多个设备联合使用, 使用 *序数词+设备* 的命名, 如 `TwoMics`, `FourMics` > - 特定的设备需要对应特定的操作, 如 "MIC1 录制左声道, MIC2 录制右声道" 可命名为 > `Mic1LeftMic2Right` > - 特定的设备数量与特定的通道数, 如两个麦克风可能分别用于录制两声道或四声道的不 > 同场景下, 可命名为 `TwoMicsTwoChs` 和 `TwoMicsFourChs` 以区分 (`Chs` 为 `Channels` > 的缩写) 5. 若定义了多个设备联合使用的 device, 需要有它们各自本身的 device 定义. 例如定义 了 `TwoMics` 对应 MIC1 和 MIC2 的联合使用, 则需要分别定义 `Mic1` 和 `Mic2` 来提供 调节音量等操作的接口 (因为麦克风统一使用 `CaptureVolume` 变量作为音量控制的接口, 在当中不能同时定义多个设备的音量) ### modifier 除了需要遵循通用命名约定外, modifier 的命名由配置文件的编写者自行决定 ### Value 推荐优先使用以下变量的命名, 若无合适的再自行定义 (自定义的名字应遵循通用命名约定) - `Card` : 需要在 card 的 `ValueDefaults` 中定义, 用于指定该 card 对应的 ALSA 虚 拟声卡设备的名字 (不能使用数字编号, 而应该把具体的名字写出来), 如: ``` ValueDefaults { Card "hw:audiocodec" } ``` - `PlaybackPCM` , `CapturePCM` : 在 verb, device 或 modifier 中用于指定具体的 PCM 设备, 如 `PlaybackPCM "hw:audiocodec,0"` (这两个变量的名字沿用了 ALSA 官方的名字, 因此没遵循本文档的通用命名约定) - `PlaybackVolume` , `CaptureVolume` : 指定音量控件的名字及其默认值, 格式为: "控 件ID 默认值", 如 `PlaybackVolume "name='Headphone Volume' 40"` - `PlaybackVolumeMin` , `PlaybackVolumeMax` , `CaptureVolumeMin` , `CaptureVolumeMax` : 指定音量数值的有效范围, 需要在定义了 `PlaybackVolume` 或 `CaptureVolume` 的同时 定义, 如: ``` Value { PlaybackVolume "name='Headphone Volume' 40" PlaybackVolumeMin "0" PlaybackVolumeMax "63" } ``` - `PlaybackRate` , `CaptureRate` : 表明支持的特定采样率, 如 `PlaybackRate "16000 32000"` 表明仅支持 16000Hz 和 32000Hz 的采样率播放 - `PlaybackChannels` , `CaptureChannels` : 表明支持的特定通道数, 如 `PlaybackChannels "2 4"` 表明仅支持两通道或四通道播放 - `PlaybackFormat` , `CaptureFormat` : 表明支持的特定格式, 语法与 *aplay* 和 *arecord* 中 "-f" 选项的参数相同, 如 `PlaybackFormat "S16_LE S32_LE"` 表明仅支持 S16_LE 和 S32_LE 的格式播放 ## 其他 ### 顺序 (与 [interfaces.md](./interfaces.md) 中的 "默认使用案例 API" 相关联) verb, device 和 modifier 的顺序是根据配置文件中的先后位置决定的, 因此在编写配置文 件时, 尽量将想要默认使用的项放在前面, 保证 alsa-ucm-aw 中的程序能优先选中它们 card 则是按照文件名的先后顺序排列, 除了修改名字外, 无法直接规定其顺序