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.
- Lo que se requiere para que Android utilice un nuevo Audio HAL
- Cómo programar un secuenciador de música simple para Android (Java)
- AudioRecord con ajuste de ganancia no funciona en el dispositivo Samsung
- Android - Mezcla de múltiples formas de onda estáticas en un solo AudioTrack
- Bucle de audio sin fisuras de Android
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).
- Múltiples pistas estéreo de audio
- Entrada y salida de audio inmediatas Android
- Múltiples AudioTracks, Single Thread o Multiple?
- Cómo reproducir una notificación de sonido con Chrome para Android con Javascript?
- AudioFlinger (59): RecordThread: desbordamiento del búfer cuando la actividad está pausada?
- Cómo hacer un sonido de longitud variable en Android
- Flujo de problemas de audio en ICS
- Cómo recoger la ruta de un archivo de audio uri desde el dispositivo en Android?
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; }
- Android enviar archivo / datos desde la aplicación a través de Bluetooth a la PC / impresora / escáner / otro dispositivo
- Enviar un objeto personalizado mediante la carpeta nativa de Android