Reproducir archivos de sonido

facebook-svg gplus-svg twitter-svg

Reproducir archivos de sonido es muy fácil. Bastan unas cuantas líneas de HTML.

See the Pen Web Audio API TEST #1 by Gabi (@enxaneta) on CodePen.

Sin embargo los controles del audio utilizan los estilos por defecto de cada navegador. Si queremos personalizar la apariencia de los controles, necesitamos hacerlo utilizando el Web Audio API y JavaScript.
Sin más preámbulos, veamos un ejemplo basico:

El HTML

El HTML es muy sencillo: tenemos solo un boton: al hacer click, si esta parado, el audio empieza a reproducirse. de lo contrario el audio se para.


<p>
  <button id="boton">▷</button>
</p>

El JavaScript

En el JavaScript primero creamos un nuevo AudioContext.

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();

También declaramos dos variables globales para el audio buffer y para la fuente de reproducción:

var audioBuffer, fuenteDeReproduccion;

Y otra variable para controlar la reproducción y la detención del archivo de sonido.

var start = false;

La función solicitarAudio()

Para reproducir un archivo de sonido primero tenemos que almacenarlo temporalmente en memoria ( buffer ). Para esto utilizamos XMLHttpRequest. Después utilizamos el método decodeAudioData() para decodificar los datos del audio.
Para todo esto vamos a crear la función solicitarAudio().


function solicitarAudio(url) {
  var request = new XMLHttpRequest();
  request.open("GET",url,true);
  request.responseType = "arraybuffer";
  request.onload = function() {
    audioCtx.decodeAudioData(request.response, function(buffer) {
      audioBuffer = buffer;
    });
  };
  request.send();
}
La explicación del código

La función crea un nuevo objeto XMLHttpRequest();

var request = new XMLHttpRequest();

Inicializa el request ( solicitud ) utilizando el método open(). El método utilizado es "GET", el url representa el archivo de sonido, y "true" quiere decir que sí, el request es asíncrono.

request.open("GET", url,  true);

A continuación la función establece el tipo de request: "arraybuffer". El objeto arraybuffer se utiliza para almacenar datos binarios ( como por ejemplo archivos de sonido ).

request.responseType = "arraybuffer";

Si hay respuesta utilizamos el método decodeAudioData() para decodificar los datos del audio.

request.onload = function() {
audioCtx.decodeAudioData(request.response, function(buffer) {

Una función de retrollamada ( callback function ) captura los datos decodificados y los guarda en una variable global ( audioBuffer ) para hacer el buffer accesible más tarde.

audioBuffer = buffer; 

Finalmente envía la solicitud utilizando el método send().

request.send();

La función reproducirAudio()

Ahora que tenemos el archivo de sonido almacenado temporalmente en memoria, tenemos que escribir una función que nos permita reproducirlo. Recuerde que la fuenteDeReproduccion es una variable global.


  function reproducirAudio(){
  fuenteDeReproduccion = audioCtx.createBufferSource();
  fuenteDeReproduccion.buffer = audioBuffer;
  fuenteDeReproduccion.playbackRate.value = .8;
  fuenteDeReproduccion.connect(audioCtx.destination);
  fuenteDeReproduccion.start(audioCtx.currentTime);
  }
La explicación del código:

1. La función crea primero una nueva fuente de reproducción

fuenteDeReproduccion = audioCtx.createBufferSource()href;

2. Establece el valor del buffer de la fuente de reproducción. Recuerde que el audioBuffer contiene los datos decodificados del archivo de sonido.

fuenteDeReproduccion.buffer = audioBuffer;

3. La función también establece el valor de algunas propiedades. Este paso es opcional, pero es aquí donde pasan las cosas interesantes.
En este caso la propiedad playbackRate ajusta la velocidad a la que el archivo de audio se reproduce.
El valor por defecto de playbackRate es 1, en cual caso se mantiene la velocidad original del archivo.
Si el valor de playbackRate es 2, el audio se reproduce al doble de velocidad, y
si el valor de playbackRate es 0.5 el audio se reproduce muy lentamente, a la mitad de la velocidad original.

fuenteDeReproduccion.playbackRate.value = .8;

4. Conecta la fuente de reproducción con el dispositivo de destino ( altavoces, auriculares . . . etc ). Para esto utilizamos el método connect();

fuenteDeReproduccion.connect(audioCtx.destination);

5. Finalmente inicia la reproducción utilizando el método start().

fuenteDeReproduccion.start(audioCtx.currentTime);

La función detenerAudio()

También necesitamos una función para parar la reproducción. En este caso se trata de una función es muy sencilla.


function detenerAudio() {
      fuenteDeReproduccion.stop();
}

La función audio()

La función audio() se encarga de como y cuando llamar la función reproducirAudio() o detenerAudio().


function audio(){
  if (stop) {// si el audio está parado
    stop = false;
    boton.innerHTML = "||"
    reproducirAudio();
  }else{// de lo contrario
    stop = true;
    boton.innerHTML = "▷"
    detenerAudio();
  }
}

El evento click

Finalmente, después de llamar la función solicitarAudio ( que toma como argumento el mp3 que deseamos reproducir ),

solicitarAudio("el.mp3");

necesitamos una manera de llamar la función audio(). En este caso utilizamos el evento click.

boton.addEventListener("click", audio, false);
Vea este ejemplo en codepen:

See the Pen Reproducir archivos de sonido #1 by Gabi (@enxaneta) on CodePen.

Mejorando el ejemplo anterior

Aunque el ejemplo anterior funciona muy bien, lo podemos mejorar.
Primero veamos que falla:
Cuando la canción se acaba el texto del botón sigue siendo "detener". Así que si quiero escuchar de nuevo la canción, tengo que hacer clic dos veces: una primera vez para parar la música ( que ya está parada ), y la segunda vez para reiniciar el audio.
Para arreglar este problema necesitamos saber cuando se acaba la música. Veamos como.
Ya sabemos que el AudioContext tiene un reloj interno audioCtx.currentTime que empieza a cronometrar en el momento en el cual el nuevo objeto AudioContext es creado.

 var audioCtx = new (window.AudioContext || window.webkitAudioContext)();

El archivo audio empieza a reproducirse en el momento en el cual el usuario hace clic en el botón. Tenemos que guardar este momento en una variable global, así que declaramos la var tiempo junto con las demás variables. Asimismo tenemos que modificar la función audio() añadiendo una línea de código:

  function audio(){  
  ¬†¬†if (stop) {// si el audio está parado
      tiempo = audioCtx.currentTime;
      stop = false;
      boton.innerHTML = "||"
      reproducirAudio();
    }else{// de lo contrario
      stop = true;
  ¬†¬†¬† boton.innerHTML = "▷"
      detenerAudio();
    }
  }

O sea: cada vez que hacemos clic en el botón actualizamos el valor del tiempo.

También necesitamos saber la duración del audio, y esto ya lo tenemos: audioBuffer.duration.

Ahora podemos utilizar el método setInterval para comprobar cada segundo si el tiempo que lleva reproduciéndose el audio ( audioCtx.currentTime - tiempo ) es más grande o igual a la duración ( audioBuffer.duration ), y si lo es:

stop = true;
boton.innerHTML = "▷"

window.setInterval( function(){  
   if(audioBuffer && audioCtx.currentTime - tiempo >= audioBuffer.duration / .8){
      stop = true;
  ¬†¬†¬† boton.innerHTML = "▷"
   }
},1000);

En este caso necesitamos dividir la duración por 0.8 porque este es el valor de playbackRate.value. Recuerde que la propiedad playbackRate ¬†establece la velocidad de reproducción del audio.

Vea el ejemplo modificado en codepen

See the Pen Reproducir archivos de sonido #2* by Gabi (@enxaneta) on CodePen.