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