Skip to content

Instantly share code, notes, and snippets.

@chathudan
Last active January 21, 2024 09:53
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save chathudan/95d9acdd741b2a577483 to your computer and use it in GitHub Desktop.
Save chathudan/95d9acdd741b2a577483 to your computer and use it in GitHub Desktop.
Android Audio recording, MediaRecorder example
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/grey50">
<LinearLayout
android:id="@+id/button_bar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/timer"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
<Button
android:id="@+id/cancel_button"
style="?android:attr/borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/cancel"
android:textColor="@color/black87" />
<View
android:layout_width="1dp"
android:layout_height="fill_parent"
android:layout_marginBottom="7dp"
android:layout_marginTop="7dp"
android:background="@color/black12" />
<Button
android:id="@+id/share_button"
style="?android:attr/borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/share"
android:textColor="@color/black87" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="50sp"
android:text="0:00.0"
android:textColor="@color/red3000"
android:typeface="monospace"
android:textStyle="bold"
android:id="@+id/timer"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"/>
</RelativeLayout>
/**
* @author Chathura Wijesinghe <cdanasiri@gmail.com> on 9/9/15.
*/
public class RecordingActivity extends AppCompatActivity implements View.OnClickListener {
private TextView mTimerTextView;
private Button mCancelButton;
private Button mStopButton;
private MediaRecorder mRecorder;
private long mStartTime = 0;
private int[] amplitudes = new int[100];
private int i = 0;
private Handler mHandler = new Handler();
private Runnable mTickExecutor = new Runnable() {
@Override
public void run() {
tick();
mHandler.postDelayed(mTickExecutor,100);
}
};
private File mOutputFile;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recording);
this.mTimerTextView = (TextView) this.findViewById(R.id.timer);
this.mCancelButton = (Button) this.findViewById(R.id.cancel_button);
this.mCancelButton.setOnClickListener(this);
this.mStopButton = (Button) this.findViewById(R.id.share_button);
this.mStopButton.setOnClickListener(this);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
@Override
protected void onStart() {
super.onStart();
Log.d("Voice Recorder", "output: " + getOutputFile());
startRecording();
}
@Override
protected void onStop() {
super.onStop();
if (mRecorder != null) {
stopRecording(false);
}
}
private void startRecording() {
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC);
mRecorder.setAudioEncodingBitRate(48000);
} else {
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.setAudioEncodingBitRate(64000);
}
mRecorder.setAudioSamplingRate(16000);
mOutputFile = getOutputFile();
mOutputFile.getParentFile().mkdirs();
mRecorder.setOutputFile(mOutputFile.getAbsolutePath());
try {
mRecorder.prepare();
mRecorder.start();
mStartTime = SystemClock.elapsedRealtime();
mHandler.postDelayed(mTickExecutor, 100);
Log.d("Voice Recorder","started recording to "+mOutputFile.getAbsolutePath());
} catch (IOException e) {
Log.e("Voice Recorder", "prepare() failed "+e.getMessage());
}
}
protected void stopRecording(boolean saveFile) {
mRecorder.stop();
mRecorder.release();
mRecorder = null;
mStartTime = 0;
mHandler.removeCallbacks(mTickExecutor);
if (!saveFile && mOutputFile != null) {
mOutputFile.delete();
}
}
private File getOutputFile() {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
return new File(Environment.getExternalStorageDirectory().getAbsolutePath().toString()
+ "/Voice Recorder/RECORDING_"
+ dateFormat.format(new Date())
+ ".m4a");
}
private void tick() {
long time = (mStartTime < 0) ? 0 : (SystemClock.elapsedRealtime() - mStartTime);
int minutes = (int) (time / 60000);
int seconds = (int) (time / 1000) % 60;
int milliseconds = (int) (time / 100) % 10;
mTimerTextView.setText(minutes+":"+(seconds < 10 ? "0"+seconds : seconds)+"."+milliseconds);
if (mRecorder != null) {
amplitudes[i] = mRecorder.getMaxAmplitude();
//Log.d("Voice Recorder","amplitude: "+(amplitudes[i] * 100 / 32767));
if (i >= amplitudes.length -1) {
i = 0;
} else {
++i;
}
}
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.cancel_button:
stopRecording(false);
setResult(RESULT_CANCELED);
finish();
break;
case R.id.share_button:
stopRecording(true);
Uri uri = Uri.parse("file://" + mOutputFile.getAbsolutePath());
Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
scanIntent.setData(uri);
sendBroadcast(scanIntent);
setResult(Activity.RESULT_OK, new Intent().setData(uri));
finish();
break;
}
}
}
@Mahawbc
Copy link

Mahawbc commented Mar 13, 2018

Hi .,

Am Getting Null pointer exception while stop my recording. what should i have to do now?

Code

package com.wbc.bpandroid;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.SystemClock;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.ExecutionException;

/**

  • Created by maha on 3/13/2018.
    */

public class PhoneStateReceiver extends BroadcastReceiver
{
private static int lastState = TelephonyManager.CALL_STATE_IDLE;
private static Date callStartTime;
private static boolean isIncoming;
private static String savedNumber; //because the passed incoming is only valid in ringing
String Tag ="PhoneStateReceiver ";

CommonVariables variables = new CommonVariables();

//Audio recording varibles

private MediaRecorder mRecorder;
private long mStartTime = 0;

private int[] amplitudes = new int[100];
private int i = 0;

private Handler mHandler = new Handler();
private Runnable mTickExecutor = new Runnable() {
    @Override
    public void run() {
        tick();
        mHandler.postDelayed(mTickExecutor,100);
    }
};



@Override
public void onReceive(Context context, Intent intent)
{
    //We listen to two intents.  The new outgoing call only tells us of an outgoing call.  We use it to get the number.
    if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
        savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
        Log.e("Out Going call Number",":##########"+savedNumber);
    }
    else{
        String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
        String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
        int state = 0;
        if(stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)){
            state = TelephonyManager.CALL_STATE_IDLE;
        }
        else if(stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){
            state = TelephonyManager.CALL_STATE_OFFHOOK;
        }
        else if(stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)){
            state = TelephonyManager.CALL_STATE_RINGING;
        }


        onCallStateChanged(context, state, number);
    }
}
//Derived classes should override these to respond to specific events of interest
protected void onIncomingCallStarted(Context ctx, String number, Date start)
{
    Log.e("Incoming Call Number",":###########"+number);
    Log.e("Started Time",":###########"+start.toString());
    startRecording();
}
protected void onOutgoingCallStarted(Context ctx, String number, Date start)
{
    Log.e("Outgoing Call Number",":###########"+number);
    Log.e("Started Time",":###########"+start.toString());
    startRecording();
}
protected void onIncomingCallEnded(Context ctx, String number, Date start, Date end)
{
    Log.e("Incoming Call Number",":###########"+number);
    Log.e("start Time",":###########"+start.toString());
    Log.e("End Time",":###########"+end.toString());
    stopRecording(true);

}
protected void onOutgoingCallEnded(Context ctx, String number, Date start, Date end)
{
    Log.e("Outgoing Call Number",":###########"+number);
    Log.e("Start Time",":###########"+start.toString());
    Log.e("End Time",":###########"+end.toString());
    stopRecording(true);
}
protected void onMissedCall(Context ctx, String number, Date start)
{
    Log.e("Missed Call Number",":###########"+number);
    Log.e("Start Time",":###########"+start.toString());
}

//Deals with actual events

//Incoming call-  goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up
//Outgoing call-  goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up
public void onCallStateChanged(Context context, int state, String number) {
    if(lastState == state){
        //No change, debounce extras
        return;
    }
    switch (state) {
        case TelephonyManager.CALL_STATE_RINGING:
            isIncoming = true;
            callStartTime = new Date();
            savedNumber = number;
           // savedNumber = TelephonyManager.EXTRA_INCOMING_NUMBER;
            onIncomingCallStarted(context, savedNumber, callStartTime);
            break;
        case TelephonyManager.CALL_STATE_OFFHOOK:
            //Transition of ringing->offhook are pickups of incoming calls.  Nothing done on them
            if(lastState != TelephonyManager.CALL_STATE_RINGING){
                isIncoming = false;
                callStartTime = new Date();
                onOutgoingCallStarted(context, savedNumber, callStartTime);
            }
            break;
        case TelephonyManager.CALL_STATE_IDLE:
            //Went to idle-  this is the end of a call.  What type depends on previous state(s)
            if(lastState == TelephonyManager.CALL_STATE_RINGING){
                //Ring but no pickup-  a miss
                onMissedCall(context, savedNumber, callStartTime);
            }
            else if(isIncoming){
                onIncomingCallEnded(context, savedNumber, callStartTime, new Date());
            }
            else{
                onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());
                Log.e("Recorded Audio file",":#############"+getOutputFile().toString());
            }
            break;
    }
    lastState = state;
}


//Audio recording Methods
private void startRecording()
{
    Log.e("start Recording",":######333333");
    mRecorder = new MediaRecorder();
    mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC);
        mRecorder.setAudioEncodingBitRate(48000);
    } else {
        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        mRecorder.setAudioEncodingBitRate(64000);
    }
    mRecorder.setAudioSamplingRate(16000);
    variables.mOutputFile = getOutputFile();
    variables.mOutputFile.getParentFile().mkdirs();
    mRecorder.setOutputFile(variables.mOutputFile.getAbsolutePath());

    try {
        mRecorder.prepare();
        mRecorder.start();
        mStartTime = SystemClock.elapsedRealtime();
        mHandler.postDelayed(mTickExecutor, 100);
        Log.d("Voice Recorder","started recording to "+variables.mOutputFile.getAbsolutePath());
    } catch (IOException e) {
        Log.e("Voice Recorder", "prepare() failed "+e.getMessage());
    }
}

protected  void stopRecording(boolean saveFile)
{
    Log.e("Stop recording",":"+saveFile);
    mRecorder.stop();
    mRecorder.release();
    mRecorder = null;
    mStartTime = 0;
    mHandler.removeCallbacks(mTickExecutor);
    if (!saveFile && variables.mOutputFile != null) {
        variables.mOutputFile.delete();
    }

    Uri uri = Uri.parse("file://" + variables.mOutputFile.getAbsolutePath());
    Log.e("Recorded File URI",":$$$$$$$$$$$$$$$$"+uri.toString());
}

private File getOutputFile() {
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
    return new File(Environment.getExternalStorageDirectory().getAbsolutePath().toString()
            + "/Voice Recorder/RECORDING_"
            + dateFormat.format(new Date())
            + ".m4a");

}

private void tick() {
    long time = (mStartTime < 0) ? 0 : (SystemClock.elapsedRealtime() - mStartTime);
    int minutes = (int) (time / 60000);
    int seconds = (int) (time / 1000) % 60;
    int milliseconds = (int) (time / 100) % 10;
    Log.e("Call Duration",":############"+minutes+"mins "+seconds+" secs "+milliseconds+" milliseconds");
   // mTimerTextView.setText(minutes+":"+(seconds < 10 ? "0"+seconds : seconds)+"."+milliseconds);
    if (mRecorder != null) {
        amplitudes[i] = mRecorder.getMaxAmplitude();
        //Log.d("Voice Recorder","amplitude: "+(amplitudes[i] * 100 / 32767));
        if (i >= amplitudes.length -1) {
            i = 0;
        } else {
            ++i;
        }
    }
}

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment