Como gravar áudio do usuário

Agora, muitos navegadores podem acessar entradas de vídeo e áudio do usuário. No entanto, dependendo do navegador, pode ser uma experiência dinâmica e inline completa ou delegada a outro app no dispositivo do usuário.

Comece de forma simples e progressiva

A coisa mais fácil a fazer é simplesmente pedir ao usuário um arquivo pré-gravado. Para isso, crie um elemento de entrada de arquivo simples e adicione um filtro accept, que indique que só podemos aceitar arquivos de áudio, e um atributo capture que indique que queremos recebê-lo diretamente do microfone.

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

Esse método funciona em todas as plataformas. No computador, será solicitado que o usuário faça upload de um arquivo do sistema de arquivos (ignorando o atributo capture). No Safari para iOS, o app de microfone será aberto, permitindo que você grave áudio e o envie de volta à página da Web. No Android, isso dará ao usuário a escolha de qual app usar para gravar o áudio antes de enviá-lo de volta à página da Web.

Depois que o usuário terminar a gravação e voltar ao site, você vai precisar conseguir os dados do arquivo. Para ter acesso rápido, anexe um evento onchange ao elemento de entrada e leia a propriedade files do objeto de 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>

Quando você tiver acesso ao arquivo, poderá fazer o que quiser com ele. Por exemplo, você pode:

  • Anexar diretamente a um elemento <audio> para poder tocar
  • Fazer o download no dispositivo do usuário
  • Faça upload do arquivo em um servidor anexando-o a um XMLHttpRequest
  • Passar pela API de áudio da Web e aplicar filtros a ele

Embora o uso do método do elemento de entrada para acessar dados de áudio seja onipresente, é a opção menos atraente. Queremos ter acesso ao microfone e oferecer uma experiência boa diretamente na página.

Acessar o microfone de forma interativa

Os navegadores mais recentes podem ter uma ligação direta com o microfone, o que nos permite criar experiências totalmente integradas à página da Web, sem que o usuário nunca saia do navegador.

Conseguir acesso ao microfone

Podemos acessar diretamente o microfone usando uma API na especificação WebRTC chamada getUserMedia(). getUserMedia() vai solicitar acesso ao microfone e à câmera conectados ao usuário.

Se bem-sucedida, a API vai retornar um Stream que vai conter os dados da câmera ou do microfone. Em seguida, é possível anexá-lo a um elemento <audio>, anexá-lo a um stream WebRTC, anexá-lo a um AudioContext de áudio da Web ou salvá-lo usando a API MediaRecorder.

Para extrair dados do microfone, basta definir audio: true no objeto de restrições transmitido à 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>

Se quiser escolher um microfone específico, primeiro enumera os microfones disponíveis.

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

Você pode transmitir o deviceId que quer usar ao chamar getUserMedia.

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

Sozinha, isso não é tão útil. Tudo o que podemos fazer é pegar os dados de áudio e reproduzi-lo.

Acessar os dados brutos do microfone

Para acessar os dados brutos do microfone, é preciso pegar o stream criado por getUserMedia() e usar a API Web Audio para processar os dados. A API Web Audio é uma API simples que usa fontes de entrada e as conecta a nós que podem processar os dados de áudio (ajustar o ganho etc.) e, por fim, a um alto-falante para que o usuário possa ouvir.

Um dos nós que você pode conectar é um AudioWorkletNode. Esse nó oferece o recurso de baixo nível para processamento de áudio personalizado. O processamento real do áudio acontece no método de callback process() no AudioWorkletProcessor. Chame essa função para alimentar entradas e parâmetros e buscar saídas.

Confira Inserir Worklet de áudio para saber mais.

<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);

Os dados retidos nos buffers são os dados brutos do microfone, e você tem várias opções:

  • Fazer upload dele diretamente para o servidor
  • Armazenar localmente
  • Converta-o para um formato de arquivo dedicado, como WAV e salve-o nos seus servidores ou localmente

salvar os dados do microfone

A maneira mais fácil de salvar os dados do microfone é usando a API MediaRecorder.

A API MediaRecorder usa o fluxo criado por getUserMedia e salva progressivamente os dados que estão no fluxo no destino de sua preferência.

<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>

No nosso caso, estamos salvando os dados diretamente em uma matriz que pode ser transformada posteriormente em Blob, que pode ser usada para salvar os dados no servidor da Web ou diretamente no armazenamento no dispositivo do usuário.

Peça permissão para usar o microfone com responsabilidade

Se o usuário ainda não tiver concedido ao site acesso ao microfone, no momento em que você chamar getUserMedia, o navegador vai solicitar que o usuário conceda ao site permissão para usar o microfone.

Os usuários odeiam receber solicitações de acesso a dispositivos avançados na máquina e, com frequência, bloqueiam a solicitação ou a ignoram se não entendem o contexto para o qual a solicitação foi criada. A prática recomendada é só pedir acesso ao microfone quando necessário. Depois que o usuário concede acesso, ele não recebe uma solicitação novamente. No entanto, se ele recusar o acesso, não será possível solicitar a permissão novamente.

Use a API de permissões para verificar se você já tem acesso

A API getUserMedia não informa se você já tem acesso ao microfone. Isso apresenta um problema. Para fornecer uma boa interface e fazer o usuário conceder acesso ao microfone, você precisa pedir acesso ao microfone.

Isso pode ser resolvido em alguns navegadores usando a API Permission. A API navigator.permission permite que você consulte o estado da capacidade de acessar APIs específicas sem ter que solicitar novamente.

Para consultar se você tem acesso ao microfone do usuário, transmita {name: 'microphone'} para o método de consulta. Ele retornará:

  • granted: o usuário já deu a você acesso ao microfone.
  • prompt: o usuário não deu acesso e será avisado quando você chamar getUserMedia.
  • denied: o sistema ou o usuário bloqueou explicitamente o acesso ao microfone, e você não poderá ter acesso a ele.

Agora você pode verificar rapidamente se precisa alterar sua interface do usuário para acomodar as ações que o usuário precisa 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 () {};
});

Feedback