Web Audio API: ¿Por qué componer cuando puedes programar?
Publicado: 2022-03-11El primer borrador de una API de audio web apareció en W3C durante 2011. Aunque el audio en las páginas web ha sido compatible durante mucho tiempo, hasta hace poco no estuvo disponible una forma adecuada de generar audio desde el navegador web. Yo personalmente atribuyo esto a Google Chrome, porque en cuanto al interés de Google, el navegador comenzó a convertirse en la parte más importante de una computadora. Puede recordar que el ámbito del navegador web no comenzó a cambiar mucho hasta que apareció Google Chrome. Si usó sonido en una página web en este momento, habría sido una mala decisión de diseño. Pero desde que apareció la idea de los experimentos web, el audio web volvió a tener sentido. Los navegadores web hoy en día son otra herramienta para la expresión artística, y el video y el audio en el navegador web juegan un papel vital en ello.
Web Audio API puede ser bastante difícil de usar para algunos fines, ya que aún está en desarrollo, pero ya existen varias bibliotecas de JavaScript para facilitar las cosas. En este caso, le mostraré cómo comenzar con Web Audio API usando una biblioteca llamada Tone.js. Con esto, podrá cubrir la mayoría de las necesidades de sonido de su navegador con solo aprender lo básico.
Hola API de audio web
Empezando
Comenzaremos sin usar la biblioteca. Nuestro primer experimento implicará hacer tres ondas sinusoidales. Como este será un ejemplo simple, crearemos solo un archivo llamado hola.html, un archivo HTML simple con una pequeña cantidad de marcado.
<!DOCTYPE html> <html> <head> <meta charset="utf‐8"> <title> Hello web audio </title> </head> <body> </body> <script> </script> </html>
El núcleo de Web Audio API es el contexto de audio. El contexto de audio es un objeto que contendrá todo lo relacionado con el audio web. No se considera una buena práctica tener más de un contexto de audio en un solo proyecto. Comenzaremos instanciando un contexto de audio siguiendo las recomendaciones dadas por la documentación de la API de audio web de Mozilla.
var audioCtx = new (window.AudioContext || window.webkitAudioContext);
hacer un oscilador
Con un contexto de audio instanciado, ya tiene un componente de audio: el audioCtx.destination. Esto es como su altavoz. Para hacer un sonido, debe conectarlo a audioCtx.destination. Ahora, para producir algo de sonido, creemos un oscilador:
var sine = audioCtx.createOscillator();
Genial, pero no suficiente. También debe iniciarse y conectarse a nuestro audioCtx.destination:
sine.start(); sine.connect(audioCtx.destination);
Con estas cuatro líneas, tendrás una página web bastante molesta que reproduce un sonido sinusoidal, pero ahora entiendes cómo los módulos pueden conectarse entre sí. En el siguiente script, habrá tres tonos en forma de seno, conectados a la salida, cada uno con un tono diferente. El código es muy autoexplicativo:
//create the context for the web audio var audioCtx = new (window.AudioContext || window.webkitAudioContext)(); //create, tune, start and connect each oscillator sinea, sineb and sinec var sinea = audioCtx.createOscillator(); sinea.frequency.value = 440; sinea.type = "sine"; sinea.start(); sinea.connect(audioCtx.destination); var sineb = audioCtx.createOscillator(); sineb.frequency.value = 523.25; sineb.type = "sine"; sineb.start(); sineb.connect(audioCtx.destination); var sinec = audioCtx.createOscillator(); sinec.frequency.value = 698.46; sinec.type = "sine"; sinec.start(); sinec.connect(audioCtx.destination);
Los osciladores no están restringidos a ondas sinusoidales, sino que también pueden ser triangulares, de diente de sierra, cuadrados y con forma personalizada, como se establece en el MDN.
Lógica de aplicación de parches de audio web
A continuación, agregaremos un módulo de ganancia a nuestra orquesta de componentes de Web Audio. Este módulo nos permite cambiar la amplitud de nuestros sonidos. Es similar a una perilla de volumen. Ya hemos usado la función de conexión para conectar un oscilador a la salida de audio. Podemos usar la misma función de conexión para conectar cualquier componente de audio. Si está utilizando Firefox y echa un vistazo a la consola de audio web, verá lo siguiente:
Si queremos cambiar el volumen, nuestro parche debería verse así:
Lo que significa que los osciladores ya no están conectados al destino de audio, sino a un módulo de ganancia, y ese módulo de ganancia está conectado al destino. Siempre es bueno imaginar que haces esto con pedales y cables de guitarra. El código se verá así:
var audioCtx = new (window.AudioContext || window.webkitAudioContext) // we create the gain module, named as volume, and connect it to our var volume = audioCtx.createGain(); volume.connect(audioCtx.destination); //these sines are the same, exept for the last connect statement. //Now they are connected to the volume gain module and not to the au var sinea = audioCtx.createOscillator(); sinea.frequency.value = 440; sinea.type = "sine"; sinea.start(); sinea.connect(volume); var sineb = audioCtx.createOscillator(); sineb.frequency.value = 523.25; sineb.type = "sine"; sineb.start(); sineb.connect(volume); var sinec = audioCtx.createOscillator(); sinec.frequency.value = 698.46; sinec.type = "sine"; sinec.start(); sinec.connect(volume); volume.gain.value=0.2;
Puede encontrar la solución en https://github.com/autotel/simple-webaudioapi/.
GainNode es la unidad de efectos más básica, pero también hay un retardo, un convólver, un filtro bicuadrático, un panorama estéreo, un modelador de ondas y muchos otros. Puede obtener nuevos efectos de bibliotecas como Tone.js.
Almacenar uno de estos parches de sonido en sus propios objetos le permitirá reutilizarlos según sea necesario y crear orquestaciones más complejas con menos código. Este podría ser un tema para un próximo post.
Haciendo las cosas más fáciles con Tone.js
Ahora que hemos analizado brevemente cómo funcionan los módulos Vanilla Web Audio, echemos un vistazo al increíble marco Web Audio: Tone.js. Con esto (y NexusUI para los componentes de la interfaz de usuario), podemos construir muy fácilmente sintetizadores y sonidos más interesantes. Para probar cosas, hagamos una muestra y apliquemos algunos efectos interactivos para el usuario, y luego agregaremos algunos controles simples para esta muestra.
Muestra de Tone.js
Podemos comenzar creando una estructura de proyecto simple:
simpleSampler |-- js |-- nexusUI.js |-- Tone.js |-- noisecollector_hit4.wav |-- sampler.html
Nuestras bibliotecas JavaScript residirán en el directorio js . A los efectos de esta demostración, podemos utilizar el archivo hit4.wav de NoiseCollector que se puede descargar de Freesound.org.

Tone.js proporciona sus funcionalidades a través de objetos Player. La capacidad básica del objeto es cargar una muestra y reproducirla en un bucle o una vez. Nuestro primer paso aquí es crear un objeto de reproductor en una variable de "muestra", dentro del archivo sampler.html:
<!doctype html> <html> <head> <title> Sampler </title> <script type="text/javascript" src="js/nexusUI.js" ></script> <script type="text/javascript" src="js/Tone.js" ></script> <script> var sampler = new Tone.Player("noisecollector_hit4.wav", function() { console.log("samples loaded"); }); </script> </head> <body> </body> </html>
Tenga en cuenta que el primer parámetro del constructor del reproductor es el nombre del archivo WAV y el segundo es una función de devolución de llamada. WAV no es el único tipo de archivo admitido, y la compatibilidad depende más del navegador web que de la biblioteca. La función de devolución de llamada se ejecutará cuando el reproductor haya terminado de cargar la muestra en su búfer.
También tenemos que conectar nuestro sampler a la salida. La forma Tone.js de hacer esto es:
sampler.toMaster();
… donde sampler es un objeto Tone.Player, después de la línea 10. La función toMaster es la abreviatura de connect(Tone.Master).
Si abre su navegador web con la consola del desarrollador abierta, debería ver el mensaje "muestras cargadas", que indica que el reproductor se creó correctamente. En este punto, es posible que desee escuchar la muestra. Para hacer eso, necesitamos agregar un botón a la página web y programarlo para que reproduzca la muestra una vez presionado. Vamos a usar un botón NexusUI en el cuerpo:
<canvas nx="button"></canvas>
Ahora debería ver un botón redondeado que se representa en el documento. Para programarlo para reproducir nuestra muestra, agregamos un oyente NexusUI, que se ve así:
button1.on('*',function(data) { console.log("button pressed!"); })
Algo destacado de NexusUI es que crea una variable global para cada elemento de NexusUI. Puede configurar NexusUI para que no haga eso y, en su lugar, tener estas variables solo en nx.widgets[] configurando nx.globalWidgets en falso. Aquí vamos a crear solo un par de elementos, por lo que nos limitaremos a este comportamiento.
Al igual que en jQuery, podemos poner estos eventos .on y el primer argumento será el nombre del evento. Aquí solo estamos asignando una función a lo que sea que se haga con el botón. Este lo que sea se escribe como “*”. Puede obtener más información sobre los eventos de cada elemento en la API de NexusUI. Para reproducir la muestra en lugar de registrar mensajes cuando presionamos el botón, debemos ejecutar la función de inicio de nuestra muestra.
nx.onload = function() { button1.on('*',function(data) { console.log("button pressed!"); sampler.start(); }); }
También observe que el oyente entra en una devolución de llamada de carga. Los elementos de NexusUI se dibujan en el lienzo y no puede consultarlos hasta que nx llame a la función de carga. Tal como lo haría con los elementos DOM en jQuery.
El evento se activa al presionar el mouse y al soltarlo. Si desea que se active solo al presionar, debe evaluar si event.press es igual a uno.
Con esto, deberías tener un botón que reproduzca la muestra en cada pulsación. Si establece sampler.retrigger en verdadero, le permitirá reproducir la muestra independientemente de si se está reproduciendo o no. De lo contrario, debe esperar hasta que finalice la muestra para volver a activarla.
Aplicación de efectos
Con Tone.js, podemos crear fácilmente un retraso:
var delay= new Tone.FeedbackDelay("16n",0.5).toMaster();
El primer argumento es el tiempo de retardo, que se puede escribir en notación musical como se muestra aquí. El segundo es el nivel húmedo, que significa la mezcla entre el sonido original y el sonido que tiene un efecto sobre él. Para los delays no sueles querer un 100% mojado, porque los delays son interesantes con respecto al sonido original, y el mojado por sí solo no es muy atractivo como los dos juntos.
El siguiente paso es desconectar nuestro sampler del master y conectarlo al retardo. Modifique la línea donde la muestra está conectada al maestro:
sampler.connect(delay);
Ahora pruebe el botón de nuevo y vea la diferencia.
A continuación, agregaremos dos diales al cuerpo de nuestro documento:
<canvas nx="dial"></canvas> <canvas nx="dial"></canvas>
Y aplicamos los valores de los diales al efecto de retardo usando NexusUIlistener:
dial1.on('*',function(data) { delay.delayTime.value=data.value; }) dial2.on('*',function(data) { delay.feedback.value=data.value; })
Los parámetros que puede modificar en cada evento se pueden encontrar en la documentación de Tone.js. Por demora, está aquí. Ahora está listo para probar el ejemplo y ajustar los parámetros de retardo con los diales NexusUI. Este proceso se puede realizar fácilmente con cada elemento de NexusUI, sin limitarse solo a los efectos. Por ejemplo, también intente agregar otro dial y agregue su oyente de la siguiente manera:
dial3.on('*',function(data) { sampler.playbackRate=data.value; })
Puede encontrar estos archivos en github.com/autotel/simpleSampler
Conclusión
Cuando revisé estas API, comencé a sentirme abrumado por todas las posibilidades e ideas que comenzaron a venir a mi mente. La gran diferencia entre esta implementación de audio y las implementaciones tradicionales de audio digital no está en el audio en sí, sino en el contexto. No hay nuevos métodos de hacer síntesis aquí. Más bien, la innovación es que la creación de audio y música ahora se encuentra con las tecnologías web.
Estoy personalmente involucrado en la creación de música electrónica, y esta área siempre ha tenido esta paradoja de la ambigüedad entre tocar música y simplemente presionar reproducir una pista grabada. Si realmente quieres hacer música electrónica en vivo, debes ser capaz de crear tus propias herramientas interpretativas o "robots para hacer música" para la improvisación en vivo. Pero si la interpretación de la música electrónica se convierte simplemente en ajustar parámetros en algoritmos de creación de música preparados previamente, entonces la audiencia también puede participar en este proceso. He estado trabajando en pequeños experimentos con respecto a esta integración de la web y el audio para la música colaborativa, y tal vez pronto estemos asistiendo a fiestas donde la música proviene de la audiencia a través de sus teléfonos inteligentes. Después de todo, no es tan diferente de las improvisaciones rítmicas que podríamos haber disfrutado en la era de las cavernas.
Lecturas adicionales en el blog de ingeniería de Toptal:
- Tutorial de WebAssembly/Rust: Procesamiento de audio perfecto
- Tutorial MIDI: creación de aplicaciones de audio basadas en navegador controladas por hardware MIDI