Red de conocimiento informático - Material del sitio web - Cómo utilizar AudioTrack para reproducir archivos en formato WAV

Cómo utilizar AudioTrack para reproducir archivos en formato WAV

Si ya sabe un poco sobre AudioTrack, es posible que le gusten los beneficios que brinda, como la baja latencia (en modo ESTÁTICO), la capacidad de generar transmisión de audio (en modo STREAM) y la capacidad de acceder y modificar. datos de sonido originales antes de la reproducción.

Sin embargo, el problema ahora es cómo obtener datos de la fuente de audio. Muchas aplicaciones requieren el uso de AudioTrack, pero no genera simplemente audio PCM (por ejemplo, Ethereal Dialpad u otras aplicaciones similares). Es posible que necesite cargar datos desde una fuente de archivo, como un archivo WAV o MP3.

No esperes utilizar MediaPlayer para decodificar archivos WAV y audio MP3. Si bien MediaPlayer reproduce bien estos archivos, su lógica de reproducción está completamente en la capa nativa y no nos brinda opciones adicionales para usar otros códecs para nuestros propósitos. Por lo tanto, tenemos que decodificar manualmente el PCM en el archivo de audio.

Este artículo analizará los archivos en formato WAV. En la próxima lección, analizaremos más sobre la lectura de audio de archivos MP3.

Antecedentes: cierta terminología de audio digital

Si su aplicación no está diseñada específicamente para audio digital, existen algunas siglas básicas que quizás desee conocer antes de continuar. No te preocupes, todo es muy sencillo y no hace falta que profundicemos en esto.

PCM (Método de Modulación de Pulso): el método más sencillo para digitalizar señales de audio físicas. El principio básico es convertir una señal en una serie de números, donde cada número representa el nivel o la energía (amplitud) del sonido en un momento específico. (Perdón si la explicación no es científica). Lo creas o no, puedes representar cualquier sonido complejo de esta manera y reproducirlo con mucha precisión. Aquí solo estamos hablando de PCM lineal, es decir, cada número en la matriz es una representación lineal de la amplitud del sonido original. Hay algunos casos en los que un mapeo logarítmico representa mejor la relación de las amplitudes del sonido original, pero no discutiremos esos casos.

Frecuencia de muestreo: - Cuántas muestras de sonido digital por segundo (la amplitud del sonido se expresa como un número). Cuantas más muestras, mejor será la calidad del sonido. Las frecuencias de muestreo actuales utilizadas en los sistemas de audio de consumo suelen ser 22050, 44100 y 48000 Hz/s.

Por muestra/Tamaño de muestra/Resolución de bits: define el tamaño y el formato de los números que representan la amplitud. Por ejemplo, si usa números enteros de 8 bits, solo puede representar 256 niveles de amplitud, por lo que la forma de onda física original se reducirá a 256 niveles discretos, al tiempo que se perderá algo de precisión sonora (también llamada calidad). La calidad del sonido será mejor si utiliza 16 bits. De hecho, probablemente utilizarás audio de 16 bits en la mayoría de los casos. Otras opciones incluyen 24 bits, 32 bits (Android actualmente no admite estas opciones) o usar punto flotante.

Canales: Mono, Estéreo (2 canales) o más (no compatible con Android). Si desea tener sonido estéreo, necesita tener audio estéreo, necesitará una matriz PCM separada para cada canal y, en consecuencia, duplicará la cantidad de información.

Las definiciones anteriores también le ayudan a comprender la cantidad de datos y la longitud del búfer de audio para un formato específico, de modo que pueda preparar el búfer con antelación. Por ejemplo, necesita un búfer para almacenar 5 segundos de datos PCM lineales estéreo de 16 bits muestreados a 44100 Hz.

Los datos se calculan de la siguiente manera:

5 segundos * 44.100 muestras por segundo * 2 bytes por muestra * 2 canales = 882.000 bytes

Esta cantidad de memoria requerida puede sorprender a los principiantes, porque cuando Si almacena audio en un archivo MP3 en el disco, un archivo de 880 KB puede almacenar una pista de audio de 1 minuto con la misma frecuencia de muestreo y resolución. ¿Por qué es esto? Por el uso de formatos avanzados como el formato MP3. Dado que nuestro cerebro no puede reconocer el contenido de cierto audio, se utilizan muchos métodos sofisticados para eliminar este contenido durante el proceso de compresión. Sin embargo, la mayoría de las API de audio de bajo nivel (incluida AudioTrack de Android) solo pueden aceptar PCM lineal. Es por eso que si no podemos mantener la muestra completa en la memoria, debemos lidiar con flujos de datos, buffers circulares y otras formas inteligentes de usar la API de audio.

Espero que la explicación anterior no te haya confundido. Ahora, ¡comencemos a hacer cosas relacionadas con el audio digital en Android!

Formato de archivo WAV

Nuestro objetivo es proporcionar datos de bytes sin procesar utilizando un InputStream que carga datos PCM desde un archivo WAV. Luego podemos enviar los datos PCM sin procesar directamente a AudioTrack.write utilizando la API AudioTrack.write() correctamente configurada.

Los archivos WAV contienen un encabezado y datos específicos. Necesitamos leer el encabezado del archivo para comprender la frecuencia de muestreo, la resolución, etc. Además, cuando usamos el encabezado del archivo, también usamos datos, como la frecuencia de muestreo. Además, también podemos saber si el formato es compatible a través del encabezado. WAV se puede encapsular en múltiples formatos y es imposible para nosotros admitir todos los formatos. Quizás bastará con un formato PCM lineal con una frecuencia de muestreo, resolución y canales razonables.

La información detallada sobre el formato WAV está ampliamente disponible en Internet, sólo hay que buscarla en Google. Pero desafortunadamente, no encontré una biblioteca Java para leer archivos WAV que se pueda portar a Android. Así que yo mismo escribí un código simple.

El siguiente método explica cómo leer el encabezado de un archivo WAV:

private static final String RIFF_HEADER = "RIFF"

private static final String WAVE_HEADER; = " WAVE";

cadena final estática privada FMT_HEADER = "fmt ";

cadena final estática privada DATA_HEADER = "datos"

int final estática privada HEADER_SIZE = 44;

Cadena final estática privada CHARSET = "ASCII";

/* ...*/

WavInfo estática pública readHeader(InputStream wavStream) lanza IOException,

DecoderException {

ByteBuffer buffer = ByteBuffer.allocate(HEADER_SIZE);

buffer.order(ByteOrder.LITTLE_ENDIAN

<); p> wavStream.read(buffer.array(), buffer.arrayOffset(), buffer.capacity());

buffer.rewind()

buffer.position(buffer); .posición () 20);

int formato = buffer.position(buffer.position()

int bits = buffer.getShort(); p> checkFormat(bits == 16, "Bits no admitidos: " bits);

int dataSize = 0

mientras (buffer.getInt() !rewind();

p>

}

dataSize = buffer.getInt();

checkFormat(dataSize gt; 0, "tamaño de datos incorrecto: " dataSize);

return new WavInfo(new FormatSpec(rate, canales == 2), dataSize

}

La parte que falta en el código anterior debería ser obvia. Como puede ver, solo se admiten 16 bits, pero puede modificar el código para que admita 8 bits (AudioTrack no admite otras resoluciones).

Por otro lado, se utiliza el siguiente método para leer la parte restante del archivo: los datos de audio.

byte estático público[] readWavPcm(información de WavInfo, flujo de flujo de entrada)

lanza IOException {

byte[] datos = nuevo byte[info.length);

datos de retorno;

}

La estructura WavInfo que leemos contiene la frecuencia de muestreo, la resolución y el número de canales, lo cual es suficiente para reproducir el audio que leemos. .

Si no necesitamos almacenar todos los datos de audio en la memoria a la vez, podemos usar InputStream para leerlos poco a poco.

Transferir PCM a pista de audio

Ahora nos enfrentamos a dos situaciones: crear una nueva pista de audio adecuada para este formato o utilizar una que puede no coincidir con el formato de datos de audio WAV existente. pistas de audio.

En el primer caso, las cosas son simples, simplemente usamos el constructor AudioTrack para construir un AudioTrack que ya tenemos a partir del encabezado WAV.

En el segundo caso, necesitamos convertir el audio al formato de destino requerido por AudioTrack. Necesitamos lograr esto de varias maneras:

Si las frecuencias de muestreo son diferentes, elimine o copie una muestra para que coincida con la frecuencia de muestreo objetivo. Si las resoluciones son diferentes, asigne la resolución de la señal de origen a la resolución de destino, de 16 bits a 8 bits y viceversa. Si los canales son diferentes, mezclamos los canales estéreo en mono o copiamos los datos mono para convertirlos en cuasi estéreo. (Considere implementar estos algoritmos en la capa local, ya que la capa local tiene grandes ventajas al realizar este tipo de procesamiento).

En otros casos, nos aseguramos de que los formatos coincidieran. Usamos AudioTrack.write() para escribir en el búfer para su reproducción.

Recuerde que si está utilizando el modo estático, necesitará crear una nueva AudioTrack con el tamaño exacto del búfer antes de reproducir() y escribir() los datos de audio al mismo tiempo. En modo streaming, primero podemos usar play() de AudioTrack