2. 音频基础
在 Android Audio HAL 适配中,几乎所有问题最终都会落到“数据长什么样、以什么节奏流动、由谁搬运”这三个层面。声音本身是连续变化的模拟压力波,而 Android 音频框架、HAL、驱动和 DSP 处理的都是离散的数字样本。理解从模拟声波到 PCM 数据、再到音频格式封装的过程,是读懂 HAL 接口参数、排查爆音/杂音/无声/延迟问题的前提。
一、核心原理:声音如何被数字化
声音数字化的本质是“采样 + 量化 + 编码”。麦克风或线路输入把声压变化转换为连续电压信号,ADC(模数转换器)按固定时间间隔对电压进行采样,得到离散时间点上的幅度值;随后对幅度进行量化,用有限位数的二进制数表示;最后按约定格式排列成帧,形成 PCM 数据流。播放方向则相反:PCM 数据经 DAC 还原为模拟电压,再驱动扬声器或耳机发声。
这里有两个关键概念:采样率决定“每秒取多少个点”,位深决定“每个点用多少位表示幅度”。采样率越高,能还原的高频越多;位深越大,幅度分辨率越高,动态范围越大、量化噪声越低。二者共同决定单位时间的数据量和音质上限。
二、关键参数:采样率、位深、声道与 PCM
采样率常见取值包括 8kHz(窄带通话)、16kHz(宽带语音)、44.1kHz(CD)、48kHz(Android 默认媒体输出)、96kHz/192kHz(高解析)。Android 音频框架内部常以 48kHz 为主时钟,HAL 若声明支持其他采样率,需要处理重采样或由硬件直接支持。
位深常见为 16bit、24bit、32bit。16bit 动态范围约 96dB,24bit 约 144dB。Android HAL 中 PCM 数据常以 16bit 或 32bit 容器承载,注意“有效位深”和“存储位深”可能不同,例如 24bit 样本存放在 32bit 字中,低位补零或高位对齐,配置错误会导致音量异常或噪声。
声道决定帧内样本排列方式。单声道每帧 1 个样本,立体声每帧 2 个样本(L、R 交替),5.1/7.1 则按固定顺序排列。HAL 的 channel_mask 必须与数据排列一致,否则会出现左右声道错位、人声消失或环绕声异常。
PCM(Pulse Code Modulation,脉冲编码调制)是最基础的未压缩音频表示。它只描述“每个采样点的幅度”,不包含压缩、元数据或容器信息。Android Audio HAL 的 audio_stream_out / audio_stream_in 传输的核心就是 PCM 数据块。
三、音频格式与常见术语
PCM 是“裸数据”,实际文件或流通常需要容器和编码。WAV 是带 RIFF 头的 PCM 容器;FLAC 是无损压缩;AAC/MP3/Opus 是有损压缩;Android 中还有 AUDIO_FORMAT_PCM_16_BIT、AUDIO_FORMAT_PCM_32_BIT、AUDIO_FORMAT_PCM_FLOAT 等 HAL 格式枚举。适配时要区分“应用层格式”“框架格式”“HAL 格式”“硬件格式”,它们可能经过重采样、位深转换或声道映射。
| 术语 | 含义 | Android/HAL 中的典型值或影响 |
|---|---|---|
| 采样率 | 每秒采样点数 | 48kHz 默认;不匹配会重采样,增加延迟与 CPU 开销 |
| 位深 | 每个样本的二进制位数 | 16bit/24bit/32bit;错误对齐导致爆音或音量异常 |
| 声道数 / channel_mask | 帧内样本排列与声道位置 | 立体声、5.1;错位导致左右反相或人声丢失 |
| PCM | 未压缩脉冲编码调制数据 | HAL 读写的基本数据形态 |
| 帧 / frame | 同一时刻所有声道的样本集合 | 帧率等于采样率;缓冲区常以帧为单位 |
| 码率 | 单位时间数据量 | PCM 码率 = 采样率 × 位深 × 声道数 |
| 位深容器 | 样本存储宽度与有效位 | 24bit in 32bit 需明确对齐方式 |
四、案例与场景:一次“无声”问题的排查
某项目在 Android 设备上播放音乐无声,但通话正常。排查发现:应用输出 44.1kHz 立体声 16bit PCM,HAL 的 out_get_supported_formats 只声明了 48kHz 单声道 16bit。框架进行了重采样和声道下混,但 HAL 的 channel_mask 配置为立体声,导致数据被错误解释,最终输出静音。修正方式是在 HAL 中正确声明支持的采样率、声道掩码和格式,或让硬件支持 44.1kHz 立体声。
// HAL 中常见的格式与参数声明片段
static const struct audio_config {
uint32_t sample_rate; // 48000
audio_channel_mask_t channel_mask; // AUDIO_CHANNEL_OUT_STEREO
audio_format_t format; // AUDIO_FORMAT_PCM_16_BIT
};
// 若硬件仅支持单声道,应声明:
// channel_mask = AUDIO_CHANNEL_OUT_MONO
// 由框架完成下混,而不是在 HAL 中假装支持立体声
适配建议:HAL 声明的能力必须与硬件真实能力一致。不要为了“兼容”而虚报采样率、位深或声道数,否则框架不会做必要的转换,问题会以无声、爆音、变调、延迟等形式暴露。
五、误区与复盘
- 误区一:采样率越高越好。高采样率增加数据量和功耗,若硬件不支持还会引入重采样失真。应按场景选择:通话 16kHz、媒体 48kHz、高解析再考虑 96kHz。
- 误区二:位深只影响音量。位深影响动态范围和量化噪声,24bit 与 16bit 在低电平细节上差异明显;但错误对齐会让 24bit 数据变成噪声。
- 误区三:PCM 就是 WAV。PCM 是编码方式,WAV 是容器。HAL 处理的是 PCM 流,不关心 WAV 头。
- 误区四:声道数只是数量。声道顺序和掩码同样重要,5.1 的 L/R/C/LFE/LS/RS 顺序错误会导致声场混乱。
- 复盘方法:遇到音频异常时,先确认“应用格式 → 框架格式 → HAL 格式 → 硬件格式”这条链路上每一级的采样率、位深、声道掩码是否一致,再检查缓冲区帧数与周期,最后用
dumpsys media.audio_flinger或 HAL 日志验证实际数据流。
本节核心记忆:采样率决定时间分辨率,位深决定幅度分辨率,声道决定空间排列,PCM 是 HAL 传输的裸数据形态,音频格式是容器与编码的外衣。四者一致,音频链路才稳定。