javascript

Grabar audio del micrófono con JavaScript y descargar la grabación

En este post voy a explicar cómo grabar audio del micrófono con JavaScript, MediaRecorder y la API de getUserMedia.

Grabar audio del micrófono con JavaScript, getUserMedia y MediaRecorder

Al final seremos capaces de:

  • Pedir permiso de acceder al micrófono
  • Obtener una lista de micrófonos
  • Seleccionar el micrófono para grabar
  • Comenzar una grabación
  • Detener la grabación de audio
  • Descargar el audio grabado

Todo eso sin usar plugins, extensiones ni esas cosas: se utiliza JavaScript puro.

Código fuente y recomendados

Aquí voy a explicar las cosas más importantes, pero el código actualizado puede verse en mi GitHub.

En caso de que quieras probar la aplicación final, visita este enlace.

Nota: recuerda que ya vimos cómo tomar una foto con JavaScript y la cámara web para descargar, o para enviarla a PHP.

Lista de dispositivos

Para obtener la lista de dispositivos llamamos a navigator.mediaDevices.enumerateDevices que devolverá una promesa que, al ser resuelta, traerá la lista.

Esta lista tiene todos los dispositivos pero a nosotros solo nos importan aquellos cuyo tipo (kind) sea de audioinput; hacemos un pequeño filtro y ponemos los dispositivos de grabación en un select o lista desplegable.

 // Consulta la lista de dispositivos de entrada de audio y llena el select
const llenarLista = () => {
    navigator
        .mediaDevices
        .enumerateDevices()
        .then(dispositivos => {
            limpiarSelect();
            dispositivos.forEach((dispositivo, indice) => {
                if (dispositivo.kind === "audioinput") {
                    const $opcion = document.createElement("option");
                    // Firefox no trae nada con label, que viva la privacidad
                    // y que muera la compatibilidad
                    $opcion.text = dispositivo.label || `Dispositivo ${indice + 1}`;
                    $opcion.value = dispositivo.deviceId;
                    $listaDeDispositivos.appendChild($opcion);
                }
            })
        })
};

En algunos casos el dispositivo no tiene label, así que simplemente usamos las plantillas de cadena e indicamos “Dispositivo X”.

El punto importante es tener el deviceId, ya que ese es pasado cuando se llama a getUserMedia.

Comenzar grabación

Para grabar el audio creamos un MediaRecorder, su constructor acepta un stream que obtenemos con getUserMedia.

getUserMedia acepta un parámetro para que le indiquemos el dispositivo con el que vamos a grabar, el cual es el deviceId obtenido desde la lista desplegable.

const comenzarAGrabar = () => {
  if (!$listaDeDispositivos.options.length) return alert("No hay dispositivos");
  // No permitir que se grabe doblemente
  if (mediaRecorder) return alert("Ya se está grabando");

  navigator.mediaDevices.getUserMedia({
          audio: {
              deviceId: $listaDeDispositivos.value,
          }
      })
      .then(
          stream => {
              // Comenzar a grabar con el stream
              mediaRecorder = new MediaRecorder(stream);
              mediaRecorder.start();
              comenzarAContar();
              // En el arreglo pondremos los datos que traiga el evento dataavailable
              const fragmentosDeAudio = [];
              // Escuchar cuando haya datos disponibles
              mediaRecorder.addEventListener("dataavailable", evento => {
                  // Y agregarlos a los fragmentos
                  fragmentosDeAudio.push(evento.data);
              });
          }
      )
      .catch(error => {
          // Aquí maneja el error, tal vez no dieron permiso
          console.log(error)
      });
};

En la llamada a getUserMedia se pedirá permiso al usuario, si el usuario accede y cuenta con el hardware de grabación entonces se resuelve la promesa; en caso de que no, se rechaza y manejamos el error en catch.

El verdadero comienzo de la grabación es en mediaRecorder.start (la variable es global, y no te confundas, es la variable, no la clase MediaRercorder).

La variable va a emitir el evento de dataavailable (data available, datos disponibles) el cual escuchamos. Dicho evento trae datos (que son la grabación, bueno, pedazos de ella) que colocamos en un arreglo.

Detener grabación y escuchar evento

Para detener la grabación llamamos a mediaRecorder.stop(), eso desencadenará el evento stop, el cual escuchamos y dentro del mismo:

  • Detenemos la pista del stream
  • Convertimos el arreglo de fragmentos de la grabación a un Blob
  • Descargamos el blob como webm

Eso lo hace el siguiente código:

// Cuando se detenga (haciendo click en el botón) se ejecuta esto
mediaRecorder.addEventListener("stop", () => {
    // Detener el stream
    stream.getTracks().forEach(track => track.stop());
    // Detener la cuenta regresiva
    detenerConteo();
    // Convertir los fragmentos a un objeto binario
    const blobAudio = new Blob(fragmentosDeAudio);

    // Crear una URL o enlace para descargar
    const urlParaDescargar = URL.createObjectURL(blobAudio);
    // Crear un elemento <a> invisible para descargar el audio
    let a = document.createElement("a");
    document.body.appendChild(a);
    a.style = "display: none";
    a.href = urlParaDescargar;
    a.download = "grabacion_parzibyte.me.webm";
    // Hacer click en el enlace
    a.click();
    // Y remover el objeto
    window.URL.revokeObjectURL(urlParaDescargar);
});
Descargar audio grabado con JavaScript

Extra: el conteo

Para mostrar el tiempo que se lleva grabado puse un contador que muestra el tiempo de inicio de la grabación, el mismo se resetea cuando se descarga el audio.

const segundosATiempo = numeroDeSegundos => {
    let horas = Math.floor(numeroDeSegundos / 60 / 60);
    numeroDeSegundos -= horas * 60 * 60;
    let minutos = Math.floor(numeroDeSegundos / 60);
    numeroDeSegundos -= minutos * 60;
    numeroDeSegundos = parseInt(numeroDeSegundos);
    if (horas < 10) horas = "0" + horas;
    if (minutos < 10) minutos = "0" + minutos;
    if (numeroDeSegundos < 10) numeroDeSegundos = "0" + numeroDeSegundos;

    return `${horas}:${minutos}:${numeroDeSegundos}`;
};

// Ayudante para la duración; no ayuda en nada pero muestra algo informativo
const comenzarAContar = () => {
    tiempoInicio = Date.now();
    idIntervalo = setInterval(refrescar, 500);
};

const refrescar = () => {
    $duracion.textContent = segundosATiempo((Date.now() - tiempoInicio) / 1000);
}

¿Por qué webm?

No quiero meterme con formatos de audio pero todo esto tiene que ver con los códecs libres y esas cosas; el formato audio/webm funciona en los navegadores que probé y por eso se descarga con esa extensión.

Conclusión

Así podemos grabar un audio con JavaScript desde cualquier micrófono y descargar el archivo de la grabación. Funciona (lo he probado) en:

  • Firefox y Google Chrome en Windows 10
  • Firefox y Google Chrome en Android 9
  • No funciona en Edge sobre Windows 10 porque no está disponible MediaRecorder.

Recuerda que puedes ver la demostración en este enlace, y el código (ya que no puse el HTML) en mi GitHub.

Ah, cabe mencionar que getUserMedia funciona solo en localhost o en servidores https.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Ver comentarios

Entradas recientes

Servidor HTTP en Android con Flutter

El día de hoy te mostraré cómo crear un servidor HTTP (servidor web) en Android…

3 días hace

Imprimir automáticamente todos los PDF de una carpeta

En este post te voy a enseñar a designar una carpeta para imprimir todos los…

4 días hace

Guía para imprimir en plugin versión 1 desde Android

En este artículo te voy a enseñar la guía para imprimir en una impresora térmica…

1 semana hace

Añadir tasa de cambio en sistema de información

Hoy te voy a mostrar un ejemplo de programación para agregar un módulo de tasa…

2 semanas hace

Comprobar validez de licencia de plugin ESC POS

Los usuarios del plugin para impresoras térmicas pueden contratar licencias, y en ocasiones me han…

2 semanas hace

Imprimir euro € en impresora térmica

Hoy voy a enseñarte cómo imprimir el € en una impresora térmica. Vamos a ver…

3 semanas hace

Esta web usa cookies.