Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can someone explain how this code converts volume to decibels using the Accelerate Framework?

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.


EZAudioDelegate

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

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 Code

The solution uses the following methods from the Accelerate Framework to convert the volume into decibels:

  • vDSP_vsq
  • vDSP_meanv
  • vDSP_vdbcon

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;
}
like image 588
Jamie Avatar asked Feb 20 '15 10:02

Jamie


1 Answers

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.

First, compute the RMS sum of a chunk of data

double sumSquared = 0;
for (int i = 0 ; i < numSamples ; i++)
{
   sumSquared += samples[i]*samples[i];
}
double rms = sumSquared/numSamples;

For more information on RMS

Next convert the RMS value to dB

double dBvalue = 20*log10(rms);

How this relates to the example code

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.

like image 158
jaket Avatar answered Oct 26 '22 10:10

jaket