简单记录一下Android系统中AudioFormat类中参数的定义,这个参数主要应用于AudioTrack[播放音频]和AudioRecord[录制音频],只要是介绍一下,偶尔忘记。
记录于此,方便自己查阅。
正文
AudioFormat 主要由三个关键参数组成,它们共同决定了一个音频“帧”(Frame)的大小和含义:
采样率 (Sample Rate)
含义:表示每秒对声音信号进行采样的次数,单位是赫兹 (Hz)。
说明:它描述了每秒每个声道的音频样本数量。
常见值:通常推荐使用 48kHz 或 44.1kHz,以避免系统频繁进行采样率转换,从而提高效率并减少音质损失。
如果设置的采样率与设备硬件不匹配,平台会自动进行转换,但这可能会导致性能开销。
编码格式 (Encoding)
含义:描述音频数据的位表示方式,即每个采样点占用多少比特,以及是整数还是浮点数。
主要类型:
线性 PCM (未压缩)
ENCODING_PCM_16BIT:最常用。16位有符号整数,范围 [-32768, 32767]ENCODING_PCM_8BIT:8位无符号整数,范围 [0, 255],精度较低ENCODING_PCM_FLOAT:32位浮点数,范围通常在 [-1.0, 1.0]。适合需要高精度处理的场景(如音频特效处理)ENCODING_PCM_24BIT_PACKED/ENCODING_PCM_32BIT:更高精度的整数格式
压缩音频 (Compressed)
如
ENCODING_AC3,ENCODING_DTS,ENCODING_MP3等。这些通常用于直接透传给支持解码的硬件(如 HDMI 连接的家庭影院),而不是由手机 CPU 解码
声道掩码 (Channel Mask)
这个分声道位置掩码 (Channel Position Mask)和声道索引掩码 (Channel Index Mask)介绍。
声道位置掩码
它定义了扬声器或麦克风的物理位置。
扬声器
CHANNEL_OUT_MONO:单声道
CHANNEL_OUT_STEREO:立体声(左 + 右)
CHANNEL_OUT_5POINT1:5.1 环绕声
原理:通过位运算组合,例如立体声 = CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT
等
麦克风
CHANNEL_IN_MONO:单声道录制(保证在所有设备上可用)。
CHANNEL_IN_STEREO :双声道录制
等
场景:使用手机的麦克风录制一段立体声音乐
AudioFormat positionBasedFormat = new AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(48000)
// 指定使用“前方左侧”和“前方右侧”的麦克风
.setChannelMask(AudioFormat.CHANNEL_IN_STEREO)
.build();声道索引掩码
声道索引掩码 (Channel Index Mask),主要用于 USB 音频设备等场景,当你不关心物理位置,只关心“第几个通道”时使用。
它是一个二进制数,从右往左数,第 N 位为 1,就代表选中了第 N 个通道(通道编号从 0 开始)。
通道 0 对应二进制的第 0 位 (...0001) //第一个通道
通道 1 对应二进制的第 1 位 (...0010) // 第二个通道
通道 2 对应二进制的第 2 位 (...0100) // 第三个通道
通道 3 对应二进制的第 3 位 (...1000) // 第四个通道
案例
假设你有一个4通道的USB麦克风,通道编号为 0, 1, 2, 3。
案例1:录制第1个和第3个通道的声音
根据上面知识,需要通道 0 和通道 2,对应的二进制是 0101。转换为十六进制就是 0x5。
AudioFormat format = new AudioFormat.Builder() .setChannelIndexMask(0x5) // 选中第1和第3个通道 .setEncoding(AudioFormat.ENCODING_PCM_16BIT) .setSampleRate(48000) .build();
案例2:录制全部4个通道的声音
根据上面知识,四个通道都需要,对应的二进制是 1111。转换为十六进制就是 0xf。
AudioFormat format = new AudioFormat.Builder() .setChannelIndexMask(0xf) // 选中1、2、3、4个通道 .setEncoding(AudioFormat.ENCODING_PCM_16BIT) .setSampleRate(48000) .build();
音频帧 (Audio Frame)
Audio Frame 对计算缓冲区大小至关重要:
定义:一个音频帧包含同一时刻所有声道的采样数据集合。
举例
立体声 (2声道) + 16位 PCM (2字节):1个帧 = 2 × 2 = 4字节。
单声道 (1声道) + 8位 PCM (1字节):1个帧 = 1 × 1 = 1字节。
小结
| 参数类别 | 常量名 | 说明 |
|---|---|---|
| 编码 | ENCODING_PCM_16BIT | 16位整型,兼容性好,音质尚可 |
ENCODING_PCM_FLOAT | 32位浮点型,动态范围大,适合音频处理 | |
ENCODING_PCM_8BIT | 8位整型,音质较差,一般不推荐 | |
| 声道 | CHANNEL_OUT_MONO | 单声道输出 |
CHANNEL_OUT_STEREO | 双声道立体声输出(最常用) | |
CHANNEL_IN_MONO | 单声道录制(保证在所有设备上可用) | |
CHANNEL_IN_STEREO | 双声道录制 |
使用 AudioFormat.Builder 来创建实例,这样代码更清晰且不易出错。
demo1
// 1. 使用声道位置掩码来配置 AudioFormat AudioFormat positionBasedFormat = new AudioFormat.Builder() .setEncoding(AudioFormat.ENCODING_PCM_16BIT) .setSampleRate(48000) // 指定使用“前方左侧”和“前方右侧”的麦克风 .setChannelMask(AudioFormat.CHANNEL_IN_STEREO) .build(); // 2. 创建 AudioRecord 对象 int minBufferSize = AudioRecord.getMinBufferSize( 48000, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT); AudioRecord audioRecord = new AudioRecord( MediaRecorder.AudioSource.DEFAULT, 48000, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize); // 3. 开始录音 audioRecord.startRecording(); // 4. 读取数据 // 因为是立体声 (2个声道),16位编码 (每个采样点2字节) // 所以每一帧音频数据的大小是 2 * 2 = 4 字节 byte[] buffer = new byte[1024]; int bytesRead = audioRecord.read(buffer, 0, buffer.length); // 此时,buffer 中的数据就是交替排列的:[左声道样本1, 右声道样本1, 左声道样本2, 右声道样本2, ...]
demo2
// 1. 使用声道索引掩码来配置 AudioFormat AudioFormat indexBasedFormat = new AudioFormat.Builder() .setEncoding(AudioFormat.ENCODING_PCM_16BIT) .setSampleRate(48000) // 核心:不设置 setChannelMask,而是设置 setChannelIndexMask // 0x5 的二进制是 0101,表示选中第1个(位0)和第3个(位2)通道 .setChannelIndexMask(0x5) .build(); // 2. 创建 AudioRecord 对象 // 注意:AudioRecord 的构造函数也需要传入 channelMask 参数, // 但当我们使用索引掩码时,这里传入的 channelMask 会被忽略。 // 为了代码规范,通常还是传入一个与索引掩码通道数匹配的值,比如 CHANNEL_IN_STEREO。 int minBufferSize = AudioRecord.getMinBufferSize( 48000, AudioFormat.CHANNEL_IN_STEREO, // 因为索引掩码选中了2个通道 AudioFormat.ENCODING_PCM_16BIT); AudioRecord audioRecord = new AudioRecord( MediaRecorder.AudioSource.DEFAULT, 48000, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize); // 3. 开始录音 audioRecord.startRecording(); // 4. 读取数据 byte[] buffer = new byte[1024]; int bytesRead = audioRecord.read(buffer, 0, buffer.length); // 此时,buffer 中的数据是:[来自USB设备通道0的样本1, 来自USB设备通道2的样本1, ...] // 系统会自动把物理上的通道0和通道2的数据,填充到你逻辑上的第1和第2个声道位置。

