I have a son who is turning fourteen in a few weeks. He is every bit as much into gadgets and in particular Android phones as me; however, we don’t always see eye to eye on what is “cool.” Take for instance the app Songify — my son loves it, and he cranks out half a dozen “songs” on the car ride from the house to the grocery. And he’s not alone in his obsession. The app currently touts over a million downloads in Google Play.
If you’re not familiar with the premise of the app, the tag line says it all: “turn speech into music.” Just don’t set your internal bar too high for the resulting “music.” It’s entertaining — let’s leave it at that. With the immense popularity in the last 30 days of Songify and similar apps, I’ve seen a new surge of questions in the Android forums related to recording voice, so I figured what better subject to tackle this week in my post. You can follow along step by step tutorial, or download and import the entire project.
1. Start a new Android project in Eclipse. Target Android 1.6 or higher. Be sure to change the startup activity name to Main.java.
2. Add a couple of permissions to the manifest. We require access to the SD file system, as well as the media recorder.
AndroidManifest.xml<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.authorwjf.voice_rec"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="4" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".Main"
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>
</manifest>
3. Create a simple table layout in the /res/layout folder. The UI for our demo consists of two buttons and a label.
Main.xml
<?xml version="1.0" encoding="utf-8"?><TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="*">
<TableRow>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Voice Recorder Demo"android:layout_span="2"
android:layout_gravity="center"
android:padding="12dip"/>
</TableRow>
<TableRow>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/record_button"
android:text="record" android:layout_margin="12dip"/><Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/play_back_button"
android:text="play" android:layout_margin="12dip"/></TableRow>
</TableLayout>
4. We are ready for the /src/Main.java file. Let’s start by declaring some class level variables and wiring up our buttons in the on create override.
Main.java package com.authorwjf.voice_rec;
import android.app.Activity; import android.media.MediaPlayer; import android.media.MediaRecorder; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast;
public class Main extends Activity implements OnClickListener {
MediaRecorder mRecorder = new MediaRecorder(); MediaPlayer mPlayer = new MediaPlayer(); boolean isRecording = false;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(R.layout.main);
findViewById(R.id.play_back_button).setOnClickListener(this); findViewById(R.id.record_button).setOnClickListener(this); }
}
5. We have come to the heart of our code — the on-click handler. This is where we will do the recording and the playback.
Main.java@Override
public void onClick(View v) {mPlayer.stop();
switch (v.getId()) { case R.id.play_back_button: if (!isRecording && !mPlayer.isPlaying()) { try {mPlayer.reset();
mPlayer.setDataSource("/sdcard/audio_demo.3gp");
mPlayer.prepare();
mPlayer.start();
} catch (Exception e) { Toast.makeText(this, "Error playing back audio.",Toast.LENGTH_SHORT).show();}
}
break; case R.id.record_button: if (isRecording) { isRecording = false;((Button)(findViewById(R.id.record_button))).setText("record");
mRecorder.reset();
} else { try {mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mRecorder.setOutputFile("/sdcard/audio_demo.3gp");
mRecorder.prepare();
mRecorder.start();
((Button)(findViewById(R.id.record_button))).setText("stop");
isRecording = true; } catch (Exception e) { Toast.makeText(this, "Error starting recorder.",Toast.LENGTH_SHORT).show(); }
} break;}
}
6. We need to handle cleaning up our media recorder and playback objects when the activity gets destroyed — this keeps the app from crashing or leaking resources. Note: The app doesn’t attempt to continue recording through an orientation change.
Main.java@Override
public void onDestroy() { if (isRecording) { Toast.makeText(this, "Recorder stopped.",Toast.LENGTH_SHORT).show();mRecorder.stop();
}
mRecorder.release();
mPlayer.stop();
mPlayer.release();
super.onDestroy(); }
The app should be ready to take for a spin. Since voice recording isn’t supported on the emulator, you’ll need to run the demo on an actual device. Operation is self-evident: say something and then play it back (Figure A).
Figure A
Pretty easy, right? And best of all, I’m sure it will provide you with hours of entertainment. I’ve already recorded an a capella rendition of Bon Jovi’s “Dead or Alive” for our next family outing to the grocery.