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 () {};
});