Atualmente, muitos navegadores têm o recurso de acessar inserções de dados de áudio e vídeo do usuário. No entanto, dependendo do navegador, essa pode ser uma experiência dinâmica e integrada ou delegada a outro app no dispositivo do usuário.
Comece de forma simples e progressiva
A coisa mais fácil a se fazer é simplesmente solicitar 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. Em computadores, o usuário poderá
carregar um arquivo do sistema de arquivos (ignorando o atributo capture
). No Safari
para iOS, ele vai abrir o app de microfone, permitindo a gravação do áudio e
em seguida o envio para a página da Web. No Android, ele vai dar ao usuário a
opção de escolher um app para gravar o áudio antes de enviá-lo de volta para a página
da Web.
Quando o usuário terminar a gravação e for redirecionado ao site, você
precisa conseguir os dados do arquivo de alguma forma. É possível ter acesso rápido
anexando um evento onchange
ao elemento de entrada e lendo
a propriedade files
do objeto do 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 que você tiver acesso ao arquivo, poderá fazer o que quiser com ele. Por exemplo, você pode:
- Anexá-lo diretamente a um elemento
<audio>
para poder reproduzir - Fazer o download no dispositivo do usuário
- Fazer upload dele em um servidor anexando-o a um
XMLHttpRequest
- Passá-lo pela Web Audio API e aplicar filtros a ele
Embora usar o método do elemento "input" para acessar dados de áudio seja universal, é a opção menos vantajosa. Queremos ter acesso ao microfone e oferecer uma experiência bacana diretamente na página.
Acessar o microfone de forma interativa
Os navegadores modernos podem ter ligação direta com o microfone, permitindo criar experiências totalmente integradas à página da Web, sem que o usuário precise sair do navegador.
Adquirir acesso ao microfone
Podemos acessar o microfone diretamente usando uma API na especificação
WebRTC chamada getUserMedia()
. O getUserMedia()
vai solicitar ao usuário
acesso às câmeras e microfones conectados.
Se receber a autorização, a API retornará um Stream
que conterá os dados da câmera ou
do microfone. Em seguida, poderemos anexá-los a um elemento <audio>
, a um fluxo do WebRTC, a um AudioContext
do Web Audio ou salvá-los usando a API MediaRecorder
.
Para receber dados do microfone, basta definir audio: true
no objeto de restrições
que é 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 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 que podemos fazer é pegar os dados de áudio e reproduzi-los.
Acessar os dados brutos do microfone
Para acessar os dados brutos do microfone, temos que pegar o fluxo criado por
getUserMedia()
e usar a Web Audio API para processar os dados. A
API Web Audio é uma API simples que coleta as fontes de entrada e as conecta
a nós que podem processar os dados de áudio (ajustar ganho etc.) e,
por fim, conecta-os a alto-falantes para que o usuário possa ouvir.
Um dos nós a 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 diversas possibilidades do que fazer com esses dados:
- Faça o upload direto no servidor.
- Armazenar localmente
- Converter para um formato de arquivo dedicado, como WAV, e salvar nos servidores ou localmente
Salvar os dados do microfone
A forma mais fácil de salvar os dados do microfone é usar a
API MediaRecorder
.
A API MediaRecorder
vai usar o fluxo criado por getUserMedia
e, em seguida,
salvar progressivamente os dados que estão no fluxo 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 poderemos transformar
em um Blob
, que pode ser usado para salvar os dados no nosso servidor da Web ou diretamente
no armazenamento do dispositivo do usuário.
Solicitar autorização para usar o microfone com responsabilidade
Se o usuário ainda não tiver concedido acesso ao microfone para o seu site, no instante em que você chamar getUserMedia
, o navegador vai pedir que o usuário
conceda permissão ao microfone.
Os usuários odeiam receber solicitações de permissão de acesso a dispositivos importantes do seu aparelho e muitas vezes bloqueiam a solicitação ou a ignoram se não entendem por que a solicitação foi criada. A prática recomendada é só pedir acesso ao microfone na primeira vez em que ele for necessário. Depois que o usuário conceder acesso, ele não vai receber mais solicitações de permissão de acesso. 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 é um problema: para oferecer uma interface bacana
para fazer o usuário conceder acesso ao microfone a você, você tem que pedir
acesso ao microfone.
Esse problema pode ser resolvido em alguns navegadores por meio do uso da Permission API. A
API navigator.permission
permite consultar o estado da capacidade de
acessar APIs específicas sem ter que pedir novamente.
Para verificar se você tem acesso ao microfone do usuário, transmita
{name: 'microphone'}
no método de consulta. Ele vai retornar:
granted
: o usuário já deu autorização de acesso ao microfone a você;prompt
: o usuário não deu acesso a você e vai receber uma solicitação quando você chamargetUserMedia
.denied
: o sistema ou o usuário bloqueou explicitamente o acesso ao microfone e você não poderá pedir acesso a ele.
Agora você pode verificar rapidamente se precisa alterar sua interface para oferecer 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 () {};
});