Get audio level from a mediastream

harshithpabbati
harshithpabbati Dailynista
edited September 2022 in Code Share 💻

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); 
Tagged: