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).
- Control del volumen de audio forzado a altavoz
- ¿Es posible obtener un búfer de bytes directamente desde un activo de audio en OpenSL ES (para Android)?
- Comunicación de voz a una frecuencia de muestreo de 8KHz para todos los dispositivos Android con OpenSL
- Cómo convertir voz humana a nota musical en Android?
- Descodificador AES OpenSL ES de Android
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.
- Android AlertDialog congela la aplicación unos segundos después de mostrarse - la causa parece relacionada con OpenSL
- ¿Biblioteca de sonido nativa en Android que puede cambiar de tono?
- ¿Es posible tener acceso a la señal del altavoz en Android?
- ¿Qué es SLDataLocator_AndroidSimpleBufferQueue (Android 4.3)?
- Reproducción de audio de baja latencia en Android
- RecorderObject en OpenSL no implementa la interfaz para configurar el volumen o configurarlo en Android
- ¿Es necesario liberar un buffer final de un reproductor de audio OpenSL ES?
- OpenSL ES en Android con SL_ANDROID_STREAM_VOICE. La entrada de micrófono es extremadamente baja
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.
- Webview en android obtener se cuelga sólo en Samsung galaxia ficha 2
- Depurar la página web en la aplicación Chrome Android desde el escritorio localhost con el reenvío de puertos