JS
CV

Capturar, gravar e enviar um áudio no navegador

mar 03, 2024 3 min - tempo de leitura

Atualmente, estou desenvolvendo uma POC para validar o desenvolvimento de um sistema que consiste em interagir com a inteligência artificial, algo semelhante a este site.

Neste artigo, demonstrarei como implementei a captura do microfone e utilizei a classe MediaRecorder para armazenar o áudio e enviá-lo para uma requisição RESTful na POC que estou desenvolvendo.

Capturando áudio pelo microfone

Primeiro, precisamos verificar se o navegador possui suporte ao microfone. Podemos fazer isso usando a seguinte condição:

if (navigator.mediaDevices.getUserMedia) {
	// possuui acesso
} else {
	// não possuui acesso
}

Em seguida, vamos utilizar o método getUserMedia, passando como parâmetro a solicitação de entrada de áudio. Ao chamar essa função, será solicitada permissão ao navegador para capturar apenas o áudio do computador. Caso o usuário recuse, será lançado um erro. Se for aceito, será enviado um objeto MediaStream. Nosso código ficará desta maneira:

if (!navigator.mediaDevices.getUserMedia) {
	// não possuui acesso
	return;
}
 
navigator.mediaDevices.getUserMedia({ audio: true }).then(
	(stream) => {
		//Usuário deu permissão
	},
	(err) => {
		//Usuário não deu permissão
		console.error("The following error occured: " + err);
	}
);

Armazenando o áudio no MediaRecorder

Assim que conseguimos permissão para capturar o áudio do navegador e recebermos o MediaStream, vamos utilizar o MediaRecorder para armazenar o nosso stream de dados.

Conforme a documentação do MDN, essa interface nos fornece alguns métodos. Para o nosso exemplo, vamos utilizar apenas 3: start, stop e requestData. Podemos implementar um botão para chamar esses métodos. Para obter mais detalhes sobre o MediaRecorder, você pode consultar diretamente a documentação: MediaStream Recording API. O nosso código vai ficar assim:

if (!navigator.mediaDevices.getUserMedia) {
	// não possuui acesso
	return;
}
 
navigator.mediaDevices.getUserMedia({ audio: true }).then(
	(stream) => {
		//Usuário deu permissão
		const mediaRecorder = new MediaRecorder(stream);
		mediaRecorder.ondataavailable = async (e) => {
			// aqui será chamado a nossa request
		}
 
		const micButton = document.querySelector(".mic-button");
		micButton.onclick = async () => {
			const pressed = micButton.getAttribute("aria-pressed") === "true";
			micButton.setAttribute("aria-pressed", !pressed);
			
			if (!pressed) {
				mediaRecorder.start();
				return;
			}
			mediaRecorder.stop();
		}
			
	},
	(err) => {
		//Usuário não deu permissão
		console.error("The following error occured: " + err);
	}
);

Enviando áudio para uma API

Agora, falta apenas enviar esse áudio por meio de uma requisição. Para fazer isso, vamos criar um arquivo blob e enviá-lo através de um POST em FormData. Finalizando assim o nosso código:

if (!navigator.mediaDevices.getUserMedia) {
	// não possuui acesso
	return;
}
 
navigator.mediaDevices.getUserMedia({ audio: true }).then(
	(stream) => {
		//Usuário deu permissão
		const mediaRecorder = new MediaRecorder(stream);
		mediaRecorder.ondataavailable = async (e) => {
			const blob = new Blob([e.data], { type: "audio/mp3" });
			const formData = new FormData();
			formData.append("audio", blob, "recording.mp3");
			const response = await fetch("http://localhost:3001/transcribe", {
				method: "POST",
				body: formData,
			});
			// resto da implementação...
		}
 
		const micButton = document.querySelector(".mic-button");
		micButton.onclick = async () => {
			const pressed = micButton.getAttribute("aria-pressed") === "true";
			micButton.setAttribute("aria-pressed", !pressed);
			
			if (!pressed) {
				mediaRecorder.start();
				return;
			}
			mediaRecorder.stop();
		}
			
	},
	(err) => {
		//Usuário não deu permissão
		console.error("The following error occured: " + err);
	}
);

Conclusão

Demonstramos acima como capturar, gravar e enviar um áudio. Você pode visualizar o código completo neste link: poc-talk-ai. Em caso de dúvidas ou sugestões, sinta-se à vontade para deixá-las nos comentários abaixo.

Até mais e obrigado pelos peixes.