En este artículo vamos a echar un vistazo a algunas de las buenas prácticas que deben seguirse para utilizar la etiqueta <audio> en las aplicaciones web, y también voy a dar una serie de consejos útiles y enseñanzas aprendidas de sitios web del mundo real.
<audio src="audio/sample.mp3" autoplay>
</audio>
var audio = document.createElement("audio");
if (audio != null && audio.canPlayType && audio.canPlayType("audio/mpeg"))
{
audio.src = "audio/sample.mp3";
audio.play();
}
<audio src="data:audio/mpeg,ID3%02%00%00%00%00%..." autoplay>
</audio>
La primera opción nos permite inicializar los componentes de audio durante la carga de la página. La segunda es más flexible y facilita la gestión del flujo de red, puesto que difiere la carga del archivo de audio a un momento concreto durante el ciclo de vida de la aplicación. La tercera alternativa (menos recomendable) consiste en embeber los propios archivos de audio como direcciones en formato de datos-uri en la propia página, lo que reduce el número de peticiones al servidor.
Podemos comprobar que el elemento de audio generado por Javascript se puede reproducir aunque no se haya añadido aún al árbol del DOM (como sucede en módulo de código anterior). No obstante, al añadir el elemento de audio a la página nos va a permitir mostrar la barra de control por defecto:
Aunque no hablaré de eso en este artículo, podemos utilizar más de un formato de archivo de audio. Además, si los archivos de audio los tenemos alojados en nuestro servidor, tenemos que acordarnos de registrar el tipo MIME para los archivos MP3 ("audio/mpeg") en el propio servidor. Aquí, por ejemplo, se puede ver la configuración Internet Information Services (IIS).

El impacto sobre la red de estas tres opciones se puede predecir ejecutando esta página y utilizando las Herramientas de Desarrollo F12 (pestaña Network). Para fines de depuración, podemos simular nuevas llamadas y desactivar la cache local marcando la opción del menú "Always refresh from sever".

preload=none:

preload=metadata:

preload=auto:

Aunque esta propiedad es sumamente útil en la fase de inicialización, puede que necesitemos saber también en qué momento el navegador ha terminado de descargar el archivo de audio y ya podemos reproducirlo. Esto lo podemos saber escuchando el evento "canplaythrough"; a este evento lo llama el Agente de Usuario una vez que considera que si en ese momento se empezara a reproducir el archivo, el sonido podría empezar a escucharse a la tasa de reproducción prevista hasta su finalización, sin tener que detenerse para volver a cargar el buffer.
var audio = document.createElement("audio");
audio.src = "audio/sample.mp3";
audio.addEventListener("canplaythrough", function () {
alert('The file is loaded and ready to play!');
}, false);
<audio src="audio/sample.mp3" autoplay loop>
</audio>
Otra forma de reproducir en bucle los archivos de audio consiste en llamar desde programa al método play() cuando finaliza la pista de sonido; de esta manera podemos también controlar el tiempo de retardo entre una reproducción y la siguiente.
var audio = document.createElement("audio");
audio.src = "piano/3C.mp3";
audio.addEventListener('ended', function () {
// Wait 500 milliseconds before next loop
setTimeout(function () { audio.play(); }, 500);
}, false);
audio.play();
Interesa saber que cualquier llamada play() que se ejecute sobre el elemento de audio antes de llegar al final de la pista no tiene efecto alguno. Si tienes interés en "cancelar y recuperar" la reproducción de audio en un punto concreto, necesitarás resetear el valor de currentTime.
var audio = null;
audio = document.createElement("audio");
audio.src = "piano/3C.mp3";
audio.addEventListener('ended', function () {
audio.play();
}, false);
function play() {
audio.play();
}
function restart() {
audio.currentTime = 0;
audio.play();
}
Este ejemplo de código nos muestra cómo se cargan y reproducen múltiples archivos de audio desde el código HTML. Los archivos de ejemplo tienen todos la misma duración. Al final de la reproducción vuelven a reproducirse desde el inicio. Si se ejecuta este código en Internet Explorer 9, podremos ver que se sincronizan de manera automática durante cada una de las vueltas del bucle. También podrás comprobar que la combinación de estos cinco sonidos devuelve un sonido final que se parece mucho al archivo de audio utilizado en la demostración anterior ("sample.mp3").
<body>
<audio src="audio/Bass.mp3" autoplay loop>
</audio>
<audio src="audio/Drum.mp3" autoplay loop>
</audio>
<audio src="audio/Crunch.mp3" autoplay loop>
</audio>
<audio src="audio/Guitar.mp3" autoplay loop>
</audio>
<audio src="audio/Pizzicato.mp3" autoplay loop>
</audio>
</body>
Aunque la solución es muy sencilla y funciona bien, en la mayoría de casos los desarrolladores prefieren crear sus clips de audio por programación. En el ejemplo de código siguiente se muestra cómo se añaden dinámicamente tres archivos de audio por programa. Si se reproducen a la vez, nos devuelven el acorde MI Mayor al piano.
AddNote("3C");
AddNote("3E");
AddNote("3G");
function AddNote(name) {
var audio = document.createElement("audio");
audio.src = "piano/" + name + ".mp3";
audio.autoplay = true;
}
Este modelo de código funciona en cualquier navegador y nos permite diseñar escenarios de audio muy atractivos
Es muy importante tener en cuenta que a medida que una aplicación o juego se hacen más complicados, podríamos encontrarnos con dos límites en un momento dado: el número de elementos de audio que se pueden precargar desde la misma página y el número de elementos de audio que el sistema es capaz de reproducir de forma simultánea.
Estos límites dependen del navegador y de la capacidad del propio equipo. Basándome en mi experiencia personal, Internet Explorer 9 puede manejar decenas de elementos de audio a la vez sin problemas. Otros navegadores no llegan a tanto, y podemos encontrar ejemplos evidentes de retardos y distorsiones cuando se reproducen varios archivos en bucle.

Como podemos ver en la columna Timings, cada archivo puede que esté disponible en un momento distinto.
Una estrategia de sincronización muy utilizada consiste en precargar todos los archivos. Una vez que todos ellos están disponibles, se meten rápidamente en un bucle para empezar a escucharlos.
var audios = [];
var loading = 0;
AddNote("2C");
AddNote("2E");
AddNote("2G");
AddNote("3C");
AddNote("3E");
AddNote("3G");
AddNote("4C");
function AddNote(name) {
loading++;
var audio = document.createElement("audio");
audio.loop = true;
audio.addEventListener("canplaythrough", function () {
loading--;
if (loading == 0) // All files are preloaded
StartPlayingAll();
}, false);
audio.src = "piano/" + name + ".mp3";
audios.push(audio);
}
function StartPlayingAll() {
for (var i = 0; i < audios.length; i++)
audios[i].play();
}
Vamos ahora a juntarlo todo. En la demo siguiente vamos a simular un concierto de piano Frère Jacques (también llamado Hermano Juan, Brother John …o Fra Martino). La página empieza cargando todas las notas y muestra el progreso a medida que se van precargando en el cliente. Cuando ya tenemos todas, la canción empieza y se mantiene en un bucle de reproducción

addAudioChannel:function(b,a,f){
var h=document.createElement("audio");
if(f!=true){
this.currAsset=h;
this.timeoutId=setTimeout($.proxy(this,"handleAudioTimeout"),e.AUDIO_TIMEOUT);
h.addEventListener("canplaythrough",$.proxy(this,"handleAudioComplete"),false);
h.addEventListener("error",$.proxy(this,"handleAudioError"),false)
}
h.setAttribute("id",a);
h.setAttribute("preload","auto");
$("<source>").attr("src",b).appendTo(h);
$("<source>").attr("src",b.split(".mp3")[0]+".ogg").appendTo(h);
document.body.appendChild(h)
}
,handleAudioComplete:function(b){
if(LoadedAssets.getAsset(b.target.id)!=true){
LoadedAssets.addAsset(b.target.id,true);
clearTimeout(this.timeoutId);
this.calculatePercentLoaded(true)
}
}
,handleAudioError:function(b){
trace("Error Loading Audio:",b.target.id);
LoadedAssets.addAsset(b.target.id,true);
clearTimeout(this.timeoutId);
this.calculatePercentLoaded(true)
}
,handleAudioTimeout:function(){
trace("Audio Timed Out:",this.currAsset.id);
LoadedAssets.addAsset(this.currAsset.id,true);
this.calculatePercentLoaded(true)
}
Grant está trabajando en estos momentos en un proyecto de Biblioteca de Sonidos que permitirá a los desarrolladores utilizar su propia lógica para el reproductor de audio con cualquier otra aplicación. ¡Esperemos verlo pronto!

Esta vez las etiquetas de audio se declaran dentro del código HTML (son 6 pistas). El progreso se controla por programa, escuchando el evento canplaythrough. Cuando todos los archivos de audio están disponibles se ejecuta un bucle que va recorriendo la lista y los reproduce.
video.addEventListener('canplaythrough', onCanPlayAudio, false);
for (var i = 0; i < 5; i++) {
var aud = document.getElementById("aud" + i);
targetVolumes.push(0);
aud.volume = 0;
audioTags.push({
"tag": aud,
"ready": false
});
aud.addEventListener('canplaythrough', onCanPlayAudio, false);
}
// Set audio/video tracks
document.getElementById("tompkins").src = MediaHelper.GetVideoUrl("Firework_3");
for (var i = 0; i < audioTracks.length; i++) {
document.getElementById("aud" + i).src = MediaHelper.GetAudioUrl(audioTracks[i]);
}
En este caso los desarrolladores han optado por empezar con el volumen fijado a cero y aumentarlo dinámicamente a 1 en cuanto puede empezar la reproducción. Dependiendo de la calidad de tu tarjeta de audio y los drivers, este truco reduce la probabilidad de escuchar un ruido "toc" al principio, cuando empieza a oírse el sonido.

El generador de pulsos nos aporta un control total sobre el tempo y la firma de tiempo. Con la ayuda de una sofisticada lógica de timers y un modelo de acoplamiento, el resultado final es una experiencia realmente agradable.
Si quieres informarte más sobre los controles de audio y vídeo, te recomiendo que veas la sesión de media hora del MIX titulada "5 cosas que debes saber para utilizar <audio> y <video> hoy mismo" o leer en MSDN estos interesantes artículos.
Quiero agradecer a DoubleDominant por los archivos de audio utilizados en este artículo, y a Grant Skinner y Archetype por sus excelentes producciones en HTML5.