KunSpace
返回博客

语音唤醒代码流程:audio_preprocess 从打开到读数

2026年6月22日
2 次阅读

读完了?别空手走啊 😏

读都读完了,不点个赞说不过去吧

写评论

赞同、吐槽、提问都行,评论区开放

这篇写什么?

Soundbar / 带麦设备上的「OK Google」一类远场唤醒,表面上是 Assistant 在听,底下往往是:

麦阵 + 回采 Ref
  → HAL 里做 AEC/NS(可选)
  → 唤醒引擎出事件
  → 再拉 AudioRecord 给云端 ASR

本文按 代码调用链audio_preprocess_* 从开机打开到读数、再到关闭捋清楚,并对比 谷歌原生架构HAL 内优化架构

一句话:

adev_open → preprocess_open(建 speech_info / 麦配置)
Assistant AudioRecord → open_input → preprocess_start(选库、起 AEC、起麦)
回调写队列 → preprocess_read 读出
standby/close → stop / close / release

语音唤醒 preprocess 调用流程

相关:Google 远场与 HAL AECMic + Ref多路唤醒


一、架构:原生 vs 优化后

1)谷歌原生(示意)

常见路径更「分层清晰」:

  • 热词 / DSP 或专用通路做唤醒
  • Assistant 再通过 Framework 录音做识别
  • HAL 主要负责采数,重算法不一定落在 primary HAL 里

2)优化后:在 Audio HAL 里做 AEC + 唤醒

优化后:HAL 内 AEC/NS + 唤醒事件

优化架构里多了什么
Client YouTube 等走 AudioTrack;Assistant 走 AudioRecord + 云端 ASR
Audio Server Flinger / Policy 照常 Binder 上下
Audio HAL AEC+NS唤醒引擎;命中后抛 Wake-up event 通知 Assistant
Kernel / ALSA PDM 麦、TDM Out / Loopback Ref、EQ/DRC 等

对比一句话:

原生:唤醒多在专用/系统通路,HAL 偏「采数」
优化:Mic+Ref 在 HAL 里先 AEC/NS 再唤醒,事件回调上层,再 AudioRecord 拉流做 ASR

示意对比:

原生远场 vs HAL 内嵌唤醒

这样改的动机通常是:产品麦阵/功放拓扑固定,把 回声消除与唤醒产品类型、通道数 绑在同一套 HAL 配置里,便于量产换型。


二、总流程分五色(对应实现阶段)

阶段 代表 API 做什么
系统初始化 audio_preprocess_open speech_info、麦基础配置
配置请求 audio_init_req_config 按每路录音请求建路由 / 队列
核心处理 audio_preprocess_start 选产品库、起 AEC、起麦设备
音频处理 audio_preprocess_read + 回调 队列读写、AEC 后数据给上层
清理释放 stop / close / release_* 停麦、拆插件、释放结构

下面按调用顺序展开。


三、系统初始化(开机 / adev_open

系统启动
  → adev_open
  → audio_preprocess_open
       → init_speech_info
       → audio_init_mic_config
函数 作用
audio_preprocess_open preprocess 主入口,创建/挂接 speech_info
init_speech_info 初始化基础数据结构
audio_init_mic_config 配置麦克风基础参数(通道、周期等,与 ALSA period 同类概念)

此时往往 还没开始真正录音,只是 HAL 打开时把语音预处理模块备好。


四、配置请求(打开输入流)

当 Framework 打开录音输入时:

AudioFlinger::openInputStream / InHWHal::openInputStream
  → adev_open_input_stream
  → audio_init_req_config   // 为该路请求建配置与 byte queue
  → in_get_buffer_size / audio_mic_get_period

Assistant(如 Katniss)侧典型触发:

AudioRecord::set
  → AudioFlinger::openInput_l
  → adev_open_input_stream
函数 作用
audio_init_req_config 每一路 AudioRecord/输入流准备录音路由与缓冲队列
set_spk_product_type(若有) 设置产品类型 / 配置文件路径,供后面选库

五、核心处理:audio_preprocess_start(最关键)

真正「选哪套算法库、起麦、起 AEC」多在 start:

audio_preprocess_start
  → initialize_aec_and_mic_devices
       ├─ 产品/库映射(见下)
       ├─ init_aec_wakeup_config → createAECPlugin → 注册唤醒事件
       └─ audio_start_mic_device → 注册回调 audioMicDevCallback

5.1 产品类型 → 动态库名

parse_spk_type_to_library
  → getLibraryMappingFromConfig / getProductType(读系统属性)
  → 决定 searchKey
  → 在 config_lib_map 里查找
  → 生成库名并加载

searchKey 怎么选:

条件 searchKey
configName 为空 productType(如属性里的 "b"
configName 非空 配置文件名(如 A600BAR_EX51.cfg

库名常见模式(示意):

libElevocVoice_{productType}_{mic_ch}mic_{spk_type}_{ref_ch}ref

含义拆开:

字段 含义
productType 产品线 / 机型档
mic_ch 麦克风通道数
spk_type 喇叭/箱体类型(影响 AEC 模型)
ref_ch 回采 Ref 通道数

同时会落下 mic_ch / ref_ch / spk_type / spk_ch 等运行参数。

换机型却唤醒变差,优先查:属性 productType、cfg 名、通道数是否和库后缀一致。

5.2 AEC 与唤醒

init_aec_wakeup_config
  → createAECPlugin
  → 注册 wakeup 回调 / 事件

命中热词后,HAL 侧抛 Wake-up event,上层 Assistant 进入会话,再持续 AudioRecord 读识别流(可上云 ASR)。

5.3 启动麦设备与回调

audio_start_mic_device
  → 注册 handler
  → audioMicDevCallback
       → ev_byte_queue_write   // 处理后的数据写入队列

采数与算法在回调线程跑;读路径与写路径用 byte queue 解耦,避免阻塞 ALSA 周期。


六、音频处理:audio_preprocess_read

上层(经 Flinger)读输入流时:

audio_preprocess_read
  →(必要时再确认)initialize_aec_and_mic_devices
  → ev_byte_queue_read

数据路径示意:

ALSA Mic(+Ref)
  → 回调里 AEC/NS / 唤醒分析
  → queue_write
  → preprocess_read → queue_read → 交给 AudioRecord

唤醒事件是 控制面preprocess_read数据面。两者配合:先事件叫醒,再拉流识别。


七、清理释放

入口 调用 释放什么
in_standby audio_preprocess_stop 停处理、释放麦设备(release_mic_devices
adev_close_input_stream audio_close_req_config 该路请求的路由/队列
adev_close audio_preprocess_close release_mic_devices + release_aec_info,整模块收干净

原则:开流配对关流,open 配对 close,避免 AEC 插件或麦设备泄漏导致第二次唤醒失败。


八、和 Framework 的衔接(便于联调)

Google Assistant / Katniss
  → AudioRecord::set / start
  → AudioFlinger openInput
  → HAL adev_open_input_stream
  → audio_init_req_config
  → audio_preprocess_start(选库、起麦、AEC)
  → 回调写队列;read 出数
  → 唤醒事件回上层 → 云端 ASR(可选)

播放侧(YouTube 等)仍走 AudioTrack → Flinger → 功放;AEC 用 TDM Loopback / Ref 对齐喇叭声,才能在大声播放时还听得清唤醒词——见 Mic + Ref


九、排查清单

  • productType / cfg 名是否指向正确库后缀
  • mic_ch / ref_ch 与硬件、ALSA 配置一致
  • start 是否走到 audio_start_mic_device,回调是否在写队列
  • read 是否在读同一队列(空队列会像「录到全 0」)
  • 唤醒事件是否注册、上层是否收到
  • stop/close 是否成对,避免二次 open 失败

十、小结

要点 内容
架构差异 优化方案把 AEC/NS + 唤醒 放进 HAL,用事件叫醒 Assistant
生命周期 open → init_req → start(选库+起麦) → read/回调 → stop/close
选库 configName 空用 productType,否则用 cfg 名;库名编码通道与箱体类型
数据 回调写队列,read 读队列,和唤醒事件分工明确

audio_preprocess_start 里的 选库菱形 看懂,换机型、换麦数、换 Ref 时就不容易「库加载成功但唤醒奇差」。


相关阅读

评论

还没有评论,来做第一个吧

赞同、吐槽、提问都行,评论区开放