Introducción a la API de Web Audio

Antes del elemento HTML5 <audio>, se necesitaba Flash o algún otro complemento. romper el silencio de la Web. Si bien el audio en la Web ya no requiere un complemento, la etiqueta de audio conlleva limitaciones significativas para para implementar juegos sofisticados y aplicaciones interactivas.

La API de Web Audio es una API de JavaScript de alto nivel para procesar y sintetizar audio en aplicaciones web. El objetivo de esta API es incluyen las capacidades de los motores de audio de juegos modernos y algunas de las las tareas de mezcla, procesamiento y filtrado que se encuentran en las computadoras de escritorio modernas aplicaciones de producción de audio. Lo que sigue es una introducción amable a con esta potente API.

Comienza a usar AudioContext

Un AudioContext es para administrar y reproducir todos los sonidos. Para producir un sonido con la API de Web Audio, crear una o más fuentes de sonido y conéctalas al destino de sonido proporcionado por AudioContext. instancia. No es necesario que esta conexión sea directa. cualquier cantidad de AudioNodes intermedios que actúan como procesamiento módulos para la señal de audio. Este enrutamiento se describe de forma más detallada en la especificación de Web Audio.

Una sola instancia de AudioContext puede admitir varias entradas de sonido. gráficos de audio y complejos, por lo que solo necesitaremos uno de estos para cada de audio que creamos.

El siguiente fragmento crea un AudioContext:

var context;
window.addEventListener('load', init, false);
function init() {
    try {
    context = new AudioContext();
    }
    catch(e) {
    alert('Web Audio API is not supported in this browser');
    }
}

Para los navegadores más antiguos basados en WebKit, usa el prefijo webkit, como con webkitAudioContext

Muchas de las funciones interesantes de la API de Web Audio, como Los AudioNodes y la decodificación de datos de archivos de audio son métodos de AudioContext.

Cargando sonidos

La API de Web Audio usa un AudioBuffer para contenido de corto a medio sonidos. El enfoque básico consiste en usar XMLHttpRequest para para recuperar archivos de sonido.

La API admite la carga de datos de archivos de audio en varios formatos, como WAV, MP3, AAC, OGG y otros La compatibilidad de los navegadores con diferentes formatos de audio varían.

En el siguiente fragmento, se muestra cómo cargar una muestra de sonido:

var dogBarkingBuffer = null;
var context = new AudioContext();

function loadDogSound(url) {
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';

    // Decode asynchronously
    request.onload = function() {
    context.decodeAudioData(request.response, function(buffer) {
        dogBarkingBuffer = buffer;
    }, onError);
    }
    request.send();
}

Los datos del archivo de audio son binarios (no de texto), por lo que configuramos responseType. de la solicitud a 'arraybuffer'. Para obtener más información ArrayBuffers, consulta este artículo sobre XHR2.

Una vez que se reciben los datos del archivo de audio (sin decodificar), se pueden guardar para su decodificación posterior, o se puede decodificar al instante usando AudioContext decodeAudioData(). Este método toma la ArrayBuffer de datos de archivos de audio almacenados en request.response y lo decodifica de forma asíncrona (sin bloquear la ejecución principal de JavaScript) conversación).

Cuando finaliza decodeAudioData(), llama a una función de devolución de llamada que proporciona los datos de audio PCM decodificados como un AudioBuffer.

Reproduciendo sonidos

Un gráfico de audio simple
Un gráfico de audio simple

Una vez que se carguen uno o más AudioBuffers, estará todo listo para jugar. sonidos. Supongamos que acabamos de cargar un AudioBuffer con el sonido. de un perro ladrando y que la carga finalizó. Luego podemos jugar este búfer con el siguiente código.

var context = new AudioContext();

function playSound(buffer) {
    var source = context.createBufferSource(); // creates a sound source
    source.buffer = buffer;                    // tell the source which sound to play
    source.connect(context.destination);       // connect the source to the context's destination (the speakers)
    source.noteOn(0);                          // play the source now
}

Se podría llamar a la función playSound() cada vez que alguien presione una tecla o hace clic en algo con el mouse.

La función noteOn(time) facilita la programación de sonidos precisos la reproducción de juegos y otras aplicaciones urgentes. Sin embargo, para obtener esta programación funcione bien, asegúrate de que los búferes de sonido precargado.

Cómo abstraer la API de Web Audio

Por supuesto, sería mejor crear un sistema de carga más general. que no está codificado para cargar este sonido específico. Existen muchas para lidiar con los numerosos sonidos de corta y media duración que que usaría una aplicación o un juego de audio: esta es una forma de usar BufferLoader (que no forma parte del estándar web).

El siguiente es un ejemplo de cómo puedes usar la clase BufferLoader. Creemos dos AudioBuffers; y, en cuanto se cargan, vamos a reproducirlos al mismo tiempo.

window.onload = init;
var context;
var bufferLoader;

function init() {
    context = new AudioContext();

    bufferLoader = new BufferLoader(
    context,
    [
        '../sounds/hyper-reality/br-jam-loop.wav',
        '../sounds/hyper-reality/laughter.wav',
    ],
    finishedLoading
    );

    bufferLoader.load();
}

function finishedLoading(bufferList) {
    // Create two sources and play them both together.
    var source1 = context.createBufferSource();
    var source2 = context.createBufferSource();
    source1.buffer = bufferList[0];
    source2.buffer = bufferList[1];

    source1.connect(context.destination);
    source2.connect(context.destination);
    source1.noteOn(0);
    source2.noteOn(0);
}

Cómo lidiar con el tiempo: reproducir sonidos con ritmo

La API de Web Audio permite a los desarrolladores programar la reproducción con precisión. Para demuestran esto, configuremos una pista rítmica simple. Probablemente el el patrón de batería más conocido es el siguiente:

Un patrón simple de tambores de rock
Un patrón simple de tambores de rock

en el que se toca un hihat cada corchea, y que se se juega de forma alternada cada trimestre, cada 4 horas.

Supongamos que cargamos los búferes kick, snare y hihat. código para hacerlo es simple:

for (var bar = 0; bar < 2; bar++) {
    var time = startTime + bar * 8 * eighthNoteTime;
    // Play the bass (kick) drum on beats 1, 5
    playSound(kick, time);
    playSound(kick, time + 4 * eighthNoteTime);

    // Play the snare drum on beats 3, 7
    playSound(snare, time + 2 * eighthNoteTime);
    playSound(snare, time + 6 * eighthNoteTime);

    // Play the hi-hat every eighth note.
    for (var i = 0; i < 8; ++i) {
    playSound(hihat, time + i * eighthNoteTime);
    }
}

Aquí, hacemos solo una repetición en lugar del bucle ilimitado que vemos en la partitura. La función playSound es un método que reproduce un búfer en un momento específico, de la siguiente manera:

function playSound(buffer, time) {
    var source = context.createBufferSource();
    source.buffer = buffer;
    source.connect(context.destination);
    source.noteOn(time);
}

Cambiar el volumen de un sonido

Una de las operaciones más básicas que puedes realizar con un sonido es cambiar el volumen. Usando la API de Web Audio, podemos enrutar nuestro origen a su destino a través de un AudioGainNode con el fin de manipular la Volumen:

Gráfico de audio con un nodo de ganancia
Grafo de audio con un nodo de ganancia

Esta conexión se configura de la siguiente manera:

// Create a gain node.
var gainNode = context.createGainNode();
// Connect the source to the gain node.
source.connect(gainNode);
// Connect the gain node to the destination.
gainNode.connect(context.destination);

Después de configurar el gráfico, puedes cambiar el manipulando gainNode.gain.value de la siguiente manera:

// Reduce the volume.
gainNode.gain.value = 0.5;

Fundido cruzado entre dos sonidos

Supongamos que tenemos un escenario un poco más complejo, reproducir varios sonidos, pero quieres hacer un fundido cruzado entre ellos. Este es un común en una aplicación similar a un DJ, donde tenemos dos tocadiscos y y quieren desplazarse de una fuente de sonido a otra.

Esto se puede hacer con el siguiente gráfico de audio:

Gráfico de audio con dos fuentes conectadas a través de nodos de ganancia
Gráfico de audio con dos fuentes conectadas mediante nodos de ganancia

Para configurarlo, solo crearemos dos AudioGainNodes y conectaremos cada fuente a través de los nodos, con algo como esta función:

function createSource(buffer) {
    var source = context.createBufferSource();
    // Create a gain node.
    var gainNode = context.createGainNode();
    source.buffer = buffer;
    // Turn on looping.
    source.loop = true;
    // Connect source to gain.
    source.connect(gainNode);
    // Connect gain to destination.
    gainNode.connect(context.destination);

    return {
    source: source,
    gainNode: gainNode
    };
}

Encadenado de igual potencia

Un enfoque simple de encadenado lineal muestra una baja de volumen mientras te desplazas lateralmente entre las muestras.

Un encadenado lineal
Encadenado lineal

Para abordar este problema, usamos una curva de potencia igual, en la que el valor las curvas de ganancia correspondientes son no lineales y se cruzan a una mayor en la amplitud. Esto minimiza las caídas de volumen entre regiones de audio, lo que con un encadenado más uniforme entre regiones que podrían de diferentes niveles.

Un encadenado de potencias iguales.
Un encadenado de potencia igual

Encadenado de la playlist

Otra aplicación de encadenado común es para una aplicación de reproducción de música. Cuando cambia una canción, queremos atenuar la pista actual y atenuar para evitar que la transición sea molesta. Para hacerlo, programa una se cruzan hacia el futuro. Si bien podríamos usar setTimeout para hacerlo programación, esto no es preciso. Con la API de Web Audio, Puedes usar la interfaz AudioParam para programar valores futuros para parámetros, como el valor de ganancia de un AudioGainNode.

Así, con una playlist, podemos hacer una transición entre pistas programando una una disminución en la pista que se está reproduciendo y un aumento en la el siguiente, ambos justo antes de que la pista actual termine de reproducirse:

function playHelper(bufferNow, bufferLater) {
    var playNow = createSource(bufferNow);
    var source = playNow.source;
    var gainNode = playNow.gainNode;
    var duration = bufferNow.duration;
    var currTime = context.currentTime;
    // Fade the playNow track in.
    gainNode.gain.linearRampToValueAtTime(0, currTime);
    gainNode.gain.linearRampToValueAtTime(1, currTime + ctx.FADE_TIME);
    // Play the playNow track.
    source.noteOn(0);
    // At the end of the track, fade it out.
    gainNode.gain.linearRampToValueAtTime(1, currTime + duration-ctx.FADE_TIME);
    gainNode.gain.linearRampToValueAtTime(0, currTime + duration);
    // Schedule a recursive track change with the tracks swapped.
    var recurse = arguments.callee;
    ctx.timer = setTimeout(function() {
    recurse(bufferLater, bufferNow);
    }, (duration - ctx.FADE_TIME) - 1000);
}

La API de Web Audio proporciona un conjunto conveniente de métodos RampToValue para cambiar gradualmente el valor de un parámetro, como linearRampToValueAtTime y exponentialRampToValueAtTime.

Mientras que la función de sincronización de la transición se puede elegir de la y exponenciales (como se muestra más arriba), también puedes especificar tu propio valor a través de un array de valores con la función setValueCurveAtTime.

Aplicar un efecto de filtro simple a un sonido

Un gráfico de audio con un BiquadFilterNode
Un gráfico de audio con un BiquadFilterNode

La API de Web Audio permite canalizar sonido de un nodo de audio a otro, lo que crea una cadena potencialmente compleja de procesadores para agregar efectos a tus formas de sonido.

Una forma de hacerlo es colocar los BiquadFilterNode entre tu sonido fuente y destino. Este tipo de nodo de audio puede hacer filtros de bajo orden que se pueden usar para crear ecualizadores gráficos e incluso efectos más complejos, relacionados principalmente con la selección de qué partes de la espectro de frecuencia de un sonido para enfatizar y cuál suponer.

Entre los tipos de filtros admitidos, se incluyen los siguientes:

  • Filtro de paso bajo
  • Filtro de pase alto
  • Filtro de pase de banda
  • Filtro de barra baja
  • Filtro de estantería alta
  • Filtro de picos
  • Filtro de muesca
  • Filtro para todos los pases

Y todos los filtros incluyen parámetros para especificar aumento, la frecuencia con la que se aplica el filtro y un factor de calidad. El filtro de paso bajo mantiene el rango de frecuencia más bajo, pero descarta alto frecuencias. El punto de interrupción está determinado por el valor de la frecuencia, y el factor Q no tiene unidades y determina la forma de la gráfico. La ganancia solo afecta ciertos filtros, como los de biblioteca baja los filtros de aumento repentino, y no este filtro de paso bajo.

Configuremos un filtro simple de paso bajo para extraer solo las bases de un muestra de sonido:

// Create the filter
var filter = context.createBiquadFilter();
// Create the audio graph.
source.connect(filter);
filter.connect(context.destination);
// Create and specify parameters for the low-pass filter.
filter.type = 0; // Low-pass filter. See BiquadFilterNode docs
filter.frequency.value = 440; // Set cutoff to 440 HZ
// Playback the sound.
source.noteOn(0);

En general, los controles de frecuencia deben ajustarse para funcionar en una escala logarítmica, ya que la audición humana en sí funciona según el mismo principio (es decir, A4 es 440 Hz y A5 es 880 Hz). Para obtener más detalles, consulta la función FilterSample.changeFrequency en el vínculo del código fuente anterior.

Por último, ten en cuenta que el código de muestra te permite conectar y desconectar la y cambia dinámicamente el gráfico AudioContext. Podemos desconectar AudioNodes del gráfico llamando a node.disconnect(outputNumber). Por ejemplo, para redirigir el gráfico de pasar por un filtro, a un directa, podemos hacer lo siguiente:

// Disconnect the source and filter.
source.disconnect(0);
filter.disconnect(0);
// Connect the source directly.
source.connect(context.destination);

Más escucha

Abordamos los conceptos básicos de la API, incluidas la carga y la reproducción de audio. de muestra. Creamos gráficos de audio con nodos y filtros de ganancia, y sonidos programados y ajustes de parámetros de audio para habilitar algunos sonidos comunes efectos. Ya tienes todo listo para crear una Web genial aplicaciones de audio.

Si buscas inspiración, muchos desarrolladores ya crearon gran trabajo usando la API de Web Audio. Algunas de mis favoritas incluyen:

  • AudioJedit, una herramienta de empalme de sonido en el navegador que utiliza Vínculos permanentes de SoundCloud.
  • ToneCraft, un secuenciador de sonido en el que los sonidos se crean apilando bloques 3D.
  • Plink, un juego colaborativo de creación de música que usa Web Audio y Web Enchufes