Session 模块 - 从小白到专家
1. 什么是 Session?
如果说 Stream 是"订单",Device 是"餐桌",那么 Session (会话) 就是 厨房和传菜员。
- 功能 : 它负责建立一条从 CPU (Android) 到 DSP (音频处理器) 再到 硬件接口 (Speaker) 的数据通路 (Graph)。
- 本质 : Session 管理着底层的 PCM 设备节点 (如
/dev/snd/pcmC0D0p) 和 DSP 上的音频处理图 (Graph)。
2. 核心类结构
Session 屏蔽了底层与内核/DSP 通信的差异。
Session(基类): 定义通用接口。SessionAlsaPcm(最常用) :- 基于 TinyALSA。
- 通过打开 PCM 设备节点 (
pcm_open) 来传输数据。 - 通过 Mixer Controls (
mixer_ctl_set_value) 来配置 DSP 模块(Tag/Module/Calibration)。
SessionAgm:- 基于 AGM (Audio Graph Manager) 服务。
- 新一代架构,通过 IPC 调用 AGM 服务来加载图,不再直接操作 PCM 节点。
SessionGsl:- 基于 GSL (Graph Services Layer)。
- 更底层的实现,直接与 GSL 库交互。
代码位置 : session/src/Session.cpp, session/src/SessionAlsaPcm.cpp。
3. 核心概念:FrontEnd (FE) 与 BackEnd (BE)
Session 起到了连接 FE 和 BE 的作用。
- FrontEnd (前端) : CPU 侧的接口。即 Android 看见的 PCM 设备 (如
pcm0p)。Session 负责分配和打开这个 FE。 - BackEnd (后端) : 硬件侧的接口。如 Speaker 的 I2S 接口 (如
SLIMBUS_0_RX)。Device 负责管理 BE。
Session 的工作就是把 FE 和 BE 在 DSP 内部连起来。
4. 关键接口与调用流程
4.1 打开会话 (open)
- 输入 :
Stream指针。 - 流程 :
- 分配 FE : 调用
ResourceManager::allocateFrontEndIds。比如分配了pcm0p。 - 获取 BE : 询问 Stream "你要去哪个设备?",得到 BE 名字 (如
QUAT_TDM_RX_0)。 - 建立连接: 告诉底层 "把 PCM0 和 TDM_RX_0 连起来"。
- 注册回调: 注册 Mixer Event 回调,用于接收 DSP 事件(如 VAD 唤醒,Buffer 播放完毕)。
- 分配 FE : 调用
4.2 配置参数 (setConfig)
这是 Session 最强大的功能。它用于向 DSP 发送各种参数。
- Type :
MODULE: 设置特定模块参数(如由 GKV 决定的Tag)。CALIBRATION: 发送校准数据(如 ACDB 数据)。
- 实现 (SessionAlsaPcm) :
- 它会将参数打包成
TKV(Tag-Key-Value) 结构。 - 通过
mixer_ctl_set_array发送给内核驱动,驱动再转给 DSP。
- 它会将参数打包成
4.3 准备与启动 (prepare / start)
prepare:- 通常用于申请 buffer,或者预加载 Graph。
start:- 对于
SessionAlsaPcm,这里会调用pcm_start(如果之前已经 write 了数据) 或者确保 PCM 节点处于 Ready 状态。
- 对于
4.4 读写数据 (read / write)
- 流程 :
- 直接调用
pcm_write(TinyALSA) 将音频数据写入内核缓冲区。 - 如果是 mmap 模式,则直接拷贝到共享内存。
- 直接调用
5. 进阶:Graph 与 Tag
Session 的灵魂在于对 Graph 的控制。
- Graph : DSP 内部的处理流程图。例如:
Resampler -> Volume -> Effects -> Mix。 - GKV (Graph Key Vector) : 一组 Key-Value 对,唯一确定一个 Graph。
- Session 根据 Stream 的属性(采样率、位宽、用例类型)构建 GKV。
- Tag Config :
- 为了配置 Graph 中的某个模块(比如 Volume),我们需要知道它的
Tag ID。 - Session 通过 XML 解析或硬编码知道:
STREAM_VOLUME对应Tag ID 0x1234。
- 为了配置 Graph 中的某个模块(比如 Volume),我们需要知道它的
6. 总结表格
| 接口函数 | 功能 | 对应厨房的角色 | 核心操作 |
|---|---|---|---|
open |
建立连接 | 确定哪个厨师(FE)做菜,送到哪个窗口(BE) | allocateFrontEndIds, pcm_open |
setConfig |
配置DSP | 告诉厨师:少放辣,多放盐 | mixer_ctl_set_array |
start |
开始处理 | 开火炒菜 | pcm_start |
write |
传输数据 | 把切好的菜(PCM)给厨师 | pcm_write |
getTimestamp |
获取时间戳 | 问厨师菜做到哪一步了 | pcm_get_htimestamp |
7. 开发者建议
- 排查无声 :
- 检查
open时 FE 和 BE 是否正确匹配。 - 检查
setConfig是否成功下发,DSP 如果参数错误可能会拒绝运行 Graph。 - 查看
pcm_write是否报错(如-EPIPEunderrun)。
- 检查
- 杂音问题: 通常是 Session 配置的采样率/位宽与 Device (BE) 不一致,导致 DSP 内部重采样错误或时钟不同步。