Sensor Fun: Creating a simple audio recorder/player

May 2nd, 2010 by

Sound recording and playback is really simple using the MediaRecorder and MediaPlayer classes .. see the example below ..

 

Sample App

  • First some layout .. a button to start/stop recording and a button to play the recorded stuff (main.xml):
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >
    <TextView
    	android:id="@+id/output"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text=""
        />
    <Button android:text="@+string/record" android:id="@+id/btRecord" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
    <Button android:text="@+string/play" android:id="@+id/btPlay" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
    </LinearLayout>
  • Adjusting the externalized strings in the strings.xml
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">Soundrecorder Tutorial</string>
    	<string name="record">Record!</string>
    	<string name="play">Play</string>
    </resources>
  • Set permissions needed in the AndroidManifest.xml
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.hascode.android"
          android:versionCode="1"
          android:versionName="1.0">
        <application android:icon="@drawable/icon" android:label="@string/app_name">
            <activity android:name=".RecorderActivity"
                      android:label="@string/app_name">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
     
        </application>
        <uses-sdk android:minSdkVersion="7" />
     
    <uses-permission android:name="android.permission.RECORD_AUDIO"></uses-permission>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
    </manifest>
  • Now the activity to bring in some action .. RecorderActivity.java
    package com.hascode.android;
     
    import java.io.File;
    import java.io.IOException;
     
    import android.app.Activity;
    import android.media.MediaPlayer;
    import android.media.MediaRecorder;
    import android.os.Bundle;
    import android.os.Environment;
    import android.util.Log;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.TextView;
     
    public class RecorderActivity extends Activity {
    	private static final String APP_TAG = "com.hascode.android.soundrecorder";
     
    	private MediaRecorder recorder = new MediaRecorder();
    	private MediaPlayer player = new MediaPlayer();
     
    	private Button btRecord;
    	private Button btPlay;
    	private TextView resultView;
     
    	private boolean recording = false;
    	private boolean playing = false;
    	private File outfile = null;
     
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.main);
    		resultView = (TextView) findViewById(R.id.output);
     
    		try {
    			// the soundfile
    			File storageDir = new File(Environment
    					.getExternalStorageDirectory(), "com.hascode.recorder");
    			storageDir.mkdir();
    			Log.d(APP_TAG, "Storage directory set to " + storageDir);
    			outfile = File.createTempFile("hascode", ".3gp", storageDir);
     
    			// init recorder
    			recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    			recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    			recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    			recorder.setOutputFile(outfile.getAbsolutePath());
     
    			// init player
    			player.setDataSource(outfile.getAbsolutePath());
    		} catch (IOException e) {
    			Log.w(APP_TAG, "File not accessible ", e);
    		} catch (IllegalArgumentException e) {
    			Log.w(APP_TAG, "Illegal argument ", e);
    		} catch (IllegalStateException e) {
    			Log.w(APP_TAG, "Illegal state, call reset/restore", e);
    		}
     
    		btRecord = (Button) findViewById(R.id.btRecord);
    		btRecord.setOnClickListener(handleRecordClick);
     
    		btPlay = (Button) findViewById(R.id.btPlay);
    		btPlay.setOnClickListener(handlePlayClick);
     
    	}
     
    	private final OnClickListener handleRecordClick = new OnClickListener() {
    		@Override
    		public void onClick(View view) {
    			if (!recording) {
    				startRecord();
    			} else {
    				stopRecord();
    			}
    		}
    	};
     
    	private final OnClickListener handlePlayClick = new OnClickListener() {
    		@Override
    		public void onClick(View view) {
    			if (!playing) {
    				startPlay();
    			} else {
    				stopPlay();
    			}
    		}
    	};
     
    	private void startRecord() {
    		Log.d(APP_TAG, "start recording..");
    		printResult("start recording..");
    		try {
    			recorder.prepare();
    			recorder.start();
    			recording = true;
    		} catch (IllegalStateException e) {
    			Log
    					.w(APP_TAG,
    							"Invalid recorder state .. reset/release should have been called");
    		} catch (IOException e) {
    			Log.w(APP_TAG, "Could not write to sd card");
    		}
    	}
     
    	private void stopRecord() {
    		Log.d(APP_TAG, "stop recording..");
    		printResult("stop recording..");
    		recorder.stop();
    		recorder.reset();
    		recorder.release();
    		recording = false;
    	}
     
    	private void startPlay() {
    		Log.d(APP_TAG, "starting playback..");
    		printResult("start playing..");
    		try {
    			playing = true;
    			player.prepare();
    			player.start();
    		} catch (IllegalStateException e) {
    			Log.w(APP_TAG, "illegal state .. player should be reset");
    		} catch (IOException e) {
    			Log.w(APP_TAG, "Could not write to sd card");
    		}
    	}
     
    	private void stopPlay() {
    		Log.d(APP_TAG, "stopping playback..");
    		printResult("stop playing..");
    		player.stop();
    		player.reset();
    		player.release();
    		playing = false;
    	}
     
    	private void printResult(String result) {
    		resultView.setText(result);
    	}
    }
  • Finally the app looks like this on my emulator

    The Application in the Android Emulator

  • Now play around with the recorder

How to connect a virtual sdcard to the emulator

  • A SD Card is created using the android tool or the mksdcard tool
    android create avd -n <youravd> -t <targetID> -c <size>[K|M]
    or:
    mksdcard <size> <file>
  • Start the emulator with the param -sdcard /path/to/sdcard.img
  • More information is available at the Android Developers Website

Troubleshooting

  • In general use the logcat tool from the Android Debugging Bridge to look for debugging messages, exceptions, strack traces ..
    # adb -s emulator-5554 shell
    logcat
    I/ActivityManager(   52): Displayed activity com.hascode.android/.RecorderActivity:
    I/ARMAssembler(   52): generated scanline__00000077:03545404_00000A04_00000000 [ 29
    I/ARMAssembler(   52): generated scanline__00000177:03515104_00001A01_00000000 [ 73
    D/com.hascode.android.soundrecorder(  198): start recording..
    W/com.hascode.android.soundrecorder(  198): Could not write to sd card
  • W/com.hascode.android.soundrecorder(  209): File not accessible – Check the permissions set for the directory you are trying to save the files to – more information is available at the Android Developers Website – also take a look in your AndroidManifest.xml and check if the permission WRITE_EXTERNAL_STORAGE is set:
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
  • Using the Android Debugging Bridge it is possible to check the user permissions set in the emulator
    adb -s emulator-5554 shell
    # ls -l sdcard
    d---rwxr-x system   sdcard_rw          2010-05-02 14:50 LOST.DIR
    # ls -l sdcard/com.hascode.recorder
    ----rwxr-x system   sdcard_rw        0 2010-05-02 15:33 hascode44433.3gp
  • If you need a GUI – try the the Dalvik Debug Monitor Service by running ddms – screenshots below:

    Debugging in DDMS

    File System

Resources

Article Updates

  • 2015-03-03: Table of contents and image captions added.
    <uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE”></uses-permission>

    Tags: , , , , ,

    2 Responses to “Sensor Fun: Creating a simple audio recorder/player”

    1. ravi Says:

      Hi thanks for providing the good tutorial for recording app
      I make use of your code for recording i’m getting the errors as below, pls help me how to solve this errors ..

      10-04 11:40:52.407: DEBUG/voicerecorder(1255): start recording..
      10-04 11:40:52.574: ERROR/PVOMXEncNode(31): PVMFOMXEncNode-Audio_AMRNB::DoPrepare(): Got Component OMX.PV.amrencnb handle
      10-04 11:48:17.900: ERROR/audio_input(31): unsupported parameter: x-pvmf/media-input-node/cap-config-interface;valtype=key_specific_value
      10-04 11:48:17.910: ERROR/audio_input(31): VerifyAndSetParameter failed

      Thank you

    2. Bharath Says:

      Hi,
      ur blog has been very helpful for me, but no where u have mentioned the code for the other part of ur interface(the dial button and arrow keys).
      And for the above error i think the fault might be of not setting the class path right(cause it differs frm windows or ubunto) or the package is in-approprate.. correct me if i m wrong.

    Leave a Reply

    Please note, that no personal information like your IP address is stored and you're not required to enter you real name.

    Comments must be approved before they are published, so please be patient after having posted a comment - it might take a short while.

    Please leave these two fields as-is:
    Search
    Categories