I'm building an iOS app using EZAudio. It's delegate returns back a float**
buffer, which contains float values indicating the volume detected. This delegate is called constantly and it's work is done a different thread.
What I am trying to do is to take the float value from EZAudio and convert it into decibels.
Here's my simplified EZAudio Delegate for getting Microphone Data:
- (void)microphone:(EZMicrophone *)microphone hasAudioReceived:(float **)buffer withBufferSize:(UInt32)bufferSize withNumberOfChannels:(UInt32)numberOfChannels {
/*
* Returns a float array called buffer that contains the stereo signal data
* buffer[0] is the left audio channel
* buffer[1] is the right audio channel
*/
// Using a separate audio thread to not block the main UI thread
dispatch_async(dispatch_get_main_queue(), ^{
float decibels = [self getDecibelsFromVolume:buffer withBufferSize:bufferSize];
NSLog(@"Decibels: %f", decibels);
});
}
The problem is that after implementing solutions from the links below, I do not understand how it works. If someone could explain how it converts volume to decibels I would be very grateful
How to convert audio input to DB? #85
How to change the buffer size to increase FFT window? #50
Changing Buffer Size #84
The solution uses the following methods from the Accelerate Framework to convert the volume into decibels:
Below is the method getDecibelsFromVolume
that is called from the EZAudio Delegate. It is passed the float** buffer
and bufferSize
from the delegate.
- (float)getDecibelsFromVolume:(float**)buffer withBufferSize:(UInt32)bufferSize {
// Decibel Calculation.
float one = 1.0;
float meanVal = 0.0;
float tiny = 0.1;
float lastdbValue = 0.0;
vDSP_vsq(buffer[0], 1, buffer[0], 1, bufferSize);
vDSP_meanv(buffer[0], 1, &meanVal, bufferSize);
vDSP_vdbcon(&meanVal, 1, &one, &meanVal, 1, 1, 0);
// Exponential moving average to dB level to only get continous sounds.
float currentdb = 1.0 - (fabs(meanVal) / 100);
if (lastdbValue == INFINITY || lastdbValue == -INFINITY || isnan(lastdbValue)) {
lastdbValue = 0.0;
}
float dbValue = ((1.0 - tiny) * lastdbValue) + tiny * currentdb;
lastdbValue = dbValue;
return dbValue;
}
I'll explain how one would compute a dB value for a signal using code and then show how that relates to the vDSP example.
double sumSquared = 0;
for (int i = 0 ; i < numSamples ; i++)
{
sumSquared += samples[i]*samples[i];
}
double rms = sumSquared/numSamples;
For more information on RMS
double dBvalue = 20*log10(rms);
vDSP_vsq(buffer[0], 1, buffer[0], 1, bufferSize);
This line loops over the buffer and computes squares all of the elements in the buffer. If buffer contained the values [1,2,3,4]
before the call then after the call it would contain the values [1,4,9,16]
vDSP_meanv(buffer[0], 1, &meanVal, bufferSize);
This line loops over the buffer, summing the values in the buffer and then returning the sum divided by the number of elements. So for the input buffer [1,4,9,16]
in computes the sum 30
, divides by 4
and returns the result 7.5
.
vDSP_vdbcon(&meanVal, 1, &one, &meanVal, 1, 1, 0);
This line converts the meanVal
to decibels. There is really no point in calling a vectorized function here since it is only operating on a single element. What it is doing however is plugging the parameters into the following formula:
meanVal = n*log10(meanVal/one)
where n
is either 10
or 20
depending on the last parameter. In this case it is 10
. 10
is used for power measurements and 20
is used for amplitudes. I think 20
would make more sense for you to use.
The last little bit of code looks to be doing some simple smoothing of the result to make the meter a little less bouncy.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With