2011-03-13 4 views
10

Je joue des fichiers WAV sur mon téléphone Android en chargeant le fichier et en introduisant les octets dans AudioTrack.write() via la méthode FileInputStream> BufferedInputStream> DataInputStream. L'audio joue bien et quand il est, je peux facilement ajuster la fréquence d'échantillonnage, le volume, etc à la volée avec de bonnes performances. Cependant, il faut environ deux secondes pour qu'une piste commence à jouer. Je sais que AudioTrack a un délai inévitable, mais c'est ridicule. Chaque fois que je joue une piste, je reçois ceci:AudioTrack lag: gettingBuffer a expiré

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 

J'ai remarqué que le nombre écriture retardées augmente d'un à chaque fois que je joue une piste - même à travers plusieurs sessions - à partir du moment où le téléphone a été allumé. Le temps de blocage est toujours de 230 à 240 ms, ce qui est logique si l'on considère une taille de tampon minimale de 9600 sur cet appareil (9600/44100). J'ai vu ce message dans d'innombrables recherches sur Internet, mais il semble généralement lié à ne pas jouer du tout ou sauter de l'audio. Dans mon cas, c'est juste un départ différé.

Je cours tout mon code dans un fil de haute priorité. Voici une version tronquée et fonctionnelle de ce que je fais. C'est le rappel de thread dans ma classe de lecture. Encore une fois, cela fonctionne (en ne jouant que des fichiers stéréo 16 bits, 44,1 kHz en ce moment), il faut juste une éternité pour commencer et a ce message writeBuffer/delay write à chaque fois.

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

Si je peux passer le retard artificiellement long, je peux facilement faire face à la latence de Hériter en démarrant mon écriture à une position plus tard, prévisible (c.-à-sauter-delà de la longueur minimale de tampon lorsque je commence à jouer un fichier).

+1

J'ai un problème similaire au moment. Avez-vous trouvé une solution? – sunside

+1

pas encore, malheureusement. :/ – BTR

Répondre

2

J'ai rencontré un problème similaire, bien que j'utilisais un RandomAccessFile, au lieu d'un BufferedInputStream, pour lire les données PCM. Le problème était que le fichier E/S était trop lent. Je suspecte que vous aurez ce problème même avec un flux tamponné, parce que l'I/O se déroule toujours sur le même fil que le traitement audio.

La solution consiste à avoir deux threads: un thread qui lit les tampons d'un fichier et les met en file d'attente en mémoire, et un autre thread qui lit dans cette file d'attente et écrit sur le matériel audio. J'ai utilisé un ConcurrentLinkedQueue pour accomplir ceci.

J'ai utilisé la même technique pour enregistrer, en utilisant AudioRecord, mais dans le sens inverse. La clé consiste à placer le fichier I/O sur un thread séparé.

+0

Merci! Ce projet a été en attente d'une solution. Je vais essayer ça. – BTR

1

Un peu tard pour répondre à la question, mais au cas où cela aiderait quelqu'un dans le futur - j'ai rencontré ce problème avec un code assez similaire au code de la question, où l'AudioTrack est créé et mis en lecture, mais pas écrit immédiatement.

J'ai trouvé que la création de l'AudioTrack immédiatement avant que vous ne commenciez à écrire dessus fait disparaître le délai. Pour une raison quelconque, AudioTrack ne semble pas aimer rester assis avec un tampon vide.

En ce qui concerne le code ci-dessus, vous voulez faire quelque chose comme

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