8
votes

I've created a getSpectrum method using the getByteFrequencyData method on the Web Audio API's Analyser Node. The array of audio data returned is relative to the audio source's (either an el, or Audio() instance) volume, a value from 0 to 1.

Using the audio source's volume I'm trying to normalize each value received from getByteFrequencyData so that the user of getSpectrum doesn't have to worry about volume when they're visualizing the audio data.

This is the striped down version of getSpectrum

var audioData = new Uint8Array(analyser.binCount);
var spectrum = [];

analyser.getByteFrequencyData(audioData);

for (var i = 0; i < audioData.length; i++) {
  var value = audioData[i];

  //Where I'm trying to calculate the value independent of volume
  value = ((value / audioEl.volume) / 255);

  spectrum.push(value);
}

return spectrum;

The W3C spec references the equation used to calculate the returned value given a maxDecibels and minDecibels. With my rudimentary understanding, I've tried to inverse the math so I get a normalized value, but I can't getting it working exactly right. I'm having trouble accomplishing this with just a volume value from 0 to 1.

Any incite would be greatly appreciated! Heres a working example of the issue. Changing the volume slider will illustrate the problem.

Update 7/22/16: Thanks to @raymond-toy's answer I figured out how to convert the 0 to 1 volume value to decibels.

volumeDB = Math.abs((Math.log(volume)/Math.LN10)*20);

After getting the DB, I inversed the equation in the W3C spec,

value = ((audioDataValue * volumeDB) / 255) - volumeDB

Unfortunately, value somehow still ends up relative to volume. Does anyone see what I'm missing?

2
You could get around this problem by putting a GainNode in between the analyser and your context.destination. Then you would have a volume slider that adjusted the GainNode instead of the audio's volume. This way the volume gets adjusted only after passing through your analyser. - idbehold
For what I'm working on, I wouldn't have control of what sets the volume, so I wouldn't be able to put a GainNode in front of whatever that would be. This is a good idea tho. Will consider it for other projects - Chris Dolphin

2 Answers

2
votes

getByteFrequencyData returns values in dB. You don't want to divide these values by the audioE1.volume. You want to convert (somehow!) audioE1.volume to a dB value and add (or subtract) that from values from getByteFrequencyData

It might be easier to understand things if you used getFloatFrequencyData first to see what's happening.

2
votes

Apparently I was on a fool's errand. As @raymond-toy pointed out, Spectrum values are implicitly relative to volume. Normalizing would mean losing a portion of data "off the bottom of the spectrum", which was not my goal.

If anyone's curious, I ended up just dividing the audioDataValue by 255, getting a float from 0 to 1.