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