音频编码器
音频编码器(Audio Encoder)是杰理 AD15N/AD1NN MCU SDK 中负责将 ADC 采集的 PCM 音频数据实时压缩为 IMA ADPCM、MP2 等格式并写入文件的子系统。它由统一的编码器管理层(enc_obj)、可插拔的编解码操作集(ENC_OPS)以及软件中断驱动的数据搬运机制组成,是录音类应用(如 voice_toy 录音)的核心通路。
Purpose and Scope
本页面向 audio-processing/encoder 目录,完整说明音频编码器子系统的工作原理:
- 编码器对外 API 契约(
EN_FILE_IO、ENC_DATA_INFO、ENC_OPS)与对象模型(enc_obj); - 从 ADC 采集到文件落盘的完整数据通路(
encoder_api.c); - 编码器的启停流程、状态标志与软件中断(
kick_encode_isr/kick_wfile_isr)调度机制; - 支持的编码格式(IMA ADPCM、MP2、MP2 Standard)及扩展方式;
- 配置项、故障模式与并发/中断上下文注意事项。
以下主题属于其他页面范畴,不在本页展开:音频解码器与 DAC 播放通路、sound_effect_api.h 中的音效处理、ANS 降噪算法本身(仅在编码通路中说明其接入点)、以及上层录音应用(如 voice_toy/toy_record)的业务逻辑。SDK 预编译的编码库(lib_mp2_encode.a、lib_mp2standard_encode.a、encoder_mge_lib.a)只作为二进制依赖提及,其内部实现不在源码中公开。
概述
在录音类应用里,系统需要把麦克风(MIC)输入的连续模拟信号数字化并压缩存储。编码器子系统承担以下职责:
- 采集接入:通过
audio_adc注册 ADC 通道,将 ADC 中断产出的 PCM 数据送入环形缓冲区(cbuffer); - 格式转换:按
ENC_DATA_INFO指定的采样率(sr)与码率/块大小(br)配置底层编码器; - 实时编码:在软件中断(软中断 1)上下文中驱动
ENC_OPS->run()完成 PCM → 压缩码流转换; - 落盘输出:在软中断 2 上下文中把压缩数据经
output_data回调写入文件(通过vfs文件系统 IO); - 优雅停止:
stop_encode()会逐级排空输入/输出环形缓冲,再卸载软中断并同步文件。
设计上有三个关键决策值得注意:
- 可插拔编码器(strategy 模式):
ENC_OPS统一了need_buf/open/run三个操作,新增编码格式只需实现这三个函数并注册即可,管理层代码无需改动; - 中断驱动 + 环形缓冲解耦:ADC ISR 与编码器运行在不同上下文,通过
cbuffer解耦,避免在中断里做耗时编码; - 软中断作任务调度器:
kick_encode_isr()/kick_wfile_isr()以bit_set_swi()触发两个软件中断,作为"编码就绪"与"写文件就绪"的调度信号,实现简单且开销低。
架构
flowchart TD
subgraph sg_Input["采集层"]
MIC["MIC 模拟输入"]
ADC["audio_adc 通道<br/>rec_sound / enc_in_sound"]
CBUF_ADC["环形缓冲 cbuf_adc<br/>(.rec_data 段)"]
ANS["ANS 降噪(可选)<br/>cbuf_ans / ans_init"]
end
subgraph sg_EncMgr["编码器管理层 encoder_api.c"]
ENC_OBJ["enc_obj 编码器对象"]
KICK1["软中断1 kick_encode_isr"]
KICK2["软中断2 kick_wfile_isr"]
ENC_IN["enc_input 回调"]
ENC_OUT["enc_output 回调"]
end
subgraph sg_Codect["编码器实例(可插拔)"]
OPS["ENC_OPS 操作集<br/>need_buf / open / run"]
IMA["IMA ADPCM<br/>get_ima_code_ops"]
MP2["MP2<br/>get_mp2_ops"]
MP2S["MP2 Standard<br/>get_mp2standard_ops"]
end
subgraph sg_File["输出层"]
VFS["vfs 文件系统"]
FILE["录音文件(NOR Flash)"]
end
MIC --> ADC
ADC -->|"ADC ISR 填充"| CBUF_ADC
CBUF_ADC -->|"ans_init 可选链路"| ANS
ANS -->|"kick_encode_api"| ENC_OBJ
CBUF_ADC -->|"kick_encode_api 注册回调"| ENC_OBJ
ENC_OBJ -->|"p_ibuf 输入"| ENC_IN
ENC_IN --> OPS
OPS -->|"实例选择"| IMA
OPS -->|"实例选择"| MP2
OPS -->|"实例选择"| MP2S
OPS -->|"p_obuf 输出"| ENC_OUT
KICK1 -->|"触发编码"| OPS
KICK2 -->|"触发写盘"| ENC_OUT
ENC_OUT --> VFS
VFS --> FILE
架构说明:
- 采集层:
rec_phy_init()初始化两个sound_out_obj(rec_sound、enc_in_sound)并共享同一 ADC 环形缓冲cbuf_adc;当开启ANS_EN宏时,ADC 数据先经ans_init()降噪后再进入cbuf_ans,编码输入改为从cbuf_ans读取(见 encoder_api.c)。 - 编码器管理层:
enc_obj是贯穿全程的状态载体,持有文件句柄、输入/输出缓冲、编码操作集与enable状态位(见 encoder_mge.h)。 - 编码器实例层:
ENC_OPS策略接口由各编码库实现(IMA ADPCM 内置于 SDK,MP2 系列为预编译静态库),通过get_ima_code_ops()/get_mp2_ops()/get_mp2standard_ops()获取(见 audio_enc_api.h)。 - 输出层:编码后的数据经
enc_output回调、vfs_ioctl文件同步写入 Flash,避免数据丢失。
核心数据契约
EN_FILE_IO — 编码器与外部世界的 IO 桥
编码器本身不关心数据来自哪里、写到哪去,只依赖两个回调完成数据搬运(见 audio_enc_api.h):
typedef struct _EN_FILE_IO_ {
void *priv;
u16(*input_data)(void *priv, s16 *buf, u16 len);
u32(*output_data)(void *priv, u8 *buf, u16 len);
} EN_FILE_IO;
input_data:从priv指向的源(这里是enc_obj的输入环形缓冲)读取len个 16 位 PCM 采样;output_data:把编码后的len字节压缩码流写入priv指向的目的地(文件)。
priv 的设计让同一套 ENC_OPS 可以被多个录音实例复用——只需传入不同的 priv 上下文。SDK 中 enc_input / enc_output 即作为该回调的桥接实现。
ENC_DATA_INFO — 编码参数
typedef struct _ENC_DATA_INFO_ {
u16 sr; ///<sample rate
u16 br; ///<mp2的时候它是bitrate,但是adpcm的时候,它是blockSize,一般配成256/512/1024/2048,超过2048会被限制成2048
} ENC_DATA_INFO;
(见 audio_enc_api.h)
sr:采样率(如 8000/16000 Hz);br:语义随编码格式变化——MP2 系列表示码率(bitrate);IMA ADPCM 表示块大小(blockSize),常用 256/512/1024/2048,超过 2048 会被底层钳制到 2048。这一"一字段两义"的设计是编码器层为统一接口付出的代价:不同编码器对同一字段的解释不同,上层配置时必须知道当前选用哪种编码器。
ENC_OPS — 编码器策略接口
typedef struct _ENC_OPS {
u32(*need_buf)();
void (*open)(u8 *ptr, ENC_DATA_INFO *data_info, EN_FILE_IO *audioIO);
u32(*run)(u8 *ptr);
} ENC_OPS;
(见 audio_enc_api.h)
三个操作构成编码器完整生命周期:
need_buf():返回该编码器工作所需的私有缓冲区字节数,管理层据此分配p_dbuf;open(ptr, data_info, audioIO):用数据指针、编码参数和 IO 桥完成初始化;run(ptr):执行一次编码,从input_data取 PCM、向output_data吐码流,返回本次处理长度。run被设计成"可反复被软中断调用",天然适配无阻塞的中断驱动模型。
enc_obj 与状态标志
#define B_ENC_ENABLE BIT(0)
#define B_ENC_STOP BIT(1)
#define B_ENC_FULL BIT(2)
typedef struct _enc_obj {
void *p_file;
void *p_ibuf;
void *p_obuf;
void *enc_ops;
void *p_dbuf;
ENC_DATA_INFO info;
volatile u32 enable;
} enc_obj;
(见 encoder_mge.h)
enable 是 volatile 位掩码,表示编码器的运行状态:
| 标志 | 含义 |
|---|---|
B_ENC_ENABLE | 编码器处于运行状态(encoder_io 成功后置位) |
B_ENC_STOP | 收到停止请求(stop_encode 置位),排空流程据此推进 |
B_ENC_FULL | 缓冲已满/异常,允许排空循环提前退出,防止死循环 |
volatile 关键字说明这些标志会被中断上下文读写,编译器不得对其优化缓存。
实现走读(encoder_api.c)
1. 物理通路初始化 rec_phy_init()
void rec_phy_init(void)
{
memset(&rec_sound, 0, sizeof(rec_sound));
memset(&enc_in_sound, 0, sizeof(enc_in_sound));
cbuf_init(&cbuf_adc, &adc_buff[0], sizeof(adc_buff));
enc_in_sound.p_obuf = &cbuf_adc;
rec_sound.p_obuf = &cbuf_adc;
#if (defined(ANS_EN) && (ANS_EN))
cbuf_init(&cbuf_ans, &ans_buff[0], sizeof(ans_buff));
enc_in_sound.p_obuf = &cbuf_ans;
ans_init(&cbuf_adc, &cbuf_ans, kick_encode_api);
regist_audio_adc_channel(&rec_sound, (void *) ans_check_kick_start); //注册到ADC;
#else
regist_audio_adc_channel(&rec_sound, (void *) kick_encode_api); //注册到ADC;
#endif
}
(见 encoder_api.c)
要点:
- ADC 数据缓冲
adc_buff[512 * 10 / 4]与 ANS 缓冲ans_buff[512 * 5 / 4]分别放在.rec_data/.ans_data内存段,便于链接脚本做 RAM 布局(中断频繁访问的数据尽量靠近 CPU); regist_audio_adc_channel()把 ADC 通道与"kick 回调"绑定:每有新的 ADC 数据,就调用一次kick_encode_api;- 开启 ANS 时数据路径变为
cbuf_adc → ans_init 处理 → cbuf_ans → 编码器,且 ADC 通道注册的回调换成ans_check_kick_start——由 ANS 模块决定何时有足够数据可编码。
2. 启动 encoder_io() 与 start_encode()
void encoder_io(u32(*fun)(void *), void *pfile)
{
s32 err;
rec_phy_init();
enc_hdl = (void *)fun(pfile);
if (0 != enc_hdl) {
enc_phy_init();
enc_hdl->enable = B_ENC_ENABLE;
start_encode();//adc_enable();
log_info("encode succ: \n");
} else {
log_info("encode fail \n");
}
}
(见 encoder_api.c)
void start_encode(void)
{
START_ADC_RUN;
audio_adc_enable(MIC_PGA_G);
}
(见 encoder_api.c)
encoder_io 是录音应用的统一入口:传入一个创建编码器对象的工厂函数 fun(pfile)(返回非零即成功),成功后:
enc_phy_init()安装编码器专用软中断(软中断 1 负责编码、软中断 2 负责写文件);enc_hdl->enable = B_ENC_ENABLE置运行位;start_encode()置B_DEC_RUN_EN | B_REC_RUN使能位并打开audio_adc_enable,让 ADC 开始采样。
3. 停止 stop_encode()
void stop_encode(void *pfile, u32 dlen)
{
enc_obj *obj = enc_hdl;
u32 err;
audio_adc_disable();
STOP_ADC_RUN;
log_info("stop encode\n");
if (NULL == enc_hdl) {
rec_phy_suspend();
return;
}
obj->enable |= B_ENC_STOP;
log_info("stop encode A\n");
while (0 != cbuf_get_data_size(obj->p_ibuf)) {
if (obj->enable & B_ENC_FULL) {
break;
}
kick_encode_isr();
delay(100);
}
log_info("stop encode C\n");
while (0 != cbuf_get_data_size(obj->p_obuf)) {
if (obj->enable & B_ENC_FULL) {
break;
}
kick_wfile_isr();
delay(100);
}
log_info("stop encode D\n");
obj->enable &= ~B_ENC_ENABLE;
HWI_Uninstall(IRQ_SOFT1_IDX);
HWI_Uninstall(IRQ_SOFT2_IDX);
u32 flen = dlen;
err = vfs_ioctl(pfile, FS_IOCTL_FILE_SYNC, (int)&flen);
rec_phy_suspend();
enc_hdl = 0;
}
(见 encoder_api.c)
停止流程设计为两级排空,保证不留半帧数据:
- 先停 ADC(
audio_adc_disable+ 清B_DEC_RUN_EN | B_REC_RUN),不再产生新数据; - 置
B_ENC_STOP后循环kick_encode_isr()+delay(100)排空输入缓冲(p_ibuf),直到cbuf_get_data_size()==0; - 再循环
kick_wfile_isr()+delay(100)排空输出缓冲(p_obuf); - 两个排空循环都以
B_ENC_FULL作为"异常但必须退出"的保护条件,避免硬件故障时看门狗复位; - 清
B_ENC_ENABLE、卸载两个软中断、vfs_ioctl(FS_IOCTL_FILE_SYNC)把缓冲落盘、rec_phy_suspend()反注册 ADC 通道、置enc_hdl = 0释放句柄。
4. 中断调度宏
#define kick_encode_isr() bit_set_swi(1)
#define kick_wfile_isr() bit_set_swi(2)
(见 encoder_mge.h)
kick_encode_api()(ADC 回调,见 encoder_api.c)在 ADC 上下文里被调用,仅做 kick_encode_isr() 触发软中断 1,把耗时编码延迟到软中断上下文执行——这是"中断里只做最轻量工作"的经典嵌入式实践。
核心流程
sequenceDiagram
participant APP as 录音应用
participant MGR as encoder_io / enc_obj
participant ADC as audio_adc 通道
participant CB as 环形缓冲 cbuffer
participant OPS as ENC_OPS 编码器实例
participant VFS as vfs 文件系统
APP->>MGR: encoder_io(fun, pfile)
MGR->>MGR: rec_phy_init() 初始化 cbuf_adc
MGR->>MGR: fun(pfile) 创建 enc_obj(含 enc_ops/info)
MGR->>MGR: enc_phy_init() 安装软中断1/2
MGR->>MGR: enable = B_ENC_ENABLE
MGR->>ADC: start_encode() → audio_adc_enable()
Note over ADC,CB: 周期采样开始
loop 录音期间
ADC->>CB: ADC ISR 填充 cbuf_adc
CB->>MGR: kick_encode_api()(或 ans_check_kick_start)
MGR->>MGR: kick_encode_isr() → 软中断1
MGR->>OPS: ENC_OPS->run(ptr)
OPS->>CB: input_data 读取 PCM
OPS->>MGR: output_data 输出码流 → p_obuf
MGR->>MGR: kick_wfile_isr() → 软中断2
MGR->>VFS: 写文件
end
APP->>MGR: stop_encode(pfile, dlen)
MGR->>ADC: audio_adc_disable() + STOP_ADC_RUN
MGR->>MGR: enable |= B_ENC_STOP
loop 排空 p_ibuf
MGR->>OPS: kick_encode_isr() 直到缓冲为空
end
loop 排空 p_obuf
MGR->>MGR: kick_wfile_isr() 直到缓冲为空
end
MGR->>MGR: 卸载软中断、vfs_ioctl 文件同步
MGR->>MGR: enc_hdl = 0
流程要点:
- 数据只在"ADC ISR → 环形缓冲"、"软中断 → 编码器 → 环形缓冲"、"软中断 → 文件"三段之间流动,任一段都不会长时间占用中断;
B_ENC_STOP置位后编码器仍可被run驱动以排空残留数据,直到缓冲清空——这是"停止但不丢数据"的关键;- 写文件通过
vfs(VFS 抽象层)完成,文件实际落在 NOR Flash 等存储介质上。
用法示例
以下示例均取自仓库真实源码。
示例 1:创建编码器对象(上层工厂函数模式)
encoder_io 的第一个参数是一个"以文件句柄创建编码器对象"的工厂函数。上层录音模块(如 voice_toy/toy_record)按此模式构造 enc_obj,并填写编码参数与 IO 回调:
static void *enc_create(void *pfile)
{
// ... 分配 enc_obj,设置 p_file / p_ibuf / p_obuf / info 等字段
// info.sr = 16000; info.br = 512; // ADPCM 时 br 为 blockSize
// obj->enc_ops = get_ima_code_ops(); // 选择 IMA ADPCM 编码器
return obj;
}
工厂返回非零句柄后,encoder_io 内部会依次完成 enc_phy_init()、置 B_ENC_ENABLE 并启动 ADC(见 encoder_api.c)。上层只需在需要开始时调用 encoder_io(enc_create, pfile)。
示例 2:启动与停止编码
void start_encode(void)
{
START_ADC_RUN;
audio_adc_enable(MIC_PGA_G);
}
void stop_encode(void *pfile, u32 dlen)
{
enc_obj *obj = enc_hdl;
u32 err;
audio_adc_disable();
STOP_ADC_RUN;
// ... 排空 p_ibuf / p_obuf 后:
obj->enable &= ~B_ENC_ENABLE;
HWI_Uninstall(IRQ_SOFT1_IDX);
HWI_Uninstall(IRQ_SOFT2_IDX);
err = vfs_ioctl(pfile, FS_IOCTL_FILE_SYNC, (int)&flen);
rec_phy_suspend();
enc_hdl = 0;
}
(见 encoder_api.c 与 encoder_api.c)
start_encode / stop_encode 是录音应用最常用的两个对外函数:开始录音调用前者,结束录音调用后者,并传入最终文件长度 dlen 以便文件系统同步。
示例 3:ADC 数据就绪回调
AT(.audio_a.text.cache.L2)
void kick_encode_api(void *obj)
{
kick_encode_isr();
}
(见 encoder_api.c)
该函数被 regist_audio_adc_channel 注册为 ADC 通道回调,并用 AT(.audio_a.text.cache.L2) 链接属性放到 L2 cache 文本段以加快取速度。回调体只触发软中断 1,绝不在 ADC 上下文做编码。
配置选项
编码器子系统的配置分散在编译宏、结构体字段与运行时标志三层:
编译期宏(config.h / 构建系统)
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
ANS_EN | 宏 | 未定义 | 定义并置 1 时启用 ANS 降噪链路,编码输入从 cbuf_ans 读取 |
FPGA | 宏 | 0 | 为 1 时使用 FPGA 平台的 adc_obj 替代 sound_out_obj(见 encoder_mge.h) |
MIC_PGA_G | 常量 | 外部定义 | MIC 前置增益,传入 audio_adc_enable |
运行时参数(ENC_DATA_INFO)
| 字段 | 类型 | 取值范围 | 说明 |
|---|---|---|---|
sr | u16 | 如 8000/16000 | 采样率 |
br | u16 | MP2:码率;ADPCM:256/512/1024/2048 | 超过 2048 会被限制为 2048(ADPCM 块大小) |
状态标志(enc_obj.enable)
| 标志 | 值 | 置位时机 |
|---|---|---|
B_ENC_ENABLE | BIT(0) | encoder_io 成功后 |
B_ENC_STOP | BIT(1) | stop_encode 进入排空阶段 |
B_ENC_FULL | BIT(2) | 缓冲满/异常,由编码器层置位以中断排空循环 |
API 参考
ENC_OPS *get_ima_code_ops(void)
获取 IMA ADPCM 编码器操作集(内置实现)。返回的 ENC_OPS 包含 need_buf / open / run,用法与 MP2 系列一致。
ENC_OPS *get_mp2_ops(void)
获取 MP2 编码器操作集(由预编译库 lib_mp2_encode.a 提供)。
ENC_OPS *get_mp2standard_ops(void)
获取 MP2 Standard 编码器操作集(由预编译库 lib_mp2standard_encode.a 提供)。
(三个获取函数声明见 audio_enc_api.h)
ENC_OPS 生命周期方法
| 方法 | 签名 | 说明 |
|---|---|---|
need_buf | u32(*)(void) | 返回编码器私有缓冲所需字节数 |
open | void(*)(u8 *ptr, ENC_DATA_INFO *data_info, EN_FILE_IO *audioIO) | 初始化编码器:ptr 为 need_buf 分配的缓冲,data_info 携带 sr/br,audioIO 为 IO 桥 |
run | u32(*)(u8 *ptr) | 执行一次编码,返回处理字节数;内部经 audioIO 回调取 PCM、写码流 |
管理层对外函数(encoder_mge.h)
| 函数 | 签名 | 说明 |
|---|---|---|
enc_input | u16 enc_input(void *priv, s16 *buf, u16 len) | 供编码器从输入缓冲读取 PCM(EN_FILE_IO.input_data 桥) |
enc_output | u32 enc_output(void *priv, u8 *data, u16 len) | 供编码器把码流写入文件(EN_FILE_IO.output_data 桥) |
encoder_io | void encoder_io(u32(*fun)(void *), void *pfile) | 统一入口:初始化物理通路、创建编码器、启动 ADC |
stop_encode | void stop_encode(void *pfile, u32 dlen) | 停 ADC、排空缓冲、卸载软中断、文件同步 |
enc_phy_init | void enc_phy_init(void) | 安装编码软中断(软中断 1/2) |
kick_encode_api | void kick_encode_api(void *obj) | ADC 数据就绪回调,触发软中断 1 |
wfil_soft2_isr_hook | void wfil_soft2_isr_hook(enc_obj *hdl) | 写文件软中断钩子(当前为空实现,预留喂狗等用途) |
(声明见 encoder_mge.h)
故障模式与边界情况
1. 创建失败
encoder_io 中工厂函数 fun(pfile) 返回 0 时,只打印 "encode fail" 且不启动任何硬件——这是合理的防御:编码器对象创建失败(通常因内存不足或文件打开失败)时,绝不让 ADC 空转。
2. 停止时缓冲排空死循环保护
stop_encode 的两个排空循环:
while (0 != cbuf_get_data_size(obj->p_ibuf)) {
if (obj->enable & B_ENC_FULL) {
break;
}
kick_encode_isr();
delay(100);
}
如果编码器或写盘异常导致缓冲永不排空,B_ENC_FULL 提供逃生出口,防止系统卡死在 delay 循环中触发看门狗复位。这是"宁可丢数据、不可挂死系统"的嵌入式取舍。
3. 空句柄防御
stop_encode 开头检查 enc_hdl == NULL:若编码从未成功启动(例如创建失败),则只做 rec_phy_suspend() 直接返回,避免解引用空指针。
4. ADPCM 块大小越界
br 超过 2048 会被编码器内部限制为 2048(见 audio_enc_api.h)。上层配置时无需校验,但要知道设置超大块不会报错、只会被钳制——这可能影响码流帧结构与播放兼容性。
5. 写盘慢导致的系统压力
源码注释(wfil_soft2_isr_hook 的 #if 0 块)明确指出:若设备写入速度较慢导致看门狗复位,可通过该钩子在写入前喂狗。即写文件路径是录音链路上最可能成为瓶颈的环节,SDK 预留了钩子函数但默认空实现。
并发与中断上下文注意事项
- 三段式上下文隔离:ADC ISR、软中断 1(编码)、软中断 2(写文件)工作在不同优先级上下文,共享数据全部经过环形缓冲(
cbuffer),生产者/消费者模型天然避免竞争; volatile enable:enc_obj.enable被encoder_io(任务上下文)与编码软中断(中断上下文)同时读写,声明为volatile防止编译器缓存优化导致状态不可见;- 排空循环中的
delay(100):stop_encode在任务上下文忙等排空,期间系统仍响应中断,因此不会死锁;但每次轮询间隔 100ms,停止延迟最大取决于缓冲深度; - 软中断卸载时序:必须先等两个排空循环结束,再
HWI_Uninstall两个软中断——若先卸载,未排空的数据将永远留在缓冲里丢失。
性能与运行注意
- 内存段布局:ADC 缓冲放
.rec_data、ANS 缓冲放.ans_data、kick_encode_api放.audio_a.text.cache.L2(L2 cache 文本段),均为高频访问路径,链接脚本的段分配直接影响中断响应与功耗; - 缓冲容量:
adc_buff[512 * 10 / 4](约 1280 字)、ans_buff[512 * 5 / 4](约 640 字)。在 8kHz/16kHz 采样下,缓冲深度决定"ADC 中断频率"与"编码器批量大小"的平衡:缓冲越大,编码器可批量处理的数据越多,但 RAM 占用与停止延迟也越大; - 软中断开销:每个 ADC 数据块触发一次软中断 1,编码完成后触发一次软中断 2,中断频率 = ADC 块率,属可接受的轻量调度开销;
- 文件同步:
stop_encode末尾的vfs_ioctl(FS_IOCTL_FILE_SYNC)是确保录音数据落盘的关键,必须在卸载中断之后、释放句柄之前完成。
扩展点
新增编码格式
- 实现
ENC_OPS的三个方法(need_buf/open/run); - 在 audio_enc_api.h 中仿照
get_ima_code_ops声明并导出获取函数; - 在上层
enc_obj创建时把obj->enc_ops指向新操作集。
管理层(encoder_api.c)完全不用改动——这就是策略模式带来的扩展性。仓库中 sdk/app/bsp/common/encoder/list/ 下的 a_encoder.c、mp3_encoder.c、ump3_encoder.c 即按此模式组织的编码器实例列表(对应 a_encoder.h、mp3_encoder.h)。
自定义数据源/目的地
EN_FILE_IO.priv 是任意指针,input_data / output_data 可指向任意存储介质。当前实现写 NOR Flash,理论上可替换为 SD 卡、网络等,只需提供相应回调。
写盘前钩子
wfil_soft2_isr_hook(enc_obj *hdl) 为写文件软中断预留钩子,可用于喂狗或统计写入量(当前为空实现,见 encoder_api.c)。
测试与验证路径
仓库中与编码器直接相关的可执行/验证材料包括:
- 预编译编码库(
lib_mp2_encode.a、lib_mp2standard_encode.a)——MP2 系列编码器的二进制交付物,编码结果正确性由库内部保证; sdk/app/bsp/common/encoder/list/下各编码器实例源码(a_encoder.c/mp3_encoder.c/ump3_encoder.c)——可通过替换enc_ops做格式对比测试;- 录音应用
sdk/app/src/voice_toy/toy_record/enc_in_norfs.c——演示"编码器 + NOR Flash 文件"的完整用法,是验证encoder_io/stop_encode时序的最直接样例。
实际验证方法:录音后回读文件,用 PC 端解码器(IMA ADPCM / MP2)校验码流帧头与采样率/块大小是否与 ENC_DATA_INFO 配置一致。
相关链接
- audio_enc_api.h — 编码器 API 契约(EN_FILE_IO / ENC_DATA_INFO / ENC_OPS / 编码器获取函数)
- encoder_mge.h — 编码器管理对象 enc_obj、状态标志与软中断调度宏
- encoder_api.c — 编码器管理层实现(初始化/启动/停止/中断回调)
- audio_enc_api.h 编码器列表 — IMA/MP3/ump3 编码器实例目录
- 相邻主题:音频解码与 DAC 播放通路、音效处理(
sound_effect_api.h)、ANS 降噪模块(ans_api.h)——详见各自目录页