I'm trying to understand AVAudioPlayer and audio level metering. What I have below is an object "AudioPlayer" that is playing a short audio sound. Now I want to output the power of this sound (decibels). Somehow I don't think I'm doing this right.
audioPlayer.meteringEnabled = YES;
[audioPlayer play];
int channels = audioPlayer.numberOfChannels;
[audioPlayer updateMeters];
for (int i=0; i<channels; i++) {
//Log the peak and average power
NSLog(@"%d %0.2f %0.2f", i, [audioPlayer peakPowerForChannel:0],[audioPlayer averagePowerForChannel:0]);
The NSLog output of this is 0 -160.00 -160.00 1 -160.00 -160.00
Now according to Apple "A return value of 0 dB indicates full scale, or maximum power; a return value of -160 dB indicates minimum power" So does this mean this sound is at minimum power? I don't think this is true because the audio snippet is a fairly loud sound. I think I'm missing something here, any clarification would be appreciated.
There are several issues with your code - Jacques has already pointed out most of them.
You have to call [audioPlayer updateMeters];
each time before reading the values.
You'll probably be best off instantiating a NSTimer
.
Declare an iVar NSTimer *playerTimer;
in your class @interface
.
Also it doesn't hurt to adopt <AVAudioPlayerDelegate>
in your class so you'll be able to invalidate the timer after player has finished playing.
Then change your code to:
audioPlayer.meteringEnabled = YES;
audioPlayer.delegate = self;
if (!playerTimer)
{
playerTimer = [NSTimer scheduledTimerWithTimeInterval:0.001
target:self selector:@selector(monitorAudioPlayer)
userInfo:nil
repeats:YES];
}
[audioPlayer play];
Add this two methods to your class:
-(void) monitorAudioPlayer
{
[audioPlayer updateMeters];
for (int i=0; i<audioPlayer.numberOfChannels; i++)
{
//Log the peak and average power
NSLog(@"%d %0.2f %0.2f", i, [audioPlayer peakPowerForChannel:i],[audioPlayer averagePowerForChannel:i]);
}
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
NSLog (@"audioPlayerDidFinishPlaying:");
[playerTimer invalidate];
playerTimer = nil;
}
And you should be good to go.
You're updating and then asking for the value of the meters almost immediately after the sound starts -- that updateMeters
is probably running a few tens of milliseconds after you send play
. So if there's any silence at the beginning of the clip, you could very well be getting the correct reading. You should trying delaying your inspection, and you may also need to send updateMeters
inside the loop, right before you inspect the values.
You're also never actually getting the meter values for channels > 0, because you pass 0 no matter what the value of i
is in the loop. I think you meant to do this:
for (int currChan = 0; currChan < channels; currChan++) {
//Log the peak and average power
NSLog(@"%d %0.2f %0.2f", currChan, [audioPlayer peakPowerForChannel:currChan], [audioPlayer averagePowerForChannel:currChan]);
}
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