Am I doing the right thing to convert decibel from -120 - 0 to 0 - 120
Asked Answered
D

5

8

I will like to measure the sound volume of the surrounding, not too sure if I am doing the right thing.

I will like to create a VU meter of a range of 0(quiet) to 120(very noisy).

I gotten the Peak and Avg power but are very high in normal quiet environment. Do give me some pointer.

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.


    //creating an audio CAF file in the temporary directory, this isn’t ideal but it’s the only way to get this class functioning (the temporary directory is erased once the app quits). Here we also specifying a sample rate of 44.1kHz (which is capable of representing 22 kHz of sound frequencies according to the Nyquist theorem), and 1 channel (we do not need stereo to measure noise).

    NSDictionary* recorderSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                      [NSNumber numberWithInt:kAudioFormatLinearPCM],AVFormatIDKey,
                                      [NSNumber numberWithInt:44100],AVSampleRateKey,
                                      [NSNumber numberWithInt:1],AVNumberOfChannelsKey,
                                      [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,
                                      [NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey,
                                      [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,
                                      nil];
    NSError* error;

    NSURL *url = [NSURL fileURLWithPath:@"/dev/null"];
    recorder = [[AVAudioRecorder alloc] initWithURL:url settings:recorderSettings error:&error];

    //enable measuring
    //tell the recorder to start recording:
    [recorder record];

    if (recorder) {
        [recorder prepareToRecord];
        recorder.meteringEnabled = YES;
        [recorder record];
        levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.01 target: self selector: @selector(levelTimerCallback:) userInfo: nil repeats: YES];

    } else
    {
        NSLog(@"%@",[error description]);
    }        
}

- (void)levelTimerCallback:(NSTimer *)timer {
    [recorder updateMeters];

    const double ALPHA = 0.05;
    double peakPowerForChannel = pow(10, (0.05 * [recorder averagePowerForChannel:0]));
    lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;     

    NSLog(@"Average input: %f Peak input: %f Low pass results: %f", [recorder averagePowerForChannel:0], [recorder peakPowerForChannel:0], lowPassResults);

    float tavgPow =[recorder averagePowerForChannel:0] + 120.0;
    float tpPow = [recorder peakPowerForChannel:0] + 120.0;

    float avgPow = tavgPow;//(float)abs([recorder averagePowerForChannel:0]);
    float pPow = tpPow;//(float)abs([recorder peakPowerForChannel:0]);

    NSString *tempAvg = [NSString stringWithFormat:@"%0.2f",avgPow];
        NSString *temppeak = [NSString stringWithFormat:@"%0.2f",pPow];
    [avg setText:tempAvg];
        [peak setText:temppeak];
    NSLog(@"Average input: %f Peak input: %f Low pass results: %f", avgPow,pPow , lowPassResults);
}   
Desiccate answered 12/2, 2012 at 7:45 Comment(0)
L
8

The formula for converting a linear amplitude to decibels when you want to use 1.0 as your reference (for 0db), is

20 * log10(amp);

So I'm not sure about the intent from looking at your code, but you probably want

float db = 20 * log10([recorder averagePowerForChannel:0]);

This will go from -infinity at an amplitude of zero, to 0db at an amplitude of 1. If you really need it to go up to between 0 and 120 you can add 120 and use a max function at zero.

So, after the above line:

db += 120;
db = db < 0 ? 0 : db;

The formula you are using appears to be the formula for converting DB to amp, which I think is the opposite of what you want.

Edit: I reread and it seems you may already have the decibel value.

If this is the case, just don't convert to amplitude and add 120.

So Change

double peakPowerForChannel = pow(10, (0.05 * [recorder averagePowerForChannel:0]));

to

double peakPowerForChannel = [recorder averagePowerForChannel:0];

and you should be okay to go.

Lather answered 12/2, 2012 at 7:57 Comment(4)
Hi Michael, thanks for the reply. i believe that the averagePowerForChannel are the decibel value in -x will like to convert it to 0 - 120 valueDesiccate
@Desmond: Ok, so do the last step I suggest to change peakPowerForChannel to use the decibel value directly. You are adding 120 later. You will also need to make sure it is not less than zero by using a max(0, db) like how I did with 'db = db < 0 ? 0 : db;'Lather
thanks Michael, however the decibel are very very high in a quiet room....i download an decibel10 app to check against it the decibel different are huge. The app show about 40db,mine shows 70db. my main goal here are to check if the user are making noise. if it exceed the threshold will trigger something.Desiccate
That's probably because db is referential. The other app is probably using a reference less than 1.0, and you are using 1.0. You will need to find a new reference and divide your amplitude by the reference before doing the conversion to DB (This is done in code that you haven't shown us yet). The sound pressure level change of a pin drop is often used as the reference - you some how need to figure out how much that means in your amplitude. If you trust the other app, you can guess and check until the dbs align.Lather
S
30

Apple uses a lookup table in their SpeakHere sample that converts from dB to a linear value displayed on a level meter. This is to save device power (I guess).

I also needed this, but didn't think a couple of float calculations every 1/10s (my refresh rate) would cost so much device power. So, instead of building up a table I moulded their code into:

float       level;                // The linear 0.0 .. 1.0 value we need.
const float minDecibels = -80.0f; // Or use -60dB, which I measured in a silent room.
float       decibels    = [audioRecorder averagePowerForChannel:0];

if (decibels < minDecibels)
{
    level = 0.0f;
}
else if (decibels >= 0.0f)
{
    level = 1.0f;
}
else
{
    float   root            = 2.0f;
    float   minAmp          = powf(10.0f, 0.05f * minDecibels);
    float   inverseAmpRange = 1.0f / (1.0f - minAmp);
    float   amp             = powf(10.0f, 0.05f * decibels);
    float   adjAmp          = (amp - minAmp) * inverseAmpRange;

    level = powf(adjAmp, 1.0f / root);
}

I'm using an AVAudioRecorder, hence you see getting the dB's with averagePowerForChannel:, but you can fill your own dB value there.

Apple's example used double calculations, which I don't understand because for audio metering float accuracy is more than sufficient, and costs less device power.

Needless to say, you can now scale this calculated level to your 0 .. 120 range with a simple level * 120.0f.

The above code can be sped up when we fix root at 2.0f, by replacing powf(adjAmp, 1.0f / root) with sqrtf(adjAmp); but that's a minor thing, and a very good compiler might be able to do this for us. And I'm almost sure that inverseAmpRange will be calculated once at compile-time.

Strephonn answered 24/4, 2013 at 12:49 Comment(0)
L
8

The formula for converting a linear amplitude to decibels when you want to use 1.0 as your reference (for 0db), is

20 * log10(amp);

So I'm not sure about the intent from looking at your code, but you probably want

float db = 20 * log10([recorder averagePowerForChannel:0]);

This will go from -infinity at an amplitude of zero, to 0db at an amplitude of 1. If you really need it to go up to between 0 and 120 you can add 120 and use a max function at zero.

So, after the above line:

db += 120;
db = db < 0 ? 0 : db;

The formula you are using appears to be the formula for converting DB to amp, which I think is the opposite of what you want.

Edit: I reread and it seems you may already have the decibel value.

If this is the case, just don't convert to amplitude and add 120.

So Change

double peakPowerForChannel = pow(10, (0.05 * [recorder averagePowerForChannel:0]));

to

double peakPowerForChannel = [recorder averagePowerForChannel:0];

and you should be okay to go.

Lather answered 12/2, 2012 at 7:57 Comment(4)
Hi Michael, thanks for the reply. i believe that the averagePowerForChannel are the decibel value in -x will like to convert it to 0 - 120 valueDesiccate
@Desmond: Ok, so do the last step I suggest to change peakPowerForChannel to use the decibel value directly. You are adding 120 later. You will also need to make sure it is not less than zero by using a max(0, db) like how I did with 'db = db < 0 ? 0 : db;'Lather
thanks Michael, however the decibel are very very high in a quiet room....i download an decibel10 app to check against it the decibel different are huge. The app show about 40db,mine shows 70db. my main goal here are to check if the user are making noise. if it exceed the threshold will trigger something.Desiccate
That's probably because db is referential. The other app is probably using a reference less than 1.0, and you are using 1.0. You will need to find a new reference and divide your amplitude by the reference before doing the conversion to DB (This is done in code that you haven't shown us yet). The sound pressure level change of a pin drop is often used as the reference - you some how need to figure out how much that means in your amplitude. If you trust the other app, you can guess and check until the dbs align.Lather
R
1

Actually, the range of decibels is from -160 to 0, but it can go to positive values.(AVAudioRecorder Class Reference - averagePowerForChannel: method)

Then is better write db += 160; instead of db += 120;. Of course you can also put an offset to correct it.

Rondo answered 20/3, 2013 at 12:45 Comment(0)
K
0

I make a regression model to convert the mapping relation between the wav data generated from NSRecorder and the decibel data from NSRecorder.averagePowerForChannel

NSRecorder.averagePowerForChannel (dB) = -80+6 log2(wav_RMS)

Where wav_RMS is root mean square value of wav data in a short time, i.e. 0.1 sec.

Kamala answered 9/8, 2017 at 2:32 Comment(0)
M
-3

Simply set your maximum and minimum value. Like you getting range of 0-120. If you want range of 0-60. Simply divide value to half to get the half range and so on..

Minette answered 18/4, 2015 at 12:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.