# How can I get the instantaneous volume level of a sound at a particular playback point?

How can I go about getting the current volume output of an audio file in libGDX? By that I mean of the actual file at its current play position.

Essentially I want to write my own version of a plugin I was using in Unity for basic lip syncing. Depending how loud the audio is, a different mouth shape sprite will be shown.

So for example if there was a sound with a sentance "what is that?" you would expect the "that" to be a different shape than the "what is," as it's louder. Is there a way to do this, to get the volume at a certain point?

• If you are using the Music Interface, you can use getVolume(), which returns a float. But it would seem that it returns the static volume level of the entire track. You are saying you need a function like getVolume at a specified position of the track, Correct? – Shroeder Nov 12 '14 at 14:35
• You may need to write the file into the Audio Recorder structure, which puts it into an array of bytes. Then you would have to find out how to get the volume from a byte array. There may already be an extension of Libgdx that has this capability? Reference for the Above mentioned Byte Array : gamefromscratch.com/post/2013/11/19/… – Shroeder Nov 12 '14 at 14:40
• what about using full equalizer? github.com/gtomee/AudioSpectrumGDX – bluszcz Nov 12 '14 at 22:32
• Do you want to track when there is speech in the song? If I think your problem is more complicated that taking the volume. – concept3d Nov 13 '14 at 10:59

If you have read access to the audio wave, there are a few ways to do it. I will cover an accurate, and a fast method. If you do not have read access to the audio wave, you need to get access to the audio wave.

I'm not familiar with libGDX, but I highly doubt it has built-in functionality for this. It's not functionality that is typically needed, or asked of, in any software. Including software like Audacity. That said, here are my solutions:

Accurate Method

What you want are the green lines there. The length of that green line (the difference between the top of the wave, and the bottom), is going to correlate to a percentage of the max volume. The percentage could be calculated by float percentage = greenLineLength / MaxLength; If it ranged from -1 to 1, MaxLength = 2. If it ranges from 0 to 255, MaxLength = 255

The green lines going to be a little tricky though. First your going to need to identify those peaks. say you're analyzing the wave at point i. If you were to take the difference between i and i+1 (getSample(i) - getSample(i+1)), you would end up with a value representing the direction that the wave is headed at point i. If the value is positive, then it is going up. If it is negative, then it is going down.

If that were put into sudo code:

float GetDifference(int i)
{
return GetSample(i) - GetSample(i+1);
}


A peak is when it changes from going up to going down, or when it's going down and then up. Since we already know how to find out if it's going up or down. We just need to compare the value with the previous value. In sudo code:

bool IsPeak(int i)
{
float a, b;
a = GetDifference(i); // direction of the current sample
b = GetDifference(i-1); // direction of the previous sample

if ( ((a > 0) and (b < 0)) // if it's going up then down.
or ((a < 0) and (b > 0)) ) // if it's going down then up.
{
return true; // it's a peak!
}

// otherwise
return false;
}


From there, we just need to find peak 1, store that value, then find peak 2, and difference the two.

float FindVolumeFromSample(int i)
{
int  Peak1, Peak2; // our two peaks
bool FoundPeak;   // = true; if we have found the peak. Otherwise, false;

// find our first peak
FoundPeak = false;
Peak1 = i;         // start out at the point we're searching from
{
if (IsPeak(Peak1)) // check to see if this is a peak
{
FoundPeak = true; // found the peak
}
else // this is not a peak
{
Peak1 = Peak1 + 1; // move to the next sample
}
}

// loop to our second peak
FoundPeak = false;
Peak2 = Peak1 + 1; // Start off just after the previous peak
{
if (IsPeak(Peak2)) // check to see if this is a peak
{
FoundPeak = true; // found the peak
}
else // this is not a peak
{
Peak2 = Peak2 + 1; // move to the next sample
}
}

return (GetSample(Peak1) - GetSample(Peak2)) / Maxlength;
}


Fast Method

Since looping through all that data can be a little too cpu intensive, there is also a simpler method: Loop through a small section of audio, find the highest point, and the lowest point, and find the difference between those. The difference between them being proportional to the percentage of the max volume as stated in the first paragraph, above.

So lets say you can access the samples through GetSample(i), your given a start point, and an end point. You want to find the volume between start and end. You loop through all values from start to end, and keep the two most extreme values. In sudo code:

float FindVolumeBetweenSamples(int start, int end)
{
float max, min; // the maximum, and minimum, points.

// the current sample i, is initially equal to start
// continue the loop while i has not reached or passed end, and,
// increment until then.
for (int i = start; i < end; i++)
{
float sample = GetSample(i);

if (sample > max) // the new one is bigger
{
max = sample;
}
else if (sample < min) // the new one is smaller
{
min = sample
}
}

return (max - min) / MaxLength;
}


As your start and end point get closer and closer, it will become more and more accurate, to a point. This method fails when you start trying to measure volume between peaks. You'll just have to hope that doesn't happen.