La extension de audio de la API se extiende de la especificación de HTML5 {HTMLElement("audio")}} y <video>
multimedia mediante la exposición de elementos metadata de audio y datos puros en audio. Esto permite a los usuarios visualizar los datos de audio, para procesarlos y crear nuevos datos de audio.
Lectura de flujo de Audio
El loadedmetadata evento
Cuando la metadata multimedia del elemento está disponible, se desencadena un envento loadedmetadata. Este evento tiene los siguientes atributos:
- mozChannels: Número de canales
- mozSampleRate: Frecuencia de muestreo por segundo
- mozFrameBufferLength:Número de muestras recolectadas en todos los canales
Esta información es necesaria más adelante para descifrar la secuencia de datos de audio. En los siguientes ejemplos se extraen los datos de un elemento de audio:
<!DOCTYPE html> <html> <head> <title>Ejemplo JavaScript Metadata </title> </head> <body> <audio id="audio-element" src="song.ogg" controls="true" style="width: 512px;"> </audio> <script> function loadedMetadata() { channels = audio.mozChannels; rate = audio.mozSampleRate; frameBufferLength = audio.mozFrameBufferLength; } var audio = document.getElementById('audio-element'); audio.addEventListener('loadedmetadata', loadedMetadata, false); </script> </body> </html>
El evento MozAudioAvailable
A medida que el audio se reproduce, los datos de muestras están disponible para la capa de audio y el buffer de audio (size definido en mozFrameBufferLength) se llenan con las muestras. Una vez que el buffer esta lleno, el eventoMozAudioAvailable se activa. Por lo tanto el evento contiene las muestras primas en un perido de tiempo. Esos muestreos pueden ser reproducidos o no en el momento que se hayan reproducido el evento y no se han ajustado para la configuración de mudo o volumen en el elemento de multimedia. Reproducir, pausar, y la búsqueda de audio tambien afecta transmision de estos datos de audio prima.
El evento MozAudioAvailable tiene 2 atributos:
- frameBuffer: Framebuffer (i.e., an array) contiene decodificado de audio datos de la muestra (i.e., floats)
- time: Marca de tiempo de esas muestras medidas desde el principio en segundos
El framebuffer contiene una serie de muestras de audio. Es importnt tener en cuenta que las muestras no son separados por canales, todos ellos son entregados juntos. Por ejemplo, para una señal de dos canales: Canal1-Ejemplo1 Canal2-Ejemplo1 Canal1-ejemplo2 Canal2-Ejemplo2 Canal1-Ejemplo3 Canal2-Ejemplo3.
Podemos extender el ejemplo anterior para visualizar la fecha y hora y las muestras de los dos primeros en un <div>
elemento:
<!DOCTYPE html> <html> <head> <title>Ejemplo de visualizacion en JavaScript</title> <body> <audio id="audio-element" src="revolve.ogg" controls="true" style="width: 512px;"> </audio> <pre id="raw">hello</pre> <script> function loadedMetadata() { channels = audio.mozChannels; rate = audio.mozSampleRate; frameBufferLength = audio.mozFrameBufferLength; } function audioAvailable(event) { var frameBuffer = event.frameBuffer; var t = event.time; var text = "Ejemplo: " + t + "\n" text += frameBuffer[0] + " " + frameBuffer[1] raw.innerHTML = text; } var raw = document.getElementById('raw') var audio = document.getElementById('audio-element'); audio.addEventListener('MozAudioAvailable', audioAvailable, false); audio.addEventListener('loadedmetadata', loadedMetadata, false); </script> </body> </html>
Creación de un flujo de audio
También es posible crear y configurar un <audio>
elemento por escrito por raw de script (i.e., sin src atributos. Contiene scripts que pueden especificar audios caracterÃsticos de los flujos, a continuación, escribir las muestras de audio. Los usuarios deben crear un objeto de audio y luego usar el mozSetup()
función especifica el numero de canales y frecuencia (en Hz). Por Ejemplo:
// Crear un nuevo elemento de audio var audioOutput = new Audio(); // Crea un elemento de audio con 2 canales, 44.1KHz audio flujo. audioOutput.mozSetup(2, 44100);
Una vez terminado, los ejemplos necesitan ser creado. Estas muestras tienen el mismo formato que los de el eventomozAudioAvailable. Luego las muestra son escritas en el audio de flujo con la función mozWriteAudio()
. Es importante observar que no todas las muestras podrÃa obtener por escrito en el arroyo. La función retorna el número de muestras por escrito, que es útil para la redacción siguiente. Puede ver un ejemplo a continuación:
// Escribir ejemplo usando un JS Array var samples = [0.242, 0.127, 0.0, -0.058, -0.242, ...]; var numberSamplesWritten = audioOutput.mozWriteAudio(samples); // Escribir ejemplo usando un Typed Array var samples = new Float32Array([0.242, 0.127, 0.0, -0.058, -0.242, ...]); var numberSamplesWritten = audioOutput.mozWriteAudio(samples);
En el siguiente ejemplo, creamos un pulso de audio:
><!doctype html> <html> <head> <title>Generando audio en tiempo real</title> <script type="text/javascript"> function playTone() { var output = new Audio(); output.mozSetup(1, 44100); var samples = new Float32Array(22050); var len = samples.length; for (var i = 0; i < samples.length ; i++) { samples[i] = Math.sin( i / 20 ); } output.mozWriteAudio(samples); } </script> </head> <body> <p>Esta demo tiene un tono de un segundo al hacer clic en el botón de abajo.</p> <button onclick="playTone();">Play</button> </body> </html>
El mozCurrentSampleOffset()
método da la posición sonora de la corriente de audio, es decir, la posición de la última muestra escuchado.
// Obtener la posición actual sonora de la secuencia de audio subyacentes, medido en las muestras. var currentSampleOffset = audioOutput.mozCurrentSampleOffset();
Los datos de audio por escrito con el mozWriteAudio()
método tiene que ser escrita en un intervalo regular en partes iguales, a fin de mantener un poco por delante de la muestra actual de compensación (offset de la muestra que está siendo desempeñado por el hardware se puede obtener con mozCurrentSampleOffset()
), donde "un poco" quiere decir algo del orden de 500 ms de las muestras. Por ejemplo, si se trabaja con dos canales de 44.100 muestras por segundo, un intervalo de escritura de 100 ms, y un buffer de pre-igual a 500 ms, se podrÃa escribir una serie de(2 * 44100 / 10) = 8820 muestras, y un total de (currentSampleOffset + 2 * 44100 / 2).
También es posible detectar automáticamente la duración mÃnima de la memoria previa, de tal manera que el sonido se reproduce sin interrupciones, y el desfase entre la escritura y la reproducción es mÃnimo. Para ello empezar a escribir los datos en pequeñas porciones y esperar a que el valor devuelto por mozCurrentSampleOffset()
debe ser mayor que 0.
var prebufferSize = sampleRate * 0.020; // Initial buffer is 20 ms var autoLatency = true, started = new Date().valueOf(); ... // Auto latency detection if (autoLatency) { prebufferSize = Math.floor(sampleRate * (new Date().valueOf() - started) / 1000); if (audio.mozCurrentSampleOffset()) { // Play position moved? autoLatency = false; }
El procesamiento de un audio de flujo
Desde el MozAudioAvailable evento y elmozWriteAudio()
metodo ambos utilizan Float32Array
valores, es posible tomar la salida de un flujo de audio y pasarlo directamente (o primer proceso y luego pasar) a un segundo. La secuencia de audio primero debe ser silenciado de manera que sólo el elemento secundario de audio que se escucha.
<audio id="a1" src="song.ogg" controls> </audio> <script> var a1 = document.getElementById('a1'), a2 = new Audio(), buffers = []; function loadedMetadata() { // Mute a1 audio. a1.volume = 0; // Configuracion a2 de ser idéntica a la a1, y jugar por ahÃ. a2.mozSetup(a1.mozChannels, a1.mozSampleRate); } function audioAvailable(event) { // Escriba el actual framebuffer var frameBuffer = event.frameBuffer; writeAudio(frameBuffer); } a1.addEventListener('MozAudioAvailable', audioAvailable, false); a1.addEventListener('loadedmetadata', loadedMetadata, false); function writeAudio(audio) { buffers.push(audio); // Si hay buffer de datos, escribe que while(buffers.length > 0) { var buffer = buffers.shift(); var written = a2.mozWriteAudio(buffer); // // Si todos los datos no se ha escrito,mantener los buffers: if(written < buffer.length) { buffers.unshift(buffer.slice(written)); return; } } } </script>