杰理 SDK 文档中心
首页
首页
  • 项目概览

    • AD23N SDK 概述与芯片平台
    • 工程结构与模块划分
  • 快速开始

    • 开发环境搭建与工具链
    • 编译构建指南
    • 烧录与固件升级工具
  • 应用框架与产品工作流

    • 应用入口与模式调度
    • 音乐播放应用
    • MIDI 解码与键盘演奏
    • 录音应用
    • LINEIN 与扩音应用
    • USB 从设备应用
    • 待机、软关机与空闲检测
    • 公共 UI 与 LED 显示
  • 音频子系统

    • 音频解码器框架
    • 音频编码器框架
    • 音效算法库
    • 音频管理与输出通路
  • 存储与文件系统

    • 文件系统层
    • NOR Flash 与虚拟机存储
    • 设备与设备管理
  • 系统服务与运行时

    • 消息机制与事件分发
    • 按键扫描与输入处理
    • 电源管理与低功耗控制
    • 定时器与系统任务
  • 外设驱动与平台

    • CPU 平台与启动流程
    • USB 协议栈与主机/设备驱动
    • SPI 与通用外设接口
  • 固件升级与构建工具

    • 固件升级机制
    • 编译后处理与镜像打包
    • 构建系统与命令行工具

音效算法库

音效算法库(Audio Effects Algorithm Library)是 AD23N 系列 MCU SDK 中负责音频效果处理的算法集合,涵盖混响(Reverb/Echo)、EQ 均衡、啸叫抑制(Howling)、变调(Pitch Shifter)、陷波滤波、能量检测与重采样等 DSP 处理模块,并通过统一的数据类型层与面向对象式的句柄接口供解码/录音通路调用。

Purpose and Scope

本文档介绍 AD23N SDK 中音效算法库的整体架构与实现机制,包括:

  • 音效数据的统一类型描述(af_DataType、PCMDataType)
  • 音效对象的抽象模型(EFFECT_OBJ、sound_in_obj、sound_out_obj)及其与解码器/录音通路的衔接方式
  • 混响/回声算法的接口约定(ECHO_FUNC_API 函数指针表模式)
  • 音效库在 sound_effect_list 下的组织方式与运行标志(B_DEC_*)语义
  • 各音效模块(EQ、Howling、Pitch Shifter、Notch、Energy、Resample)的作用边界

以下主题属于相邻页面,不在本文展开:解码器主流程(见“解码器”相关页)、DAC/ADC 模拟通路(见“音频模拟通路”页)、PCM 编码格式(见“音频编解码”页)。

Overview

在嵌入式音频系统中,音效处理需要同时满足三个约束:低延迟(实时性)、定点运算(MCU 通常无硬件浮点单元或浮点性能不足)以及可裁剪(不同产品形态需要不同音效组合)。AD23N 的音效算法库围绕这三个约束设计:

  1. 统一数据描述:af_DataType 用位宽、通道步进和 Q 值描述 PCM 数据格式,让各算法模块可以独立适配 16bit/24bit/32bit、单声道/双声道输入输出,而无需各自维护格式判断逻辑。
  2. 句柄 + 函数指针表:每个音效(如 Echo)通过 get_echo_func_api() 返回一组 need_buf / open / init / run / reset_wetdry 函数指针,调用方只依赖接口表而不依赖具体实现,便于 ROM 化(算法常驻只读区)与换肤式替换。
  3. 对象化流水线:EFFECT_OBJ 将"输入对象 + 运行回调 + 输出对象"封装为可挂接的音效节点,配合 sound_out_obj.enable 的位标志(B_DEC_*)实现运行/暂停/错误等状态控制,使同一套音效框架既能服务解码播放通路,也能服务录音/混音通路。

该库位于 sdk/include_lib/audio/(对外头文件)与 sdk/app/bsp/common/sound_effect_list/(具体实现)两个层面,体现了"接口与实现分离、算法与调用分离"的嵌入式分层设计思想。

Architecture

flowchart TD
    subgraph sg_App["应用/解码层"]
        Decoder["解码器通路<br/>sound_effect_api.h"]
        Recorder["录音/混音通路"]
    end

    subgraph sg_Frame["音效框架层"]
        EFFECT_OBJ["EFFECT_OBJ<br/>(p_si + run + sound)"]
        SIn["sound_in_obj<br/>(p_dbuf/ops/priv)"]
        SOut["sound_out_obj<br/>(p_obuf/effect/mio/enable)"]
    end

    subgraph sg_Algo["算法接口层 (include_lib/audio)"]
        EchoAPI["echo_api.h / reverb_api.h<br/>ECHO_FUNC_API"]
        PcmEq["pcm_eq_api.h"]
        Howling["howling_pitchshifter_api.h"]
        Notch["notch_howling_api.h"]
        Energy["energe_api.h"]
        Resample["resample_api.h / src.h"]
        IIR["fix_iir_filter_api.h"]
    end

    subgraph sg_Type["数据类型层"]
        DT["af_DataType<br/>(位宽/步进/Qval)"]
        PCMType["PCMDataType 枚举<br/>(INT16/INT32/FLOAT32)"]
    end

    subgraph sg_Impl["算法实现层 (sound_effect_list)"]
        EchoImpl["echo/echo_api.c<br/>get_echo_func_api()"]
        OtherImpl["eq/howling/notch/resample 等"]
    end

    Decoder --> EFFECT_OBJ
    Recorder --> EFFECT_OBJ
    EFFECT_OBJ --> SIn
    EFFECT_OBJ --> SOut
    EFFECT_OBJ --> EchoAPI
    EchoAPI --> DT
    EchoAPI --> PCMType
    EchoImpl -->|"实现接口表"| EchoAPI
    OtherImpl -->|"实现接口表"| PcmEq
    OtherImpl --> Howling
    OtherImpl --> Notch
    OtherImpl --> Energy
    OtherImpl --> Resample
    PcmEq --> IIR
    DT -.-> EchoAPI
    PCMType -.-> DT

架构分层说明:

  • 音效框架层:EFFECT_OBJ 是音效在解码/录音通路中的挂载点。p_si 指向输入声音对象,run 是每帧处理回调(输入 short *inbuf 与长度 len),sound 是输出对象。sound_out_obj.enable 以位标志承载运行状态(B_DEC_RUN_EN、B_DEC_EFFECT、B_DEC_ERR、B_DEC_PAUSE 等),实现不依赖具体算法的状态机。
  • 算法接口层:include_lib/audio/ 下每个音效一个头文件,以"参数结构体 + 函数指针表 + 句柄结构体"三段式暴露 API。以 reverb_api.h 为例:ECHO_PARM_SET 为可动态调节参数,EF_REVERB_FIX_PARM 为固定参数(采样率、最大延时),ECHO_FUNC_API 为算法能力表,ECHO_API_STRUCT 为运行时句柄。
  • 数据类型层:AudioEffect_DataType.h 定义所有算法共享的数据格式描述。Qval 字段(16bit→15、24bit→23)直接决定定点算法的缩放因子,是算法与格式解耦的关键。
  • 算法实现层:sound_effect_list/echo/echo_api.c 通过 get_echo_func_api() 返回接口表,先 need_buf 计算工作区大小,再 open 打开、init 动态改参、run 逐帧处理,实现"空间预分配 + 参数热更新"的嵌入式 DSP 标准流程。

数据类型层:统一 PCM 描述

音效算法库的基石是 AudioEffect_DataType.h 中定义的枚举与结构体。它解决了嵌入式音效系统中最常见的适配难题:同一套算法代码要服务于不同位宽、不同声道数的数据流。

enum PCMDataType {
    DATA_INT_16BIT = 0,
    DATA_INT_32BIT,
    DATA_FLOAT_32BIT
};

enum {
    af_DATABIT_NOTSUPPORT = 0x404,
};

typedef struct _af_DataType_ {
    unsigned char IndataBit;   //输入数据位宽
    unsigned char OutdataBit;  //输出数据位宽
    char IndataInc;            //输入数据相同通道下一点的步进,单声道步进是1个点,所以选1;双声道步进是2个点,所以选2
    char OutdataInc;           //输出数据相同通道下一点的步进,单声道步进是1个点,所以选1;双声道步进是2个点,所以选2
    char Qval;                 //输入数据的pcm位宽,16bit的pcm位宽是15,24bit的pcm位宽是23
} af_DataType;

Source: AudioEffect_DataType.h

字段语义与设计意图

字段含义设计意图
IndataBit / OutdataBit输入/输出数据位宽(字节级)让算法在入口处即可校验格式兼容性,不匹配时返回 af_DATABIT_NOTSUPPORT (0x404)
IndataInc / OutdataInc同通道相邻采样点的步进(1=单声道,2=双声道)把"声道数"抽象为"步进",算法只需按步进取数即可同时支持单/双声道,避免为每种声道数写分支
Qval定点 Q 格式的小数位宽(16bit→15,24bit→23)定点算法的缩放因子;算法内部据此决定乘法后移位位数,保证溢出安全与精度

af_DATABIT_NOTSUPPORT = 0x404 是库内约定的格式不支持错误码。选择 0x404 这类非常规值(而非 0/-1)是为了避免与普通错误码混淆,便于在调试日志中直接识别"数据位宽不支持"这一特定失败原因。

PCMDataType 枚举则给出了更宏观的数据类型分类(16bit 整型 / 32bit 整型 / 32bit 浮点)。两者的关系是:af_DataType 描述"实际内存布局"(位宽+步进+Q 值),PCMDataType 描述"抽象类型标签";算法实现可先按枚举分类,再按结构体细节精调。

音效对象模型:EFFECT_OBJ 与输入输出对象

音效库在解码/录音通路上以 sound_effect_api.h 定义的对象模型运行:

typedef struct _sound_in_obj {
    void *p_dbuf;
    void *ops;
    void *priv;
} sound_in_obj;

typedef struct _sound_out_obj {
    void *p_obuf;
    void *effect;
    void *mio;
    volatile u32 enable;
    u32  para;
} sound_out_obj;

typedef struct _EFFECT_OBJ__ {
    void *p_si;                                     /*point to sound in*/
    int (*run)(void *hld, short *inbuf, int len);
    sound_out_obj sound;

} EFFECT_OBJ;

Source: sound_effect_api.h

各字段职责

  • sound_in_obj:音效的输入侧描述。p_dbuf 指向输入数据缓冲,ops 为输入侧操作接口,priv 为私有上下文。输入对象对音效算法是"只读数据源"。
  • sound_out_obj:音效的输出侧描述。p_obuf 为输出缓冲,effect 指向后续音效节点(可链式串联),mio 为混音/多路输出相关句柄,enable 是易失性状态位(由中断/任务异步修改),para 为参数透传字段。
  • EFFECT_OBJ:音效节点本身。p_si 指向输入声音对象;run 是核心处理回调,签名 int (*run)(void *hld, short *inbuf, int len)——传入句柄、PCM 短整型帧与帧长,返回处理结果;sound 内嵌输出对象。

运行状态位(B_DEC_* 系列)

sound_out_obj.enable 是音效框架与解码器之间的状态协议,全部以位标志定义在 sound_effect_api.h:

标志位含义
B_DEC_RUN_ENBIT(0)解码/运行使能
B_DEC_OBUF_ENBIT(1)输出缓冲使能
B_DEC_ENABLEBIT(0)|BIT(1)两者组合,表示完全使能
B_DEC_EFFECTBIT(2)音效使能(挂在解码输出上的音效开关)
B_DEC_ERRBIT(3)错误标志(如位宽不支持、缓冲不足)
B_DEC_MIOBIT(4)多路输入输出(混音)相关
B_DEC_PAUSEBIT(7)暂停
B_DEC_KICKBIT(8)踢出/唤醒(用于打断阻塞)
B_REC_RUNBIT(9)录音运行
B_DEC_FIRSTBIT(10)首帧标志

设计意图:把"状态"与"数据"分离。run() 只负责纯数据计算,状态切换由外部通过置位/清位 enable 完成,因此同一套 EFFECT_OBJ 可被解码器、录音器、混音器复用,且状态查询(如 if (sound.enable & B_DEC_EFFECT))是 O(1) 位测试,适合中断上下文。

框架辅助接口

int sound_output(void *priv, void *data, int len);
int sound_input(void *priv, void *data, int len);
void kick_sound(void *_sound);
void stream_sound_init(void *psound, void *kick);
void stream_sound_uninit(void);
bool sound_out_init(sound_out_obj *psound, void *cbuf, u8 info);

Source: sound_effect_api.h

  • sound_input/sound_output:音效节点向外部(解码器/播放器)读取或提交 PCM 数据的回调入口;
  • kick_sound:唤醒可能阻塞在缓冲等待中的音效任务(配合 B_DEC_KICK 位);
  • stream_sound_init/stream_sound_uninit:注册/注销整条音效流(含 kick 回调),用于流的生命周期管理;
  • sound_out_init:初始化输出对象,绑定环形缓冲 cbuf 与信息位 info。

混响/回声算法:接口表模式详解

reverb_api.h 是音效算法接口的典型范式,完整展示了"参数-能力表-句柄"三段式设计:

typedef struct _EF_ECHO__PARM_ {
    unsigned int delay;                      //回声的延时时间 0-max_ms
    unsigned int decayval;                   // 0-70%
    unsigned int direct_sound_enable;        //直达声使能  0/1
    unsigned int energy_vad_threshold;       //绝对值能量阈值
} ECHO_PARM_SET;

typedef struct  _EF_REVERB_FIX_PARM {
    unsigned int wetgain;           //湿声增益
    unsigned int drygain;           //干声增益
    unsigned int sr;
    unsigned int max_ms;
} EF_REVERB_FIX_PARM;

typedef struct _ECHO_IO_CONTEXT_ {
    void *priv;
    int(*output)(void *priv, void *data, int len);
} ECHO_IO_CONTEXT;

typedef struct __ECHO_FUNC_API_ {
    unsigned int (*need_buf)(unsigned int *ptr, EF_REVERB_FIX_PARM *echo_fix_parm);
    int (*open)(unsigned int *ptr, ECHO_PARM_SET *echo_parm, EF_REVERB_FIX_PARM *echo_fix_parm, ECHO_IO_CONTEXT *echooutput_io);
    int (*init)(unsigned int *ptr, ECHO_PARM_SET *echo_parm);
    int (*run)(unsigned int *ptr, short *inbuf, int len);
    void (*reset_wetdry)(unsigned int *ptr, int wetgain, int drygain);
} ECHO_FUNC_API;

typedef struct _EHCO_API_STRUCT_ {
    ECHO_PARM_SET echo_parm_obj;  //参数
    EF_REVERB_FIX_PARM echo_fix_parm;
    unsigned int *ptr;                   //运算buf指针
    ECHO_FUNC_API *func_api;            //函数指针
} ECHO_API_STRUCT;

extern ECHO_FUNC_API *get_echo_func_api();

Source: reverb_api.h

参数分层

  • ECHO_PARM_SET(动态参数):可在运行中通过 init 热更新。delay 是回声延时(范围 0~max_ms),decayval 是衰减系数(0~70%,上限刻意限制以抑制自激),direct_sound_enable 开关直达声(干声直通),energy_vad_threshold 是绝对值能量阈值——用于回声路径中的 VAD(语音活动检测),低于阈值时抑制回声处理,避免噪声被放大。
  • EF_REVERB_FIX_PARM(固定参数):wetgain/drygain 分别为湿声(效果声)与干声(原声)增益,sr 为采样率,max_ms 为最大延时容量——这三个参数决定工作区大小,因此必须在 open 前确定,不可热更新。
  • ECHO_IO_CONTEXT(输出回调):提供 output(priv, data, len) 回调,使算法可以主动推送处理结果,而不必依赖调用方拉取,适合多级音效串联场景。

接口表生命周期

ECHO_FUNC_API 的方法顺序即音效的完整生命周期:

  1. need_buf(ptr, fix_parm):仅根据固定参数计算所需工作区字节数(不执行运算),供调用方在初始化阶段一次性分配静态/动态内存——嵌入式系统要求"先算内存、后建对象",避免运行期分配失败;
  2. open(ptr, parm, fix_parm, io):传入工作区指针与全部参数,完成内部状态(延时线、滤波器系数)初始化;
  3. init(ptr, parm):运行时更新动态参数(不重建内部状态,只改系数/阈值);
  4. run(ptr, inbuf, len):逐帧处理 PCM 数据,这是唯一的热路径函数;
  5. reset_wetdry(ptr, wet, dry):快速调整干湿比例,用于现场混音微调(如 KTV 场景的人声/伴奏比例)。

实现侧的装配流程

echo_api.c 展示了调用方如何装配一个回声音效:

ops = (ECHO_FUNC_API *)get_echo_func_api(); //接口获取
buf_len = ops->need_buf(NULL, (EF_REVERB_FIX_PARM *)&parm->echo_fix_parm);           //运算空间获取
log_info("echo work_buf_len %d\n", buf_len);

Source: echo_api.c

memcpy(&echo_hdl->echo.echo_parm_obj, &parm->echo_parm_obj, sizeof(ECHO_PARM_SET));
memcpy(&echo_hdl->echo.echo_fix_parm, &parm->echo_fix_parm, sizeof(EF_REVERB_FIX_PARM));
echo_parm_debug(echo_hdl);

Source: echo_api.c

注意实现中先 get_echo_func_api() 取接口表,再 need_buf 计算工作区——这与头文件定义的 ECHO_API_STRUCT(参数对象 + 工作区指针 + 函数指针表)完全对应。将参数拷贝进句柄后调用 echo_parm_debug 打印,便于在产品调试阶段核对寄存器/内存中的实际生效参数。

音效族谱:其他算法模块

除回声/混响外,sdk/include_lib/audio/ 还提供以下音效头文件,构成完整音效族谱:

头文件功能典型场景
pcm_eq_api.h / pcm_eq.hPCM 域均衡器(EQ),配合 fix_iir_filter_api.h 定点 IIR 滤波器音色调节、频响校正
fix_iir_filter_api.h定点 IIR 滤波器通用接口EQ 的底层滤波原语
howling_pitchshifter_api.h啸叫抑制 + 变调麦克风扩声防啸叫、人声变调(K 歌/变声)
notch_howling_api.h陷波式啸叫抑制检测啸叫频点并动态陷波
energe_api.h能量检测(VAD/电平)回声门限、自动增益、人声检测
resample_api.h / src.h采样率转换音效链中不同采样率衔接、变速播放

这些模块共享相同设计范式:参数结构体 + 函数指针能力表 + 句柄结构体 + get_xxx_func_api() 获取接口。新增音效只需在 sound_effect_list/ 下新建目录实现接口表,并在上层按 EFFECT_OBJ 模型挂接即可,框架无需改动——这是"音效算法库"作为可扩展库的核心价值。

Core Flow:音效节点的完整生命周期

以"解码播放 + 回声音效"为例,展示音效从装配到逐帧处理的真实控制流:

sequenceDiagram
    participant App as 应用/解码器
    participant EApi as echo_api.c (装配层)
    participant Lib as 算法实现 get_echo_func_api()
    participant Obj as EFFECT_OBJ / sound_out_obj
    participant HW as DAC/播放通路

    App->>EApi: 配置 ECHO_PARM_SET + EF_REVERB_FIX_PARM
    EApi->>Lib: get_echo_func_api() 获取接口表
    Lib-->>EApi: ECHO_FUNC_API* (need_buf/open/init/run/reset_wetdry)
    EApi->>Lib: need_buf(NULL, fix_parm) 计算工作区
    Lib-->>EApi: buf_len
    EApi->>EApi: 分配/挂接运算 buf (ptr)
    EApi->>Lib: open(ptr, parm, fix_parm, io_ctx) 初始化延时线与系数
    EApi->>EApi: memcpy 参数到 ECHO_API_STRUCT 句柄
    EApi->>Obj: 建立 EFFECT_OBJ,置位 B_DEC_ENABLE | B_DEC_EFFECT
    loop 每帧音频 (run 热路径)
        App->>Obj: run(hld, inbuf, len)
        Obj->>Lib: run(ptr, inbuf, len) 处理回声音效
        Lib->>HW: output(priv, data, len) 推送结果
        alt 用户调节
            App->>Lib: init(ptr, parm) 热更新 delay/decayval
            App->>Lib: reset_wetdry(ptr, wet, dry) 调干湿比例
        end
    end
    App->>Obj: 清 B_DEC_RUN_EN / 置 B_DEC_PAUSE 暂停
    App->>EApi: 释放工作区,stream_sound_uninit()

流程关键点解读

  1. 先算内存再建对象:need_buf 在 open 之前调用,且只依赖固定参数(采样率、最大延时)。这保证工作区大小在编译/启动阶段即可确定,MCU 上可选择静态数组或启动时一次性分配,杜绝运行期 malloc 失败导致的音频中断。
  2. 接口表是唯一的算法入口:装配层(echo_api.c)与算法实现(库内 get_echo_func_api)通过函数指针表解耦。算法可被放在 ROM 中(函数指针可重定位),或由不同产品版本提供不同实现而无需改动装配代码。
  3. 状态位驱动而非函数调用驱动:播放/暂停/错误通过 sound_out_obj.enable 的位操作完成(如 B_DEC_PAUSE),run() 内部轮询这些位决定处理或直通。这样中断回调只需置位,不会阻塞在算法执行上。
  4. 热更新与稳态分离:动态参数(delay/decayval)走 init 热更新,固定参数(sr/max_ms/增益基准)在 open 时固化——避免运行期重建延时线导致爆音(pop noise)。
  5. 输出回调驱动:算法通过 ECHO_IO_CONTEXT.output 主动推送结果,多级音效时上一级的 output 即下一级的输入,形成数据驱动的流水线,无需中央调度器逐级拉取。

Usage Examples

示例 1:装配回声音效(接口获取 + 工作区计算)

ops = (ECHO_FUNC_API *)get_echo_func_api(); //接口获取
buf_len = ops->need_buf(NULL, (EF_REVERB_FIX_PARM *)&parm->echo_fix_parm);           //运算空间获取
log_info("echo work_buf_len %d\n", buf_len);

Source: echo_api.c

这段代码演示了音效库的标准装配第一步:先取接口表、后算工作区。need_buf 传入 NULL 指针仅用于计算大小(不写内存),返回值即所需字节数,供上层分配后调用 open 填充。

示例 2:参数写入句柄(运行时状态建立)

memcpy(&echo_hdl->echo.echo_parm_obj, &parm->echo_parm_obj, sizeof(ECHO_PARM_SET));
memcpy(&echo_hdl->echo.echo_fix_parm, &parm->echo_fix_parm, sizeof(EF_REVERB_FIX_PARM));
echo_parm_debug(echo_hdl);

Source: echo_api.c

动态参数与固定参数分别整体拷贝到句柄中,随后打印调试。这一"整体拷贝"策略简化了参数管理:调用方修改参数结构体后再次调用 init,句柄内即同步,无需逐字段同步接口。

示例 3:定义音效数据格式(算法输入适配)

af_DataType dt = {
    .IndataBit  = 16,   // 16bit 输入
    .OutdataBit = 16,   // 16bit 输出
    .IndataInc  = 2,    // 双声道输入,步进 2
    .OutdataInc = 2,    // 双声道输出,步进 2
    .Qval       = 15,   // 16bit PCM 的 Q 值
};

Source: AudioEffect_DataType.h(结构体定义,初始化示例为按字段语义构造)

对于立体声 16bit PCM 数据,IndataInc = OutdataInc = 2 表示同一通道相邻采样点间隔 2 个样本(L/R 交织),算法按步进取样即自动适配交织布局;Qval = 15 告知定点算法乘法后右移 15 位。

示例 4:音效节点的运行回调签名(挂接解码通路)

EFFECT_OBJ obj = {
    .p_si  = (void *)&sound_in,       /* point to sound in */
    .run   = my_effect_run,           /* 每帧处理回调 */
    .sound = { .enable = B_DEC_ENABLE | B_DEC_EFFECT },
};

Source: sound_effect_api.h(结构体定义,初始化示例按字段语义构造)

run 回调签名 int (*run)(void *hld, short *inbuf, int len) 固定为短整型 PCM 帧接口,因此所有音效算法在框架层统一以 16bit PCM 交换数据;内部如需更高精度(32bit/浮点),由算法在 run 内部通过 af_DataType 描述自行转换。

Configuration Options

音效算法库的配置以参数结构体形式在编译期/运行期传入,无独立配置文件。主要配置项如下:

ECHO_PARM_SET(回声音效动态参数)

字段类型默认/范围说明
delayunsigned int0 ~ max_ms回声延时时间,单位由 max_ms 决定(毫秒级)
decayvalunsigned int0 ~ 70%回声衰减系数;上限限制为 70% 以防止反馈自激
direct_sound_enableunsigned int0/1直达声(干声)使能开关
energy_vad_thresholdunsigned int应用自定义绝对值能量阈值,用于回声路径 VAD 门限

EF_REVERB_FIX_PARM(固定参数,open 前确定)

字段类型说明
wetgainunsigned int湿声(效果声)增益
drygainunsigned int干声(原声)增益
srunsigned int采样率,决定延时线换算与滤波器系数
max_msunsigned int最大延时容量,直接决定 need_buf 返回的工作区大小

af_DataType(数据格式描述)

字段类型说明
IndataBitunsigned char输入数据位宽(16/24/32)
OutdataBitunsigned char输出数据位宽
IndataIncchar输入同通道步进(1=单声道,2=双声道)
OutdataIncchar输出同通道步进
Qvalchar定点 Q 值(16bit→15,24bit→23)

运行状态位(sound_out_obj.enable)

标志值用途
B_DEC_RUN_ENBIT(0)运行使能
B_DEC_OBUF_ENBIT(1)输出缓冲使能
B_DEC_ENABLEBIT(0)|BIT(1)完全使能
B_DEC_EFFECTBIT(2)音效开关
B_DEC_ERRBIT(3)错误标志
B_DEC_MIOBIT(4)混音相关
B_DEC_PAUSEBIT(7)暂停
B_DEC_KICKBIT(8)踢出/唤醒
B_REC_RUNBIT(9)录音运行
B_DEC_FIRSTBIT(10)首帧标志

API Reference

PCMDataType 枚举

enum PCMDataType { DATA_INT_16BIT = 0, DATA_INT_32BIT, DATA_FLOAT_32BIT };
  • DATA_INT_16BIT(0):16bit 整型 PCM
  • DATA_INT_32BIT:32bit 整型 PCM
  • DATA_FLOAT_32BIT:32bit 浮点 PCM

Source: AudioEffect_DataType.h

ECHO_FUNC_API 接口表

函数指针签名说明
need_bufunsigned int (*)(unsigned int *ptr, EF_REVERB_FIX_PARM *fix_parm)计算工作区字节数;ptr 为 NULL 时仅计算。返回所需字节数
openint (*)(unsigned int *ptr, ECHO_PARM_SET *parm, EF_REVERB_FIX_PARM *fix_parm, ECHO_IO_CONTEXT *io)打开音效并初始化内部状态。返回 0 成功,非 0 失败
initint (*)(unsigned int *ptr, ECHO_PARM_SET *parm)热更新动态参数。返回 0 成功
runint (*)(unsigned int *ptr, short *inbuf, int len)处理一帧 PCM(热路径)。返回处理长度或错误码
reset_wetdryvoid (*)(unsigned int *ptr, int wetgain, int drygain)运行时调整干湿声增益比例

get_echo_func_api()

extern ECHO_FUNC_API *get_echo_func_api();
  • 返回:回声音效的接口表指针;调用方通过该表完成 need_buf → open → init → run 全生命周期。返回 NULL 表示该音效未被编译进当前固件(裁剪场景)。

音效框架接口(sound_effect_api.h)

函数签名说明
sound_outputint sound_output(void *priv, void *data, int len)向外部输出 PCM 数据
sound_inputint sound_input(void *priv, void *data, int len)从外部读取 PCM 数据
kick_soundvoid kick_sound(void *_sound)唤醒阻塞中的音效流
stream_sound_initvoid stream_sound_init(void *psound, void *kick)注册音效流与 kick 回调
stream_sound_uninitvoid stream_sound_uninit(void)注销音效流
sound_out_initbool sound_out_init(sound_out_obj *psound, void *cbuf, u8 info)初始化输出对象并绑定环形缓冲

Failure Modes, Edge Cases & Concurrency

数据格式不匹配

  • 现象:输入数据位宽与算法支持位宽不一致。
  • 机制:af_DataType 的 IndataBit/OutdataBit 提供前置校验依据,库内以 af_DATABIT_NOTSUPPORT (0x404) 作为专属错误码返回。
  • 处理建议:调用方在 open/首次 run 前比对 IndataBit 与 Qval 是否匹配;收到 0x404 时应走格式转换(如重采样/位深转换)或旁路(bypass)而非直接报错中断播放。

回声自激与过度衰减

  • decayval 上限被设计为 70%,其设计意图是保证回声环路增益 < 1,防止长延时反馈下形成自激啸叫。若产品需更强回声,应同时增大 energy_vad_threshold 以在 VAD 门限处抑制无语音段的噪声反馈。
  • 边界:delay = 0 时算法应表现为干声直通(若 direct_sound_enable = 1);delay 接近 max_ms 时工作区占用接近满负荷,需确认 need_buf 分配足够,否则发生延时线越界。

并发与中断上下文

  • sound_out_obj.enable 声明为 volatile u32,因为状态位由中断/任务异步修改(如解码中断置 B_DEC_KICK、任务置 B_DEC_PAUSE)。所有状态读取应使用位测试宏(如 if (snd->enable & B_DEC_PAUSE)),避免缓存陈旧值。
  • run 为纯数据计算,不应对 enable 做非原子读改写;状态切换统一由外部单写者完成,保证无锁设计下的一致性。
  • 参数热更新(init)与 run 若在不同优先级上下文执行,可能出现"读半边参数"窗口。实现上通过整体 memcpy 参数块(见 echo_api.c)减少不一致窗口,但仍建议在音频任务空闲点(如帧边界)调用 init。

缓冲不足与阻塞

  • sound_input/sound_output 依赖环形缓冲(sound_out_init 绑定的 cbuf)。缓冲写满/读空时,kick_sound 配合 B_DEC_KICK 位唤醒等待任务;若迟迟未唤醒,B_DEC_ERR 置位表示错误路径。系统设计时应按最大音效链延迟(max_ms + 各级缓冲)规划缓冲深度。

裁剪与链接失败

  • 未编译入固件的音效,get_echo_func_api() 返回 NULL。调用方必须判空,否则空指针解引用。这也是"算法可裁剪"特性的使用前提:产品 ROM 空间不足时剔除不需要的音效,框架层不受影响。

Performance & Operational Considerations

  • 热路径唯一:run(ptr, inbuf, len) 是每帧执行的唯一热路径,内部应避免动态内存分配、日志打印与浮点运算(除非目标芯片支持硬件 FPU 且 PCMDataType 为浮点)。need_buf/open/init 均为低频路径。
  • 定点精度:Qval 决定定点运算缩放(16bit→Q15,24bit→Q23)。算法内部乘法后需按 Qval 移位,溢出保护(饱和钳位)应放在 run 出口,避免削波失真。
  • 工作区预分配:所有音效遵循"need_buf 先行"约定,工作区可在启动时静态分配或从专用内存池一次性申请,避免运行期碎片化;多级音效链的工作区总大小 = 各级 need_buf 之和,可在编译期评估内存预算。
  • 调试手段:echo_parm_debug 这类参数打印函数在实现层提供,产品阶段可保留用于现场核对实际生效参数(采样率、延时、增益),便于与规格书对照。
  • 多音效串联:sound_out_obj.effect 指向下一级音效,输出回调 ECHO_IO_CONTEXT.output 驱动流水线。串联时需注意各级 max_ms/缓冲叠加导致的端到端延迟,KTV/实时监听场景应控制总延迟在可接受范围(通常 < 数十毫秒)。

Extension Points

音效算法库的扩展遵循"新增目录 + 实现接口表 + 框架挂接"三步:

  1. 新增算法实现:在 sdk/app/bsp/common/sound_effect_list/ 下新建子目录(如 my_effect/),实现与 ECHO_FUNC_API 同构的接口表(need_buf/open/init/run/reset_wetdry),并导出 get_my_effect_func_api()。
  2. 暴露头文件:在 sdk/include_lib/audio/ 增加参数结构体与接口表声明,保持与现有 reverb_api.h 一致的三段式风格(动态参数 / 固定参数 / 能力表)。
  3. 框架挂接:将新音效封装为 EFFECT_OBJ(填充 p_si、run、sound),置位 B_DEC_ENABLE | B_DEC_EFFECT 后挂入解码/录音通路;若需支持新的数据格式(如 32bit 浮点),扩展 PCMDataType 枚举并确保 af_DataType 描述与算法内部转换一致。

该设计使音效库成为面向接口的可插拔算法集合:框架、装配层、算法实现三者可独立演进,新增音效不需要改动解码器主流程。

Tests

源仓库中音效库的测试主要体现在装配层自检与调试打印模式:echo_api.c 在 open 流程中通过 need_buf 计算并 log_info 打印工作区大小,随后 echo_parm_debug 打印实际生效参数。这种"先算后建、边建边查"的方式在无宿主环境的 MCU 上承担了单元自检角色:

  • 工作区大小打印可人工核对内存预算是否合理;
  • 参数打印可验证 memcpy 后句柄内数据与调用方配置一致;
  • af_DATABIT_NOTSUPPORT 错误码可在集成测试中验证格式适配路径。

若需系统级测试,建议在 PC 端以相同 af_DataType 描述回放固定测试向量,对比 run 输出与参考 DSP 实现(如浮点参考模型)的误差,重点覆盖 Qval 定点精度与溢出饱和行为。

Related Links

  • AudioEffect_DataType.h(音效数据类型定义)
  • reverb_api.h(回声/混响算法接口)
  • sound_effect_api.h(音效对象模型与解码器衔接)
  • echo_api.c(回声音效装配实现)
  • 相邻主题:EQ 均衡(pcm_eq_api.h)、啸叫抑制/变调(howling_pitchshifter_api.h、notch_howling_api.h)、能量检测(energe_api.h)、采样率转换(resample_api.h/src.h)——详见音频相关目录页
  • 相邻页面:解码器主流程(见"解码器"页)、DAC/ADC 模拟通路(见"音频模拟通路"页)
Prev
音频编码器框架
Next
音频管理与输出通路