Question

I am using the microphone input with web audio api and need to get the volume value.

Right now I have already got the microphone to work: http://updates.html5rocks.com/2012/09/Live-Web-Audio-Input-Enabled

Also, i know there's a method manipulating the volume of audio file: http://www.html5rocks.com/en/tutorials/webaudio/intro/

    // Create a gain node.
    var gainNode = context.createGain();
    // Connect the source to the gain node.
    source.connect(gainNode);
    // Connect the gain node to the destination.
    gainNode.connect(context.destination);
   // Reduce the volume.
   gainNode.gain.value = 0.5;

But how to combine these two and get the input volume value? I just need to the value, no need to manipulate it.

Does anybody know?

Was it helpful?

Solution

There are two main reasons to want to get the "volume":

  1. detect when the source "clips" - i.e. the absolute value of the signal goes over a preset level, usually very near 1.0, where it will start clipping.
  2. give the user a feel for how loud their signal is.

The reason I list these separately is because the first requires that you process every sample - because you might miss a short transient otherwise. For this, you'll need to use a ScriptProcessor node, and you'll have to iterate through every sample in the buffer inside onaudioprocess to look for absolute values above your clip level. You could just determine the RMS level then, too - just sum the squares of each sample, divide by N and take the square root. DO NOT render from within onaudioprocess, though - set values that you access on requestAnimationFrame.

You can also use an AnalyserNode to do the level detection, and just average out the data, kind of like what the other answer does with getAverageVolume. However, the other answer is NOT a good use of ScriptProcessor - in fact, it's doing no processing of the script node at all, not even passing the data through, it's just using it like a timer callback. You would be FAR better served by using requestAnimationFrame as the visual callback; don't ever set layout or visual parameters from inside onaudioprocess like this, or you're begging to thrash your audio system. If you don't need clip detection, just do the getByteFrequencyCount/getAverageVolumefrom above on an AnalyserNode (but you should minimize the number of bands in the Analyser - 64 is the minimum, I think), and you should pre-allocate and reuse a Uint8Array rather than allocating it each time (that will amp up the garbage collection).

OTHER TIPS

I did a volume display for a playing audio when I was studying HTML 5.

I followed this great tutorial

http://www.smartjava.org/content/exploring-html5-web-audio-visualizing-sound

 // setup a analyzer
 analyser = context.createAnalyser();
 analyser.smoothingTimeConstant = 0.3;
 analyser.fftSize = 1024;

 javascriptNode = context.createScriptProcessor(2048, 1, 1);


 javascriptNode.onaudioprocess = function() {

        // get the average, bincount is fftsize / 2
        var array =  new Uint8Array(analyser.frequencyBinCount);
        analyser.getByteFrequencyData(array);
        var average = getAverageVolume(array)

         console.log('VOLUME:' + average); //here's the volume
 }

 function getAverageVolume(array) {
        var values = 0;
        var average;

        var length = array.length;

        // get all the frequency amplitudes
        for (var i = 0; i < length; i++) {
            values += array[i];
        }

        average = values / length;
        return average;
  }

NOTE: I just don't know if it will work on an audio input came from a microphone

Although it is a bit late, I still hope to help you.

h5_get_microphone_volume

var audioContext = new (window.AudioContext || window.webkitAudioContext)()
var mediaStreamSource = null
var meter = null

if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
  navigator.mediaDevices.getUserMedia({audio: true}).then((stream) => {
    mediaStreamSource = audioContext.createMediaStreamSource(stream)
    meter = createAudioMeter(audioContext)
    mediaStreamSource.connect(meter)
  })
}

function createAudioMeter(audioContext, clipLevel, averaging, clipLag) {
  const processor = audioContext.createScriptProcessor(512)
  processor.onaudioprocess = volumeAudioProcess
  processor.clipping = false
  processor.lastClip = 0
  processor.volume = 0
  processor.clipLevel = clipLevel || 0.98
  processor.averaging = averaging || 0.95
  processor.clipLag = clipLag || 750

  // this will have no effect, since we don't copy the input to the output,
  // but works around a current Chrome bug.
  processor.connect(audioContext.destination)

  processor.checkClipping = function () {
    if (!this.clipping) {
      return false
    }
    if ((this.lastClip + this.clipLag) < window.performance.now()) {
      this.clipping = false
    }
    return this.clipping
  }

  processor.shutdown = function () {
    this.disconnect()
    this.onaudioprocess = null
  }

  return processor
}

function volumeAudioProcess(event) {
  const buf = event.inputBuffer.getChannelData(0)
  const bufLength = buf.length
  let sum = 0
  let x

  // Do a root-mean-square on the samples: sum up the squares...
  for (var i = 0; i < bufLength; i++) {
    x = buf[i]
    if (Math.abs(x) >= this.clipLevel) {
      this.clipping = true
      this.lastClip = window.performance.now()
    }
    sum += x * x
  }

  // ... then take the square root of the sum.
  const rms = Math.sqrt(sum / bufLength)

  // Now smooth this out with the averaging factor applied
  // to the previous sample - take the max here because we
  // want "fast attack, slow release."
  this.volume = Math.max(rms, this.volume * this.averaging)
  document.getElementById('audio-value').innerHTML = this.volume
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top