音频算法插件开发指南
本文档介绍如何在 HiDiTing V100 的音频框架中集成、配置和验证音频算法插件。以语音唤醒(SEA)关键字检测场景为主线,说明 DSP 算法库、主核音频业务和音频数据通路之间的关系,并结合 SDK 中现有的 SEA 场景代码走读插件加载、数据绑定、事件通知和资源释放过程。
本文档适用于需要接入自研或第三方 SEA 算法库的开发者。AEF 算法插件的工程组织、配置和加载机制与 SEA 相同,可按本文流程扩展。
算法插件背景知识
算法插件在音频系统中的位置
音频算法通常在 DSP 侧运行,以降低主核负载并满足实时性要求。主核负责创建和管理 SEA 实例、建立音频数据通路、接收算法事件;DSP 侧的插件负责接收 PCM 数据并执行关键字检测等算法。

上图描述的是关键字检测的业务数据流:AI 采集原始 PCM 数据并将 SEA 作为输出绑定,SEA 将算法库加载到 DSP 并把算法结果通知给应用。需要保存原始 PCM 或 SEA 输出数据时,再为对应输出创建 ADP。插件不应直接承担 AI 初始化或应用业务处理职责。
SEA、ADP 与 AI 的职责
| 组件 | 作用 | 关键关系 |
|---|---|---|
| AI | 从麦克风、I2S 等输入端采集 PCM 数据 | 作为 SEA 的输入;也可绑定一路 ADP 保存原始数据。 |
| ADP | 音频数据处理和输出通道 | 在需要保存、转发或获取 AI / SEA 的 PCM 输出时创建。 |
| SEA | 语音算法管理模块 | 创建算法实例、加载 DSP 插件、管理算法参数并向应用报告事件。 |
| SEA 插件 | 实际运行的 DSP 算法库 | 例如关键字检测、语音前处理等;由 SEA 按插件标识加载。 |
| 主核应用 | 处理算法结果并编排业务 | 注册事件处理函数,例如唤醒后启动录音或网络业务。 |
说明: AI 没有可供应用直接读取的音频数据出口。若应用需要获取或保存 AI 采集的 PCM 数据,必须创建一路 ADP,并将 ADP 绑定到 AI 输出。SEA 关键字检测场景则直接将 SEA 绑定到 AI 输出;若需要保存算法输入或输出,可再按场景为 AI 或 SEA 创建 ADP。
插件名称、配置与编译约束
SEA 的扩展插件名称限定为 sea_lib0、sea_lib1、sea_lib2 和 sea_lib3,分别对应扩展算法类型 UAPI_SEA_LIB_EXT0 至 UAPI_SEA_LIB_EXT3。创建或加载插件时必须保持名称、组件目录和算法类型一致,否则会导致 DSP 侧找不到目标组件。
以 sea_lib0 为例,插件开关通常在 sap_idp.mak 中配置:
CFG_SAP_SEA_LIB0_SUPPORT=y
ifeq ($(CFG_SAP_SEA_LIB0_SUPPORT), y)
SAP_FEATURE_CONFIGS += -DSAP_SEA_LIB0_SUPPORT
endif
新增自定义插件时,可保持同一配置模式,将 LIB0 替换为对应插件编号。DSP 工具链路径和许可证信息由 xtensa_cfg.mak 中的 XTENSA_HOME、XTENSAD_LICENSE_FILE 等配置项提供;应使用项目实际安装的工具链,不要将本机绝对路径固化到提交的公共配置中。
说明: 该目录下存在脚本sea_replace_overlay_3322.sh,该脚本会编译镜像并将其打包至dsp_overlay.bin,其中会将sea_lib0重命名成see_1mic,see_1mic对应的扩展算法类型为UAPI_SEA_LIB_SEE,/src/application/audio下的sample代码中有关sea的场景一般默认使用UAPI_SEA_LIB_SEE
说明: 若算法库新增了 C 标准库或平台库函数依赖,除 DSP 侧库链接外,还需要检查主镜像 overlay 的函数转发表是否已注册相应函数。缺少函数转发或导出时,插件可能能够编译,却会在加载或运行时失败。
注意: 代码编译镜像依赖 RG-2017.5-linux、H_3Z_ides 的工具链和编译证书;在 xtensa_cfg.mak 中配置 XTENSA_HOME 工具链文件地址和 XTENSAD_LICENSE_FILE 证书文件地址。工具链和证书需要向 Cadence 购买。
快速跑通音频算法插件 Demo
编译
说明:本文示例命令以一站式 CLI 为主。实际开发可从以下三种环境中选择,推荐使用一站式 CLI。
| 开发环境 | 适用场景 | 使用指南 |
|---|---|---|
| 一站式 CLI(推荐) | 快速完成目标选择、构建、烧录和串口监视 | 一站式 CLI 开发环境使用指南 |
| HiSpark Studio for VS Code | 图形化编辑、编译、烧录和调试 | HiSpark Studio for VS Code 开发环境使用指南 |
| WSL 与 Docker | 在 Windows 上使用一致的 Linux 容器构建环境 | WSL 与 Docker 环境使用指南 |
按 一站式 CLI 开发环境使用指南 准备环境、构建和烧录;构建前必须确保 fbb doctor 成功。本文只说明场景代码与插件配置,具体构建、烧录和串口连接操作请参考该指南。
本场景直接使用 SDK 现有的 SEA 场景代码:src/application/audio/sample_sea/sample_sea.c。该代码负责主核侧业务流程;DSP 算法库由 sample_dsp_overlay/sea/component/sea/plugins/ 下的插件工程提供。
功能说明与使用方式
关键字检测场景的目标是:从指定 AI 输入采集 PCM 数据,送入 SEA 插件,算法命中关键字后由主核回调处理结果。

SDK 场景分发函数为 audio_execute_function(),它会根据 AT 命令传入的场景名调用 sample_sea。串口中可使用以下形式启动关键字检测场景:
参数的实际含义以 sample_sea_parse_param() 的解析逻辑为准:第一个参数为存储时长或场景相关数值,null 表示不指定存储文件,kws 选择关键字检测算法类型,lpadc0 选择音频输入端口。运行后通过 AT^AUDIO=sample_sea q 结束当前音频场景。
功能与接口列表
| 功能 | 现有代码中的关键调用 | 接口说明 |
|---|---|---|
| 初始化 SEA 管理模块 | uapi_sea_init() | 头文件声明 |
| 创建 SEA 实例 | uapi_sea_create() | 头文件声明 |
| 加载算法引擎 | uapi_sea_load_engine() | 头文件声明 |
| 启用/关闭 SEA 服务 | AudioManagerSetSeaEnable() |
音频服务接口说明 |
| 配置 SEA 参数 | AudioManagerSetSeaParam() |
音频服务接口说明 |
| 为 SEA 输出绑定 ADP | uapi_sea_attach_output() | 头文件声明 |
| 接收算法事件 | sample_sea_event_proc() | 源码实现 |
文件结构与代码走读
算法集成包与工程组织
SDK 的 DSP 算法集成包以 sample_dsp_overlay/sea 为参考组织。不同版本中的目录层次可能略有差异,但职责应保持一致。
| 目录或文件 | 作用 |
|---|---|
build/ |
Xtensa DSP 工具链、编译脚本和构建参数。 |
include/td_type.h |
DSP 侧基础类型定义。 |
include/audio_alg.h |
音频算法通用接口定义。 |
include/audio_sea.h |
SEA 算法插件相关接口定义。 |
overlay/component.h |
overlay 组件描述和注册信息。 |
overlay/core_overlay.c |
主镜像与 DSP overlay 之间的函数转发、启动关系。 |
component/sea/plugins/sea_lib0/ |
一个 SEA 插件的示例目录,包含 include、lib、src 等内容。 |
component/sea/plugins/sea_lib0/xasea_lib0_comp.c |
插件组件入口和组件描述。 |
插件库、组件入口和 overlay 注册信息缺一不可:仅把算法静态库放入目录并不会让 SEA 自动识别该算法;组件必须参与 DSP 侧构建,并被主镜像的 overlay 管理逻辑正确加载。
说明: 脚本sea_replace_overlay_3322.sh中的dsp_version_name 会影响编译时sea.bin的加载地址,请根据自己的需求选择相应的dsp_version_name
实现流程与核心代码走读
1. 解析用户参数并确定输入与算法类型
sample_sea_parse_param() 集中解析命令参数;其内部通过 sample_sea_parse_storage_size()、sample_sea_parse_storage_type()、sample_sea_parse_lib_type() 和 sample_sea_parse_ai_port() 将文本参数转换为存储配置、算法库类型和 AI 端口。应用新建场景时应保留这一层参数校验,避免把非法端口或不存在的算法类型传入底层接口。
static int sample_sea_parse_param(int argc, char *argv[], sample_sea_param *param)
{
/* 依次解析存储、算法库类型和 AI 端口;失败时直接返回错误。 */
ret = sample_sea_parse_lib_type(argv[3], ¶m->lib_type);
ret |= sample_sea_parse_ai_port(argv[4], ¶m->ai_port);
return ret;
}
代码中的字段和参数个数应以当前 SDK 源码为准。自定义应用不建议绕过该解析与校验过程直接写死底层枚举。
2. 初始化 SEA 并注册事件处理函数
sample_sea_sys_init() 依次调用 uapi_adp_init()、uapi_ai_init() 和 uapi_sea_init(),并在任一步失败时反序去初始化已完成的模块。SEA 实例创建成功后,sample_sea_open_sea() 使用 uapi_sea_register_event_proc() 注册事件处理函数;算法命中、加载失败等运行状态由该回调返回到主核。
static td_s32 sample_sea_event_proc(td_handle sea, uapi_sea_event_type event,
td_void *param, td_void *context)
{
/* 判断 UAPI_SEA_EVENT_KWS_MATCH,读取 phrase_id 并通知后续业务。 */
}
static int sample_sea_sys_init(void)
{
ret = uapi_adp_init();
ret = uapi_ai_init();
return uapi_sea_init();
}
事件回调应保持轻量:只做结果判断、状态更新或消息投递。耗时网络操作、文件写入和阻塞等待应转移到应用任务中,以免影响连续音频处理。
3. 创建 AI、ADP,并建立采集数据通路
场景代码根据解析后的 ai_port 创建 AI 输入,再将 SEA 作为 AI 的输出绑定。若用户请求保存原始数据、SEA 输出数据或 ASR 数据,则额外创建相应 ADP,并分别绑定在 AI 或 SEA 输出侧。绑定顺序与上图一致,任何一步失败都应立即释放已创建的资源。
/* 伪代码:实际属性和句柄类型以 sample_sea.c 为准。 */
h_ai = sample_sea_create_ai(param.ai_port);
uapi_ai_attach_output(h_ai, h_sea);
if (need_save_sea_output) {
uapi_sea_attach_output(h_sea, UAPI_SEA_OUTPUT_ASR_SRC, h_adp);
}
AI 到 SEA 的绑定是该场景的主数据通路。若需要保存或转发 PCM,再使用 ADP 建立支路;AI 采样率、位宽或声道数必须与算法输入要求一致,不应让算法插件猜测输入格式。
4. 加载 DSP 插件、创建 SEA 实例并注册事件
sample_sea_open_sea() 先按算法类型调用 uapi_sea_load_engine() 加载前处理和关键字检测引擎,再取得默认属性、创建 SEA 实例并注册事件回调。插件标识、模型名称和 DSP 插件工程中的组件名称必须一致。
ret = uapi_sea_load_engine(UAPI_SEA_LIB_SEE, "imedia_2mic");
ret = uapi_sea_load_engine(sea_inst->lib_id, "imedia_keyword");
ret = uapi_sea_get_default_attr(&sea_eng, &sea_attr);
ret = uapi_sea_create(&sea_inst->h_sea, &sea_eng, &sea_attr);
if (ret == TD_SUCCESS) {
ret = uapi_sea_register_event_proc(sea_inst->h_sea, sample_sea_event_proc, ctx);
}
sample_sea_only.c 还提供了仅运行 SEA 的参考流程,例如通过 uapi_sea_load_engine(UAPI_SEA_LIB_SEE, "imedia_2mic") 选择内置算法。自研插件应优先复用 sample_sea.c 的资源管理顺序,再替换为对应的 UAPI_SEA_LIB_EXT0 至 UAPI_SEA_LIB_EXT3 和模型标识。
5. 启动处理并按反序释放资源
启动时先保证所有对象创建和绑定成功,再启动音频输入和算法处理;退出时按数据流反方向解绑、销毁 SEA、ADP、AI。sample_sea.c 中的 sample_detach、sample_close、sample_deinit 错误退出分支同样应保留,因为它覆盖了创建中途失败时的资源回收。
| 阶段 | 检查重点 |
|---|---|
| 创建前 | AI 端口合法、插件开关已启用、模型名与插件一致。 |
| 绑定后 | AI→SEA 主链路已建立;启用保存时相应的 AI→ADP 或 SEA→ADP 支路也已建立。 |
| 运行中 | 事件回调是否收到命中或错误事件,DSP 是否正常加载组件。 |
| 退出时 | 先停止数据产生,再解绑和销毁句柄,避免回调访问已释放对象。 |
运行与预期结果
启动 sample_sea 后,对设备输入预置关键字。算法模型匹配时,sample_sea_event_proc() 应收到相应事件,串口打印或应用回调可观察到关键字命中结果;执行 AT^AUDIO=sample_sea q 后,场景停止且相关 AI、ADP、SEA 资源被释放。
若仅用于验证插件装载,可先使用 sample_sea_only.c 中的流程隔离 AI 采集链路问题,再接入完整的 AI→SEA->ADP 数据通路。
基于音频算法插件 Demo 开发自己的插件
可以 sea_lib0 作为模板建立新插件,但应在扩展编号范围内选择一个未使用的名称,并同步完成以下改动:
- 复制
component/sea/plugins/sea_lib0的include、lib、src和组件入口组织,替换为目标插件编号。 - 在插件组件入口中注册算法库的初始化、处理和反初始化函数;算法 PCM 格式必须与 ADP 输出约定一致。
- 在
sap_idp.mak中增加对应的CFG_SAP_SEA_LIBx_SUPPORT开关,并将宏传递给 DSP 构建。 - 在 overlay 组件配置中加入新插件,使 DSP 镜像能够编译和加载该组件。
- 使用
sample_sea.c的创建、绑定、启动和清理顺序,在主核应用中选择对应UAPI_SEA_LIB_EXTx和模型名称进行验证。
新增插件时,先在 sap_idp.mak 增加独立开关,并把宏传递给 DSP 构建:
CFG_SAP_SEA_LIB1_SUPPORT=y
ifeq ($(CFG_SAP_SEA_LIB1_SUPPORT), y)
SAP_FEATURE_CONFIGS += -DSAP_SEA_LIB1_SUPPORT
endif
随后在 component/sea/plugins/Makefile 中纳入插件目录:
插件自身的 Makefile 应分别支持算法静态库和 overlay 组件构建:
CUR_DIR := $(shell if [ "$$PWD" != "" ]; then echo $$PWD; else pwd; fi)
SAP_CORE_DIR := $(realpath $(CUR_DIR)/../../../..)
include $(SAP_CORE_DIR)/sap_idp.mak
include $(XTENSA_BUILD_DIR)/xtensa_cfg.mak
ifdef BUILD_LIB
TARGET := libsea_lib1_$(CFG_SAP_DSP_ARCH)
INCLUDES := -Iinclude -I$(SAP_CORE_DIR)/include
LIB_INSTALL_DIR := lib
SRCFILE := $(wildcard src/*.c)
else
TARGET := sea_lib1
INCLUDES := -Iinclude -I$(SAP_CORE_DIR)/include
LIBS := -l:lib/libsea_lib1_$(CFG_SAP_DSP_ARCH).a
SRCFILE := xasea_lib1_comp.c
endif
include ../rules.mak
若算法依赖主镜像尚未导出的 C 库函数,需要在 overlay_func 中增加同名钩子,并由 core_overlay.c 转发到主镜像:
typedef struct {
int (*func)(void);
} overlay_func;
int func(void)
{
return g_overlay_func->func();
}
不应直接复用多个插件的同一个编号或模型标识;这会使 SEA 在加载时无法区分目标算法。算法库升级后,还应同时检查 ABI、头文件版本和 overlay 导出函数是否一致。
说明: 当前目录下的脚本 sea_replace_overlay_3322.sh 可以实现编译镜像并打包至 dsp_overlay.bin;根据需求修改脚本中的 dsp_version_name、lib_src_name、lib_dest_name。若使用不同脚本编译,需要修改 hifi_lsp.mak 和 rules.mak 中的 XTENSA_LSP_FILE,使其指向实际的 LSP 文件。
注意事项
调试时建议先验证 DSP 插件是否可被单独加载,再验证 AI→SEA→ADP 数据通路,最后排查算法模型本身。这样可以将工具链、组件加载、PCM 格式和算法效果四类问题分开定位。
常见编译错误
| 现象 | 排查方向 |
|---|---|
uapi_sea_load_engine() 失败 |
检查 sea_lib0~sea_lib3 名称、算法类型、模型名和插件开关是否一致;确认插件已进入 DSP 镜像。 |
| 插件可编译但启动时异常 | 检查 Xtensa 工具链和许可证配置,以及算法库新增依赖是否已加入 overlay 函数转发。 |
| 算法始终未命中 | 检查 AI 端口、ADP 采样率/位宽/声道数与算法训练模型要求是否匹配,并确认 AI→ADP→SEA 的绑定顺序。 |
| 运行一段时间后无事件或崩溃 | 检查事件回调是否执行了阻塞操作;检查退出路径是否在停止数据通路前销毁了 SEA、ADP 或 AI 句柄。 |
| 修改插件后仍运行旧版本 | 清理与重建 DSP 插件产物,确认实际构建配置已启用新的 CFG_SAP_SEA_LIBx_SUPPORT。 |