Android Tutorial: Sound Recorder

Android Sound Recorder
The Android platform ships with a multitude of APIs, including one for recording audio and video. In this article, you learn how to use the MediaRecorder class to sample sound levels. This is the same class you used for making videos in Android Tutorial: Video Recording API
The MediaRecorder Class
Support for multimedia is rock solid in Android. There are classes that you can use to play audio and video as well as record them. In the SoundMeter project discussed in this chapter, you will use the MediaRecorder class to sample sound or noise levels.MediaRecorder is used to record audio and video. The output can be written to a file and the input source can be easily selected. It is relatively easy to use too. You start by instantiating the MediaRecorder class.
MediaRecorder mediaRecorder = new MediaRecorder();
Then, configure the instance by calling its setAudioSource, setVideoSource, setOutputFormat, setAudioEncoder, setOutputFile, or other methods. Next, prepare the MediaRecorder by calling its prepare method:
mediaRecorder.prepare();
Note that prepare may throw an exception if the MediaRecorder is not configured properly or if you do not have the right permissions.
To start recording, call its start method. To stop recording, call stop.
When you’re done with a MediaRecorder, call its reset method to return it to its initial state and its release method to release resources it currently holds.
mediaRecorder.reset(); mediaRecorder.release();
Example
Now that you know how to use the MediaRecorder, let’s take a look at the SoundMeter project. The application samples sound amplitudes at certain intervals and displays the current level as a bar.
As usual, let’s start by looking at the manifest (the AndroidManifest.xml file) for the project. It is given in Listing 1.1.
Listing 1.1: The manifest for SoundMeter
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.soundmeter" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.example.soundmeter.MainActivity" 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>
One thing to note here is the use of the uses-permission element in the manifest to ask for the user’s permission to record audio. If you don’t include this element, your application will not work. Also, if the user does not consent, the application will not install.
There is only one activity in this project as can be seen in the manifest.
Listing 1.2 shows the layout file for the main activity. A RelativeLayout is used for the main display and it contains a TextView for displaying the current sound level and a button that will act as a sound indicator.
Listing 1.2: The res/layout/activity_main.xml file in SoundMeter
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <TextView android:id="@+id/level" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/button1" style="?android:attr/buttonStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/level" android:layout_below="@+id/level" android:background="#ff0000" android:layout_marginTop="30dp" /> </RelativeLayout>
There are two Java classes in this application. The first one, given in Listing 1.3, is a class called SoundMeter that encapsulates a MediaRecorder and exposes three methods to manage it. The first method, start, creates an instance of MediaRecorder, configures it, and starts it. The second method, stop, stops the MediaRecorder. The third method, getAmplitude, returns a double indicating the sampled sound level.
Listing 1.3: The SoundMeter class
package com.example.soundmeter; import java.io.IOException; import android.media.MediaRecorder; public class SoundMeter { private MediaRecorder mediaRecorder; boolean started = false; public void start() { if (started) { return; } 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(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } mediaRecorder.start(); started = true; } } public void stop() { if (mediaRecorder != null) { mediaRecorder.stop(); mediaRecorder.release(); mediaRecorder = null; started = false; } } public double getAmplitude() { return mediaRecorder.getMaxAmplitude() / 100; } }
The second Java class, MainActivity, is the main activity class for the application. It is presented in Listing 1.4.
Listing 1.4: The MainActivity class in SoundMeter
package com.example.soundmeter; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.view.Menu; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity { Handler handler = new Handler(); SoundMeter soundMeter = new SoundMeter(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it // is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public void onStart() { super.onStart(); soundMeter.start(); handler.postDelayed(pollTask, 150); } @Override public void onPause() { soundMeter.stop(); super.onPause(); } private Runnable pollTask = new Runnable() { @Override public void run() { double amplitude = soundMeter.getAmplitude(); TextView textView = (TextView) findViewById(R.id.level); textView.setText("amp:" + amplitude); Button button = (Button) findViewById(R.id.button1); button.setWidth((int) amplitude * 10); handler.postDelayed(pollTask, 150); } }; }
The MainActivity class overrides two activity lifecycle methods, onStart and onPause. You may recall that the system calls onStart right after the activity was created or after it was restarted. The system calls onPause when the activity was paused because another activity was started or because an important event occurred. In the MainActivity class, the onStart method starts the SoundMeter and the onPause method stops it. The MainActivity class also uses a Handler to sample the sound level every 150 milliseconds.
Figure 1.1 shows the application. The horizontal bar shows the current sound amplitude.

Figure 1.1: The SoundMeter application
Summary
In this article, you learned to use the MediaRecorder class to record audio. You also created an application for sampling noise levels.