我通过 FileInputStream > BufferedInputStream > DataInputStream 方法加载文件并将字节输入 AudioTrack.write() 在我的 Android 手机上播放 WAV。音频播放良好,当它播放时,我可以轻松地动态调整采样率、音量等,并具有良好的性能。但是,开始播放曲目大约需要整整两秒钟。我知道 AudioTrack 有不可避免的延迟,但这很荒谬。每次播放曲目时,我都会得到以下信息:
03-13 14:55:57.100: WARN/AudioTrack(3454): obtainBuffer timed out (is the CPU pegged?) 0x2e9348 user=00000960, server=00000000
03-13 14:55:57.340: WARN/AudioFlinger(72): write blocked for 233 msecs, 9 delayed writes, thread 0xba28
我注意到,从打开手机的那一刻起,每次播放一首曲目(甚至跨越多个会话)时,延迟写入计数都会增加 1 次。块时间始终为 230 - 240 毫秒,考虑到此设备上的最小缓冲区大小为 9600 (9600 / 44100),这是有道理的。我在互联网上无数次搜索中看到过这条消息,但它通常似乎与根本不播放音频或跳过音频有关。就我而言,这只是一个延迟的开始。
我在高优先级线程中运行我的所有代码。这是我正在做的一个截断但功能性的版本。这是我的播放类中的线程回调。同样,这有效(现在只播放 16 位、44.1kHz、立体声文件),它只需要永远启动,并且每次都有获取缓冲区/延迟写入消息。
public void run() {
// Load file
FileInputStream mFileInputStream;
try {
// mFile is instance of custom file class -- this is correct,
// so don't sweat this line
mFileInputStream = new FileInputStream(mFile.path());
} catch (FileNotFoundException e) {
// log
}
BufferedInputStream mBufferedInputStream = new BufferedInputStream(mFileInputStream, mBufferLength);
DataInputStream mDataInputStream = new DataInputStream(mBufferedInputStream);
// Skip header
try {
if (mDataInputStream.available() > 44) {
mDataInputStream.skipBytes(44);
}
} catch (IOException e) {
// log
}
// Initialize device
mAudioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC,
ConfigManager.SAMPLE_RATE,
AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT,
ConfigManager.AUDIO_BUFFER_LENGTH,
AudioTrack.MODE_STREAM
);
mAudioTrack.play();
// Initialize buffer
byte[] mByteArray = new byte[mBufferLength];
int mBytesToWrite = 0;
int mBytesWritten = 0;
// Loop to keep thread running
while (mRun) {
// This flag is turned on when the user presses "play"
while (mPlaying) {
try {
// Check if data is available
if (mDataInputStream.available() > 0) {
// Read data from file and write to audio device
mBytesToWrite = mDataInputStream.read(mByteArray, 0, mBufferLength);
mBytesWritten += mAudioTrack.write(mByteArray, 0, mBytesToWrite);
}
}
catch (IOException e){
// log
}
}
}
}
如果我能克服人为的长时间延迟,我可以通过在稍后的可预测位置开始写入来轻松处理继承延迟(即,当我开始播放文件时跳过最小缓冲区长度)。