API GUIDE / ICODEC / MIDDLEWARE
ICODEC Middleware 接口
audio mid api
1. 文档范围
本文档定义 G32S10 SDK Audio middleware 的正式接口,面向使用 Audio Core 完成录音、 放音和循环采集的应用、BSP 和测试代码。
本文档以当前 SDK 源码为准:
| 类型 | 路径 |
|---|---|
| 应用公开接口 | code/middleware/audio/include/audio/audio.h |
| 可选零拷贝接口 | code/middleware/audio/include/audio/audio_ext.h |
| 公共类型 | code/middleware/audio/include/audio/audio_types.h |
| 公共音频格式与 DAI 类型 | code/middleware/audio/include/im_audio_dai_types.h |
| 板级设备描述类型 | code/middleware/audio/include/im_audio_device.h |
| PCM buffer 描述 | code/middleware/audio/include/im_audio_buffer_types.h |
| Audio Core 实现 | code/middleware/audio/core/ |
| Basic Stream 实现 | code/middleware/audio/audio_stream/ |
| Controller adapter | code/middleware/audio/audio_adapter/ |
| 构建配置 | code/middleware/audio/ 下的各级 Kconfig |
| 示例 | code/middleware/audio/example/ |
补充说明:
- 普通应用只需要包含
audio/audio.h。需要直接消费 DMA ring 的算法可以额外包含audio/audio_ext.h。 im_audio_controller_*、im_audio_dma_transport_*、im_aic_adapter_*、im_pwma_adapter_*和im_audio_stream_*是 BSP、controller adapter 和 transport 的集成接口,不是普通应用的业务接口。- Audio Core 使用调用方提供的 PCM ring,不负责分配或释放 PCM 数据内存。
- 板级音频设备表由 BSP 提供。Audio Core 只写设备表中的
state、configured和active运行字段,不修改板级静态连接关系。
目录
- 1. 文档范围
- 2. 分层关系与依赖
- 3. 构建配置与文件组织
- 4. 资源模型与公开宏
- 5. 公开类型
- 6. 生命周期与状态机
- 7. Audio Core 应用接口
- 8. 零拷贝扩展接口
- 9. 事件、位置与错误恢复
- 10. 并发、所有权与实时约束
- 11. BSP 与移植集成接口
- 12. 典型用法
- 13. 示例程序与构建
- 14. 常见错误码
2. 分层关系与依赖
Application / Test
|
| audio/audio.h
| audio/audio_ext.h
v
Audio Core
|
v
Basic Audio Stream
|
+----------------------+
| |
v v
Controller Adapter DMA/Transport
| |
+----------+-----------+
|
v
HAL / LL Drivers
|
v
AIC / PWMA / Codec Hardware
Audio Core 的职责:
- 维护全局初始化状态和固定大小的 stream 对象池。
- 根据 BSP 设备表初始化 controller、codec 和 transport。
- 管理一条 device 对应的一条方向明确的 stream。
- 提供应用可移植的 open/start/read/write/wait/stop/close 接口。
- 将 controller 周期完成事件转换为可等待的 Audio Core 事件。
- 提供时间戳、播放延迟、增益、静音和零拷贝访问。
Audio Core 不负责:
- 自动创建业务线程。
- 自动进行 PCM 文件解析、重采样或格式转换。
- 为应用分配 PCM ring。
- 在后台自动填满播放 ring。
- 在 XRUN 后自动重启业务流。
主要依赖如下:
| 依赖 | 用途 |
|---|---|
MIDDLEWARE_AUDIO_CORE |
Audio Core 注册表、stream 池和公开 API |
MIDDLEWARE_AUDIO_STREAM |
固定 period 的基本音频流 |
MIDDLEWARE_AUDIO_ADAPTER |
controller 通用 adapter 接口 |
MIDDLEWARE_AUDIO_ADAPTER_AIC |
G32S10 AIC 硬件路径 |
MIDDLEWARE_AUDIO_ADAPTER_PWMA |
G32S10 PWMA 硬件路径 |
MIDDLEWARE_AUDIO_TRANSPORT_DMA |
DMA transport 边界 |
MIDDLEWARE_AUDIO_BACKEND_G32S10_PDMA |
G32S10 PDMA backend;按 period 使用软件分段 cyclic 搬运 |
MIDDLEWARE_DMA |
descriptor、channel 和 PDMA IRQ 管理 |
| Codec device | ES8311、ICODEC 等具体 codec |
| BSP audio board | 提供 controller、codec、DAI 和 transport 的设备表 |
3. 构建配置与文件组织
3.1 Kconfig
| 选项 | 默认值 | 说明 |
|---|---|---|
MIDDLEWARE_AUDIO_CORE |
n |
编译 Audio Core;自动选择 MIDDLEWARE_AUDIO_STREAM 和 OS_API。 |
MIDDLEWARE_AUDIO_CORE_MAX_STREAMS |
4 |
静态 stream 对象池大小,范围 1 到 16。 |
MIDDLEWARE_AUDIO_STREAM |
n |
编译基本音频流和固定 period ring 管理。 |
MIDDLEWARE_AUDIO_TRANSPORT_DMA |
n |
编译通用 DMA transport。 |
MIDDLEWARE_AUDIO_BACKEND_G32S10_PDMA |
n |
编译 G32S10 PDMA backend。 |
MIDDLEWARE_AUDIO_ADAPTER |
n |
编译 controller adapter 公共层。 |
MIDDLEWARE_AUDIO_ADAPTER_AIC |
n |
编译 AIC adapter,并选择 DRIVER_AIC。 |
MIDDLEWARE_AUDIO_ADAPTER_PWMA |
n |
编译 PWMA adapter,并选择 DRIVER_PWMA。 |
MIDDLEWARE_AUDIO_BOARD |
BSP 决定 | audio_init() 自动调用当前 BSP 的 board_audio_register()。 |
最小软件配置:
CONFIG_OS_API=y
CONFIG_MIDDLEWARE_AUDIO_CORE=y
CONFIG_MIDDLEWARE_AUDIO_BOARD=y
G32S10 芯片板使用 AIC、PWMA 和 PDMA 时通常由 BSP Kconfig 自动选择:
CONFIG_MIDDLEWARE_AUDIO_ADAPTER_AIC=y
CONFIG_MIDDLEWARE_AUDIO_ADAPTER_PWMA=y
CONFIG_MIDDLEWARE_AUDIO_TRANSPORT_DMA=y
CONFIG_MIDDLEWARE_AUDIO_BACKEND_G32S10_PDMA=y
3.2 文件职责
| 文件或目录 | 说明 |
|---|---|
include/audio/audio.h |
Audio Core 应用 API |
include/audio/audio_ext.h |
可选零拷贝 API |
include/audio/audio_types.h |
stream 配置、状态、事件、时间戳和延迟类型 |
include/im_audio_dai_types.h |
采样格式、DAI 格式和时钟角色 |
include/im_audio_device.h |
板级设备表和 audio_device_t |
include/im_audio_buffer_types.h |
PCM buffer 描述与 placement flags |
core/audio.c |
全局初始化、board 注册和 deinit |
core/audio_stream.c |
stream 池、生命周期、读写、事件和扩展 API |
audio_stream/im_audio_stream.c |
ring 几何、position 和基本流状态机 |
audio_stream/transport/ |
DMA transport 和 G32S10 PDMA backend |
audio_adapter/ |
controller、AIC 和 PWMA adapter |
example/main/main.c |
AIC + ES8311 录音、ES8311 回放和 PWMA 回放示例 |
应用包含方式:
#include "audio/audio.h"
需要零拷贝时:
#include "audio/audio_ext.h"
4. 资源模型与公开宏
4.1 stream 池
Audio Core 在静态内存中预留
CONFIG_MIDDLEWARE_AUDIO_CORE_MAX_STREAMS 个 stream 对象。该对象池只保存控制结构,
不包含 PCM ring 或 DMA descriptor。
同一个 audio_device_t 同一时刻最多只能打开一条 stream。重复打开同一 device 返回
IM_ERR_BUSY。
调用方关闭 stream 后,slot 立即回到 AUDIO_STREAM_STATE_CLOSED,可以再次使用。
4.2 PCM ring 所有权
PCM ring 由调用方分配并持有,audio_stream_open() 只保存 buffer 描述。ring 必须从
audio_stream_open() 成功返回后保持有效,直到 audio_stream_close() 返回。
最小 ring 大小:
memory_sample_bytes = 8-bit: 1
16-bit: 2
18/20/24/32-bit: 4
frame_bytes = memory_sample_bytes * channels
period_bytes = frame_bytes * period_frames
ring_bytes = period_bytes * period_count
data_bytes 必须不小于计算出的 ring_bytes。实际使用的 ring 只占 buffer 起始位置
的前 ring_bytes 字节,多余的尾部空间不会使用。
当前 G32S10 PDMA 路径还要求:
buffer.data按 64 byte cache line 对齐。period_bytes为 64 byte 的整数倍。period_count不小于 2。- buffer 对 CPU 和 DMA 均可见。
4.3 公开宏
| 宏 | 当前值 | 说明 |
|---|---|---|
AUDIO_WAIT_FOREVER_MS |
UINT32_MAX |
audio_stream_wait() 无限等待 |
AUDIO_CLOCK_DOMAIN_LOCAL_MTIME |
0 | 本 SoC 音频时间戳使用的 CLINT mtime 域 |
AUDIO_DIRECTION_PLAYBACK |
1 << 0 |
放音方向 |
AUDIO_DIRECTION_CAPTURE |
1 << 1 |
录音方向 |
AUDIO_DIRECTION_LOOP |
1 << 2 |
controller 本地循环采集方向 |
AUDIO_EVENT_PERIOD_DONE |
1 << 0 |
一个或多个 period 已完成 |
AUDIO_EVENT_XRUN |
1 << 1 |
播放欠载或录音溢出 |
AUDIO_EVENT_STATE_CHANGED |
1 << 2 |
XRUN/ERROR 导致的 stream 状态变化 |
AUDIO_EVENT_ERROR |
1 << 3 |
transport 或硬件错误 |
4.4 buffer flags
| flag | 说明 |
|---|---|
IM_AUDIO_BUFFER_FLAG_DMA_ACCESSIBLE |
调用方声明 buffer 可被 DMA 访问。 |
IM_AUDIO_BUFFER_FLAG_CACHEABLE |
调用方声明 buffer 使用 cacheable 内存。 |
IM_AUDIO_BUFFER_FLAG_PSRAM |
调用方声明 buffer 位于 PSRAM。 |
这些 flags 是调用方的 placement 提示,最终可访问性和 cache 一致性仍由 transport
backend 校验。即使填写了 DMA_ACCESSIBLE,地址或 period 对齐不合法时 open 仍会失败。
5. 公开类型
5.1 audio_stream_t
typedef struct audio_stream audio_stream_t;
不透明 stream 句柄。对象由 Audio Core 静态池持有,调用方不得定义、复制或修改对象内容。
所有 API 中的 audio_stream_t * 必须来自成功的 audio_stream_open()。
5.2 audio_stream_state_t
typedef enum {
AUDIO_STREAM_STATE_CLOSED = 0U,
AUDIO_STREAM_STATE_OPEN,
AUDIO_STREAM_STATE_CONFIGURED,
AUDIO_STREAM_STATE_PREPARED,
AUDIO_STREAM_STATE_RUNNING,
AUDIO_STREAM_STATE_XRUN,
AUDIO_STREAM_STATE_ERROR,
} audio_stream_state_t;
| 状态 | 说明 |
|---|---|
CLOSED |
slot 空闲,句柄无效。 |
OPEN |
stream 已分配但仍在完成配置。 |
CONFIGURED |
格式和 ring 已配置。 |
PREPARED |
transport 和 DMA 资源已准备好,可以 start。 |
RUNNING |
硬件流正在运行。 |
XRUN |
播放欠载或录音溢出。 |
ERROR |
不可继续运行,需要 stop/close 或重新初始化。 |
audio_stream_open() 成功返回的稳定状态是 PREPARED。
5.3 audio_stop_mode_t
typedef enum {
AUDIO_STOP_MODE_DROP = 0U,
} audio_stop_mode_t;
当前只支持立即停止并丢弃尚未播放的数据。需要听到最后一个样本时,应先等待播放位置
完成或根据 audio_stream_delay_get() 判断 ring 已排空,再调用 stop。
5.4 im_audio_stream_format_t
typedef struct {
im_audio_sample_rate_t sample_rate;
im_audio_sample_width_t sample_width;
uint8_t channels;
} im_audio_stream_format_t;
公共 Basic Stream 层接受 1 或 2 个通道。实际硬件是否支持目标采样率、位宽、通道数 和 DAI 格式由 controller 与 codec 决定。
支持的采样率枚举:
5512, 8000, 11025, 12000, 16000, 22050, 24000, 32000,
44100, 48000, 64000, 88200, 96000, 176400, 192000 Hz
支持的样本位宽:
| 位宽 | 内存容器 |
|---|---|
| 8 bit | 1 byte |
| 16 bit | 2 byte |
| 18 bit | 4 byte |
| 20 bit | 4 byte |
| 24 bit | 4 byte |
| 32 bit | 4 byte |
5.5 audio_stream_config_t
typedef struct {
im_audio_stream_format_t format;
uint32_t period_frames;
uint32_t period_count;
uint32_t min_start_frames;
} audio_stream_config_t;
| 字段 | 约束 |
|---|---|
format |
采样率必须是公开枚举值,通道数必须为 1 或 2。 |
period_frames |
必须大于 0;G32S10 PDMA 下 period byte 数必须为 64 的倍数。 |
period_count |
必须大于等于 2。 |
min_start_frames |
播放启动前最低 queued frames;0 表示一个 period,且不得超过 ring 总帧数。 |
5.6 audio_stream_open_config_t
typedef struct {
audio_stream_config_t stream;
im_audio_stream_buffer_t buffer;
} audio_stream_open_config_t;
buffer 必须是调用方持有的连续 PCM ring:
typedef struct {
void *data;
uint32_t data_bytes;
uint32_t flags;
} im_audio_stream_buffer_t;
5.7 audio_timestamp_t
typedef struct {
uint64_t monotonic_us;
uint64_t frame_index;
im_audio_sample_rate_t sample_rate;
uint32_t period_frames;
uint32_t sequence;
uint32_t clock_domain_id;
uint32_t flags;
} audio_timestamp_t;
| 字段 | 说明 |
|---|---|
monotonic_us |
最近完成 period 的时间戳,单位 us。 |
frame_index |
硬件完成帧位置。 |
sample_rate |
stream 采样率。 |
period_frames |
一个 period 的帧数。 |
sequence |
已完成 period 序号。 |
clock_domain_id |
时间戳所属时钟域。 |
flags |
discontinuity、estimated、XRUN 标志。 |
5.8 audio_delay_info_t
typedef struct {
uint32_t queued_frames;
uint32_t backlog_frames;
uint32_t buffer_level_frames;
uint32_t delay_us;
uint32_t flags;
} audio_delay_info_t;
当前三个帧数字段都表示软件已写入但硬件尚未完成的帧数。delay_us 使用 stream 的
采样率换算。该快照只对采样瞬间有效。
5.9 增益类型
typedef enum {
IM_AUDIO_GAIN_ANALOG = 0U,
IM_AUDIO_GAIN_DIGITAL,
} im_audio_gain_domain_t;
typedef enum {
IM_AUDIO_GAIN_UNIT_RAW = 0U,
IM_AUDIO_GAIN_UNIT_CENTI_DB,
} im_audio_gain_unit_t;
RAW 是 codec 寄存器码值,不是 dB 值或音量百分比。CENTI_DB 只有在
audio_stream_gain_capability_get() 返回 db_supported == true 时才可用。
6. 生命周期与状态机
closed slot
|
| audio_stream_open()
v
OPEN -> CONFIGURED -> PREPARED
|
| audio_stream_start()
v
RUNNING
|
+---- period events -> RUNNING
|
+---- underrun/overflow -> XRUN
|
+---- fatal transport error -> ERROR
audio_stream_stop() from RUNNING/XRUN/ERROR -> PREPARED
audio_stream_close() from any open state -> CLOSED
audio_deinit() requires CLOSED for every stream
audio_init() 是幂等的。重复调用已初始化 Audio Core 返回 IM_OK。
audio_deinit() 只有在所有 stream 都已 close 时才允许执行。存在任何 open stream 时
返回 IM_ERR_BUSY。
7. Audio Core 应用接口
7.1 audio_init
error_code_t audio_init(void);
初始化 Audio Core 和静态 stream 池。
当 CONFIG_MIDDLEWARE_AUDIO_BOARD=y 时,本函数调用当前 BSP 的
board_audio_register(),由 BSP 注册 AIC/PWMA controller、codec 和 transport。
返回:
| 返回值 | 条件 |
|---|---|
IM_OK |
初始化成功或已经初始化。 |
| BSP 注册错误 | board adapter、codec、transport 或 GPIO 初始化失败。 |
BSP 注册失败时 Audio Core 会回滚初始化标志和 stream 池,调用方可以在修复失败原因后 重试。
7.2 audio_deinit
error_code_t audio_deinit(void);
反初始化所有已注册音频 device,并清空 stream 池。
返回:
| 返回值 | 条件 |
|---|---|
IM_OK |
已反初始化,或当前本来就未初始化。 |
IM_ERR_BUSY |
仍有 stream 未关闭。 |
| BSP deinit 错误 | controller、codec 或 transport 释放失败。 |
deinit 失败时 Audio Core 保持已初始化状态,允许调用方修复后再次调用。
7.3 audio_is_initialized
bool audio_is_initialized(void);
返回 Audio Core 当前是否处于初始化完成状态。本接口不访问硬件。
7.4 audio_device_get
error_code_t audio_device_get(const char *name,
audio_device_t **device_out);
按板级设备名查找设备。name 必须与 audio_device_t.name 完全一致。
| 返回值 | 条件 |
|---|---|
IM_OK |
找到设备,device_out 指向板级静态记录。 |
IM_ERR_NOT_READY |
audio_init() 尚未完成。 |
IM_ERR_NOT_FOUND |
当前板级设备表中没有该名称。 |
IM_ERR_ALREADY_EXISTS |
当前板级设备表存在重复名称。 |
IM_ERR_INVALID_PARAM |
参数为空或设备名非法。 |
返回指针由 BSP 持有,应用不能释放或复制该设备记录。
7.5 audio_stream_open
error_code_t audio_stream_open(audio_device_t *device,
const audio_stream_open_config_t *config,
audio_stream_t **stream_out);
打开一个板级 device 对应的单向 stream。
参数:
| 参数 | 说明 |
|---|---|
device |
board_audio_config_get() 设备表中的一项,不能复制或修改。 |
config |
PCM 格式、period、ring 大小和启动预填充配置。 |
stream_out |
成功时返回 Audio Core 静态池中的 stream 句柄。 |
行为:
- 校验 Audio Core 已初始化。
- 校验 device 状态以及该 device 是否已有打开的 stream。
- 第一次打开 device 时初始化 controller、codec 和 transport。
- 配置硬件格式、DAI、PCM ring 和 DMA transport。
- 成功返回
AUDIO_STREAM_STATE_PREPARED。
返回:
| 返回值 | 条件 |
|---|---|
IM_OK |
stream 已准备好。 |
IM_ERR_NOT_READY |
Audio Core 未初始化或 device 状态不允许打开。 |
IM_ERR_INVALID_PARAM |
device、config、格式、ring 或周期参数非法。 |
IM_ERR_BUSY |
同一 device 已有 stream,或硬件资源被占用。 |
IM_ERR_NO_MEMORY |
stream 池已满或 ring 小于所需空间。 |
IM_ERR_UNSUPPORTED |
controller/codec 不支持该格式或 DAI 组合。 |
| HAL/transport 错误 | 硬件初始化、DMA 配置或中断资源配置失败。 |
正常情况下失败后 *stream_out == NULL。如果失败后的清理本身也失败,函数可能返回
一个 ERROR stream;调用方必须继续调用 audio_stream_close() 重试清理。
7.6 audio_stream_close
error_code_t audio_stream_close(audio_stream_t *stream);
停止并释放 stream 的 ring、transport 和硬件路径配置,使 slot 回到 CLOSED。
调用方应在 close 后把本地句柄置 NULL,不要再次使用旧句柄。
返回:
| 返回值 | 条件 |
|---|---|
IM_OK |
stream 已关闭。 |
IM_ERR_BUSY |
同一 controller 的 LOOP stream 仍引用该 playback。 |
| HAL/transport 错误 | 停止或资源释放失败,stream 保留用于重试 close。 |
7.7 audio_stream_start
error_code_t audio_stream_start(audio_stream_t *stream);
启动一个 PREPARED stream。
播放流要求在调用前已经写入至少 min_start_frames 个 PCM 帧。录音流没有预填充要求。
LOOP stream 要求它引用的 playback stream 已经完成配置并处于 PREPARED 或
RUNNING。允许先启动 LOOP,再启动 playback。
返回:
| 返回值 | 条件 |
|---|---|
IM_OK |
transport 和 controller 已启动。 |
IM_ERR_NOT_READY |
stream 不是 PREPARED,或播放 ring 未达到启动水位。 |
| HAL/controller 错误 | codec enable、controller start 或 transport start 失败。 |
7.8 audio_stream_stop
error_code_t audio_stream_stop(audio_stream_t *stream,
audio_stop_mode_t mode);
立即停止 stream,丢弃尚未播放的数据,并保留 stream 为 PREPARED,允许再次 start。
当前 mode 必须为 AUDIO_STOP_MODE_DROP。
如果停止的是 playback 且同一 controller 上存在 LOOP stream,Audio Core 先停止 LOOP。
7.9 audio_stream_read
error_code_t audio_stream_read(audio_stream_t *stream,
void *dst,
uint32_t max_frames,
uint32_t *frames_out,
audio_timestamp_t *timestamp_out);
从 capture 或 loop stream 的 ring 读取 PCM。
参数:
| 参数 | 说明 |
|---|---|
dst |
调用方目标缓冲区。 |
max_frames |
本次最多读取帧数。 |
frames_out |
实际读取帧数。 |
timestamp_out |
可选,返回第一个读取帧的元数据。 |
返回:
| 返回值 | 条件 |
|---|---|
IM_OK |
至少读取了一帧。 |
IM_ERR_NOT_READY |
当前没有可读帧,或 stream 已进入 XRUN。 |
IM_ERR_UNSUPPORTED |
对 playback stream 调用。 |
IM_ERR_INVALID_PARAM |
stream、目标地址或输出参数非法。 |
7.10 audio_stream_write
error_code_t audio_stream_write(audio_stream_t *stream,
const void *src,
uint32_t max_frames,
uint32_t *frames_out);
把 PCM 写入 playback ring 的空闲空间。
返回:
| 返回值 | 条件 |
|---|---|
IM_OK |
至少写入了一帧。 |
IM_ERR_NOT_READY |
ring 已满。 |
IM_ERR_UNSUPPORTED |
对 capture 或 loop stream 调用。 |
IM_ERR_INVALID_PARAM |
stream、源地址或输出参数非法。 |
调用方应使用 frames_out 推进输入位置,不能假定一次写入了 max_frames 帧。
7.11 audio_stream_state_get
error_code_t audio_stream_state_get(const audio_stream_t *stream,
audio_stream_state_t *state_out);
同步 Basic Stream 和 transport 的最新状态,再返回公开生命周期状态。
本接口适合在等待失败、读写返回异常或 stop 前确认 stream 状态。它不替代事件等待。
7.12 audio_stream_position_get
error_code_t audio_stream_position_get(const audio_stream_t *stream,
audio_timestamp_t *timestamp_out);
原子复制最近一次 hardware period 完成位置。
事件是 pending bit,可能被合并。需要准确知道跨过多少个 period 时,应比较相邻两次
读取的 sequence 和 frame_index,不要只统计 wait 返回次数。
7.13 audio_stream_gain_capability_get
error_code_t audio_stream_gain_capability_get(
const audio_stream_t *stream,
im_audio_gain_domain_t domain,
im_audio_gain_capability_t *capability_out);
查询当前 endpoint 支持的增益范围。若设备带 codec,Audio Core 查询 codec 能力; 若 controller 提供本地增益路径,则查询 controller adapter 的能力。
返回:
| 返回值 | 条件 |
|---|---|
IM_OK |
capability 已填充。 |
IM_ERR_UNSUPPORTED |
codec 和 controller 均未实现该方向/增益域。 |
IM_ERR_INVALID_PARAM |
stream 或输出参数非法。 |
raw_supported 和各 raw code 描述当前 endpoint 的原始硬件码值。只有
db_supported == true 时才能使用 centi-dB。
7.14 audio_stream_gain_set
error_code_t audio_stream_gain_set(audio_stream_t *stream,
im_audio_gain_domain_t domain,
im_audio_gain_unit_t unit,
int32_t gain_value);
设置模拟或数字增益。
stream 已运行时,codec 应立即应用新值;stream 尚未运行时,driver 可以缓存该值并在 enable 阶段恢复。
返回 IM_ERR_UNSUPPORTED 表示 codec 不支持该 path/domain/unit,超出 capability
范围时通常返回 IM_ERR_INVALID_PARAM。
ES8311 播放流可以在 AUDIO_STREAM_STATE_RUNNING 状态下动态调节音量,不需要先
stop、close 或重新打开 stream:
ret = audio_stream_gain_set(
stream,
IM_AUDIO_GAIN_DIGITAL,
IM_AUDIO_GAIN_UNIT_RAW,
gain_code);
ES8311 当前只声明 RAW 增益,gain_code 是 DAC 音量寄存器 0x32 的原始码值,
范围为 0..255,不是音量百分比。stream 正在运行时,接口直接更新 codec 寄存器;
stream 尚未运行时,driver 缓存新值并在 codec enable 时应用。
ES8311 的录音流使用 IM_AUDIO_GAIN_ANALOG + IM_AUDIO_GAIN_UNIT_RAW。
需要平滑音量变化时,应用应分多步修改 RAW code 并留出间隔,避免一次跨越过多码值
产生可闻的突变。
PWMA 没有 codec,但 controller adapter 提供 controller-local 动态增益路径。当前支持:
stream: IM_AUDIO_STREAM_PLAYBACK
domain: IM_AUDIO_GAIN_DIGITAL
unit: IM_AUDIO_GAIN_UNIT_RAW
range: 0x000..0x1FF
RAW 是完整的 9 位 AUDIO_VOLUME 寄存器码,方向位和增益字段都包含在内:
RAW = (VOLUME_DOWN << 8) | VOLUME_GAIN[7:0]
VOLUME_DOWN = RAW[8]
VOLUME_GAIN = RAW[7:0]
因此可以在同一条播放流运行期间切换方向:
0x000..0x00A -> VOLUME_DOWN=0,放大方向
0x100..0x1FF -> VOLUME_DOWN=1,衰减方向
放大方向仅允许 VOLUME_GAIN=0..10;传入 0x00B..0x0FF 时
audio_stream_gain_set() 返回 IM_ERR_INVALID_PARAM,避免高增益造成严重削波。
例如 0x000 为放大方向、VOLUME_GAIN=0;0x100 为衰减方向、
VOLUME_GAIN=0;0x10F 为衰减方向、VOLUME_GAIN=15。由于
VOLUME_GAIN 同时包含 coarse 和 fine 字段,dB 步进并不均匀,不能把整个
RAW 值当作线性音量百分比。
播放中调用:
ret = audio_stream_gain_set(
pwma_stream,
IM_AUDIO_GAIN_DIGITAL,
IM_AUDIO_GAIN_UNIT_RAW,
gain_code);
adapter 写入 AUDIO_VOLUME shadow 后立即执行 AUDIO_UPDATE,正在运行的 DMA 波形
会应用新音量和方向。初始板级 volume_down 只决定设备初始化时的音量方向;运行时
gain_set 以传入 RAW 的方向位为准。
PWMA 的持久音量仍使用 BSP 的 board_audio_set_pwma_param(),并要求 PWMA 处于
RESET。动态接口只修改当前硬件音量,PWMA deinit/reinit 后会恢复板级持久配置。
7.15 audio_stream_mute_set
error_code_t audio_stream_mute_set(audio_stream_t *stream,
bool mute);
设置当前 stream 对应方向的静音状态。并非所有 codec 都支持 capture mute;不支持时
返回 IM_ERR_UNSUPPORTED。
7.16 audio_stream_delay_get
error_code_t audio_stream_delay_get(const audio_stream_t *stream,
audio_delay_info_t *delay_out);
返回 playback ring 的当前排队帧数和预计延迟。
返回:
| 返回值 | 条件 |
|---|---|
IM_OK |
snapshot 已填充。 |
IM_ERR_UNSUPPORTED |
stream 不是 playback。 |
IM_ERR_NOT_READY |
stream 尚未进入 PREPARED 或 RUNNING。 |
IM_ERR_INVALID_PARAM |
stream 或输出参数非法。 |
本接口可用于播放结束前等待 ring 排空,避免最后一个 period 被 DROP。
7.17 audio_stream_wait
error_code_t audio_stream_wait(audio_stream_t *stream,
uint32_t event_mask,
uint32_t timeout_ms,
uint32_t *events_out);
等待一个或多个 Audio Core 事件。
参数:
| 参数 | 说明 |
|---|---|
event_mask |
AUDIO_EVENT_* 的按位组合,不能为 0。 |
timeout_ms |
超时时间;AUDIO_WAIT_FOREVER_MS 表示永久等待。 |
events_out |
返回并消费本次命中的 pending events。 |
返回:
| 返回值 | 条件 |
|---|---|
IM_OK |
至少一个请求事件已经发生。 |
IM_ERR_TIMEOUT |
超时或 timeout_ms == 0 时没有 pending event。 |
IM_ERR_BUSY |
另一个线程正在等待同一 stream。 |
IM_ERR_NOT_READY |
当前上下文不能取得 SDK thread handle。 |
IM_ERR_UNSUPPORTED |
裸机模式请求永久等待。 |
IM_ERR_INVALID_PARAM |
event mask 或输出参数非法。 |
本接口只能在 thread context 调用。同一 stream 同一时刻只允许一个等待线程。
8. 零拷贝扩展接口
零拷贝接口直接返回 ring 中一段连续 view,适合算法原地处理数据。view 跨越 ring 回绕点时会自动截断到当前连续段。
8.1 audio_stream_read_view_t
typedef struct {
const void *data;
uint32_t frame_count;
uint32_t byte_count;
audio_timestamp_t timestamp;
} audio_stream_read_view_t;
只读 capture view。data 在 release 前有效。
8.2 audio_stream_write_view_t
typedef struct {
void *data;
uint32_t frame_count;
uint32_t byte_count;
audio_timestamp_t timestamp;
} audio_stream_write_view_t;
可写 playback view。调用方必须填满整个 view 后 commit,不能部分提交。
8.3 audio_stream_read_acquire
error_code_t audio_stream_read_acquire(audio_stream_t *stream,
uint32_t max_frames,
audio_stream_read_view_t *view_out);
借出一段连续的 capture/loop ring。
约束:
- 每个 stream 同时最多一个未释放 view。
- 获取前缓存访问已经同步。
- 下一次 acquire、stop 或 close 之前必须调用 release。
- 不得保存 view 指针并在 release 后继续访问。
返回 IM_ERR_BUSY 表示已有未释放 view,IM_ERR_NOT_READY 表示当前没有数据。
8.4 audio_stream_read_release
error_code_t audio_stream_read_release(audio_stream_t *stream);
释放此前 acquire 的整个 capture view,并推进软件读取位置。
8.5 audio_stream_write_acquire
error_code_t audio_stream_write_acquire(audio_stream_t *stream,
uint32_t max_frames,
audio_stream_write_view_t *view_out);
借出 playback ring 的一段连续空闲区域。
返回的 timestamp 是 view 第一帧预计被硬件输出的时间,并带有
TIMESTAMP_ESTIMATED 标志。
8.6 audio_stream_write_commit
error_code_t audio_stream_write_commit(audio_stream_t *stream);
提交此前 acquire 的完整 playback view。调用方应先把整个 byte_count 填满。
commit 前执行必要的 cache clean,使 DMA 能观察到新 PCM 数据。
9. 事件、位置与错误恢复
9.1 pending events
AUDIO_EVENT_* 是 pending bit,不是计数。若应用处理较慢,多个 period 和 XRUN 事件
可能在一次 wait 中合并。
当一个 event 同时包含:
AUDIO_EVENT_PERIOD_DONE | AUDIO_EVENT_XRUN
说明至少一个 period 完成,但播放或录音已经越界。业务应立即停止继续依赖 ring,
读取 audio_stream_position_get() 和 audio_stream_state_get() 后执行恢复。
9.2 timestamp flags
| flag | 说明 |
|---|---|
TIMESTAMP_ESTIMATED |
时间由最近的硬件完成点投影,不是真实 IRQ 时间。 |
DISCONTINUITY |
transport 检测到时间或 position 不连续。 |
XRUN |
position 所属流发生 XRUN。 |
9.3 XRUN 恢复
推荐步骤:
- 调用
audio_stream_state_get()确认状态。 - 调用
audio_stream_stop(stream, AUDIO_STOP_MODE_DROP)。 - 重新填充播放 ring,至少达到
min_start_frames。 - 再次调用
audio_stream_start()。
Audio Core 不会自动补齐缺失 PCM。录音路径发生 overflow 后,缺失帧无法恢复,只能 丢弃当前一段并重新开始采集。
9.4 正常播放结束
audio_stream_stop() 使用 DROP 语义。若必须完整播出最后一个 period,应:
- 写完全部 PCM。
- 使用
audio_stream_delay_get()等待queued_frames降到 0,或等待 position 到达预期总帧数。 - 再调用 stop 和 close。
10. 并发、所有权与实时约束
10.1 上下文约束
| 接口类别 | 可调用上下文 |
|---|---|
audio_init() / audio_deinit() |
task/thread context |
| open/close/start/stop | task/thread context |
| read/write/acquire/release/commit | task/thread context |
| gain/mute/delay/position/state | task/thread context;position/state只做短临界区访问 |
audio_stream_wait() |
SDK OS thread context |
| transport 中断回调 | ISR/IRQ context |
所有 PCM copy、DMA 配置、codec I2C 访问和错误日志都不应在 ISR 中由应用执行。
10.2 并发所有权
同一 stream 的 close、stop、read/write 和零拷贝 acquire 必须由业务串行化,不应由 多个线程同时操作。
不同 device 的 stream 可以由不同线程并发处理,但 controller/codec 的共享资源仍由 Audio Core 和 adapter 管理。应用不得绕过 Audio Core 直接修改设备表中的运行字段。
PCM ring 在 stream 生命周期内由调用方持有。close 返回后 Audio Core 不再访问该 ring。
10.3 音频回调
im_audio_stream_notify_t 是 Basic Stream/transport 集成层的 IRQ-safe 回调。该回调
不能阻塞、不能复制 PCM、不能执行算法,也不能改变 stream 或 controller 状态。
普通应用应优先使用 audio_stream_wait(),不建议直接注册 Basic Stream notifier。
11. BSP 与移植集成接口
11.1 音频对象分层与接口归属
| 层级/前缀 | 公开头文件 | 主要职责 | 普通应用是否调用 |
|---|---|---|---|
Audio Core,audio_* |
audio/audio.h、audio/audio_ext.h |
stream 生命周期、PCM 读写、事件、增益和零拷贝 | 是 |
Shared types,audio_*_t、im_audio_*_t |
audio/audio_types.h、im_audio_dai_types.h |
格式、DAI、device、buffer、事件和时间戳 | 是,仅使用类型 |
Controller adapter,im_audio_controller_* |
im_audio_controller_adapter.h |
统一 controller 的 init/configure/start/stop 能力 | 否 |
AIC adapter,im_aic_adapter_* |
audio_adapter/im_aic_adapter.h |
把 HAL AIC 映射成 controller 对象,管理 AIC 状态 | 否,BSP/集成使用 |
PWMA adapter,im_pwma_adapter_* |
audio_adapter/im_pwma_adapter.h |
把 HAL PWMA 映射成 controller 对象,管理 PWMA 参数 | 否,BSP/集成使用 |
Basic Stream,im_audio_stream_* |
im_audio_stream.h |
固定 period ring、software position、read/write 和状态机 | 否 |
Transport provider,im_audio_stream_transport_t |
im_audio_transport.h |
提供 attach/detach 和传输模式描述 | 否 |
DMA transport,im_audio_dma_transport_* |
audio_stream/transport/im_audio_dma_transport.h |
连接 controller endpoint、Basic Stream 和平台 DMA backend | 否 |
| G32S10 PDMA backend | audio_stream/transport/im_g32s10_pdma_backend.h |
按 period 启动普通 DMA transfer,完成后启动下一 period | 否 |
BSP audio board,board_audio_* |
当前 board 的 board_audio.h |
注册 controller、codec、transport 并提供静态 device table | 仅获取设备表并设置板级策略 |
数据路径可以概括为:
audio_stream_read/write()
|
v
im_audio_stream_* 固定 period ring
|
v
im_audio_dma_transport_*
|
v
im_audio_dma_backend_ops_t
|
v
im_g32s10_pdma_backend_t
|
v
im_dma_t -> G32S10 PDMA -> AIC/PWMA FIFO
Controller adapter 位于 DMA endpoint 一侧,负责提供 FIFO 地址、request ID、帧格式和
controller 生命周期;DMA transport 负责 PCM ring 与 FIFO 之间的周期搬运。二者通过
im_audio_controller_dma_endpoint_get() 对接。
11.2 板级设备表
BSP 必须提供:
error_code_t board_audio_register(void);
const board_audio_config_t *board_audio_config_get(void);
board_audio_register() 在 audio_init() 中调用,负责注册:
- AIC 和/或 PWMA controller adapter。
- ES8311、ICODEC 或其他 codec device。
- 每个方向对应的 DMA transport。
- 外部功放、codec reset 或 GPIO 使能资源。
board_audio_config_get() 返回静态 audio_device_t 数组。
board_audio_set_aic_mode()
G32S107 SK full BSP 提供:
error_code_t board_audio_set_aic_mode(
board_audio_aic_mode_t mode);
该接口在 audio_init() 使用的 AIC connection policy 和 device table 之间切换:
| mode | AIC policy | device table |
|---|---|---|
BOARD_AUDIO_AIC_MODE_ES8311_PLAYBACK |
AIC TX/RX 均为 master,外部 ES8311 为 slave | ES8311 playback、ES8311 capture、PWMA playback |
BOARD_AUDIO_AIC_MODE_ICODEC_CAPTURE |
AIC TX master、AIC RX slave,内部 ICOD 使用 AIC 提供的共享时钟 | ICOD capture、AIC LOOP reference、PWMA playback |
前置条件:
- 必须在
audio_init()之前,或在前一次audio_deinit()成功之后调用。 - AIC 必须处于 RESET,不能有 active stream。
- AIC 已初始化或仍有打开 stream 时返回
IM_ERR_BUSY。 BOARD_AUDIO_AIC_MODE_ICODEC_CAPTURE需要 BSP 配置CONFIG_BOARD_G32S107_SK_FULL_8910_ICODEC_CAPTURE=y;未启用时返回IM_ERR_UNSUPPORTED。
该接口是板级策略接口,不属于通用 Audio Core API。调用后 device 索引可能改变,
应用必须重新调用 board_audio_config_get() 并按 direction/codec 选择设备。
11.3 audio_device_t
typedef struct audio_device {
const char *name;
im_audio_controller_t *controller;
audio_codec_device_t *codec;
audio_direction_t direction;
const im_audio_dai_bus_config_t *dai_bus;
const im_audio_stream_transport_t *transport;
uint32_t clock_domain_id;
audio_device_state_t state;
bool configured;
bool active;
} audio_device_t;
| 字段 | 归属 |
|---|---|
name |
BSP 静态配置;表内必须非空且唯一。 |
controller |
BSP 静态资源 |
codec |
BSP 静态资源;controller-only device 为 NULL |
direction |
BSP 静态配置 |
dai_bus |
BSP 静态配置;PWMA 等 controller-only path 为 NULL |
transport |
BSP 静态资源 |
clock_domain_id |
BSP 静态配置 |
state |
Audio Core 管理 |
configured |
Audio Core 管理 |
active |
Audio Core 管理 |
应用可以只读遍历设备表,根据 direction 和 codec 选择设备。应用不得复制活动
device 或修改 state、configured、active。
应用也可以按设备名查找:
audio_device_t *capture_device;
error_code_t ret;
ret = audio_device_get("es8311-capture", &capture_device);
audio_device_get() 返回板级表中的借用指针。找不到名称时返回 IM_ERR_NOT_FOUND;
板级表存在重复名称时返回 IM_ERR_ALREADY_EXISTS。
11.4 DAI 配置
im_audio_dai_bus_config_t 描述:
- I2S、left justified、right justified、PCM A 或 PCM B。
- codec 侧 BCLK/LRCK 是 master 还是 slave。
- 时钟极性。
- 左右声道顺序。
- codec clock 是否来源于 SoC 音频时钟域。
该配置属于 board policy,不是单次 open 的可变参数。应用中需要切换 DAI 格式时,应 使用 BSP 提供的专用接口,并严格在 AIC 未运行、未配置状态下操作。
11.5 Controller adapter
im_audio_controller_* 是 controller 通用对象接口:
| 接口 | 用途 |
|---|---|
im_audio_controller_init() |
初始化 controller,进入 READY。 |
im_audio_controller_deinit() |
释放 controller。 |
im_audio_controller_stream_configure() |
配置无 DAI 的 controller stream。 |
im_audio_controller_dai_configure() |
配置带串行 DAI 的 stream。 |
im_audio_controller_stream_start() |
启动指定 stream。 |
im_audio_controller_stream_stop() |
停止指定 stream。 |
im_audio_controller_stream_post_stop() |
transport 停止后执行 controller 清理。 |
im_audio_controller_dma_endpoint_get() |
取得 transport 使用的 FIFO DMA endpoint。 |
im_audio_controller_fifo_threshold_configure() |
配置 FIFO service threshold。 |
这些接口由 Audio Core、BSP 和 adapter 实现使用,应用不应直接调用。
11.6 DMA transport
im_audio_dma_transport_* 把一个 controller DMA endpoint 绑定到 Basic Stream:
| 接口 | 用途 |
|---|---|
im_audio_dma_transport_register() |
创建 controller stream 对应的持久 transport。 |
im_audio_dma_transport_attach() |
把 transport 附加到一次 open 的 stream。 |
im_audio_dma_transport_detach() |
在 stream 释放后解绑 transport。 |
transport backend 的 prepare/start/stop/release 管理 DMA transfer 和 channel。
read_sync 在 CPU 读取 capture ring 前使 cache 一致,write_sync 在 DMA 读取
playback ring 前 clean cache。
im_audio_stream_transport_t 由 BSP 静态持有,包含:
| 字段 | 说明 |
|---|---|
ops |
transport provider 的 attach/detach 回调 |
context |
transport 或 DMA backend 的上下文 |
transfer_mode |
当前音频路径使用的传输模式 |
Audio Core 在 open 时调用 ops->attach(),close 时调用 ops->detach()。transport
对象不能由多个同时打开的 stream 共用。
11.7 Basic Stream
im_audio_stream_* 提供 Audio Core 以下的固定 period ring 和状态机。普通应用不直接
使用,原因是该层要求调用方自行编排 controller、transport、buffer、notifier 和错误
恢复。
主要接口:
| 接口 | 用途 |
|---|---|
im_audio_stream_layout_calculate() |
在不修改 stream 的情况下计算 frame/period/ring 大小。 |
im_audio_stream_attach() |
将调用方对象绑定到方向和一个硬件上下文。 |
im_audio_stream_configure() |
设置 PCM 格式、period 和启动水位。 |
im_audio_stream_buffer_attach() |
绑定调用方 PCM ring。 |
im_audio_stream_prepare() |
配置 transport 并进入 PREPARED。 |
im_audio_stream_start() |
检查播放预填充并启动 transport。 |
im_audio_stream_stop() |
停止 transport 并回到 PREPARED。 |
im_audio_stream_read/write() |
在 ring 与调用方内存之间复制 PCM。 |
im_audio_stream_read_acquire() / im_audio_stream_write_acquire() |
借出 ring 连续 view。 |
im_audio_stream_read_release() / im_audio_stream_write_commit() |
归还 capture view 或提交 playback view。 |
im_audio_stream_unprepare() |
释放 transport,回到 CONFIGURED。 |
im_audio_stream_transport_period_complete() |
transport 向 stream 提交周期完成事件。 |
im_audio_stream_transport_error() |
transport 向 stream 提交致命错误。 |
BSP 或新 controller 集成可以复用该层,但必须遵守:
period_count >= 2。- PCM buffer 至少覆盖计算出的 ring 大小。
- 在 prepare 前配置 controller 和 DAI。
- transport 回调只报告周期进度和错误。
- stop 后由 transport 和 controller 按顺序释放资源。
11.8 G32S10 PDMA backend
im_g32s10_pdma_backend_t 是 im_audio_dma_backend_ops_t 的 G32S10 平台实现。
BSP 持有 backend 对象,在注册 transport 前调用:
error_code_t im_g32s10_pdma_backend_init(
im_g32s10_pdma_backend_t *backend);
backend 的公开状态对象包含:
| 字段 | 说明 |
|---|---|
dma |
本次 stream 使用的 im_dma_t 实例。 |
period_cb / error_cb |
周期完成和终端错误回调。 |
event_context |
回调使用的 transport 上下文。 |
buffer / buffer_bytes |
调用方 PCM ring。 |
period_bytes / period_count |
单个 period 大小和 ring 内 period 数量。 |
segment_index |
当前搬运的 period。 |
peripheral_address |
controller FIFO 的 DMA 地址。 |
direction |
memory-to-peripheral 或 peripheral-to-memory。 |
request_id |
AIC/PWMA DMA request ID。 |
initialized/opened/running |
backend 生命周期状态。 |
backend 执行:
- 校验 controller endpoint、direction、request unit 和 PCM buffer。
- 通过
im_dma_request()申请对应 AIC/PWMA request。 - AIC 和 PWMA 每次调用
im_dma_start()搬运一个 period。 - transfer complete 后切换
segment_index,立即启动下一 period,并调用period_cb。 - DMA 或重新启动失败时调用
error_cb。 - 在 CPU 读取 capture/loop ring 前执行 cache invalidate,在 DMA 读取 playback ring 前 执行 cache clean。
- stop 和 release 时停止 DMA、回收资源并释放 request。
im_g32s10_pdma_backend_t 不能由多个 transport 或 stream 同时复用。PDMA IRQ 由
im_dma 统一注册和分发,backend 不直接注册 PDMA IRQ。
Audio backend 的 software cyclic 完成条件是一次普通 DMA transfer 完成。backend 使用
segment_index 表示当前 period,通过 period_cb 上报完成索引和时间戳,再启动
ring 中的下一段。generic transport 将每次 callback 作为一个 period 处理,Basic
Stream 固定前进一个 period。该路径不使用 DIP.CDOA 推导 period index。
LOOP stream 的 peripheral_address 指向 AICLR。AIC controller 打开 LOOP FIFO
request 后,backend 使用与 capture 相同的 peripheral-to-memory 方式读取 reference
PCM。
12. 典型用法
12.1 播放
#include "audio/audio.h"
#define PERIOD_FRAMES 160U
#define PERIOD_COUNT 4U
static int16_t s_play_ring[PERIOD_FRAMES * PERIOD_COUNT]
__attribute__((aligned(64)));
static error_code_t play_pcm(const audio_device_t *device,
const int16_t *pcm,
uint32_t frames)
{
audio_stream_open_config_t open_config = {
.stream = {
.format = {
.sample_rate = IM_AUDIO_SAMPLE_RATE_16000_HZ,
.sample_width = IM_AUDIO_SAMPLE_WIDTH_16_BITS,
.channels = 1U,
},
.period_frames = PERIOD_FRAMES,
.period_count = PERIOD_COUNT,
.min_start_frames = PERIOD_FRAMES * 2U,
},
.buffer = {
.data = s_play_ring,
.data_bytes = sizeof(s_play_ring),
.flags = IM_AUDIO_BUFFER_FLAG_DMA_ACCESSIBLE |
IM_AUDIO_BUFFER_FLAG_CACHEABLE,
},
};
audio_stream_t *stream = NULL;
uint32_t written = 0U;
error_code_t ret;
ret = audio_stream_open((audio_device_t *)device,
&open_config,
&stream);
if (ret != IM_OK) {
return ret;
}
if (frames < open_config.stream.min_start_frames) {
ret = IM_ERR_INVALID_PARAM;
goto out;
}
while (written < open_config.stream.min_start_frames) {
uint32_t count = 0U;
ret = audio_stream_write(stream,
&pcm[written],
open_config.stream.min_start_frames - written,
&count);
if ((ret != IM_OK) || (count == 0U)) {
goto out;
}
written += count;
}
ret = audio_stream_start(stream);
if (ret != IM_OK) {
goto out;
}
while (written < frames) {
uint32_t events = 0U;
uint32_t count = 0U;
ret = audio_stream_wait(stream, AUDIO_EVENT_PERIOD_DONE, 1000U,
&events);
if (ret != IM_OK) {
break;
}
ret = audio_stream_write(stream, &pcm[written],
frames - written, &count);
if (ret == IM_ERR_NOT_READY) {
ret = IM_OK;
continue;
}
if ((ret != IM_OK) || (count == 0U)) {
break;
}
written += count;
}
out:
(void)audio_stream_stop(stream, AUDIO_STOP_MODE_DROP);
(void)audio_stream_close(stream);
return ret;
}
12.2 录音
static error_code_t capture_pcm(const audio_device_t *device,
int16_t *pcm,
uint32_t total_frames)
{
audio_stream_open_config_t open_config = {
.stream = {
.format = {
.sample_rate = IM_AUDIO_SAMPLE_RATE_16000_HZ,
.sample_width = IM_AUDIO_SAMPLE_WIDTH_16_BITS,
.channels = 1U,
},
.period_frames = PERIOD_FRAMES,
.period_count = PERIOD_COUNT,
},
.buffer = {
.data = s_capture_ring,
.data_bytes = sizeof(s_capture_ring),
.flags = IM_AUDIO_BUFFER_FLAG_DMA_ACCESSIBLE |
IM_AUDIO_BUFFER_FLAG_CACHEABLE,
},
};
audio_stream_t *stream = NULL;
uint32_t captured = 0U;
error_code_t ret;
ret = audio_stream_open((audio_device_t *)device,
&open_config,
&stream);
if (ret != IM_OK) {
return ret;
}
ret = audio_stream_start(stream);
if (ret != IM_OK) {
goto out;
}
while (captured < total_frames) {
uint32_t events = 0U;
uint32_t count = 0U;
ret = audio_stream_wait(stream, AUDIO_EVENT_PERIOD_DONE, 1000U,
&events);
if (ret != IM_OK) {
break;
}
ret = audio_stream_read(stream, &pcm[captured],
total_frames - captured, &count, NULL);
if (ret == IM_ERR_NOT_READY) {
ret = IM_OK;
continue;
}
if ((ret != IM_OK) || (count == 0U)) {
break;
}
captured += count;
}
out:
(void)audio_stream_stop(stream, AUDIO_STOP_MODE_DROP);
(void)audio_stream_close(stream);
return ret;
}
12.3 查找设备
板级设备名使用字符串:
"es8311-playback"
"es8311-capture"
"pwma-playback"
"aic-loop"
应用按名称取得设备:
audio_device_t *capture;
audio_device_t *playback;
ret = audio_device_get("es8311-capture", &capture);
if (ret != IM_OK) {
goto fail;
}
ret = audio_device_get("es8311-playback", &playback);
if (ret != IM_OK) {
goto fail;
}
设备名在板级设备表内必须唯一。应用仍可直接遍历 board_audio_config_get();设备
索引属于板级实现细节,不应作为通用应用接口。
12.4 LOOP reference 与 AEC
LOOP 是一路 AUDIO_DIRECTION_LOOP stream。它的数据来自播放 TX FIFO 的内部参考路径,
通过 AICLR 读取,不是麦克风录音。
使用顺序:
audio_stream_open(playback_device, &playback_cfg, &playback);
audio_stream_open(loop_device, &loop_cfg, &loop);
audio_stream_open(capture_device, &capture_cfg, &capture);
audio_stream_write(playback, pcm, frames, &written);
audio_stream_start(loop);
audio_stream_start(playback);
audio_stream_start(capture);
audio_stream_read(loop, reference_pcm, period_frames,
&reference_frames, &reference_ts);
audio_stream_read(capture, microphone_pcm, period_frames,
µphone_frames, µphone_ts);
约束:
- playback、loop 和 capture 使用独立的 PCM ring。
- loop 的采样率、位宽和声道数必须与 playback 相同。
audio_stream_start(loop)调用前,playback 必须已经处于 PREPARED 或 RUNNING。- AEC 使用
reference_pcm和microphone_pcm,并按时间戳或 frame index 对齐。 reference_pcm是播放数字参考,microphone_pcm包含环境声和喇叭回声。
停止顺序:
audio_stream_stop(capture, AUDIO_STOP_MODE_DROP);
audio_stream_stop(loop, AUDIO_STOP_MODE_DROP);
audio_stream_stop(playback, AUDIO_STOP_MODE_DROP);
audio_stream_close(capture);
audio_stream_close(loop);
audio_stream_close(playback);
13. 示例程序与构建
Audio 示例位于:
code/middleware/audio/example/
当前示例演示:
audio_init()。- AIC + ES8311 录音 20 秒。
- 等待 2 秒。
- AIC + ES8311 回放录音。
- 等待 2 秒。
- PWMA 回放同一段录音。
audio_deinit()。
构建命令:
tools/imde/bin/imde use-defconfig code/middleware/audio/example/audio_example_defconfig
tools/imde/bin/imde --no-preset -D SDK_APP_DIR=code/middleware/audio/example configure --fresh
tools/imde/bin/imde --no-preset build
示例使用固定采样率、period 和 ring,应用于新产品时应根据 codec/controller capability 和目标延迟重新选择 period frames 与 period count。
14. 常见错误码
| 错误码 | 常见原因 | 处理方式 |
|---|---|---|
IM_ERR_INVALID_PARAM |
格式、period、buffer、event mask 或指针非法 | 检查参数和 buffer 几何 |
IM_ERR_NOT_READY |
Audio Core 未初始化、ring 无数据、播放未达到启动水位 | 检查生命周期和预填数据 |
IM_ERR_BUSY |
device 已打开、stream 有等待线程或仍有 LOOP 引用 | 先关闭/停止相关 stream |
IM_ERR_NO_MEMORY |
stream 池已满或 ring 小于计算需求 | 增大配置或释放其他 stream |
IM_ERR_UNSUPPORTED |
codec/controller 不支持该格式、方向、gain 或操作 | 查询 capability 或板级设备表 |
IM_ERR_TIMEOUT |
等待 period 或事件超时 | 检查时钟、DMA、IRQ、codec 和采样率 |
IM_ERR_IO |
PCM 数据搬运或底层 I/O 失败 | 检查 buffer 和 transport |
IM_ERR_HARDWARE |
transport 报硬件错误 | 停止并检查 controller/codec 寄存器 |
播放欠载和录音溢出通过 AUDIO_EVENT_XRUN 通知,不是独立的 return code。
Audio Core 的 return code 使用统一 error_code_t。错误发生时,应先读取
audio_stream_state_get() 和 audio_stream_position_get(),再根据状态决定 stop、
close 或完整重新执行 audio_deinit() / audio_init()。