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

    • SDK 简介与核心特性
    • 芯片平台与硬件资料
    • SDK 版本与发布信息
  • 快速开始

    • 环境搭建与工具链
    • 编译工程
    • 烧录与量产工具
  • 工程结构与构建系统

    • 工程目录布局
    • 构建与链接配置
  • 应用层开发

    • mbox_flash 应用框架
    • 板级支持包 (BSP)
    • 公共应用模块
    • UI 显示子系统
  • 蓝牙子系统

    • BLE 控制器、链路层与 HCI 传输
    • GATT 服务框架
    • BLE 应用示例:遥控器 / Dongle / 对讲机
    • 经典蓝牙支持
  • 音频子系统

    • 音频编解码器
    • 音频设备接口 (DAC / ADC / APA)
    • 音效处理与 EQ
    • 播放、录音与 MIO 工作流
  • 设备与文件系统

    • 存储设备驱动 (NorFlash / SDMMC / USB)
    • 文件系统 (FAT / nor_fs / SYDF)
    • 设备管理框架 (dev_mg)
  • 系统服务与电源管理

    • 消息机制 (msg / hot_msg)
    • 配置与参数存储 (app_config / VM)
    • 电源管理 (SOFT OFF / POWER DOWN)
  • 固件升级

    • 升级框架总览 (code_v1 / code_v2)
    • 双 Bank 升级机制
    • 升级通道:UART / 测试盒 / BLE OTA / USB / SD
  • 补丁包与版本维护

    • 版本升级补丁链 (v1.1.0 → v1.4.0)
    • 问题修复补丁
    • 固件裁剪与资源优化
  • 开发工具与支持

    • 辅助工具与脚本
    • 文档、配置说明与常见问题

音效处理与 EQ

音频均衡器(EQ)与音效处理子系统:包含 PCM 软件 EQ(pcm_eq_api.c)、通用 EQ 效果框架(audio_eq.h)以及硬件 IIR 滤波器系数设计(hw_eq.h),为 AW30N 蓝牙 SoC 的音频播放链路提供多段均衡、音效模式切换与在线调音能力。

Purpose and Scope

本文档覆盖 AW30N BLE SDK 中与音效处理、均衡器(EQ)相关的完整能力:

  • PCM 软件 EQ:pcm_eq_api.c 中基于预计算系数表的 10 段软件均衡器实现,及其与声音输出链路的挂接方式;
  • 通用 EQ 效果框架:audio_eq.h 定义的 EQ_STUCT_API 抽象、EQ_BUFF/EQ_HDL 运行期对象、EQ 命令(EQ_CMD)与在线调音工具结构;
  • 硬件 EQ / IIR 滤波器设计:hw_eq.h 中各种滤波器类型(高通、低通、带通、搁架式、峰值)的系数设计函数与 eq_seg_info 段描述结构。

以下主题属于相邻目录页,不在本文展开:音频编解码格式与解码器管理(见解码器相关文档)、DAC/ADC 硬件驱动(见音频 DAC 相关文档)、音频链路同步与 RF 音频传输(见音频链路/audio_link 相关文档)。

Overview

均衡器是音频播放链路中最常用的音效处理单元,其本质是级联的 IIR(无限脉冲响应)滤波器,通过调整不同频段的增益来改变声音的音色特征。AW30N 的 EQ 子系统按实现方式分为两条路径:

  1. 软件 EQ(PCM EQ):在 CPU 上以定点运算处理 PCM 数据,由 PCM_SW_EQ_EN 编译宏使能,使用 EQTools V3.1.7 生成的预计算系数表(eq_filt_XXXXX),支持 8k/12k/16k/24k/32k/44.1k/48k 等采样率,10 段滤波器级联,左右声道独立增益控制。
  2. 硬件 EQ 框架(audio_eq / hw_eq):面向通用效果框架的软件实现,通过 EQ_STUCT_API 虚函数表提供 need_buf / open / run / config 四个操作,支持在线切换系数表(EQ_CMD_SWITCH_TAB)、淡入淡出(B_EQ_FADE_IN/B_EQ_FADE_OUT)、音效模式切换(eq_mode_sw)以及基于 music_eq_tool 的在线调音工具。

设计意图:将"EQ 效果"抽象为可挂接到 sound_out_obj 输出链路的 effect 节点(effect 指针 + B_DEC_EFFECT 标志),使解码器输出的音频数据可在送入 DAC 之前被任意效果节点串联处理;滤波器系数与运行逻辑分离,系数可由工具离线生成或运行时动态设计(design_pe 等函数),兼顾了嵌入式场景的实时性(定点、查表)与灵活性(在线调音)。

Architecture

flowchart TD
    subgraph sg_Decoder["解码器层"]
        Decoder["Decoder / 解码输出"]
    end

    subgraph sg_EffectChain["音效链路(sound_out_obj 串联)"]
        LinkPcm["link_pcm_eq_sound()"]
        LinkEq["link_eq_sound()"]
        PcmEq["pcm_eq_api() → pcm_eq_phy()"]
        EqHdl["EQ_HDL / EQ_BUFF"]
    end

    subgraph sg_Coeff["系数来源"]
        Tab44100["eq_filt_44100[10段×5]"]
        TabOther["eq_filt_22050/11025/48000/24000/12000/32000/16000/08000"]
        GetTab["get_pcm_eq_tab(sr)"]
        DesignFn["design_lp/hp/pe/ls/hs/bp()"]
        MusicTool["music_eq_tool(在线调音)"]
    end

    subgraph sg_Dac["输出层"]
        DacBuf["DAC 缓存(p_dac_cbuf)"]
        Dac["DAC 硬件"]
    end

    Decoder -->|"PCM 数据"| LinkPcm
    Decoder -->|"PCM 数据"| LinkEq
    LinkPcm -->|"初始化 PCM_EQ_PARM"| PcmEq
    LinkEq -->|"初始化 EQ_PARA_STRUCT"| EqHdl
    PcmEq -->|"选择系数表"| GetTab
    GetTab --> Tab44100
    GetTab --> TabOther
    EqHdl -->|"系数设计/更新"| DesignFn
    MusicTool -->|"EQ_CMD_SWITCH_TAB"| EqHdl
    PcmEq -->|"处理后数据"| DacBuf
    EqHdl -->|"处理后数据"| DacBuf
    DacBuf --> Dac

架构说明:

  • 解码器层输出原始 PCM 数据,通过 sound_out_obj 结构体上的 effect 字段将效果节点挂入链路。link_pcm_eq_sound() 与 link_eq_sound() 是两条 EQ 路径的挂接入口,二者都接受当前 sound 节点、DAC 缓存指针与采样率/声道数,返回处理后的下一个 sound 节点。
  • PCM EQ 路径(pcm_eq_api.c)使用固定 10 段、预先烧录的整型系数表,初始化开销小,适合出厂预设音效;get_pcm_eq_tab(sr) 按采样率选择对应表,左右声道各自持有系数指针,LGain/RGain 均为 1 << 20(约 0 dB)。
  • 通用 EQ 框架路径(audio_eq.h/hw_eq.h)以 EQ_STUCT_API 操作集封装 EQ 对象,支持 float 型系数(eq_oeff_tab 缓存运算后系数表)、淡入淡出与在线参数更新,适合 App 侧动态调音。
  • 系数来源分为静态表(EQTools 离线生成)与动态设计(design_* 系列 IIR 系数计算函数),两条路径最终都汇入 DAC 缓存输出。

主要实现

PCM 软件 EQ 初始化与运行

pcm_eq_api() 是 PCM EQ 的初始化入口,位于 pcm_eq_api.c。它首先清零 PCM_EQ_PARM 参数结构,固定配置 10 段(parm.nSection = 10),随后通过 pcm_eq_buf_len() 计算所需工作区大小并与静态数组 g_work_buf(660 字节,放置于 .pcm_eq_data 段)比对,空间不足时直接返回 NULL 使 EQ 挂接失败:

static u32 g_work_buf[660 / 4] AT(.pcm_eq_data);

PCM_EQ_PARM parm;
void *pcm_eq_api(void *obuf, u32 sr, u32 channel, void **ppsound)
{
    memset(&parm, 0, sizeof(PCM_EQ_PARM));
    parm.nSection = 10;

    u32 buf_size = pcm_eq_buf_len(parm.nSection, channel);

    if (sizeof(g_work_buf) < buf_size) {
        log_info("PCM EQ Work Data space is not big enough:%d : %d", sizeof(g_work_buf), buf_size);
        return NULL;
    }
    log_info("PCM EQ Work Data space is enough:%d : %d", sizeof(g_work_buf), buf_size);

    parm.LCoeff_OnChip = get_pcm_eq_tab(sr);
    if (1 < channel) {
        parm.RCoeff_OnChip = get_pcm_eq_tab(sr);
    }
    parm.LGain = 1 << 20;
    parm.RGain = 1 << 20;
    parm.SHI = 10;
    parm.SHO = 8;
    parm.channel = channel;
    return pcm_eq_phy(obuf, &g_work_buf[0], &parm, ppsound);
}

Source: pcm_eq_api.c

关键参数语义:SHI/SHO 分别为输入/输出定点移位(Q 格式缩放),LGain/RGain 为左右声道总增益(1 << 20 对应单位增益),channel > 1 时右声道复用同一系数表。整个函数被 #if defined(PCM_SW_EQ_EN) && (PCM_SW_EQ_EN) 编译开关包裹,未使能时整段代码不参与编译。

PCM EQ 与输出链路的挂接

link_pcm_eq_sound() 将 EQ 效果插入声音输出链表:它遍历当前 sound_out_obj,把 pcm_eq_api() 返回的效果句柄写入 p_curr_sound->effect,成功后置位 B_DEC_EFFECT 标志并让链表的下一节点直接使用 DAC 缓存作为输出缓冲;失败则打印日志并保留原链路:

void *link_pcm_eq_sound(void *p_sound_out, void *p_dac_cbuf, void **pp_effect, u32 sr, u32 channel)
{
    sound_out_obj *p_next_sound = 0;
    sound_out_obj *p_curr_sound = p_sound_out;
    p_curr_sound->effect = pcm_eq_api(p_curr_sound->p_obuf, sr, channel, (void **)&p_next_sound);
    if (NULL != p_curr_sound->effect) {
        if (NULL != pp_effect) {
            *pp_effect = p_curr_sound->effect;
        }
        p_curr_sound->enable |= B_DEC_EFFECT;
        p_curr_sound = p_next_sound;
        p_curr_sound->p_obuf = p_dac_cbuf;
    } else {
        log_info("pcm eq init fail\n");
    }
    log_info("pcm eq init succ\n");
    return p_curr_sound;
}

Source: pcm_eq_api.c

设计意图:sound_out_obj 是 SDK 音频输出链路的通用节点,effect 指针使任意效果(EQ、混响等)可以透明地插入解码器与 DAC 之间;B_DEC_EFFECT 标志用于通知音频管理模块"解码输出已带效果",避免二次处理。挂接失败时链路回退为直通,保证音频不中断(降级而非报错)。

预计算滤波器系数表

系数表由 EQTools V3.1.7 beta(生成时间 2021-07-13)自动生成,每段滤波器 5 个整型系数(对应 IIR 双二阶节的 b0/b1/b2/a1/a2),以 4 对齐的定点格式存储。get_pcm_eq_tab(sr) 按采样率选择表,未命中时默认回落到 32 kHz 表:

const int *get_pcm_eq_tab(u32 sr)
{
    const int *sr_tab = &eq_filt_32000[0];
    switch (sr) {
    case 8000:
        sr_tab =  &eq_filt_08000[0];
        break;
    case 12000:
        sr_tab =  &eq_filt_12000[0];
        break;
    case 16000:
        sr_tab =  &eq_filt_16000[0];
        break;
    case 24000:
        sr_tab =  &eq_filt_24000[0];
        break;
    case 32000:
        sr_tab =  &eq_filt_32000[0];
        break;
    default:
        break;
    }
    return sr_tab;
}

Source: pcm_eq_api.c

仓库中共有 9 张采样率表(8k/12k/16k/24k/32k/44.1k/48k 及 22.05k/11.025k)。以 44.1 kHz 表为例,第 9 段(seg 9)系数为 4194304, 0, 0, 0, 0,即 2^22 与全零——这是直通段(增益 0 dB、无滤波),用于把有效段数不足 10 段时的尾部补齐为直通;4194304 = 1 << 22 是定点归一化基值。每段 5 个系数中,第 1 个系数接近 4194304 的段为低增益宽带段,系数偏离较大的段(如 seg 7 的 5667101, -2169436, 515593, 4557489, -3373064)为高 Q 峰值滤波段,反映典型的 10 段图形均衡器频率分布。

硬件 EQ:IIR 滤波器类型与段描述

hw_eq.h 定义滤波器类型枚举与段描述结构 eq_seg_info,这是通用 EQ 框架的"用户可编程"接口——App 通过填充 freq/gain/q 描述期望的滤波器特性,由 design_* 系列函数实时计算 IIR 系数:

/*eq IIR type*/
typedef enum {
    EQ_IIR_TYPE_HIGH_PASS = 0x00,
    EQ_IIR_TYPE_LOW_PASS,
    EQ_IIR_TYPE_BAND_PASS,
    EQ_IIR_TYPE_HIGH_SHELF,
    EQ_IIR_TYPE_LOW_SHELF,
    EQ_IIR_TYPE_BAND_PASS_NEW,//用于频谱分析
} EQ_IIR_TYPE;

struct eq_seg_info {
    unsigned short index;   //eq段序号
    unsigned short iir_type; //滤波器类型EQ_IIR_TYPE
    int freq;               //中心截止频率
    float gain;             //增益(-12 ~12 db)
    float q;                //q值(0.3~30)
};

Source: hw_eq.h

配套的系数设计函数(design_lp/design_hp/design_pe/design_ls/design_hs/design_bp,以及供整型增益使用的 design_pe_for_int)输出 5 个 float 系数,经 eq_stable_check() 做稳定性校验,eq_db2mag() 完成 dB 到幅值的换算。定点宏 mul64/div64 与 EQ_OUTQ = 22 定义了运行期定点乘法/除法的 Q 格式,EQ_COEFF0~EQ_COEFF4 将数组下标映射为滤波器差分方程的 b0/b1/b2/a1/a2。

通用 EQ 框架与运行期对象

audio_eq.h 将 EQ 封装为可复用的效果组件:

  • EQ_STUCT_API:效果操作集(need_buf/open/run/config),通过 get_eq_context() 获取,实现"算法与对象解耦";
  • EQ_BUFF:运行期缓冲,内嵌 EQ_IO_CONTEXT、EQ_PARA_STRUCT(当前段/下一段、全局增益、采样率、最大段数、声道数)、sound_fade_obj fade(淡入淡出)、内部数据缓冲 buff[EQ_MAX_POINT * 2] 与系数缓存 eq_oeff_tab[];
  • EQ_HDL:完整句柄,包含 EFFECT_OBJ obj(通用效果对象基类)、sound_in_obj si(输入描述)、EQ_IO_CONTEXT io 与 EQ_BUFF buff;
  • 命令接口:EQ_CMD_SWITCH_TAB(在线切换系数表/音效)、EQ_GET_CURR_MODE(查询当前模式),由 eq_mode_sw()/eq_online_sw() 驱动;
  • 模式集合:EQ_MODE 枚举(见 pcm_eq_api.h)定义 EQ_ZERO/EQ_POP/EQ_ROCK/EQ_JAZZ/EQ_CLASSIC/EQ_MODEMAX 六种预设音效,get_eq_mode_total_num() 返回模式总数。

音效模式枚举(来自 pcm_eq_api.h):

typedef enum {
    EQ_ZERO = 0x01,
    EQ_POP,
    EQ_ROCK,
    EQ_JAZZ,
    EQ_CLASSIC,
    EQ_MODEMAX
} EQ_MODE;

设计意图:EQ_PARA_STRUCT 同时保存"当前段"与"下一段"(seg/next_seg),配合 next_global_gain 实现参数平滑过渡——切换音效时先更新 next_seg,运行期逐帧逼近新系数,避免爆音(pop noise)。这也是 EQ_CMD_SWITCH_TAB 与 eq_online_sw() 存在的意义:在线调音不重建 EQ 对象,只交换系数。

Core Flow

下图展示两条 EQ 路径在音频播放期间的完整数据流(以 PCM EQ 为主路径,通用 EQ 框架为并行路径):

sequenceDiagram
    participant Dec as 解码器
    participant Lnk as link_pcm_eq_sound / link_eq_sound
    participant Pcm as pcm_eq_api / EQ open
    participant Coef as 系数表 / design_* 函数
    participant Run as EQRun / eq_run_api
    participant Dac as DAC 缓存

    Dec->>Lnk: 输出 PCM 流(sr, channel)
    Lnk->>Pcm: 初始化效果(obuf, sr, channel)
    Pcm->>Pcm: 校验工作区大小(g_work_buf vs pcm_eq_buf_len)
    Pcm->>Coef: get_pcm_eq_tab(sr) / design_pe(...)
    Coef-->>Pcm: 10 段系数(LCoeff/R Coeff)
    Pcm-->>Lnk: effect 句柄 / EQ_HDL
    Lnk->>Lnk: 置 B_DEC_EFFECT,衔接 DAC 缓冲
    loop 每帧音频
        Dec->>Run: 送入 inbuf(128 点/帧)
        Run->>Run: 10 段 IIR 级联 + 增益(1<<20) + SHI/SHO 缩放
        Run-->>Dec: outbuf 处理结果
    end
    Run->>Dac: 效果后数据写入 DAC 缓存
    Dac->>Dac: 硬件播放

流程说明:

  1. 解码器输出 PCM 数据时携带采样率 sr 与声道数 channel,音频管理模块调用 link_pcm_eq_sound()(或通用框架的 link_eq_sound())构建效果链;
  2. 初始化阶段做工作区尺寸校验(660 字节静态缓冲),随后从系数表或 design_* 动态计算获取 10 段 IIR 系数,构造 PCM_EQ_PARM/EQ_PARA_STRUCT;
  3. 挂接成功后,原 sound 节点 enable |= B_DEC_EFFECT,其输出缓冲改为 DAC 缓存,效果句柄通过 pp_effect 回传供后续参数更新(pcm_eq_updata/EQ_UPDATE);
  4. 播放过程中每帧(EQ_MAX_POINT = 128 点)调用 EQRun()/eq_run_api() 执行 10 段 IIR 级联滤波,配合 SHI=10/SHO=8 的定点缩放与 LGain/RGain 总增益;
  5. 处理后的数据直接落入 DAC 缓存,由硬件播放;切换音效时通过 EQ_CMD_SWITCH_TAB 更新 next_seg 系数并平滑过渡。

Usage Examples

挂接 PCM EQ 到播放链路(播放器侧调用)

播放器在建立音频输出时调用 link_pcm_eq_sound(),将 PCM EQ 插入解码器与 DAC 之间。第二个示例展示通用框架的挂接入口 link_eq_sound(),二者签名一致(均返回下一个 sound 节点):

// PCM 软件 EQ 路径(pcm_eq_api.c)
void *link_pcm_eq_sound(void *p_sound_out, void *p_dac_cbuf, void **pp_effect, u32 sr, u32 channel);

// 通用 EQ 框架路径(audio_eq.h)
void *link_eq_sound(void *p_sound_out, void *p_dac_cbuf, void **pp_effect, u32 sr, u8 ch);

Sources:

  • pcm_eq_api.h
  • audio_eq.h

运行时更新 EQ 参数(切换音效/在线调音)

低层 API 提供 EQInit/EQUpdate/EQRun 三件套:EQInit 首次初始化工作区,EQUpdate 在播放中替换系数与增益(用于音效切换),EQRun 逐帧处理 npoint 个采样点。PCM 底层物理接口 pcm_eq_phy/pcm_eq_updata 与之对应,pcm_eq_updata 用于运行期更新参数:

int getEQBuf(int nSection, int channel);
void EQInit(void *workBuf, const int *LCoeff_OnChip, const int *RCoeff_OnChip, int LGain, int RGain, int SHI, int SHO, int nSection, int channel);
void EQUpdate(void *workBuf, const int *LCoeff_OnChip, const int *RCoeff_OnChip, int LGain, int RGain, int SHI, int SHO, int nSection, int channel);
void clearEQSOSMem(void *workBuf);
int EQRun(void *workBuf, short *in, short *out, int npoint);

int pcm_eq_buf_len(int nSection, int channel);
void *pcm_eq_phy(void *obuf, void *dbuf, PCM_EQ_PARM *parm, void **ppsound);
void pcm_eq_updata(void *dbuf, PCM_EQ_PARM *p_parm);

Source: pcm_eq_api.h

动态设计 IIR 滤波器系数(硬件 EQ 路径)

通用框架下,App 填充 eq_seg_info 段描述后调用 design_pe() 等函数实时计算系数;eq_db2mag() 完成 dB→线性增益换算,eq_stable_check() 校验系数稳定性:

extern void design_lp(int fc, int fs, float quality_factor, float *coeff);   // 低通
extern void design_hp(int fc, int fs, float quality_factor, float *coeff);   // 高通
extern void design_pe(int fc, int fs, float gain, float quality_factor, float *coeff); // 峰值
extern void design_ls(int fc, int fs, float gain, float quality_factor, float *coeff); // 低频搁架
extern void design_hs(int fc, int fs, float gain, float quality_factor, float *coeff); // 高频搁架
extern void design_bp(int fc, int fs, float quality_factor, float *coeff);   // 带通
extern int eq_stable_check(float *coeff);
float eq_db2mag(float x);

Source: hw_eq.h

Configuration Options

配置项类型默认值说明
PCM_SW_EQ_EN编译宏关闭(未定义)使能 PCM 软件 EQ;整个 pcm_eq_api.c 实现体被 #if defined(PCM_SW_EQ_EN) && (PCM_SW_EQ_EN) 包裹
parm.nSectionint10EQ 级联段数(EQ_SECTION_MAX 上限同为 10)
parm.LGain / RGainint1 << 20左右声道总增益(定点,约 0 dB)
parm.SHI / SHOint10 / 8输入/输出定点缩放移位(Q 格式)
parm.channelint运行时传入声道数;> 1 时右声道复用左声道系数表
g_work_bufstatic u32[]660 字节(.pcm_eq_data 段)EQ 运行工作区;不足时初始化失败
EQ_MAX_POINT宏128单帧最大处理点数(EQ_BUFF.buff[EQ_MAX_POINT*2])
EQ_COEFF_BASE宏0x2F0000EQ 系数存放地址基址(硬件侧)
EQ_OUTQ宏22定点乘法输出 Q 格式(mul64/div64)
增益范围--12 ~ +12 dBeq_seg_info.gain 允许范围
Q 值范围-0.3 ~ 30eq_seg_info.q 允许范围
运行模式枚举NORMALNORMAL/MONO/STEREO(hw_eq.h)
输入/输出格式枚举DATI_SHORT/DATO_SHORTshort/int/float 三选一
数据排布枚举SEQUENCE_DAT_IN/SEQUENCE_DAT_OUT序列模式(交错)或块模式(连续)

API Reference

PCM EQ 层(pcm_eq_api.h / pcm_eq_api.c)

void *pcm_eq_api(void *obuf, u32 sr, u32 channel, void **ppsound) 初始化 10 段 PCM EQ,返回效果句柄;工作区不足返回 NULL。

  • 参数:obuf 输入缓冲;sr 采样率;channel 声道数;ppsound 输出下一个 sound 节点指针。
  • 返回:EQ 效果句柄(可写入 sound_out_obj.effect);失败为 NULL。
  • 注:内部使用全局 PCM_EQ_PARM parm 与静态 g_work_buf。

const int *get_pcm_eq_tab(u32 sr) 按采样率返回预计算系数表首地址;未匹配的采样率回落 32 kHz 表。

  • 参数:sr 采样率(8000/12000/16000/24000/32000/44100/48000 等)。
  • 返回:const int * 系数表指针。

void *link_pcm_eq_sound(void *p_sound_out, void *p_dac_cbuf, void **pp_effect, u32 sr, u32 channel) 将 PCM EQ 挂入声音输出链表。

  • 参数:p_sound_out 当前 sound 节点;p_dac_cbuf DAC 缓存;pp_effect 回传效果句柄;sr/channel 采样率与声道数。
  • 返回:链路中下一个 sound 节点;EQ 初始化失败时返回原节点(直通降级)。

int pcm_eq_buf_len(int nSection, int channel):计算指定段数/声道数所需工作区长度(字节)。 void *pcm_eq_phy(void *obuf, void *dbuf, PCM_EQ_PARM *parm, void **ppsound):底层物理初始化(在静态库 lib_pcm_eq.a 中实现)。 void pcm_eq_updata(void *dbuf, PCM_EQ_PARM *p_parm):运行期更新 EQ 参数。

底层 EQ 引擎(pcm_eq_api.h 前段声明)

void EQInit(void *workBuf, const int *LCoeff_OnChip, const int *RCoeff_OnChip, int LGain, int RGain, int SHI, int SHO, int nSection, int channel):初始化工作区与系数。 void EQUpdate(...):同签名,播放中替换系数/增益/移位参数(音效切换)。 void clearEQSOSMem(void *workBuf):清零滤波器状态(SOS 延迟线),用于切换音效前避免残留状态。 int EQRun(void *workBuf, short *in, short *out, int npoint):处理 npoint 个采样点;in/out 可为同址(就地处理)。

通用 EQ 框架(audio_eq.h)

const EQ_STUCT_API *get_eq_context(void) 获取 EQ 操作集(need_buf/open/run/config)。

  • need_buf(max_eq_seg, ch):计算所需缓冲字节数。
  • open(ptr, para, rsio):按 EQ_PARA_STRUCT 打开 EQ,返回工作区指针。
  • run(ptr, inbuf, len):处理一帧数据。
  • config(work_buf, cmd, parm):执行 EQ_CMD_SWITCH_TAB/EQ_GET_CURR_MODE 等命令。

void *eq_hld_malloc():分配 EQ_HDL 句柄。 void eq_reless(void **ppeffect):释放 EQ 效果句柄。 void eq_online_sw(void *parm):在线调音切换(配合 music_eq_tool)。 void eq_mode_sw(void *parm):音效模式切换(EQ_MODE)。 extern u8 get_eq_mode_total_num():返回预设音效模式总数。 void eq_online_sw 的数据载体 struct music_eq_tool:包含 global_gain、seg_num、enable_section 与最多 10 段的 seg[EQ_SECTION_MAX],其中 music_eq_tool_online 为在线调音共享实例。

Failure Modes, Edge Cases & Concurrency

  • 工作区不足导致 EQ 挂接失败:pcm_eq_api() 用 pcm_eq_buf_len(10, channel) 与实际静态缓冲 sizeof(g_work_buf)(660 字节)比较,不足时打印 "PCM EQ Work Data space is not big enough" 并返回 NULL。调用方 link_pcm_eq_sound() 检测到 NULL 后不置位 B_DEC_EFFECT、不修改输出缓冲,链路保持直通——这是有意的降级策略:EQ 异常不阻断音频播放,仅丢失音效。若需更多段数或更多声道,必须同步扩大 g_work_buf(注意其 AT(.pcm_eq_data) 段属性,该段位于内部 SRAM,容量有限)。
  • 采样率未覆盖:get_pcm_eq_tab() 的 switch 未命中任何 case 时返回 32 kHz 表。若实际播放采样率(如 96 kHz)与 32 kHz 相差过大,系数表与采样率不匹配会造成滤波器频率响应偏移。PCM EQ 路径目前只预置了 8k/12k/16k/24k/32k(及 44.1k/48k 等)常用采样率表,使用前应确认表覆盖率。
  • 声道数边界:channel 大于 1 时左右声道共用同一张系数表(RCoeff_OnChip = LCoeff_OnChip),因此 PCM EQ 路径不区分左右声道差异化曲线;需要差异化时须走通用框架(EQ_PARA_STRUCT.ch 支持 1/2 声道)并自行提供双表。
  • 切换音效的爆音风险:直接改写系数会使 IIR 延迟线中的历史状态与新系数不匹配,产生瞬态噪声。框架通过 EQ_PARA_STRUCT 的 seg/next_seg 双缓冲与 next_global_gain 平滑逼近,且提供 clearEQSOSMem() 清空 SOS 状态;PCM EQ 路径切换前应调用 clearEQSOSMem 或 pcm_eq_updata 配合淡入淡出(B_EQ_FADE_IN/B_EQ_FADE_OUT)。
  • 并发与中断上下文:EQ 运行(EQRun/eq_run_api)发生在音频数据回调/任务上下文,与 App 侧参数更新(pcm_eq_updata/EQ_UPDATE/eq_mode_sw)属于生产者-消费者关系。PCM_EQ_PARM 为全局变量,pcm_eq_updata 直接改写其字段,若在 EQ 处理中并发更新存在数据竞争;通用框架用 next_seg 双缓冲缓解,PCM EQ 路径则依赖音频管理模块保证"先停止/再更新/后恢复"的串行时序。SDK 未提供互斥锁,跨任务更新参数时应遵循该时序约定。
  • 定点溢出:输入经 SHI=10 上移、输出经 SHO=8 下移,总增益 1<<20 提供了约 2 的净增益余量;若 App 调高 LGain/RGain 或输入接近满幅,级联滤波可能使中间累加值超过 int32 范围(mul64 的 Q22 格式限制了定点精度),应通过 eq_stable_check() 与增益上限约束规避削波/溢出。

Performance & Operational Considerations

  • 静态缓冲、零动态分配:PCM EQ 使用编译期分配的 g_work_buf(660 字节)与全局 parm,运行期无 malloc/free,符合嵌入式实时音频的确定性要求;通用框架的 eq_hld_malloc() 则用于需要多实例/动态管理的场景。
  • 定点查表 vs 浮点设计:PCM EQ 路径全整型系数(EQTools 离线生成),每帧仅做乘加与移位,开销极小;通用框架路径用 float 系数并缓存到 eq_oeff_tab[],open 时计算一次、run 时复用,避免了逐帧重算系数。
  • 单帧处理点数:EQ_MAX_POINT = 128 定义了 EQ_BUFF.buff 与 EQRun 的帧粒度,实际帧长小于 128 时余量闲置;处理延时可近似为 128 点/采样率。
  • 数据排布模式:SEQUENCE_DAT_IN/OUT(交错)与 BLOCK_DAT_IN/OUT(连续)两种排布在 hw_eq.h 中定义,audio_eq.h 默认选择序列模式,与解码器/ DAC 的 DMA 缓冲格式一致时可避免额外的重排拷贝。
  • 系数表内存占用:9 张表 × 10 段 × 5 个 int ≈ 1.8 KB 常量区,位于 Flash(const),不影响运行期 RAM;如需精简可裁剪 get_pcm_eq_tab 中不使用的采样率分支。

Extension Points

  • 新增音效模式:在 EQ_MODE 枚举(EQ_ZERO/EQ_POP/EQ_ROCK/EQ_JAZZ/EQ_CLASSIC)中追加模式并同步 get_eq_mode_total_num();PCM EQ 路径需为新模式生成对应的 10 段系数表(EQTools 离线生成后烧录),通用框架路径可在 eq_mode_sw 回调中填充 music_eq_tool/EQ_PARA_STRUCT 的 next_seg 实现动态模式。
  • 在线调音(App 侧):通过 music_eq_tool_online(global_gain + 10 段 seg[] + enable_section)实时调整各段 freq/gain/q,再经 eq_online_sw() 与 EQ_CMD_SWITCH_TAB 生效;enable_section 可动态禁用某段(直通)。
  • 扩展滤波类型:在 EQ_IIR_TYPE 枚举后追加新类型,并在 design_* 函数族中补充对应的系数设计函数;eq_stable_check() 对所有类型统一校验稳定性,新增类型应复用该检查。
  • 自定义效果节点:仿照 link_pcm_eq_sound() 的模式(sound_out_obj.effect + B_DEC_EFFECT + 返回下一节点)可将其他音效(混响、压缩等)插入同一链路,多个效果可串联。

Related Links

  • pcm_eq_api.c — PCM 软件 EQ 实现
  • pcm_eq_api.h — PCM EQ 参数与底层引擎 API
  • audio_eq.h — 通用 EQ 效果框架
  • hw_eq.h — 硬件 EQ / IIR 滤波器设计
  • 音频输出链路与声音管理:sound_mge.h / sound_out_obj(EQ 节点挂接依赖的结构定义)
  • 解码器输出接口:decoder_api.h(EQ 处理前的 PCM 数据来源)
  • 相关静态库:lib_pcm_eq.a / ae_pcm_eq_lib.a(pcm_eq_phy、EQRun 等底层实现)
  • 相邻目录页:音频编解码格式、DAC/ADC 驱动、音频链路同步(audio_link)
Prev
音频设备接口 (DAC / ADC / APA)
Next
播放、录音与 MIO 工作流