跳转至

音频算法插件开发指南

本文档介绍如何在 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_lib0sea_lib1sea_lib2sea_lib3,分别对应扩展算法类型 UAPI_SEA_LIB_EXT0UAPI_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_HOMEXTENSAD_LICENSE_FILE 等配置项提供;应使用项目实际安装的工具链,不要将本机绝对路径固化到提交的公共配置中。

说明: 该目录下存在脚本sea_replace_overlay_3322.sh,该脚本会编译镜像并将其打包至dsp_overlay.bin,其中会将sea_lib0重命名成see_1micsee_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。串口中可使用以下形式启动关键字检测场景:

AT^AUDIO=sample_sea 10 null kws lpadc0

参数的实际含义以 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 插件的示例目录,包含 includelibsrc 等内容。
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], &param->lib_type);
    ret |= sample_sea_parse_ai_port(argv[4], &param->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_EXT0UAPI_SEA_LIB_EXT3 和模型标识。

5. 启动处理并按反序释放资源

启动时先保证所有对象创建和绑定成功,再启动音频输入和算法处理;退出时按数据流反方向解绑、销毁 SEA、ADP、AI。sample_sea.c 中的 sample_detachsample_closesample_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 作为模板建立新插件,但应在扩展编号范围内选择一个未使用的名称,并同步完成以下改动:

  1. 复制 component/sea/plugins/sea_lib0includelibsrc 和组件入口组织,替换为目标插件编号。
  2. 在插件组件入口中注册算法库的初始化、处理和反初始化函数;算法 PCM 格式必须与 ADP 输出约定一致。
  3. sap_idp.mak 中增加对应的 CFG_SAP_SEA_LIBx_SUPPORT 开关,并将宏传递给 DSP 构建。
  4. 在 overlay 组件配置中加入新插件,使 DSP 镜像能够编译和加载该组件。
  5. 使用 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 中纳入插件目录:

sea_libx_$(CFG_SAP_SEA_LIB1_SUPPORT) += sea_lib1

插件自身的 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_namelib_src_namelib_dest_name。若使用不同脚本编译,需要修改 hifi_lsp.makrules.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