第3章:UCM配置文件结构:语法基础、Section划分、关键字段解析
好,咱们开始聊配置文件。说实话,我第一次接触UCM的配置文件时,心里是有点发怵的——满屏的括号、冒号、缩进,看着像JSON又像YAML,还有点像C语言的结构体。但用熟了之后你会发现,这玩意儿其实挺规整的。说白了,它就是一套用文本描述音频路由的“说明书”。
3.1 语法基础:别被花括号吓到
UCM的配置文件,全称叫 Use Case Manager 配置文件。它的语法核心就三个东西:Section、键值对、数组。嗯,就这些。
先看一个最简单的例子:
SectionVerb {
EnableSequence [
cset "name='Speaker' mixer.0 output_1_mux 0"
cset "name='Speaker' mixer.0 output_2_mux 0"
]
DisableSequence [
cset "name='Speaker' mixer.0 output_1_mux 1"
cset "name='Speaker' mixer.0 output_2_mux 1"
]
}
你看,SectionVerb 是一个Section的名字,花括号包起来的就是它的内容。里面 EnableSequence 和 DisableSequence 是键,后面的方括号 [] 里是数组,数组里每一条都是一个字符串命令。
关键点:
- Section名必须顶格写,不能有缩进
- 花括号
{}必须单独占一行(我个人习惯这样,清晰) - 键值对用空格分隔,不是等号
- 数组元素用方括号包裹,每个元素用双引号
- 每条命令以
cset开头,这是ALSA的控制命令
我在项目中遇到过有人把花括号写在同一行,结果解析器直接报错。嗯,UCM的解析器对格式要求挺严格的,少个换行都不行。
3.2 Section划分:五大核心模块
一个完整的UCM配置文件,通常由以下几个Section组成。我按使用频率排个序:
| Section名称 | 作用 | 是否必须 |
|---|---|---|
SectionUseCase |
定义音频场景(如播放音乐、通话) | 是 |
SectionVerb |
定义启用/禁用某个音频设备的操作 | 是 |
SectionDevice |
定义物理音频设备(扬声器、麦克风等) | 是 |
SectionModifier |
定义音频效果(如EQ、音量控制) | 否 |
SectionConf |
全局配置(如采样率、缓冲区大小) | 推荐有 |
你想想看,这五个Section其实对应了音频系统的五个维度:场景、动作、硬件、效果、全局参数。我刚开始学的时候,总是搞混 SectionVerb 和 SectionDevice 的区别。后来我总结了一句话:Verb是“怎么做”,Device是“用什么做”。
3.3 关键字段解析:每个Section里的核心参数
3.3.1 SectionUseCase 的关键字段
SectionUseCase."Music" {
File "music.conf"
Comment "Play music through speaker"
}
这里 File 字段指定了该场景对应的配置文件路径。注意,路径是相对于主配置文件的。我曾经踩过一个坑:把路径写成了绝对路径,结果换了个平台就找不到了。建议用相对路径。
Comment 字段是给人看的,解析器会忽略它。但别小看这个字段,团队协作时,写清楚注释能省很多沟通成本。
3.3.2 SectionVerb 的关键字段
SectionVerb {
Value {
TQ "0"
CapturePCM "hw:0,0"
PlaybackPCM "hw:0,0"
}
EnableSequence [
cset "name='Master Playback Volume' 80%"
]
DisableSequence [
cset "name='Master Playback Volume' 0%"
]
}
这里 Value 块里定义了三个关键字段:
- TQ:时间配额(Time Quota),0表示不限制。我一般设成0,除非有严格的功耗要求。
- CapturePCM:录音使用的PCM设备。注意,这里填的是ALSA的hw设备名。
- PlaybackPCM:播放使用的PCM设备。这两个字段很容易写反,我见过有人把Capture和Playback搞混,结果录音没声音,播放也没声音。
我曾经犯过的错: 在 EnableSequence 里忘了写 DisableSequence 的对应操作。结果场景切换时,上一个场景的设备没关掉,两个场景的音频流混在一起。嗯,从那以后我养成了习惯:写Enable的同时,一定把Disable也写好。
3.3.3 SectionDevice 的关键字段
SectionDevice."Speaker" {
Comment "Speaker output"
SupportedDevice [
"Speaker"
]
ConflictingDevice [
"Headphone"
]
EnableSequence [
cset "name='Speaker Switch' on"
]
DisableSequence [
cset "name='Speaker Switch' off"
]
}
这里有两个字段值得注意:
- SupportedDevice:当前设备支持哪些物理设备。说白了,就是告诉系统“我这个场景能用哪些硬件”。
- ConflictingDevice:冲突设备列表。比如插上耳机时,扬声器应该静音。这个字段就是干这个的。
我建议你在写 ConflictingDevice 时,把互斥关系列全。比如扬声器和耳机互斥,麦克风和扬声器在某些场景下也互斥。漏掉一个,就可能出现“耳机响着,扬声器也响着”的尴尬情况。
3.3.4 SectionModifier 的关键字段
SectionModifier."EQ" {
Comment "Equalizer for speaker"
EnableSequence [
cset "name='EQ Curve' 0 1 2 3 2 1 0"
]
DisableSequence [
cset "name='EQ Curve' 0 0 0 0 0 0 0"
]
}
Modifier说白了就是音频效果器。它的字段和Verb很像,但多了个 Type 字段(虽然上面没写)。常见的Type有 Volume、EQ、DRC(动态范围压缩)。
小技巧: 如果你不确定某个Modifier的Type怎么写,可以先用 Volume 试试。因为Volume是最通用的,几乎所有音频硬件都支持。等调通了再换成具体的效果器。
3.3.5 SectionConf 的关键字段
SectionConf {
SampleRate "48000"
BufferSize "1024"
PeriodSize "256"
Format "S16_LE"
}
这个Section一般放在配置文件最前面。它定义了全局的音频参数:
- SampleRate:采样率,常见的有44100、48000、96000。我建议统一用48000,兼容性最好。
- BufferSize:缓冲区大小,单位是帧。值越小延迟越低,但CPU占用越高。1024是个比较平衡的值。
- PeriodSize:周期大小,一般设为BufferSize的1/4。这个值影响中断频率。
- Format:音频格式,S16_LE是16位有符号小端格式,最通用。
嗯,这里要注意:SectionConf里的参数是全局生效的。如果你在某个场景里想用不同的采样率,得在对应的SectionVerb里单独覆盖。我一般不在SectionConf里写死,而是留个注释,方便后期调优。
3.4 一个完整的配置文件示例
好了,咱们把上面这些拼起来,看一个完整的例子。这个例子定义了一个“播放音乐”的场景:
SectionConf {
SampleRate "48000"
BufferSize "1024"
PeriodSize "256"
Format "S16_LE"
}
SectionUseCase."Music" {
File "music.conf"
Comment "Play music through speaker"
}
SectionVerb {
Value {
TQ "0"
PlaybackPCM "hw:0,0"
}
EnableSequence [
cset "name='Master Playback Volume' 80%"
]
DisableSequence [
cset "name='Master Playback Volume' 0%"
]
}
SectionDevice."Speaker" {
SupportedDevice [
"Speaker"
]
ConflictingDevice [
"Headphone"
]
EnableSequence [
cset "name='Speaker Switch' on"
]
DisableSequence [
cset "name='Speaker Switch' off"
]
}
你看,整个结构其实很清晰:先定义全局参数,再定义场景,然后定义动作,最后定义设备。我习惯按这个顺序写,不容易漏东西。
总结一下这章的核心:
- UCM配置文件由Section组成,每个Section有特定的作用
- 语法上注意花括号、缩进、数组格式
- 关键字段要理解其含义,特别是
EnableSequence和DisableSequence的配对 - 写配置文件时,先搭框架,再填细节
下一章咱们会聊怎么调试这些配置文件。说实话,写配置不难,调配置才是真正考验人的地方。到时候我会分享几个我踩过的坑,保证让你少走弯路。