2. AudioTrack播放PCM音频
任务目标
用 AudioTrack 的静态模式播放一个本地 PCM 文件(16bit 小端、单声道、44.1kHz)。核心是正确设置流类型、采样率、声道掩码、音频格式,并把 PCM 数据写入 AudioTrack 后播放。
关键参数
STREAM_MUSIC:媒体音量通道,车载场景常用。44100Hz / CHANNEL_OUT_MONO / ENCODING_PCM_16BIT:必须与 PCM 文件实际格式一致,否则会变调或噪声。MODE_STATIC:一次性写入整段数据,适合短音频;长音频用MODE_STREAM分块写。
完整实现
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class PcmPlayer {
private static final int SAMPLE_RATE = 44100;
private static final int CHANNEL_MASK = AudioFormat.CHANNEL_OUT_MONO;
private static final int ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private AudioTrack track;
/** 播放本地 PCM 文件,阻塞直到播放结束 */
public void play(File pcmFile) throws IOException {
byte[] pcm = readAll(pcmFile);
if (pcm.length == 0) throw new IOException("PCM 文件为空");
int minBuf = AudioTrack.getMinBufferSize(SAMPLE_RATE, CHANNEL_MASK, ENCODING);
if (minBuf <= 0) throw new IllegalStateException("参数不被设备支持");
AudioAttributes attrs = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
AudioFormat format = new AudioFormat.Builder()
.setSampleRate(SAMPLE_RATE)
.setChannelMask(CHANNEL_MASK)
.setEncoding(ENCODING)
.build();
// 静态模式缓冲区取数据长度与最小缓冲的较大值
int bufSize = Math.max(pcm.length, minBuf);
track = new AudioTrack(attrs, format, bufSize,
AudioTrack.MODE_STATIC, AudioManager.AUDIO_SESSION_ID_GENERATE);
int written = track.write(pcm, 0, pcm.length);
if (written <= 0) {
release();
throw new IOException("write 失败: " + written);
}
track.play();
// 静态模式:等待播放头走完数据长度
int frames = pcm.length / 2; // 16bit 单声道,每帧 2 字节
long ms = (long) frames * 1000 / SAMPLE_RATE;
try {
Thread.sleep(ms + 100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
release();
}
}
private static byte[] readAll(File f) throws IOException {
try (FileInputStream in = new FileInputStream(f)) {
byte[] data = new byte[(int) f.length()];
int off = 0, n;
while (off < data.length && (n = in.read(data, off, data.length - off)) > 0) {
off += n;
}
return data;
}
}
public void release() {
if (track != null) {
try { track.stop(); } catch (IllegalStateException ignored) {}
track.release();
track = null;
}
}
}
调用与运行
// 在 Activity / Service 中调用
new PcmPlayer().play(new File(getFilesDir(), "test.pcm"));
把一段 44.1kHz、单声道、16bit 小端的 PCM 文件命名为 test.pcm 放入应用私有目录(或用 adb push 到 /sdcard/ 后改路径)。用 ffmpeg -i in.wav -f s16le -ac 1 -ar 44100 test.pcm 可从任意音频生成符合格式的 PCM。
验证方法
- 运行后能听到与源音频一致的声音,无变调、无杂音。
- 若出现尖锐噪声或速度异常,检查采样率/声道/位深是否与 PCM 文件一致。
- 若
getMinBufferSize返回负值,说明设备不支持该组合,需换参数。 - 播放结束后
release()被调用,无AudioTrack泄漏。
常见问题
- 静态模式缓冲区必须 ≥ 数据长度,否则
write会失败。 - 长音频不要用
MODE_STATIC,应改用MODE_STREAM循环写入。 - 车载场景若需与其它音源混音,可改用
USAGE_ASSISTANCE_NAVIGATION_GUIDANCE等更合适的 usage。