2

Soooo, I'm trying to write an Android app to measure sound level of environment.

I know that those kind of questions were asked many, many times and I've read many of them, but i'm still not sure, if my code is good.

private MediaRecorder mRecorder = null;
private TextView AmpCur, DbCur;

Handler timerHandler = new Handler();
Runnable timerRunnable = new Runnable() {
    @Override
    public void run() {
        //change text to actual amplitude
        int Amp = mRecorder.getMaxAmplitude();
        double db = 20 * Math.log10((double)Amp / 32767.0);
        double db2 = 20 * Math.log10((double)Amp);
        int db2I = (int) Math.round(db2);
        Log.i("SoundMeasure", "amp=" + Amp + " db=" + db + " db2=" + db2);

        AmpCur.setText(Integer.toString(Amp));
        DbCur.setText(Integer.toString(db2I));
        timerHandler.postDelayed(this, 250);
    }
};

public void RecorderInit() {

    String mFileName = Environment.getExternalStorageDirectory().getAbsolutePath();
    mFileName += "/audiorecordtest.3gp";

    mRecorder = new MediaRecorder();
    mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    mRecorder.setOutputFile(mFileName);

    try {
        mRecorder.prepare();
    } catch (IOException e) {
        Log.e("SoundMeasure", "prepare() fail");
    }

    mRecorder.start();
    timerHandler.postDelayed(timerRunnable, 250);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_sound_measure);

    AmpCur = (TextView) findViewById(R.id.amp_cur);
    DbCur = (TextView) findViewById(R.id.db_cur);
} 
@Override
public void onPause() {
    super.onPause();
    if (mRecorder != null) {
        timerHandler.removeCallbacks(timerRunnable);
        mRecorder.release();
        mRecorder = null;
    }
}

@Override
public void onResume() {
    super.onResume();
    if (mRecorder == null) {
        RecorderInit();
    }
}

App works, but the values are... a bit weird.

In very silent room, where other apps show ~15dB, I have

amp ~80 db ~-50 db2 ~40

When in blow into mic, other apps show 110+dB, and mine shows

amp 32767 db 0 db2 90

I've read that maxAmplitude can't get over 32767 and phones mics can't measure more than 90dB, but how other apps do that?

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
gowasan
  • 21
  • 3

2 Answers2

1

At least your reference value of 32767 seems wrong. You can deduce this from the db values you get. You use a value that is per this post the maximum that a mobile phone microphone can record. Since you scale with this, in all but the maximum apmlitude cases you will get a negative value because of how logarithm works. Log10 of (32767.0 / 32767.0) will be 0 because log(1) = 0. values smaller than one will approach negative infinity.

Your formula for db2 would also be wrong because you essentially use a reference of 1. The last answer to this question seems to have figured out a proper reference value but it also states that you should not use getMaxAmplitude alone. He also converts the value to pascals before using a reference of 0.00002. This is probably how other apps go about calculating the decibels.

Community
  • 1
  • 1
milez
  • 2,201
  • 12
  • 31
1

Amplitudes in dB are always a relative measure against a reference point.

You are calculating dBFS which is power relative to the maximum sample value of the system

dBFS = 20 * log10(V/V0)

where: V is the measured signal voltage (in this case, sample value) and V0 is the reference point.

It seems that you are trying to build a Sound Pressure Level meter. These measure air pressure, and typically the dBA scale is used. This is weighted and filtered, having time and frequency response characteristics. You are measuring peak amplitude, which will read higher.

There are number of practical considerations:

  • You assume the microphone responds linearly to air-pressure variations
  • AGC and the HPF needs to be disabled for the microphone
  • The frequency response needs to be wide enough and flat.
  • The basic theory of measurement dictates that you need a pair of calibration points to calculate the gain of the system and any DC bias.

In practice, these last two points make building a practical SPL meter app for Android very difficult - you need to calibrate the app for each type of device it runs on (potentially a huge number) - and compensate for frequency response of the system if it's not linear. There is no particular requirement or commercial imperative for Android phone OEMs to normalise the signals from microphones.

marko
  • 9,029
  • 4
  • 30
  • 46