Search code examples
androidandroid-mediarecorder

How to use Android media recorder to get max amplitude?


I've seen various posts addressing issues with the method getMaxAmplitude() from MediaRecorder but I still haven't been able to resolve my problem. I want to constantly update a TextView with the amplitude of sound in decibels. I tried achieving this by running a handler every 1 second with a function that calls this method, but it seems that the MediaRecorder object is always null even after I initialize it(which I find odd). BTW, I have added all the necessary permissions for audio recording in android. What do I need to change in my code to be able to access the getMaxAmplitude() method?

package com.inversepalindrome.sensorbox;

import com.cardiomood.android.controls.gauge.SpeedometerGauge;

import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Build;
import android.os.Handler;
import android.content.pm.PackageManager;
import android.widget.TextView;
import android.media.MediaRecorder;
import android.support.v4.app.ActivityCompat;
import android.widget.Toast;

import java.io.IOException;
import java.util.Locale;


public class SoundActivity extends AppCompatActivity {
    private MediaRecorder mediaRecorder;

    private SpeedometerGauge speedometer;

    private TextView soundAmplitudeText;

    private final int AUDIO_REQUEST_CODE = 200;
    private final int AUDIO_RECORDING_DELAY = 1000;
    private final double referenceAmplitude = 0.0001;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setTheme(R.style.AppTheme);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sound);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
        {
            ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.RECORD_AUDIO,
                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, AUDIO_REQUEST_CODE);
        }

        speedometer = findViewById(R.id.speedometer);

        speedometer.setLabelConverter(new SpeedometerGauge.LabelConverter() {
            @Override
            public String getLabelFor(double progress, double maxProgress) {
                return String.format(Locale.US, "%.1f", progress);
            }
        });
        speedometer.setMaxSpeed(120);
        speedometer.setMajorTickStep(20);
        speedometer.setMinorTicks(1);
        speedometer.setLabelTextSize(32);
        speedometer.addColoredRange(0, 40, Color.GREEN);
        speedometer.addColoredRange(40, 80, Color.YELLOW);
        speedometer.addColoredRange(80, 120, Color.RED);

        soundAmplitudeText = findViewById(R.id.soundAmplitudeValueText);
    }

    @Override
    public void onResume(){
        super.onResume();

        startRecording();
    }

    @Override
    public void onPause(){
        super.onPause();

        stopRecording();
    }

    @Override
    public void onRequestPermissionsResult(final int requestCode, String[] permissions, int[] grantResults) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            if (requestCode == AUDIO_REQUEST_CODE) {
                startRecording();

                final Handler handler = new Handler();
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        final double amplitude = getAmplitude();

                        speedometer.setSpeed(amplitude);
                        soundAmplitudeText.setText(String.format(Locale.US, "%.1f", amplitude));

                        handler.postDelayed(this, AUDIO_RECORDING_DELAY);
                    }
                }, AUDIO_RECORDING_DELAY);
            } else {
                Toast.makeText(this, "No audio recording permissions given!", Toast.LENGTH_LONG).show();
            }
        }
    }

    private void startRecording(){
        if(mediaRecorder != null)
        {
            mediaRecorder = new MediaRecorder();

            mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
            mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
            mediaRecorder.setOutputFile("/dev/null");

            try {
                mediaRecorder.prepare();
                mediaRecorder.start();
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void stopRecording(){
        if(mediaRecorder != null) {
            mediaRecorder.stop();
            mediaRecorder.release();
            mediaRecorder = null;
        }
    }

    private double getAmplitude(){
        if(mediaRecorder != null){
            final double maxAmplitude = mediaRecorder.getMaxAmplitude();
            final double amplitude = 20 * Math.log10(maxAmplitude / referenceAmplitude);

            return amplitude;
        }
        else{
            return 0.0;
        }
    }
}

Edit: After fixing some issues that were pointed out my app is crashing on the getMaxAmplitudeCall with the following debug message:

1501-6480/? E/AudioFlinger: createRecordTrack_l() initCheck failed -12; no control block?
2019-01-18 15:23:54.983 2362-10949/com.google.android.googlequicksearchbox:search E/AudioRecord: AudioFlinger could not create record track, status: -12
2019-01-18 15:23:54.987 2362-10949/com.google.android.googlequicksearchbox:search E/AudioRecord-JNI: Error creating AudioRecord instance: initialization check failed with status -12.
2019-01-18 15:23:54.987 2362-10949/com.google.android.googlequicksearchbox:search E/android.media.AudioRecord: Error code -20 when initializing native AudioRecord object.

Solution

  • The answer is in my comments, but your problem is you are calling in onPause stop recording, stoping and releasing your MediaRecorder. It might happen when you don't have permission for recording. When the dialog asking for permission appears, stopRecording is being called and thus your audio recorder is null and released.

    If you had permissions already this will not happen. Give it a try.

    Also, In startRecording method check if your media recorder is null. You have if is it different of null.