Reproducir archivos de sonido - de dos maneras

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, o si queremos manipular el audio, necesitamos hacerlo utilizando el Web Audio API y JavaScript.

Una primera manera

Una primera manera de hacerlo es utilizando request.responseType = "arraybuffer";
Probablemente esto es muy poco claro así que, 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.

Otra manera

En este otro ejemplo el HTML queda igual al del ejemplo anterior.


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

Veamos el JavaScript utilizado:
Exactamente como en el ejemplo anterior, primero necesitamos declarar algunas variables:


  // crea un nuevo audio contexto
var audioCtx = new (window.AudioContext || window.webkitAudioContext)(); // otras variables necesarias: var audio, fuenteDeReproduccion; var stop = true;

La función solicitar audio

La función solicitarAudio() es muy parecida a la del ejemplo anterior: también necesitamos almacenar los datos del audio en memoria ( buffer ), y también utilizamos el método  XMLHttpRequest

function solicitarAudio(url) {
    var request = new XMLHttpRequest();
    . . . . . 

Solo que esta vez el tipo de respuesta es de tipo "blob" donde BLOB es el acrónimo de Binary Large OBject y representa una colección de datos binarios ( del audio en este caso ).

request.open("GET", url, true);
  request.responseType = "blob";

Si hay respuesta creamos un nuevo elemento audio

request.onload = function() {
  audio = new Audio();
  . . . . . 

cuyo atributo src ( source ) es un nuevo objeto URL creado utilizando el método createObjectURL

audio.src = window.URL.createObjectURL(request.response);

Es aquí donde podemos añadir más atributos para el nuevo elemento audio como por ejemplo audio.loop = true que hace que el audio comience de nuevo al acabarse.

Más atributos del elemento <audio>

Para crear la fuente de reproducción esta vez utilizamos el método createMediaElementSource(), que es el método que se utiliza para crear una nueva fuente de reproducción si hay un elemento <audio> que puede reproducirse.

fuenteDeReproduccion = audioCtx.createMediaElementSource(audio);

 function solicitarAudio(url) {
    var request = new XMLHttpRequest();
    request.open("GET", url, true);
    request.responseType = "blob";
    request.onload = function() {
        // si hay respuesta crea un nuevo elemento Audio
       audio = new Audio();
       audio.src = window.URL.createObjectURL(request.response);
        // el método createMediaElementSource se utiliza para crear una nueva fuente de reproducción si hay un elemento  o  que puede reproducirse.
       fuenteDeReproduccion = audioCtx.createMediaElementSource(audio); 
      }
    request.send();
  }

La función reproducirAudio

Esta vez  la función reproducirAudio es mucho más sencilla. La utilizamos solo para conectar la fuenteDeReproduccion con los dispositivos de destino y reproducir el audio utilizando el método play().


function reproducirAudio() {
    fuenteDeReproduccion.connect(audioCtx.destination);
    audio.play();

La función detenerAudio

Para detener el audio utilizamos el método pause() ( parrar ).


function detenerAudio() {
    audio.pause();
}

Como una observación: cuando paramos un audio utilizando el método pause(), la reproducción se reanuda desde el punto en que se detuvo. Si queremos que la reproducción empiece de nuevo desde cero tenemos que añadir esta línea de código: audio.currentTime = 0;

Vea este ejemplo en codepen:

See the Pen Reproducir archivos de sonido request.responseType = "blob"; by Gabi (@enxaneta) on CodePen.