Search code examples
iphoneiosxcodeaudio-recordingdecibel

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


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);
}   

Solution

  • 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.