Grabar audio del usuario

Muchos navegadores ahora pueden acceder a la entrada de audio y video del usuario. Sin embargo, según el navegador, puede ser una experiencia dinámica y intercalada completa, o bien puede delegarse a otra app en el dispositivo del usuario.

Comienza de forma simple y progresiva

Lo más sencillo es pedirle al usuario un archivo grabado previamente. Para ello, crea un elemento de entrada de archivo simple y agrega un filtro accept que indique que solo podemos aceptar archivos de audio y un atributo capture que indique que queremos obtenerlo directamente del micrófono.

<input type="file" accept="audio/*" capture />

Este método funciona en todas las plataformas. En computadoras, se le solicitará al usuario que cargue un archivo desde el sistema de archivos (sin tener en cuenta el atributo capture). En Safari para iOS, se abrirá la app del micrófono, lo que te permitirá grabar audio y, luego, enviarlo a la página web. En Android, se le dará al usuario la opción de elegir en qué app grabar el audio antes de enviarlo a la página web.

Una vez que el usuario termine de grabar y vuelva al sitio web, necesitarás obtener los datos del archivo de alguna manera. Para obtener acceso rápido, puedes vincular un evento onchange al elemento de entrada y, luego, leer la propiedad files del objeto del evento.

<input type="file" accept="audio/*" capture id="recorder" />
<audio id="player" controls></audio>
  <script>
    const recorder = document.getElementById('recorder');
    const player = document.getElementById('player');

    recorder.addEventListener('change', function (e) {
      const file = e.target.files[0];
      const url = URL.createObjectURL(file);
      // Do something with the audio file.
      player.src = url;
    });
  </script>
</audio>

Una vez que tengas acceso al archivo, podrás hacer lo que quieras con él. Por ejemplo, puedes hacer lo siguiente:

  • Adjúntalo directamente a un elemento <audio> para poder reproducirlo.
  • Descargarlo en el dispositivo del usuario
  • Sube el archivo a un servidor conectándolo a un XMLHttpRequest.
  • Pasarlo a través de la API de Web Audio y aplicarle filtros

Si bien el uso del método de elemento de entrada para obtener acceso a los datos de audio es omnipresente, es la opción menos atractiva. Queremos acceder al micrófono y brindar una experiencia agradable directamente en la página.

Acceder al micrófono de forma interactiva

Los navegadores modernos pueden tener una línea directa con el micrófono, lo que nos permite crear experiencias que están completamente integradas en la página web y el usuario nunca abandonará el navegador.

Adquirir acceso al micrófono

Podemos acceder directamente al micrófono con una API en la especificación de WebRTC llamada getUserMedia(). getUserMedia() le solicitará al usuario acceso a sus micrófonos y cámaras conectados.

Si se realiza correctamente, la API mostrará un Stream que contendrá los datos de la cámara o el micrófono, y luego podremos adjuntarlo a un elemento <audio>, a una transmisión de WebRTC, a un AudioContext de Web Audio o guardarlo con la API de MediaRecorder.

Para obtener datos del micrófono, solo configuramos audio: true en el objeto de restricciones que se pasa a la API de getUserMedia().

<audio id="player" controls></audio>
<script>
  const player = document.getElementById('player');

  const handleSuccess = function (stream) {
    if (window.URL) {
      player.srcObject = stream;
    } else {
      player.src = stream;
    }
  };

  navigator.mediaDevices
    .getUserMedia({audio: true, video: false})
    .then(handleSuccess);
</script>

Si quieres elegir un micrófono en particular, primero puedes enumerar los micrófonos disponibles.

navigator.mediaDevices.enumerateDevices().then((devices) => {
  devices = devices.filter((d) => d.kind === 'audioinput');
});

Luego, puedes pasar el deviceId que deseas usar cuando llames a getUserMedia.

navigator.mediaDevices.getUserMedia({
  audio: {
    deviceId: devices[0].deviceId,
  },
});

Por sí solo, esto no es muy útil. Todo lo que podemos hacer es tomar los datos de audio y reproducirlos.

Accede a los datos sin procesar del micrófono

Para acceder a los datos sin procesar del micrófono, debemos tomar el flujo que creó getUserMedia() y, luego, usar la API de Web Audio para procesar los datos. La API de Web Audio es una API simple que toma fuentes de entrada y las conecta a nodos que pueden procesar los datos de audio (ajustar la ganancia, etc.) y, en última instancia, a una bocina para que el usuario pueda escucharla.

Uno de los nodos que puedes conectar es un AudioWorkletNode. Este nodo te brinda la capacidad de bajo nivel para el procesamiento de audio personalizado. El procesamiento de audio real se realiza en el método de devolución de llamada process() en AudioWorkletProcessor. Llama a esta función para ingresar entradas y parámetros, y recuperar resultados.

Consulta Cómo ingresar una worklet de audio para obtener más información.

<script>
  const handleSuccess = async function(stream) {
    const context = new AudioContext();
    const source = context.createMediaStreamSource(stream);

    await context.audioWorklet.addModule("processor.js");
    const worklet = new AudioWorkletNode(context, "worklet-processor");

    source.connect(worklet);
    worklet.connect(context.destination);
  };

  navigator.mediaDevices.getUserMedia({ audio: true, video: false })
      .then(handleSuccess);
</script>
// processor.js
class WorkletProcessor extends AudioWorkletProcessor {
  process(inputs, outputs, parameters) {
    // Do something with the data, e.g. convert it to WAV
    console.log(inputs);
    return true;
  }
}

registerProcessor("worklet-processor", WorkletProcessor);

Los datos que se almacenan en los búferes son los datos sin procesar del micrófono y tienes varias opciones para lo que puedes hacer con ellos:

  • Subir el archivo directamente al servidor
  • Almacenarlo de forma local
  • Convierte el archivo a un formato específico, como WAV, y guárdalo en tus servidores o de forma local.

Guarda los datos del micrófono

La forma más fácil de guardar los datos del micrófono es usar la API de MediaRecorder.

La API de MediaRecorder tomará la transmisión creada por getUserMedia y, luego, guardará de forma progresiva los datos que se encuentran en la transmisión en tu destino preferido.

<a id="download">Download</a>
<button id="stop">Stop</button>
<script>
  const downloadLink = document.getElementById('download');
  const stopButton = document.getElementById('stop');


  const handleSuccess = function(stream) {
    const options = {mimeType: 'audio/webm'};
    const recordedChunks = [];
    const mediaRecorder = new MediaRecorder(stream, options);

    mediaRecorder.addEventListener('dataavailable', function(e) {
      if (e.data.size > 0) recordedChunks.push(e.data);
    });

    mediaRecorder.addEventListener('stop', function() {
      downloadLink.href = URL.createObjectURL(new Blob(recordedChunks));
      downloadLink.download = 'acetest.wav';
    });

    stopButton.addEventListener('click', function() {
      mediaRecorder.stop();
    });

    mediaRecorder.start();
  };

  navigator.mediaDevices.getUserMedia({ audio: true, video: false })
      .then(handleSuccess);
</script>

En nuestro caso, guardamos los datos directamente en un array que luego podemos convertir en un Blob, que se puede usar para guardar los datos en nuestro servidor web o directamente en el almacenamiento del dispositivo del usuario.

Solicita permiso para usar el micrófono de forma responsable

Si el usuario no le otorgó acceso al micrófono a tu sitio anteriormente, en el momento en que llames a getUserMedia, el navegador le pedirá al usuario que le otorgue permiso al micrófono.

A los usuarios no les gusta que se les solicite acceso a dispositivos potentes en sus máquinas y, con frecuencia, bloquean la solicitud o la ignoran si no comprenden el contexto en el que se creó la solicitud. Se recomienda solo solicitar acceso al micrófono cuando sea necesario. Una vez que el usuario otorgue el acceso, no se le volverá a solicitar. Sin embargo, si rechaza el acceso, no podrás volver a solicitarle permiso.

Usa la API de permisos para comprobar si ya tienes acceso

La API de getUserMedia no te informa si ya tienes acceso al micrófono. Esto te presenta un problema: para proporcionar una IU agradable que le permita al usuario otorgarte acceso al micrófono, debes solicitarle acceso al micrófono.

Esto se puede resolver en algunos navegadores con la API de Permission. La API de navigator.permission te permite consultar el estado de la capacidad de acceder a APIs específicas sin tener que volver a solicitarlo.

Para consultar si tienes acceso al micrófono del usuario, puedes pasar {name: 'microphone'} al método de consulta, y se mostrará una de las siguientes opciones:

  • granted: El usuario te otorgó acceso al micrófono anteriormente.
  • prompt: El usuario no te otorgó acceso y se le pedirá cuando llames a getUserMedia.
  • denied: El sistema o el usuario bloquearon de forma explícita el acceso al micrófono, por lo que no podrás acceder a él.

Ahora puedes verificar rápidamente si necesitas alterar la interfaz de usuario para adaptarla a las acciones que el usuario debe realizar.

navigator.permissions.query({name: 'microphone'}).then(function (result) {
  if (result.state == 'granted') {
  } else if (result.state == 'prompt') {
  } else if (result.state == 'denied') {
  }
  result.onchange = function () {};
});

Comentarios