12

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


The Code

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;
}
Wain
  • 118,658
  • 15
  • 128
  • 151
Jamie
  • 927
  • 1
  • 7
  • 18

1 Answers1

15

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.

Community
  • 1
  • 1
jaket
  • 9,140
  • 2
  • 25
  • 44
  • Thanks very much @jacket! Could you please explain what you mean by: `"There is really no point in calling a vectorized function here since it is only operating on a single element."` Are you are referring to `vDSP_vdbcon` when you say `vectorized function`? What would the change be? – Jamie Feb 26 '15 at 12:29
  • 2
    What I mean is that the `vDSP_vdbcon` is optimized to convert an array of values to dB yet your code is only calling it with the single float `meanVal`. It's not really pointless, it's just not going to have any performance benefit from writing coding it like `meanVal = 20*log10(meanVal);` – jaket Feb 27 '15 at 00:43
  • Oh god.. Apple really likes to complicate things. Thanks a lot for the explanation. – denisb411 May 17 '17 at 10:36