ManzDev
Perfil de Manz Dashboard de actividad
HTML5 Etiquetas HTML
CSS CSS vanilla
PostCSS Transformador CSS
Javascript Javascript ES2020+
NPM Node Package Manager
WebComponents Componentes web
Terminal Terminal de GNU/Linux
VueJS Framework VueJS 3
Automatizadores Empaquetadores Javascript
Dibujando con CSS ¡Dibujos hechos con sólo CSS!
Experimentos con Javascript ¡Experimentos con Javascript!

API de Audio Javascript

Javascript proporciona una serie de API nativas para trabajar con archivos multimedia directamente desde el navegador. De esta forma, podemos permitir al usuario interactuar con recursos multimedia de forma dinámica, e incluso hacer multitud de interesantes aplicaciones que utilicen tanto archivos de audio como de video.

Aunque se puede trabajar con muchos otros tipos de formatos multimedia como archivos .mp4 (video) o archivos .ogg (audio vorbis), en este artículo trataremos archivos .mp3, ya que son los más populares en cuanto a archivos multimedia de audio. Muchas veces hablaremos de recursos multimedia, ya que igual que lo hacemos con un archivo de audio, se puede hacer con un archivo de video.

Ejemplo básico

Un ejemplo básico para reproducir un archivo multimedia .mp3 (obsérvese que está alojado en una página de GitHub) desde nuestro navegador, podría ser el siguiente:

const audio = new Audio("//manzdev.github.io/codevember2017/assets/eye-tiger.mp3");
audio.play();

La constante audio contiene una instancia del objeto Audio, al que se le pasa por parámetro la URL del archivo .mp3 que queremos inicializar. En lugar de una URL, también podríamos indicar simplemente el nombre de un archivo .mp3, con lo que el navegador buscaría dicho archivo en la misma ruta donde tenemos nuestro archivo .js con el código Javascript.

Posteriormente, ejecutamos la función .play() de la instancia audio recién creada.

En este ejemplo, lo que hemos hecho realmente es crear una etiqueta HTML de audio, indicándole el archivo de audio a inicializar, sólo que esa etiqueta no reside «físicamente» en el documento HTML, si no que está en memoria, en la constante audio.

En el siguiente ejemplo podemos ver un ejemplo análogo al anterior:

const audio = document.createElement("audio");
audio.preload = "auto";
audio.src = "https://manzdev.github.io/codevember2017/assets/eye-tiger.mp3";
audio.play();
document.body.appendChild(audio);

Sin embargo, en esta última línea, hemos añadido esa etiqueta audio al documento HTML en el que nos encontramos, antes de cerrar el </body>. Si quieres más información sobre esto, mira el capítulo de trabajo en el DOM.

Propiedades básicas

Al crear un objeto de audio mediante Javascript (que no es más que una etiqueta HTML), podemos utilizar la notación de objeto para crear propiedades que reflejarán el valor en atributos de la etiqueta HTML. De esta forma, podemos utilizar las siguientes propiedades, que son todas booleanas:

Propiedades Descripción
.autoplay El audio se reproducirá automáticamente. OJO: Es muy probable que no funcione*.
.controls Muestra los controles (play, pausa, tiempo, etc...) del elemento multimedia.
.loop Una vez el audio llegue a su fin, volverá a empezar.
.muted El elemento multimedia está silenciado y no se escuchará.
.defaultMuted El valor muted inicial por defecto, aunque muted se haya modificado posteriormente.

Un ejemplo de su uso sería el siguiente, donde vemos que podemos tanto obtener su valor como asignar un booleano para modificarlo:

const audio = new Audio("sound.mp3");
audio.loop = true;
audio.controls = true;
audio.play();

Desde 2017, Chrome, Firefox y otros navegadores establecieron un cambio de políticas con el atributo de reproducción automática autoplay. Para evitar molestos comportamientos de audio indeseado en páginas webs, por defecto la autoreproducción sólo funciona si el usuario ha interactuado antes en la página. Lo recomendable es sólo reproducir música o sonido si el usuario hace una acción específica (click de ratón o similar).

Propiedades de reproducción

Si quisieramos modificar nuestro objeto de Audio para indicarle un archivo multimedia diferente, sólo tendríamos que asignarle un diferente a la propiedad url. De esta forma, tenemos muchas más propiedades, relacionadas con la reproducción multimedia:

Propiedades Descripción
.src Propiedad que devuelve la URL al archivo multimedia de audio.
.volume Volumen del audio, desde 0 (silencio) a 1 (máximo). Permite decimales.
.paused Indica si el audio está pausado (cualquier estado que no sea reproduciendo).
.ended Indica si el audio se ha terminado de reproducir.
.currentTime Número de segundos (con decimales) en el que se encuentra la reproducción.
.duration Duración total en segundos (con decimales) del audio.
.playbackRate Velocidad de reproducción, desde 0 hasta 4. Velocidad normal: 1. Muteado: 0.
.defaultPlaybackRate El valor inicial de playbackRate, tras haberlo modificado.

Observa en el siguiente ejemplo que, .currentTime además de usarse para obtener el momento exacto en el que se encuentra la reproducción, también se puede utilizar para modificar ese momento. Por ejemplo, .currentTime = 0 se podría utilizar para inicializar el sonido:

const audio = new Audio("https://manzdev.github.io/codevember2017/assets/eye-tiger.mp3");

audio.volume = 0.5;               // Volumen al 50%
console.log(audio.paused);        // true (no se está reproduciendo)
console.log(audio.currentTime);   // 0
console.log(audio.duration);      // 17.81551 (17 secs, 815.51 ms)
audio.currentTime = 5;            // Move to 5 secs
audio.play();                     // Starts audio from 5 sec

Propiedades de precarga

Cuando establecemos el atributo .src en un elemento de audio, el navegador automáticamente llama a la función .load() (ver más adelante) y se comienza a precargar el elemento de audio, dependiendo del valor que tenga la propiedad o atributo .preload. Veamos varias de las propiedades relacionadas con la precarga de un objeto de Audio:

Propiedades Descripción
.preload Indica si el audio debe precargarse: auto (valor por defecto), si sólo deben precargarse los metadatos: metadata, o si no debe precargarse nada: none.
.networkState Indica en que estado está la red:
NETWORK_EMPTY (0) - No hay datos aún
NETWORK_IDLE (1) - No está descargando información
NETWORK_LOADING (2) - Está descargando información
NETWORK_NO_SOURCE (3) - No se ha encontrado la fuente de .src
.readyState Indica en que estado está el recurso multimedia:
HAVE_NOTHING (0) - Sin información
HAVE_METADATA (1) - Tiene metadatos almacenados, pero para búsqueda.
HAVE_CURRENT_DATA (2) - Datos suficientes para la posición actual.
HAVE_FUTURE_DATA (3) - Datos suficientes para saltos cercanos.
HAVE_ENOUGH_DATA (4) - Datos suficientes para saltos sin interrupción.
.seeking Indica si está en proceso de moverse a un nuevo instante de tiempo (buscando).

Mientras que el valor preload puede indicarse como un atributo HTML, el resto de propiedades no se pueden indicar, y son exclusivas de la API de Javascript. Generalmente suelen utilizarse para saber el estado de carga de un elemento de audio o información relacionada.

Métodos o funciones de audio

Los métodos que más nos pueden interesar en la API de Audio de Javascript son los de .play() y .pause(), ya que son los que comienzan a reproducir (o continuan una reproducción pausada previamente) y detienen una reproducción respectivamente.

Quizás, el método más extraño sea .canPlayType(mimetype), el cuál se puede llamar, indicando como parámetro el mimetype de un fichero de audio: por ejemplo, en el caso de un .mp3 sería audio/mp3 y en el caso de un .ogg sería audio/ogg. En el primer caso, Chrome nos devuelve el probably que indica que es muy probable que esté soportado, mientras que en el segundo caso devuelve maybe, con el que indica que es menos probable, y depende de otros factores (sistema operativo, etc...).

Por otro lado, tenemos el método .load() que se encarga de inicializar un recurso y comenzar a procesarlo, dependiendo del valor actual del atributo preload que hablamos antes. El método .load() se llama automágicamente al cambiar el atributo o propiedad .src.

Métodos Descripción
.canPlayType(mimetype) Indica si el tipo MIME indicado está soportado (probably) o no (maybe).
.load() Inicializa el recurso multimedia para prepararlo para reproducir.
.play() Comienza a reproducir (o reanuda) el audio en cuestión.
.pause() Pausa el sonido, con la posibilidad de reanudarlo.

Por último, tenemos los métodos .play() (reproducir un audio) y .pause() (detener un audio), que tienen algunas puntualizaciones:

const audio = new Audio("audio.mp3");

audio.play();          // Comienza a reproducir el audio
audio.pause();         // Pausa el audio
audio.play();          // Continua el audio por donde fue pausado

audio.pause();         // Vuelve a pausar
audio.currentTime = 0; // Coloca la reproducción en el segundo 0 (inicio)
audio.play();          // Reproduce desde el inicio

Es importante mencionar que el método .play() devuelve una promesa que se resuelve cuando el audio está descargado y ha comenzado a reproducirse. Ten en cuenta que dicho audio está alojado en una página web y puede no estar disponible en el momento de reproducir el audio, es por esto que devuelve una promesa:

const audio = new Audio("audio.mp3");

audio.play()
  .then(() => console.log("Ha comenzado a reproducirse el sonido..."));

Sin embargo, el audio, al ser una acción auditiva, que no requiere visualización implícita, muchas veces no es necesario controlar la promesa y se puede ejecutar el .play() directamente.

Eventos de audio

Una de las partes más interesantes de la API multimedia es la posibilidad de jugar con eventos multimedia relacionados con objetos de audio. Recuerda que los eventos en Javascript son acciones muy específicas que suceden en momentos concretos de la vida de nuestra aplicación o web:

  • Cuando el usuario hace click...
  • Cuando el usuario hace scroll...
  • Cuando el usuario pausa un sonido en la barra de control...

De esta forma, tenemos múltiples tipos de eventos que podemos utilizar. En este primer bloque encontraremos los eventos que ocurren en momentos comunes de la reproducción de un recurso multimedia, como cuando comenzamos a reproducirlo, lo detenemos o vamos a un momento específico de su duración:

Eventos de reproducción Descripción
.pause Se ha pausado y detenido la reproducción del recurso multimedia.
.play Se ha comenzado (o reanudado) la reproducción del recurso multimedia.
.ended Se ha llegado al final del recurso multimedia.
.seeking Ha comenzado la búsqueda de una posición de tiempo en un recurso multimedia.
.seeked Ha terminado la búsqueda de una posición de tiempo en un recurso multimedia.

Veamos un ejemplo básico de uso de algunos de estos eventos:

const audio = new Audio("audio.mp3");

audio.addEventListener("play", () => {
  console.log("Se ha comenzado/reanudado la reproducción");
});

audio.addEventListener("pause", () => {
  console.log("Se ha pausado la reproducción");
});

Por otro lado, también podemos ejecutar una lógica específica cuando suceden ciertos eventos relacionados con el cambio de alguno de sus parámetros iniciales, como la velocidad de reproducción o el segundo actual de reproducción:

Eventos de cambios Descripción
.ratechange Se ha modificado la velocidad de reproducción del recurso multimedia.
.durationchange La duración del recurso multimedia ha cambiado.
.timeupdate El instante actual de tiempo de reproducción ha cambiado. Se dispara continuamente.
.volumechange El volumen del recurso multimedia ha sido modificado.

Observa en el siguiente ejemplo, que el evento timeupdate se ejecutará varias veces en el mismo segundo, no una sola vez por segundo como quizás se imagina inicialmente. Esto ocurre porque la precisión de la actualización de tiempo, afecta a milisegundos, por lo que se ejecuta varias veces en el mismo segundo:

const audio = new Audio("audio.mp3");

audio.addEventListener("timeupdate", () => {
  console.log("Current second: ", Number.parseInt(audio.currentTime));
});

Por último, tenemos múltiples eventos relacionados con la carga o precarga de los recursos multimedia, sus metadatos o el proceso de «buffering» donde la reproducción no se puede realizar porque aún no se ha descargado la suficiente cantidad de datos para reproducir:

Eventos de carga Descripción
.abort La carga del recurso multimedia se ha detenido, pero no por un error.
.error La carga del recurso multimedia se ha detenido, resultado de un error.
.suspend Se suspende (intencionalmente) la carga de datos del recurso multimedia.
.canplay Se puede reproducir el recurso multimedia, pero habrá que esperar por «buffering».
.canplaythrough Se puede reproducir el recurso multimedia sin necesidad de esperar «buffering».
.emptied La carga del recurso se ha quedado vacía. Ejemplo: Después de un .load().
.loadstart El navegador ha comenzado a cargar un recurso multimedia.
.loadeddata El navegador ha precargado el primer bloque de datos del recurso multimedia.
.loadedmetadata El navegador ha precargado los metadatos del recurso multimedia.
.playing Tras una falta de datos, el recurso multimedia vuelve a estar listo para reproducirse.
.progress Se dispara frecuentemente, cuando el navegador ha cargado dicho recurso.
.waiting La reproducción se ha detenido por ausencia (temporal) de datos.
.stalled El navegador intenta obtener datos de un recurso pero no los recibe.

Muchos de estos eventos nos permiten personalizar como actuará nuestra aplicación o web respecto a lo que haya sucedido.

Cuidado: No confundir el evento .onplay con el evento .onplaying. El segundo es el que ocurre cuando ya podemos reproducir un audio porque estaba en proceso de «buffering», mientras que el primero es el que se dispara cuando comenzamos o continuamos una reproducción multimedia.

Librerías de terceros

Es posible que en algunos casos, la API multimedia nativa de Javascript se nos quede corta. En ese caso, existen algunas librerías interesantes que pueden servirnos para simplificar nuestro código y realizar acciones más concretas.

Por ejemplo, Howler.js es una excelente librería para cuando la API nativa se nos queda corta o necesitamos algo un poco más potente:

Librería Descripción URL Descripción
Howler.js Librería para trabajar con audio de forma sencilla. Howler.js GitHub
Tone.js Framework de audio (DAW) para crear música interactiva. Tone.js GitHub
Pizzicato Librería para crear y manipular sonidos vía Web Audio API. Pizzicato GitHub
WaveSurferJS Visualización en forma de onda usando Web Audio / Canvas. WaveSurfer GitHub
Web Audio DAW Estación de trabajo de Audio Digital. Un «jQuery» de audio. - GitHub
Blip Librería ligera que usa Web Audio y lo simplifica. BlipJS GitHub

Otras librerías como Tone.js o Pizzicato nos permiten centrarnos en la generación de sonidos utilizando la API Web Audio y/o utilizando sonidos externos.

Manz
Publicado por Manz

Docente, divulgador informático y Streamer de código. Amante del CSS y de la plataforma web en general. Autor de Emezeta.com tiempo atrás. Ha trabajado como profesor en la Universidad de La Laguna y es director del curso de Programación web FullStack y FrontEnd de EOI en Tenerife. En sus ratos libres, busca GIF de gatos en Internet.