这篇写什么?
做 语音唤醒 / 回声消除(AEC)/ 降噪 时,算法通常同时需要两类声音:
- Mic:麦克风采到的「人声 + 环境声 + 扬声器回声」
- Ref(Reference):正在播放出去的参考信号(扬声器在播什么)
很多板子上,上层 不是直接读物理麦设备,而是从一个叫 loopback 的虚拟采集口,一次读出「Mic + Ref」的多通道混合数据。
一句话:
PDM 物理麦 → 驱动合成 → alsaPORT-loopback(Mic + Ref 多通道)
→ 开 PCM(16k / 16bit / mic_ch+ref_ch)→ 按帧读 → 拆通道
→ Ref downmixer → AEC / 降噪 / 唤醒 → 2ch 降噪语音 + 唤醒回调
相关延伸:麦克风阵列音频检查方法与标准、Google 远场与 HAL 多通道 AEC、Loopback 抗混叠实测、多路语音唤醒、验证喇叭失真 THD。本文讲算法侧如何从 loopback 拿到 Mic + Ref。
数据流示意
要点: alsaPORT-pdm-builtinmic 是物理麦克风来源;上层算法侧认的采集节点是 alsaPORT-loopback。
1. 数据来源
| 项目 | 说明 |
|---|---|
| 采集节点 | alsaPORT-loopback(全名常见为 LOOPBACK-A-dummy-alsaPORT-loopback) |
| 数据内容 | loopback 里是 Mic 原始声音 + 播放参考信号(Ref) 的多通道混合数据 |
| 物理麦 | alsaPORT-pdm-builtinmic(PDM 内置麦)——由驱动合成进 loopback,上层一般不直接读它 |
为什么要 Ref?
扬声器在播音乐 / TTS 时,Mic 会录到回声。AEC / 降噪要用 Ref 知道「现在正在播什么」,才能把回声从 Mic 里减掉,唤醒也更稳。
2. 采集参数(按产品需求定)
常见语音链路参数:
| 参数 | 取值 | 说明 |
|---|---|---|
| 采样率 | 16 kHz | 唤醒 / 通话类算法常用 |
| 位宽 | 16 bit | PCM 有符号 16-bit |
| 通道数 | mic_ch + ref_ch | 例:4mic + 6ref → 10 通道 |
通道数不是随便开:必须和 loopback 实际布局、算法期望一致,否则拆通道会错位。
3. 通道布局(框架约定)
loopback 约定:
前面 = Mic 通道:mic0 … micN-1
后面 = Ref 通道:ref0 … ref5(固定 6 路 Ref 很常见)
举例
4 麦 + 6 Ref(共 10 通道):
[ mic0 mic1 mic2 mic3 | ref0 ref1 ref2 ref3 ref4 ref5 ]
←—————— Mic 4 路 ————→ ←—————————— Ref 6 路 ——————————→
2 麦 + 6 Ref(共 8 通道):
[ mic0 mic1 | ref0 ref1 ref2 ref3 ref4 ref5 ]
←— Mic 2 —→ ←—————————— Ref 6 路 ——————————→
拆分时:前 mic_ch 路是麦,后 ref_ch 路是参考。搞反了,AEC 会完全失效。
4. 用 tinycap 抓 Mic / Ref 数据
调试时可用 tinycap 把 loopback 录成 wav,再用 Audacity 等工具看通道是否对得上。
# 2mic(按你板子上的 card/device 改 -D / -d)
tinycap /sdcard/test_2mic.wav -D 0 -d 3 -c 2 -r 16000
# 4mic
tinycap /sdcard/test_4mic.wav -D 0 -d 3 -c 4 -r 16000
# 10ch(4mic + 6ref)
tinycap /sdcard/test_10ch.wav -d 6 -c 10 -r 16000 -p 480
说明:
-c:通道数,要和「你想抓的布局」一致(只抓 mic 或抓完整 mic+ref)-r 16000:16 kHz-D/-d:card / device,以/proc/asound/pcm里 loopback 实际编号为准(不同机型可能不是 0/3 或 device 6)-p:period 大小,按驱动 / 框架习惯(例:480)
录完后建议核对:说话时前几路能量大;播放音乐时后几路 Ref 有能量。
5. 处理过程(运行时链路)
步骤 1:定位 ALSA 设备
读 /proc/asound/pcm,用设备名 alsaPORT-loopback 匹配出对应的 card / device。
不要写死编号——换机型、换内核配置后 card 号常会变,用名字匹配更稳。
步骤 2:打开 PCM
用上一步的 card/device 打开 loopback,配置例如:
- 采样率:16 kHz
- 位宽:16 bit
- 通道:
mic_ch + ref_ch(如 4+6=10)
步骤 3:获取一帧数据
循环读取 PCM buffer。当前架构常见约定:
- 每次处理固定 256 帧
- 在 16 kHz 下:
256 / 16000 ≈ 16 ms
读取长度、算法帧长、回调节奏都围绕这个帧长对齐,避免缓冲积压或欠载。
步骤 4:通道拆分
按布局约定,把一帧交错 PCM 拆成:
mic[mic_ch][256](或等价缓冲)ref[ref_ch][256]
步骤 5:Ref downmixer
loopback 往往 固定给 6 路 Ref,但算法可能只要 2 / 4 / 5 路。
这时做 downmixer:对部分 Ref 做求和 / 平均(或按产品表映射),得到算法需要的 Ref 路数。
ref0..ref5(6 路)──downmixer──▶ ref'(2/4/5 路)──▶ AEC / 降噪
步骤 6:算法处理
用 Mic + Ref 做:
- AEC(回声消除)
- 降噪
- 唤醒检测
步骤 7:输出结果
| 输出 | 用途 |
|---|---|
| 2 通道降噪语音 | 交给上层语音链路(识别 / 通话等) |
| 唤醒事件回调 | 检测到唤醒词时通知上层 |
责任怎么分(小结)
| 环节 | 谁负责 | 常见坑 |
|---|---|---|
| 物理麦 | PDM + 驱动 | 上层去读 builtinmic,读不到完整 Ref |
| Mic+Ref 合成 | loopback 驱动 | 通道数 / 布局和文档不一致 |
| 打开设备 | 按名字找 card/device | 写死 -D0 -d3 换机失败 |
| 拆通道 | 应用 / 中间件 | Mic、Ref 前后搞反 |
| Ref 路数 | downmixer | 算法要 2 路却硬塞 6 路 |
| 帧长 | PCM 读 + 算法 | 和 256@16k(约 16ms)不对齐导致卡顿 |
一句话记住
唤醒 / 降噪要的不是「随便一个麦」,而是 loopback 里按约定排好的 Mic + Ref;拆对通道、对齐帧长、必要时把 6 路 Ref downmix 成算法要的路数,后面的 AEC / 唤醒才站得住。
评论
还没有评论,来做第一个吧
有想法?直接在这说,不用跑留言板