Get audio level from a mediastream

Options
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: