What does Android's getMaxAmplitude() function for the MediaRecorder actually give me?

20.8k views Asked by At

The Android MediaRecorder has a function

.getMaxAmplitude();
which, as the API tells me, "Returns the maximum absolute amplitude that was sampled since the last call to this method." but I can't find what amplitude this is? Is it in pascal or watts?

I have found on several pages on the web that you can calculate a value closely corelated to decibels using (as suggested here).

double db = (20 * Math.log10(amplitude / REFERENCE)); 

which would let me assume that the returned value is in some linear scale (probably something like milipascal...)

REFERENCE=0.1 (I am aware that this should be something like 2*10^(-5) Pascal ((20 uPascal)), but that returns strange values... 0.1 strangely works better.)

Right now I measure the MaxAmplitude() using the

getMaxAmplitude()
and put this into the variable amplitude.

This is the method:

public double getNoiseLevel() 
{
    //Log.d("SPLService", "getNoiseLevel() ");
    int x = mRecorder.getMaxAmplitude();
    double x2 = x;
    Log.d("SPLService", "x="+x);
    double db = (20 * Math.log10(x2 / REFERENCE));
    //Log.d("SPLService", "db="+db);
    if(db>0)
    {
        return db;
    }
    else
    {
        return 0;
    }
}

This is done 5 times in half a second, which gets kind of an average

for(int i=0; i<5; i++)
{
    try 
    {
            Thread.sleep(100);
    } 
    catch (InterruptedException e) 
    {
            e.printStackTrace();
            return 0;
    }
    level = level+getNoiseLevel();
    if(level>0)
    {
        counter++;
    }
}
level=level/counter;
Log.d(LOG_TAG, "level="+level);

I get something that kinda looks like decibel but I'm not sure its actualy decibel at all...

So, could anyone help me on this? It seems very odd that the API does not specefy at all what is returned...

2

There are 2 answers

2
Lukas Ruge On

I could find the answer to this question and I'll share it here for anyone who cares: The MediaRecorder.getMaxAmplitude() function returns unsigned 16-bit integer values (0-32767). Which is probably just the abs() of the CD-quality sample values that range from -32768 to 32767. This means that they probably represent a 16-bit digitalization of the electrical output from 0-100% maximum voltage range of the microphone build into that mobile phone. Since even in one brand of mobile these microphones sometimes vary in their precise range not even to similar phones will necessarily return the same value given the same distance to the same sound source.

This value however correlates to sound pressure in Pascal since it's also a linear quantisation of the solund pressure, in the area where sound can be measured with the given microphone (which will not cover the entire sprectrum due to the limitations of the phone).

9
Lukas Ruge On

Worked on this some more. Using some tests done with calibrated SPL-Meters and smart phones with diferent pure frequencies, white noise and pink noise I now know that mobile phone microphones are not usable for anything that should register anywhere above 90 to 100 dB(SPL) depending on the phone.

Asuming that 90 dB(SPL) is the maximum one can calculate that this would correspond to a pressure of 0.6325 Pa at the mic. Now asuming that p0=0.0002 Pa is the reference minimum and asuming that this would register as 0 (which would never actually happen) from getMaxAmplitude() we can corelate the values from the getMaxAmplitude() function with maximum pressure at the mic. This means a result of 16375 from getMaxAmplitude() would correspond to 0.3165 Pa maximum pressure. This of course is not very scientific since the max and min values are pure conjesture, but it gives us a starting point. We can now calculate p with

p=getMaxAmplitude()/51805.5336

Knowing the pressure at the mic we can calculate the dB(SPL)-value with the well known formula

X = 20 log_10 (p/p0)

This still will give a value that is to high since only the maximum amplitude is used in the calculations. To solve this one must not use getMaxAmplitude() and while this is slighly outside the focus of this question, I will put the code in anyway in the hope, that it helps

public class NoiseRecorder 
{

private final String TAG = SoundOfTheCityConstants.TAG;
public static double REFERENCE = 0.00002;

public double getNoiseLevel() throws NoValidNoiseLevelException
{
    Logging.e(TAG, "start new recording process");
    int bufferSize = AudioRecord.getMinBufferSize(44100,AudioFormat.CHANNEL_IN_DEFAULT,AudioFormat.ENCODING_PCM_16BIT);
    //making the buffer bigger....
    bufferSize=bufferSize*4;
    AudioRecord recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
            44100, AudioFormat.CHANNEL_IN_DEFAULT, AudioFormat.ENCODING_PCM_16BIT, bufferSize);

    short data [] = new short[bufferSize];
    double average = 0.0;
    recorder.startRecording();
    //recording data;
    recorder.read(data, 0, bufferSize);

    recorder.stop();
    Logging.e(TAG, "stop");
    for (short s : data)
    {
        if(s>0)
        {
            average += Math.abs(s);
        }
        else
        {
            bufferSize--;
        }
    }
    //x=max;
    double x = average/bufferSize;
    Logging.e(TAG, ""+x);
    recorder.release();
    Logging.d(TAG, "getNoiseLevel() ");
    double db=0;
    if (x==0){
        NoValidNoiseLevelException e = new NoValidNoiseLevelException(x);
        throw e;
    }
    // calculating the pascal pressure based on the idea that the max amplitude (between 0 and 32767) is 
    // relative to the pressure
    double pressure = x/51805.5336; //the value 51805.5336 can be derived from asuming that x=32767=0.6325 Pa and x=1 = 0.00002 Pa (the reference value)
    Logging.d(TAG, "x="+pressure +" Pa");
    db = (20 * Math.log10(pressure/REFERENCE));
    Logging.d(TAG, "db="+db);
    if(db>0)
    {
        return db;
    }
    NoValidNoiseLevelException e = new NoValidNoiseLevelException(x);
    throw e;
}
}

These values now are derived from the average of all amplitudes in an 4 seconds sample and thus more accurate. Afterwards the above decribed calculations are done. This will give a more realistic decibel value. Note that mobile phone mics still do suck and that this algorithm will not produce actual dB(SPL) but only a slighly better approximation then the one before.

To get the performance of some apps out there some more would need to be done. Most of these apps use sliding windows, meaning the keep recording and slide a window of x seconds to contnuosly evaluate the sound level. Also I will perform some evaluation what db Value is best suited to be used as max, right now it's 90 dB(SPL)/0.6325 Pa which is just a reasonable guess, it will probably be slighly above that.

As soon as I have more I'll update the info.