AudioTrack lag: obtenerBuffer tiempo de espera agotado

Estoy jugando WAVs en mi teléfono Android cargando el archivo y la alimentación de los bytes en AudioTrack.write () a través del método FileInputStream> BufferedInputStream> DataInputStream. El audio juega muy bien y cuando lo es, puedo ajustar fácilmente la frecuencia de muestreo, volumen, etc sobre la marcha con buen rendimiento. Sin embargo, tardan unos dos segundos para que una pista empiece a tocar. Sé que AudioTrack tiene un retraso ineludible, pero esto es ridículo. Cada vez que juego una pista, consigo esto:

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 

He notado que el recuento de escritura diferida aumenta cada vez que reproduzco una pista, incluso a través de varias sesiones, desde el momento en que se ha encendido el teléfono. El tiempo de bloqueo es siempre 230 – 240ms, lo cual tiene sentido considerando un tamaño de búfer mínimo de 9600 en este dispositivo (9600/44100). He visto este mensaje en innumerables búsquedas en Internet, pero por lo general parece estar relacionado con no reproducir audio en absoluto o saltar audio. En mi caso, es sólo un comienzo retrasado.

Estoy ejecutando todo mi código en un hilo de alta prioridad. Esta es una versión truncada pero funcional de lo que estoy haciendo. Esta es la devolución de llamada de subproceso en mi clase de reproducción. Una vez más, esto funciona (sólo reproducir 16 bits, 44.1kHz, archivos estéreo ahora), sólo toma una eternidad para empezar y tiene que obtenerBuffer / retraso escribir mensaje cada vez.

 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 puedo superar el retraso artificialmente largo, puedo manejar fácilmente la latencia de herencia iniciando mi escritura en una posición posterior, predecible (es decir, omita la longitud mínima del búfer cuando comienzo a reproducir un archivo).

Me encontré con un problema similar, aunque estaba usando un RandomAccessFile, en lugar de un BufferedInputStream, para leer los datos de PCM. El problema era que el archivo I / O era demasiado lento. Sospecho que tendrá este problema incluso con un flujo almacenado en búfer, porque la E / S sigue teniendo lugar en el mismo hilo que el procesamiento de audio.

La solución es tener dos subprocesos: Un subproceso que lee buffers de un archivo y los coloca en la memoria, y otro subproceso que lee desde esta cola y escribe en el hardware de audio. Utilicé un ConcurrentLinkedQueue para lograr esto.

He utilizado la misma técnica para la grabación, utilizando AudioRecord, pero en la dirección inversa. La clave es colocar el archivo I / O en un hilo separado.

Un poco tarde a la fiesta respondiendo a esto, pero en caso de que ayude a alguien en el futuro – me encontré con este problema exacto con el código bastante similar al código en la pregunta, donde se crea el AudioTrack y se pone a jugar, pero no se escribe en inmediatamente.

Me pareció que la creación de la AudioTrack inmediatamente antes de empezar a escribir que hizo que el retraso desaparecer. Por alguna razón, AudioTrack no parece estar sentado con un buffer vacío.

En términos del código anterior, usted querría hacer algo como

 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; } 
  • ¿Es posible tener acceso a la señal del altavoz en Android?
  • El lazo de MediaPlayer de Android tiene brechas incluso con formato OGG
  • Jugar dos sonidos Simutaneosly
  • Grabar audio desde varios dispositivos internos en Android (a través de API no documentada)
  • Talking App como hablar, la grabación de audio no funcionó en todos los dispositivos
  • MediaPlayer.getCurrentPosition ()> mediaPlayer.getDuration () al final de la reproducción del archivo mp3
  • Audio Android demasiado rápido en algunos dispositivos con MediaCodec y AudioTrack
  • Android OpenAL?
  • 06-16 12: 05: 46.802: E / chromium (15693): No se puede seleccionar el dispositivo de audio
  • ¿Qué es SLDataLocator_AndroidSimpleBufferQueue (Android 4.3)?
  • El audio no se reproduce en Android con el teléfono, pero funciona bien en iOS
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.