Muitos navegadores agora têm a capacidade de acessar a entrada de vídeo e áudio do usuário. No entanto, dependendo do navegador, pode ser uma experiência dinâmica e inline completa ou pode ser delegada a outro app no dispositivo do usuário.
Comece de forma simples e progressiva
A maneira mais fácil é pedir ao usuário um arquivo pré-gravado. Para fazer 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 receber o áudio diretamente do microfone.
<input type="file" accept="audio/*" capture />
Esse método funciona em todas as plataformas. No computador, o usuário vai ser solicitado a
fazer upload de um arquivo do sistema de arquivos (ignorando o atributo capture
). No Safari
no iOS, ele vai abrir o app de microfone, permitindo que você grave o áudio e
o envie de volta para a página da Web. No Android, o usuário poderá
escolher qual app usar para gravar o áudio antes de enviá-lo de volta para a página
da Web.
Depois que o usuário terminar a gravação e voltar ao site, você
precisa de alguma forma acessar os dados do arquivo. Para ter acesso rápido,
vincule 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>
Depois de ter acesso ao arquivo, você pode fazer o que quiser com ele. Por exemplo, você pode:
- Anexá-lo diretamente a um elemento
<audio>
para que ele possa ser reproduzido - Fazer o download no dispositivo do usuário
- Faça upload para um servidor anexando-o a um
XMLHttpRequest
- Transmita-o pela API Web Audio e aplique filtros a ele
Embora o uso do método de elemento de entrada para acessar dados de áudio seja onibpresente, é a opção menos atraente. Queremos ter acesso ao microfone e oferecer uma boa experiência diretamente na página.
Acessar o microfone de forma interativa
Os navegadores modernos podem ter uma linha direta para o microfone, permitindo que criemos experiências totalmente integradas à página da Web e que o usuário nunca sai do navegador.
Adquirir acesso ao microfone
É possível acessar o microfone diretamente usando uma API na especificação
do WebRTC chamada getUserMedia()
. O getUserMedia()
vai solicitar ao usuário
acesso aos microfones e câmeras conectados.
Se bem-sucedida, a API vai retornar um Stream
que conterá os dados da câmera ou
do microfone. Em seguida, podemos anexar o elemento a um <audio>
, a um stream do WebRTC, a um AudioContext
do Web Audio ou salvar usando a API MediaRecorder
.
Para receber dados do microfone, basta definir audio: true
no objeto
de restrições que é transmitido para a 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 você quiser escolher um microfone específico, primeiro enumere os microfones disponíveis.
navigator.mediaDevices.enumerateDevices().then((devices) => {
devices = devices.filter((d) => d.kind === 'audioinput');
});
Em seguida, transmita o deviceId
que você quer usar ao chamar getUserMedia
.
navigator.mediaDevices.getUserMedia({
audio: {
deviceId: devices[0].deviceId,
},
});
Por si só, isso não é tão útil. Tudo o que podemos fazer é pegar os dados de áudio e reproduzi-los.
Acessar os dados brutos do microfone
Para acessar os dados brutos do microfone, precisamos usar o fluxo criado por
getUserMedia()
e, em seguida, usar a API Web Audio para processar os dados. A
API Web Audio é uma API simples que recebe fontes de entrada e as conecta
a nós que podem processar os dados de áudio (ajustar ganho etc.) e
finalmente a um alto-falante para que o usuário possa ouvir.
Um dos nós que você pode conectar é um AudioWorkletNode
. Esse nó oferece
a capacidade de baixo nível para processamento de áudio personalizado. O processamento de áudio
realmente acontece no método de callback process()
no AudioWorkletProcessor
.
Chame essa função para alimentar entradas e parâmetros e buscar saídas.
Confira o Enter Audio Worklet 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 armazenados nos buffers são os dados brutos do microfone, e você tem várias opções do que pode fazer com eles:
- Faça o upload diretamente no servidor
- Armazenar localmente
- Converta-o em 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 é usar a
API MediaRecorder
.
A API MediaRecorder
vai usar o stream criado por getUserMedia
e, em seguida,
salvar progressivamente os dados que estão no stream no 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>
No nosso caso, estamos salvando os dados diretamente em uma matriz que pode ser transformada
em um Blob
, que pode ser usado para salvar os dados no servidor da Web ou diretamente
no armazenamento do dispositivo do usuário.
Pedir permissão para usar o microfone de forma responsável
Se o usuário não tiver concedido ao seu site acesso ao microfone, no momento em que você chamar getUserMedia
, o navegador vai solicitar que o usuário
conceda permissão ao microfone.
Os usuários odeiam receber solicitações de acesso a dispositivos poderosos na máquina e com frequência bloqueiam a solicitação ou a ignoram se não entenderem o contexto em que a solicitação foi criada. É recomendável pedir acesso ao microfone apenas quando for necessário. Depois que o usuário conceder o acesso, ele não será solicitado novamente. No entanto, se ele rejeitar o acesso, não será possível pedir a permissão novamente.
Usar 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 interface
legal e fazer com que o usuário conceda 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 consultar o estado da capacidade de
acessar APIs específicas sem precisar solicitar novamente.
Para consultar se você tem acesso ao microfone do usuário, transmita
{name: 'microphone'}
ao método de consulta, que vai retornar:
granted
: o usuário já concedeu acesso ao microfone.prompt
: o usuário não concedeu acesso a você e será solicitado quando você chamargetUserMedia
.denied
: o sistema ou o usuário bloqueou explicitamente o acesso ao microfone, e você não poderá acessá-lo.
Agora você pode conferir rapidamente se precisa alterar a interface do usuário para acomodar as ações que ele 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 () {};
});