Grabar audio del usuario

Muchos navegadores ahora tienen la capacidad de acceder a la entrada de audio y video del usuario. Sin embargo, según el navegador, puede ser una experiencia en línea y dinámica completa o se puede delegar a otra app en el dispositivo del usuario.

Comenzar de manera simple y progresiva

Lo más fácil es simplemente pedirle al usuario un archivo pregrabado. 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 desde el micrófono.

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

Este método funciona en todas las plataformas. En una computadora de escritorio, se le solicita al usuario que suba un archivo desde el sistema de archivos (sin considerar el atributo capture). En Safari para iOS, se abrirá la app del micrófono, lo que te permitirá grabar audio y, luego, enviarlo de vuelta a la página web. En Android, le permitirá al usuario elegir en qué app desea grabar el audio antes de enviarlo de vuelta a la página web.

Una vez que el usuario haya terminado de grabar y esté de vuelta en el sitio web, de alguna manera debes conseguir los datos del archivo. Para obtener acceso rápido, adjunta un evento onchange al elemento de entrada y, luego, lee 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úntala directamente a un elemento <audio> para que puedas reproducirla
  • Descárgalo en el dispositivo del usuario
  • Para subirla a un servidor, adjúntala a un XMLHttpRequest
  • Pásalo a través de la API de Web Audio y aplícale filtros.

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

Cómo acceder al micrófono de forma interactiva

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

Adquirir acceso al micrófono

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

Si se ejecuta de forma correcta, la API mostrará un Stream que contendrá los datos de la cámara o el micrófono. 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, configura audio: true en el objeto de restricciones que se pasa a la API 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 llamas a getUserMedia.

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

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

Acceder a los datos sin procesar desde el micrófono

Para acceder a los datos sin procesar desde el micrófono, debemos tomar la transmisión creada por 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 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() de AudioWorkletProcessor. Llama a esta función para proporcionar entradas y parámetros, y recuperar resultados.

Consulta Enter Audio Worklet 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 sobre lo que puedes hacer con esos datos:

  • Súbelo directamente al servidor
  • Almacenarlos localmente
  • Convertirlos a un formato de archivo dedicado, como WAV y, luego, guardarlos en tus servidores o de forma local

Cómo guardar 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á los datos que están en ella de forma progresiva 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, guardaremos los datos directamente en un array que luego podemos convertir en un Blob, que luego se podrá usar para guardar los datos en nuestro servidor web o directamente en el almacenamiento del dispositivo del usuario.

Cómo solicitar permiso para usar el micrófono de manera 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 sitio que otorgue permiso para acceder 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 por el que se creó la solicitud. La práctica recomendada es solicitar acceso al micrófono solo la primera vez que se necesita. Una vez que el usuario otorgue el acceso, no se le volverá a solicitar permiso. Sin embargo, si lo rechaza, no podrás volver a pedirle permiso.

Usa la API de Permissions para verificar si ya tienes acceso

La API de getUserMedia no te permite saber si ya tienes acceso al micrófono. Esto te presenta un problema, a fin de proporcionar una buena IU para que el usuario te otorgue acceso al micrófono, debes solicitar acceso al micrófono.

Esto se puede resolver en algunos navegadores usando 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 solicitar permiso.

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

  • granted: El usuario te otorgó acceso al micrófono anteriormente.
  • prompt: El usuario no te otorgó acceso y se lo solicitará cuando llames a getUserMedia.
  • denied: El sistema o el usuario bloqueó explícitamente el acceso al micrófono y no podrás obtener acceso a él.

Además, ahora puedes verificar rápidamente si necesitas modificar tu interfaz de usuario para que se adapte 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