11

我通过 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
            }                
        }   
    }
}

如果我能克服人为的长时间延迟,我可以通过在稍后的可预测位置开始写入来轻松处理继承延迟(即,当我开始播放文件时跳过最小缓冲区长度)。

4

2 回答 2

2

我遇到了类似的问题,尽管我使用的是 RandomAccessFile 而不是 BufferedInputStream 来读取 PCM 数据。问题是文件 I/O 太慢了。我怀疑即使使用缓冲流也会遇到这个问题,因为 I/O 仍然在与音频处理相同的线程上进行。

解决方案是有两个线程:一个线程从文件中读取缓冲区并将它们排队到内存中,另一个线程从这个队列中读取并写入音频硬件。我使用 ConcurrentLinkedQueue 来完成此操作。

我使用相同的技术进行录音,使用 AudioRecord,但方向相反。关键是将文件 I/O 放在单独的线程上。

于 2011-08-22T16:16:30.810 回答
1

回答这个问题的一方有点晚了,但万一它对未来的任何人有帮助 - 我遇到了这个确切的问题,代码与问题中的代码非常相似,AudioTrack 被创建并设置为播放,但未写入立即地。

我发现在您开始写入 AudioTrack 之前立即创建它会使延迟消失。出于某种原因,AudioTrack 似乎不喜欢坐在空缓冲区中。

就上面的代码而言,你想要做类似的事情

mAudioTrack=null;
while (mRun) 
{ 

    // This flag is turned on when the user presses "play" 
    while (mPlaying) 
    { 

        try 
        { 
            if (mAudioTrack==null) {   
                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();   
            }
            // Rest of the playback code here
        }
    }
    mAudioTrack=null;
}
于 2012-06-06T07:27:41.903 回答