2011-03-13 5 views
10

Я играю WAV на своем Android-телефоне, загружая файл и подавая байты в AudioTrack.write() с помощью метода FileInputStream> BufferedInputStream> DataInputStream. Звук играет отлично, и когда это так, я могу легко настроить частоту дискретизации, громкость и т. Д. На лету с хорошей производительностью. Однако для начала трека требуется около двух секунд. Я знаю, что AudioTrack имеет неоспоримую задержку, но это смешно. Каждый раз, когда я играю трек, я получаю это:AudioTrack lag: getBuffer timed out

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 

Я заметил, что отложенная запись счет увеличивается на единицу каждый раз, когда я играю трек - даже через несколько сеансов - от времени телефона имеет был включен. Время блока всегда 230 - 240 мс, что имеет смысл с учетом минимального размера буфера 9600 на этом устройстве (9600/44100). Я видел это сообщение в бесчисленных поисках в Интернете, но обычно это связано с тем, что он вообще не воспроизводит звук или не пропускает звук. В моем случае это просто отложенный старт.

Я запускаю весь свой код в потоке с высоким приоритетом. Вот усеченная, но функциональная версия того, что я делаю. Это обратный вызов потока в моем классе воспроизведения. Опять же, это работает (только сейчас воспроизводить 16-битные, 44,1 кГц, стереофайлы), это просто начинается навсегда, и каждый раз он получает сообщение getBuffer/delayed write.

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 
      }     
     } 
    } 
} 

Если я могу пройти искусственно длительную задержку, я могу легко справиться с наследовать латентности, начав свою запись позже, предсказуемую позиции (т.е. пропустить мимо минимальной длиной буфера, когда я начинаю играть файл).

+1

У меня аналогичная проблема на данный момент. Вы нашли решение? – sunside

+1

не, к сожалению. :/ – BTR

ответ

2

У меня возникла аналогичная проблема, хотя я использовал RandomAccessFile вместо BufferedInputStream для чтения данных PCM. Проблема заключалась в том, что ввод/вывод файлов был слишком медленным. Я подозреваю, что у вас будет эта проблема даже с буферизованным потоком, потому что ввод-вывод все еще происходит в том же потоке, что и обработка звука.

Решение состоит из двух потоков: поток, который считывает буферы из файла и помещает их в память, а другой поток, который читает из этой очереди и записывает на аудиооборудование. Для этого я использовал ConcurrentLinkedQueue.

Я использовал ту же технику для записи, используя AudioRecord, но в обратном направлении. Ключ состоит в том, чтобы поместить файл ввода/вывода в отдельный поток.

+0

Спасибо! Этот проект был на паузе, ожидая решения. Я попробую это. – BTR

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; 
}