4、实时音频处理基础:采样率、位深、帧长、缓冲区管理、乒乓缓冲机制
好,咱们进入第四章。这一章讲的是实时音频处理里最基础、也最绕不开的几个概念。说白了,就是数字音频的“三围”加上怎么管好数据流。我刚开始做音频驱动的时候,觉得这些参数不就是配几个寄存器嘛,后来被坑过几次才明白——这些参数选不对,后面降噪算法写得再好也白搭。
4.1 采样率与位深:数字音频的“分辨率”
先聊采样率。采样率就是每秒从连续信号里抽多少个样,单位是 Hz。根据奈奎斯特定理,采样率至少要是信号最高频率的两倍,才能无失真地重建。人耳能听到的上限大概是 20kHz,所以 CD 音质的 44.1kHz 就是这么来的。
在高通平台上,我常用的采样率有这么几种:
| 采样率 | 典型场景 | 奈奎斯特频率 |
|---|---|---|
| 8 kHz | 窄带语音通话 | 4 kHz |
| 16 kHz | 宽带语音、降噪算法常用 | 8 kHz |
| 44.1 kHz | CD 音质音乐 | 22.05 kHz |
| 48 kHz | 视频、专业音频 | 24 kHz |
位深呢,就是每个采样点用多少 bit 来表示。16 bit 有 65536 个量化等级,动态范围大概 96 dB。24 bit 能到 144 dB。做实时降噪的时候,我建议内部处理至少用 32 bit 浮点,避免累加误差。输出的时候再根据硬件能力转成 16 bit 或 24 bit。
4.2 帧长:算法处理的“时间切片”
帧长就是每次处理的数据块大小,单位是采样点数。比如帧长 256,在 16 kHz 采样率下,就对应 16 毫秒的音频。
帧长选多少,直接影响延迟和频率分辨率:
- 帧长短(如 128 点):延迟低,但频率分辨率差,FFT 的 bin 间隔大。
- 帧长长(如 512 或 1024 点):频率分辨率好,但延迟高,内存占用也大。
做实时降噪,我个人习惯用 256 或 512 点帧长。256 点延迟大概 16 ms,人耳基本感觉不到。512 点降噪效果更好,但延迟会到 32 ms 左右,通话场景就有点勉强了。
4.3 缓冲区管理:数据流的“蓄水池”
缓冲区就是一块内存,用来暂存音频数据。为什么需要它?因为音频采集和算法处理是异步的。硬件 DMA 往缓冲区里写数据,算法从缓冲区里读数据。如果没有缓冲区,数据一来就得马上处理,系统根本忙不过来。
缓冲区管理有几个关键参数:
- 缓冲区大小:通常等于帧长,或者帧长的整数倍。
- 缓冲区数量:至少 2 个,才能实现乒乓操作。
- 读写指针:记录当前数据写到哪里、读到哪里。
在高通平台上,音频缓冲区通常用 ADSP 侧的共享内存。我一般会申请 2 到 4 个缓冲区,每个大小等于帧长乘以字节数(比如 16 bit 就是 2 字节)。
// 缓冲区初始化示例
#define FRAME_SIZE 256
#define NUM_BUFFERS 4
#define BYTES_PER_SAMPLE 2 // 16 bit
int16_t audio_buffers[NUM_BUFFERS][FRAME_SIZE];
int write_index = 0;
int read_index = 0;
4.4 乒乓缓冲机制:避免数据“打架”
乒乓缓冲,说白了就是两个缓冲区轮流用。一个给硬件写数据,一个给算法读数据。写满一个就切换,读空一个也切换。这样读写互不干扰,不会出现“刚写一半就被读走”的情况。
我画个图帮你理解:
你看,乒乓缓冲的核心就是“交替使用”。硬件写一个,算法读另一个。写满就换,读空也换。这样永远不会冲突。
在高通平台上实现乒乓缓冲,我一般用两个指针来管理:
// 乒乓缓冲切换逻辑
int current_write_buf = 0; // 当前硬件写入的缓冲区索引
int current_read_buf = 1; // 当前算法读取的缓冲区索引
// 硬件中断回调:一帧数据采集完成
void audio_dma_callback() {
// 切换写入缓冲区
current_write_buf = (current_write_buf + 1) % NUM_BUFFERS;
// 通知算法有新数据
signal_algorithm();
}
// 算法处理函数
void process_audio() {
// 从当前读取缓冲区取数据
int16_t *input = audio_buffers[current_read_buf];
// 执行降噪处理
noise_reduction(input, output, FRAME_SIZE);
// 切换读取缓冲区
current_read_buf = (current_read_buf + 1) % NUM_BUFFERS;
}
4.5 高通平台上的实践要点
在高通平台上做实时音频处理,有几个地方要特别注意:
- DMA 配置:音频 DMA 通常支持循环模式和乒乓模式。我建议用乒乓模式,配合双缓冲区,硬件自动切换。
- 缓存一致性:ADSP 和 CPU 共享内存时,要注意缓存刷新。我踩过这个坑——数据写进去了,但 CPU 读的是缓存里的旧数据。
- 中断优先级:音频中断优先级要设高,避免被其他任务打断导致数据丢失。
好了,这一章的内容就这些。采样率、位深、帧长、缓冲区管理、乒乓缓冲——这些概念看起来简单,但它们是实时音频处理的基石。后面讲降噪算法实现的时候,你会发现每一步都离不开这些基础参数的选择。