Precisamente Sync Looped Audio con animación en Android

Pregunta: ¿Hay alguna manera de usar AudioTrack y setLoopPoints () para configurar un bucle con precisión basada en samples / frames por milisegundo?

Edit: Entiendo que no se puede esperar una exactitud perfecta del poder de procesamiento que poseen la mayoría de los dispositivos de Android. Me gustaría, sin embargo, obtener un tiempo de bucle promedio cercano al intervalo "real" del tempo en milisegundos, ya que esto es como he basado una "animación" que también debería sincronizar con el tempo (la animación es un SurfaceView que vuelve a dibujar Las coordenadas de una línea sobre la duración del intervalo del tempo).

Detalles: Estoy tratando de utilizar AudioTrack con setLoopPoints para crear un metrónomo preciso. Para ello, estoy usando dos archivos wav (Tick y Tock) para rellenar un array de bytes [] para alimentar a AudioTrack. Considere un ejemplo en tiempo 4/4 donde rellenaría un byte [], una vez con Tick comenzando en [0] y tres veces con Tock (usando arrayCopy ()) a [length / 4], [length / 2], y [3 * length / 4], y asumir que los datos wav no se superponen entre sí.

Ejemplo aproximado de lo que hace mi código:

// read each wav file's header into its own ByteBuffer (with LITTLE_ENDIAN order) // ... then get the size of the audio data tickDataSize = tickHeaderBuffer.getInt(40); tockDataSize = tockHeaderBuffer.getInt(40); // allocate space for one loop at the current tempo into a byte[] array (to be given to AudioTrack) // eg 22050hz * 2 Bytes (1 per channel) * 2 Bytes (1 per 8 bit PCM sample) = 22050*4 Bytes/second // If my tempo were 60 BPM I'd have 88200 Bytes/second (where 1 second is the whole loop interval); // 110 BPM = 48109.0909091 Bytes per loop interval (where 1 loop interval is 0.54545 seconds) int tempo = 110; int bytesPerSecond = sampleRate * 2 * 2; int bytesPerInterval = (int)((((float)bytesPerSecond * 60.0F)/(float)tempo) * 4); byte[] wavData = new byte[bytesPerInterval]; // ... then fill wavData[] as mentioned above with 1*wavOne and 3*wavTwo // Then feed to an instance of AudioTrack and set loop points audioTrack.write(wavData, 0, bytesPerInterval); int numSamples = bytesPerInterval/4; audioTrack.setLoopPoints(0, numSamples, -1); audioTrack.play(); 

Espero que haya comenzado a ver el problema. Con ciertos tempos, sólo obtengo una jugada estática en el bucle (pero sólo durante la primera y tercera Tock [2ª y 4ª muestra en el bucle]).

La estática se detiene si:

  • No llene el byte [] con ningún dato wav, pero mantenga los bytesPerInterval y numSamples iguales (bucle silencioso de la duración correcta).
  • Set bytesPerInterval = bytesPerInterval% 4 (perdiendo así la precisión del tempo)

Ejemplos de ritmos de trabajo (no estáticos) y no funcionales (estáticos) y su número requerido de marcos (Considere un segundo = 88200 marcos):

 tempo: 110 (static) wavData.length: 192436 numFrames: 48109 tempo: 120 (no static) wavData.length: 176400 numFrames: 44100 tempo: 130 (static) wavData.length: 162828 numFrames: 40707 tempo: 140 (no static) wavData.length: 151200 numFrames: 37800 tempo: 150 (no static) wavData.length: 141120 numFrames: 35280 tempo: 160 (static) wavData.length: 132300 numFrames: 33075 tempo: 170 (static) wavData.length: 124516 numFrames: 31129 tempo: 180 (no static) wavData.length: 117600 numFrames: 29400 

Si la respuesta a la pregunta es "no, no puede utilizar setLoopPoints () para configurar un bucle de precisión a cualquier milisegundo", entonces me gustaría saber de otras opciones. ¿Sería más apropiado OpenSL ES en NDK, SoundPool o MediaPlayer para generar un bucle preciso?

Editar 2: He reducido la ubicación causando el problema estático:

 // Assume a tempo of 160 BPM which requires byte[132300] wavStream1 = this.context.getResources().openRawResource(R.raw.tick); wavStream2 = this.context.getResources().openRawResource(R.raw.tock); ByteBuffer headerBuffer1 = ByteBuffer.allocate(44); ByteBuffer headerBuffer2 = ByteBuffer.allocate(44); headerBuffer1.order(ByteOrder.LITTLE_ENDIAN); headerBuffer2.order(ByteOrder.LITTLE_ENDIAN); wavStream1.read(headerBuffer1.array(), 0, 44); wavStream2.read(headerBuffer2.array(), 0, 44); int tickDataSize = headerBuffer1.getInt(40); int tockDataSize = headerBuffer2.getInt(40); byte[] wavData = new byte[bytesPerInterval * 4]; byte[] tickWavData = new byte[bytesPerInterval]; byte[] tockWavData = new byte[bytesPerInterval]; wavStream1.read(accentWavData, 0, tickDataSize); wavStream2.read(normalWavData, 0, tockDataSize); System.arraycopy(tickWavData, 0, wavData, 0, bytesPerInterval); System.arraycopy(tockWavData, 0, wavData, 33075, bytesPerInterval); System.arraycopy(tockWavData, 0, wavData, 66150, bytesPerInterval); System.arraycopy(tockWavData, 0, wavData, 99225, bytesPerInterval); // bytesPerInterval of 33075 and 99225 (or any odd number) will be // static when wavData is played AudioTrack audioTrack = new AudioTrack(3, 22050, 12, 2, wavData.length, 0); audioTrack.write(wavData, 0, wavData.length); audioTrack.setLoopPoints(0, bytesPerInterval, -1); audioTrack.play(); 

Lo más importante, me gustaría entender por qué los datos de audio que comienzan en un índice extraño de wavData genera estática en lugar del sonido esperado y si hay algún remedio para esto.

Después de leer tus ediciones, creo que la razón por la que los índices impares causan un problema es porque estás creando el AudioTrack con ENCODING_PCM_16BIT (el "2" que pasas en el constructor). Eso significa que cada muestra debe ser de 16 bits. Pruebe con "3" o ENCODING_PCM_8BIT si las muestras son de 8 bits.

  • Cómo medir la latencia de audio con OpenSL en Android?
  • ¿Cuáles deberían ser las razones para usar OpenSL ES en lugar de AudioTrack en Android?
  • Transmisión de audio MP3 mediante comunicación de socket mediante OpenSL ES en Android
  • Android 2.3.4, OpenSL ES y enorme log-spam por razones desconocidas
  • Vinculación con la biblioteca actualizada en Android
  • Redirigir audio / crear rutas de sonido alternativas en Android
  • Alternativa de OpenSL ES en Android
  • reproducir un archivo MP3 android NDK utilizando openSL desde la memoria
  • Android con Nexus 6 - ¿cómo evitar la disminución de la prioridad de audio de OpenSL en relación con el enfoque de la aplicación?
  • Tutoriales para OpenSL ES para Android
  • attachAuxEffect y OpenSL ES
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.