Get audio level from a mediastream
harshithpabbati
Dailynista
Firstly let’s start with creating audio level processor (where we process the frames to calculate the volume)
class AudioLevelProcessor extends AudioWorkletProcessor { volume; interval; nextFrame; constructor() { super(); this.volume = 0; this.interval = 25; this.nextFrame = this.interval; } get intervalInFrames() { // sampleRate is globally defined in AudioWorklets. // See https://developer.mozilla.org/en-US/docs/Web/API/AudioWorkletGlobalScope return (this.interval / 1000) * sampleRate; } process(inputList, outputList, parameters) { const firstInput = inputList[0]; if (firstInput.length > 0) { const inputData = firstInput[0]; let total = 0; for (let i = 0; i < inputData.length; ++i) { total += Math.abs(inputData[i]); } const rms = Math.sqrt(total / inputData.length); this.volume = Math.max(0, Math.min(1, rms)); this.nextFrame -= inputData.length; if (this.nextFrame < 0) { this.nextFrame += this.intervalInFrames; this.port.postMessage({ volume: this.volume }); } } return true; } } registerProcessor('audiolevel', AudioLevelProcessor);
It's time for us to write a hook to use the audio level processor
import { useEffect, useState } from 'react'; export const useAudioLevel = (stream: MediaStream) => { const [micVolume, setMicVolume] = useState(0); useEffect(() => { if (!stream) { setMicVolume(0); return; } const AudioCtx = typeof AudioContext !== 'undefined' ? AudioContext : typeof webkitAudioContext !== 'undefined' ? webkitAudioContext : null; if (!AudioCtx) return; const audioContext: AudioContext = new AudioCtx(); const mediaStreamSource = audioContext.createMediaStreamSource(stream); let node: AudioWorkletNode; const startProcessing = async () => { try { await audioContext.audioWorklet.addModule( `${PATH_TO_AUDIO_LEVEL_PROCESSOR}/audiolevel-processor.js` ); node = new AudioWorkletNode(audioContext, 'audiolevel'); node.port.onmessage = (event) => { let volume = 0; if (event.data.volume) volume = event.data.volume; if (!node) return; setMicVolume(volume); }; mediaStreamSource.connect(node).connect(audioContext.destination); } catch {} }; startProcessing(); return () => { node?.disconnect(); node = null; mediaStreamSource?.disconnect(); audioContext?.close(); }; }, [assetPrefix, stream]); return micVolume; };
We are now ready to use the hook!
// the value of the volume lies between 0 - 1 const volume = useAudioLevel(mediaStream);
8