跳转至

AEF overlay 开发指南

AEF 驱动背景知识

AEF 是输出侧的音效和音频后处理框架。MCU 侧负责打开 SOUND、选择输出端口、使能 AEF 类型以及下发参数;DSP overlay 侧负责注册插件、创建算法实例和逐帧处理 PCM。算法不直接管理播放设备或 AT 命令,应用也不应绕过插件接口直接访问算法内部状态。

音频效果插件架构图

其中,/src/application/audio/sample_dsp_overlay/aef 是插件集成工程;/src/application/audio/sample_ao/sample_aef.c 是 MCU 侧现有 AEF 控制参考;/src/application/audio/sample_dsp_overlay/aef/component/aef/plugins/aef_lib0/xaaef_lib0_comp.c 是当前 SDK 中可直接阅读的 DSP 插件适配示例代码。

说明:本文示例命令以一站式 CLI 为主。实际开发可从以下三种环境中选择,推荐使用一站式 CLI。

开发环境 适用场景 使用指南
一站式 CLI(推荐) 快速完成目标选择、构建、烧录和串口监视 一站式 CLI 开发环境使用指南
HiSpark Studio for VS Code 图形化编辑、编译、烧录和调试 HiSpark Studio for VS Code 开发环境使用指南
WSL 与 Docker 在 Windows 上使用一致的 Linux 容器构建环境 WSL 与 Docker 环境使用指南

准备工作:一站式 CLI 开发环境使用指南 准备环境、构建和烧录;构建前必须确保 fbb doctor 成功。插件工程还需要可用的 Xtensa 工具链和许可证;其配置文件、变量含义和注意事项见“开发环境配置”。

注意: 代码编译镜像依赖 RG-2017.5-linux、H_3Z_ides 的工具链和编译证书;在 xtensa_cfg.mak 中配置 XTENSA_HOME 工具链文件地址和 XTENSAD_LICENSE_FILE 证书文件地址。工具链和证书需要向 Cadence 购买。

快速跑通 AEF Demo

关键配置

进入 AEF overlay 集成开发包目录

  • 工程路径配置

    /src/application/audio/sample_dsp_overlay/aef/sap_idp.mak 已将 IDP_OUT_DIR 定义为当前 AEF 工程下的 out/。快速跑通时保持默认值;替换脚本和后续验收都从 out/bin/ 读取插件,单独修改输出目录会使脚本找不到产物。

  • Xtensa编译工具链配置

    xtensa_cfg.mak 默认将 XTENSA_HOME 指向名为 RG-2017.5-linux 的 SDK 配套工具链目录。先检查该目录及其中的 xt-make 是否随开发包交付;仅在使用外部工具链时才修改 XTENSA_HOME。许可证路径未在仓库中写死,需要按本机授权方式设置环境变量 XTENSAD_LICENSE_FILE

  • lsp_file配置 hifi_lsp.makrules.mak 中的 XTENSA_LSP_FILE,需要设置为 arch/lsp 目录下实际使用的 lsp_afe 文件名,后续生成的插件会加载到该 LSP 定义的内存地址。

编译

在算法集成开发包所在路径下,执行以下命令:

  1. 清除历史编译结果:make clean
  2. 编译算法静态库:make lib
  3. 编译算法插件 bin 文件:make all

    须知: 执行 make 前,必须确认 xtensa_cfg.mak 解析出的 XTENSA_HOME 下存在工具链,且需要许可证的工具链已设置有效的 XTENSAD_LICENSE_FILEmake all 会调用 xt-make 编译 plugins/Makefileaef_libx 列出的全部插件;当前默认条目是 aef_lib0。 当前/build/xtensa/arch/lsp目录下,没有Hi3322芯片对应的aef加载地址的lsp文件 若想要将编译出来的aef_lib0镜像加载替换进dsp_overlay.bin中,需要修改镜像名称为aef.bin再打包到dsp_overlay.bin中。若想使用该音效算法插件,需要在调用uapi_snd_set_port_aef_enable接口时将uapi_aef_type effect_type设置为UAPI_AEF_TYPE_CUSTOMER

构建验收条件

完成“开发自己的 AEF 插件”后,必须同时满足以下条件,才能判定插件可编译、可打包:

  1. /src/application/audio/sample_dsp_overlay/aef/build/xtensa/xtensa_cfg.mak 配置的 XTENSA_HOME 下存在 xt-make,许可证有效。
  2. 新插件目录、插件 Makefile 的 TARGET、适配源文件名和 plugins/Makefileaef_libx 条目一致。
  3. 目标产品配置已启用与插件类型一致的效果宏;CUSTOMER 插件必须启用 CFG_SAP_EFFECT_CUSTOMER_SUPPORT=y
  4. make cleanmake libmake all 均返回成功,并在 AEF 工程的 out/bin 输出目录生成目标插件,例如 aef_lib0.bin
  5. 打包前存在目标产品的 dsp_overlay.bin 输入文件;打包后使用 CLI 指南完成整机镜像构建和烧录,再通过 AT^audio=sample_aef cust on/off 验证开关。

任一条件不满足时,不应将插件标记为“已编译通过”。

镜像打包、烧录与运行

生成算法镜像包

/src/application/audio/sample_dsp_overlay/aef 中执行命令:

bash ./aef_replace_overlay.sh ../../../../tools/pkg/bin/3322/dsp/normal/dsp_overlay.bin aef_lib0.bin

3322 EVB 的 Overlay 输入和输出文件均为 src/tools/pkg/bin/3322/dsp/normal/dsp_overlay.bin。脚本会校验输入文件,重新构建 AEF 插件,解包 Overlay,以 out/bin/aef_lib0.bin 替换其中的 aef.bin,再将重新打包的结果写回同一路径。

执行脚本前应备份该文件。脚本会直接覆盖产品包中的 dsp_overlay.bin;仅在 aef_lib0.bin 已按前文构建成功、且确认需要替换当前产品包的 AEF 插件时执行。

第一个参数可传 dsp_overlay.bin 的完整或相对路径;使用仓库外部交付的旧 brandy 产品包时,也可传该产品包中的 DSP 版本目录名称。第二个参数是 out/bin/ 中生成的插件 bin 文件名。接入自定义插件后,将 aef_lib0.bin 替换为该插件的实际产物名。

启动 sample_ao 加载和运行算法

  1. 将产品打包后的镜像按 一站式 CLI 开发环境使用指南 烧录到开发板。

    图 1 烧写完成示意图

    烧写完成示意图

  2. 确认产品未启用 CFG_SAP_MINIMIZE_SAMPLE_RELEASE。当前 3322 EVB 配置未启用该宏,因此 sample_aef 已注册到音频命令分发中;若产品启用了该宏,应在自己的应用中调用 AEF 接口完成开关和参数下发。

  3. 先使能 CUSTOMER 效果:

    AT^audio=sample_aef cust on
    

    cust 仅支持 onoff,对应 MCU 侧的 UAPI_AEF_TYPE_CUSTOMER 开关。

  4. 播放固定 PCM 作为使能后的样本:

    输入AT命令:AT^audio=sample_ao /user/stream/pcm/PCM_16k_1ch_16bit.pcm 0 16000 16 1

    (sample_ao的AT命令格式需要遵循格式:AT^audio=sample_ao pcm_file volume sample_rate bit_width channels。命令中的pcm_file文件需提前上传至文件系统,或替换为文件系统中已有的pcm文件)

  5. 关闭效果后播放同一 PCM,形成 A/B 对比:

    AT^audio=sample_aef cust off
    

    透传插件在使能、关闭两个阶段都应正常播放且声音无异常;接入实际算法后,应使用同一 PCM 比较输出。需要验证算法参数时,使用前文所述的应用接口下发参数,不能依赖 cust 命令。

文件结构与代码走读

算法集成工程与文件结构

概述

本文档主要介绍 AEF overlay 集成相关内容,重点介绍算法集成开发包、开发算法环境配置、开发算法插件、加载和运行算法等。AEF(Audio Effect Framework)用于承载音效算法或音频后处理算法。

算法集成开发包

集成开发包说明

集成开发包见 GitCode 的 AEF Overlay 工程

AEF 构建目录

build目录:

AEF 公共头文件目录

include目录:

/src/application/audio/sample_dsp_overlay/aef/include/td_type.h:C语言变量类型重定义。

Overlay 适配目录

overlay目录:

AEF 组件目录

component目录:

C 库钩子扩展

说明: 如果主镜像未注册相应的C库函数到该新增的“overlay_func->func”函数指针,会导致overlay新增C库函数钩子。开发者可自行实现C库函数,或是知会主镜像发布者发布新的镜像将相应的C库函数注册到“overlay_func->func”函数指针。

修改集成开发包的/src/application/audio/sample_dsp_overlay/aef/overlay/core_overlay.h文件和/src/application/audio/sample_dsp_overlay/aef/overlay/core_overlay.c文件:

/* 在core_overlay.h的overlay_func结构体中,新增一个C库同名函数类型定义 */
typedef struct {
    int (*func)(void);
} overlay_func;
/* 在core_overlay.c源码中,新增一个C库同名函数实现,重定向到主镜像的钩子上 */
int func(void)
{
    return g_overlay_func->func();
}

示例插件

示例插件见 aef_lib0

插件目录结构

/src/application/audio/sample_dsp_overlay/aef/component/aef/plugins/aef_lib0”工程目录结构如下:

当前 aef_lib0 目录实际只包含 Makefilexaaef_lib0_comp.cincludelibsrc 是自定义算法复杂度增加时的建议分层,并非仓库现有目录。

插件编译脚本配置

  • 在“/src/application/audio/sample_dsp_overlay/aef/component/aef/plugins/Makefile”编译脚本中,增加新增的AEF插件aef_lib0:

    #=========================================================================
    aef_libx = aef_lib0
    #=========================================================================
    
  • /src/application/audio/sample_dsp_overlay/aef/component/aef/plugins/aef_lib0/Makefile编译脚本内容如下:

    CUR_DIR := $(shell if [ "$$PWD" != "" ]; then echo $$PWD; else pwd; fi)
    # 从当前目录回退到集成开发包所在路径
    IDP_TOP_DIR := $(realpath $(CUR_DIR)/../../../..)
    # 包含集成开发包编译环境配置和编译工具链配置文件
    include $(IDP_TOP_DIR)/sap_idp.mak
    include $(IDP_BUILD_DIR)/xtensa_cfg.mak
    # 编译待集成的AEF算法源码成静态库 libaef_lib0.a
    ifdef BUILD_LIB
    TARGET := libaef_lib0
    CFLAGS_EXT :=
    INCLUDES := -Iinclude -I$(IDP_TOP_DIR)/include
    LIB_INSTALL_DIR := lib
    /* 只编译待集成AEF算法的源文件 */
    SRCFILE := $(wildcard src/*.c)
    # 编译待集成的AEF插件,并链接算法静态库libaef_lib0.a 到 aef_lib0.bin中
    else
    TARGET := aef_lib0
    CFLAGS_EXT :=
    INCLUDES := -Iinclude -I$(IDP_TOP_DIR)/include
    # 链接待集成AEF算法的静态库 libaef_lib0.a,如还依赖其他静态库也链接进来。
    LIBS := -l:lib/libaef_lib0.a
    # 只编译待集成AEF算法的插件适配源文件
    SRCFILE := xaaef_lib0_comp.c
    endif
    # 包含编译规则和打包规则配置文件
    include ../rules.mak
    

AEF 插件接口与注册

AEF 插件接口说明

AEF插件接口说明如下:

  • xaaef_lib0_comp.h定义结构体aef_component成员变量(即AEF插件属性):

    • reserved:保留,不用配置。
    • name:字符串,算法库名称。
    • aef_type:AEF插件类型,参考枚举类型aef_type。
    • version:版本。
  • xaaef_lib0_comp.h定义结构体aef_component成员接口(即AEF插件适配接口):

    • create:创建算法实例并输出唯一的实例句柄给AEF。(必备接口)
    • destroy:销毁实例句柄对应的算法实例,并释放相关资源。(必备接口)
    • set_config:设置算法配置。(可选接口)
    • get_config:获取算法配置。(可选接口)
    • set_parameter:设置实例句柄对应的算法实例的参数。(可选接口)
    • get_parameter:获取实例句柄对应的算法实例的参数。(可选接口)
    • set_enable:使能或停止实例句柄对应的算法实例。(可选接口)
    • get_enable:获取实例句柄对应的算法实例的使能状态。(可选接口)
    • get_max_pcm_in_size:获取输入PCM数据的最大帧长。(可选接口)
    • get_max_pcm_out_size:获取输出PCM数据的最大帧长。(可选接口)
    • get_input_pcm_attr:获取输入PCM数据的属性。(必备接口)
    • proc_frame: 执行实例句柄对应的算法实例来处理输入的PCM数据。(必备接口)

      表 1 音效算法audio_frame结构体说明

      属性项

      说明

      interleaved

      数据是否交织。

      channels

      声道。

      bit_depth

      位宽。

      sample_rate

      采样率。

      pts

      帧时间戳。

      pcm_buffer

      未使用,待扩展。

      bits_buffer

      音频PCM数据缓存指针。

      bits_bytes

      音频PCM数据大小。

      pcm_samples

      音频PCM数据采样点数。

      frame_index

      音频帧索引编号。

      eos

      帧结束标志。

      pkg_loss

      丢包标志。

AEF 插件适配样例

请参考算法集成开发包中的 xaaef_lib0_comp.c

AEF 插件注册方法

AEF插件注册参考样例及说明如下。

/* 定义一个全局的插件 entry结构体 */
static aef_component ha_aef_lib_entry = {
    .name = (const td_char *)"customer_aef",
    .type = AEF_TYPE_CUSTOMER,
    .version = 0x00800001,
    .create = customer_aef_create,
    .destroy = customer_aef_destroy,
    .set_parameter = customer_aef_set_parameter,
    .get_parameter = customer_aef_get_parameter,
    .proc_frame = customer_aef_proc_frame,
};
/* 将插件entry地址注册到.component地址段中 */
define_component(AEF, ha_aef_lib_entry)

说明: 自研 CUSTOMER 效果的 entry 使用 AEF_TYPE_CUSTOMER;产品必须同时启用 CFG_SAP_EFFECT_CUSTOMER_SUPPORT=y。其他效果类型应使用 /src/application/audio/sample_dsp_overlay/aef/include/audio_aef.h 中定义的枚举,并与产品的音效特性配置一致。

基于 AEF Demo 开发自己的插件

创建 AEF 插件工程

AEF 插件工程命名要求

AEF插件镜像bin文件命名要求:AEF插件工程命名建议与插件镜像bin文件名一致。如工程命名为“aef_lib0”,则插件镜像命名为“aef_lib0.bin”。

新建 AEF 插件工程目录

请参考“插件目录结构”,新建工程aef_lib1。

新建工程aef_lib1到“/src/application/audio/sample_dsp_overlay/aef/component/aef/plugins”,工程目录结构如下:

  • include:按需放置算法公共头文件。
  • lib:按需放置取得合法授权的算法依赖库。
  • src:按需放置拆分后的算法源码。
  • xaaef_lib1_comp.c:由现有 xaaef_lib0_comp.c 复制并改名得到的插件适配源文件。
  • xaaef_lib1_comp.h:仅在插件需要独立声明时创建,不是现有模板的必备文件。

AEF 插件编译脚本

在“/src/application/audio/sample_dsp_overlay/aef/component/aef/plugins/Makefile”编译脚本中,增加新增的AEF插件命令如下:aef_libx += aef_lib1

请参考“插件编译脚本配置”示例插件“/src/application/audio/sample_dsp_overlay/aef/component/aef/plugins/aef_lib0/Makefile”的编译脚本,创建新增插件aef_lib1的编译脚本。

开发 AEF 插件

开发流程

本章节相当于外设类指南中的“基于 Demo 开发自己的应用”,但 AEF 的交付物不是独立应用,而是一个被 DSP overlay 加载的插件。建议以现有 aef_lib0 为模板,按既定目录结构和构建规则创建新插件。

音频效果插件开发流程

设计插件边界和参数 ABI

先确定插件名称、版本、算法类型、PCM 输入格式、每帧最大采样点数和 MCU/DSP 共用参数结构。audio_frame 的字段含义、aef_component 的回调职责和插件类型限制在下方“AEF插件接口说明”中完整保留;自定义参数应带版本和长度校验,避免 MCU 与 DSP 两端结构体不一致。

功能 参考代码或接口
AEF 类型、PCM 帧和组件回调定义 /src/application/audio/sample_dsp_overlay/aef/include/audio_aef.h
插件注册宏 /src/application/audio/sample_dsp_overlay/aef/overlay/component.h
现有 DSP 适配样例 /src/application/audio/sample_dsp_overlay/aef/component/aef/plugins/aef_lib0/xaaef_lib0_comp.c
MCU 侧 AEF 控制参考 /src/application/audio/sample_ao/sample_aef.c
AEF 参数服务接口 AudioManagerSetAefParam
建立插件工程和构建目标

复制 /src/application/audio/sample_dsp_overlay/aef/component/aef/plugins/aef_lib0,建立 aef_lib1includelibsrc 和适配文件。复制后先完成以下机械改名,再开始接入算法:

  1. xaaef_lib0_comp.c 重命名为 xaaef_lib1_comp.c,复制 aef_lib0/Makefile 到新插件目录,并把其中的 SRCFILE 改为该文件名。
  2. TARGET := aef_lib0 改为 TARGET := aef_lib1;插件工程名、TARGET、生成的 .bin 文件名和打包时的插件名必须一致。
  3. /src/application/audio/sample_dsp_overlay/aef/component/aef/plugins/Makefile 中的 aef_libx := aef_lib0 改为 aef_libx := aef_lib0 aef_lib1,使新插件参与 DSP 构建。
  4. 对自研 CUSTOMER 效果,在目标产品的音频配置中启用 CFG_SAP_EFFECT_CUSTOMER_SUPPORT=y;例如 3322 EVB 配置文件为 /src/drivers/drivers/driver/audio/source/build/configs/3322/3322_evb_liteos_cfg.mak。当前 /src/drivers/drivers/driver/audio/source/sap_base.mak 以互斥分支选择效果实现,因此 3322 EVB 同时必须保持 CFG_SAP_EFFECT_HVS_SUPPORT 未启用;仅打开 CUSTOMER 而保留 HVS,不会选择 CUSTOMER 实现。
  5. 修改产品 .mak 配置后,重新按 一站式 CLI 开发环境使用指南 触发产品构建,使配置重新生成并参与后续镜像构建;不要使用修改配置前生成的构建输出判断结果。
逐函数实现插件适配层

当前 /src/application/audio/sample_dsp_overlay/aef/component/aef/plugins/aef_lib0/xaaef_lib0_comp.c 是最小、可运行的适配参考:

  1. customer_aef_create() 返回算法实例;真实算法应在此创建上下文、分配或取得所需资源。
  2. customer_aef_set_parameter() / customer_aef_get_parameter() 按命令号分发参数,先校验 param_size,再读写实例状态。
  3. customer_aef_proc_frame() 接收 audio_frame,处理 PCM 后填充输出帧。当前示例直接 *out_frame = *in_frame,仅用于验证框架数据通路;自研算法必须在这里执行实际处理。
  4. g_ha_audio_customer_aef_entry 汇总名称、类型、版本和回调;最后由 define_component(AEF, ...) 放入 .component 段,供 overlay 加载。
static td_s32 customer_aef_proc_frame(td_void *aef,
    audio_frame *in_frame, audio_frame *out_frame)
{
    aef_check_null_ptr_return(aef);
    aef_check_null_ptr_return(in_frame);
    aef_check_null_ptr_return(out_frame);

    *out_frame = *in_frame; /* 透传版本:先验证 overlay 数据通路。 */
    return AUDIO_SUCCESS;
}

static aef_component g_ha_audio_customer_aef_entry = {
    .name = (const td_char *)"customer_aef",
    .type = AEF_TYPE_CUSTOMER,
    .version = 0x00800001,
    .create = customer_aef_create,
    .destroy = customer_aef_destroy,
    .set_parameter = customer_aef_set_parameter,
    .get_parameter = customer_aef_get_parameter,
    .proc_frame = customer_aef_proc_frame,
};
define_component(AEF, g_ha_audio_customer_aef_entry)

以上代码来自当前 aef_lib0 适配层,作为可编译的透传基线。确认该基线构建、打包和运行成功后,再仅替换 customer_aef_proc_frame() 内部的 PCM 处理,并保留参数长度校验、组件注册和资源释放路径。插件类型应以当前 /src/application/audio/sample_dsp_overlay/aef/include/audio_aef.h 支持的枚举和产品的加载策略为准。

编译、打包、运行和验证

完成适配后,依次执行下方“编译命令”和“生成算法镜像包”的步骤,将生成的 DSP overlay 纳入产品镜像。具体构建、烧录操作仍以 CLI 快速入门为准。

sample_aefcust 分支只调用 uapi_snd_set_port_aef_enable() 打开或关闭 UAPI_AEF_TYPE_CUSTOMER,不会下发自定义参数。算法需要参数时,应在应用中调用 uapi_snd_set_aef_param();PEQ、DRC 的参数读写流程可参考 /src/application/audio/sample_ao/sample_aef.c,接口声明见 /src/drivers/drivers/driver/audio/include/soc_uapi_sound.h,媒体服务封装接口见 AudioManagerSetAefParam

注意事项与常见问题

调试与常见问题

现象 优先排查
插件可编译但没有被调用 检查 define_component、plugins Makefile、插件 bin 名称和 overlay 打包目标是否一致。
加载或运行异常 检查 C 库依赖是否已加入 overlay_func->func 转发表,以及工具链、许可证和 DSP 镜像是否匹配。
参数下发失败或音效异常 检查 MCU/DSP 参数结构的版本、长度、对齐和命令号;先验证默认参数。
听感没有变化 确认播放路径经过目标 SOUND 端口、插件类型已使能,使用动态范围较大的固定 PCM 做 A/B 对比。
爆音、断音或延迟变大 检查 PCM 格式、帧长、算法耗时、内存和缓冲区;不要在 proc_frame 中执行阻塞操作。