Record WAVE Audio on Android

This post discusses how to record raw audio (PCM) and save it to wave file on Android. If you’re not familiar with WAVE audio file format, please refer to a previous post, WAVE Audio File Format.

The post is a follow up post for Record PCM Audio on Android. The code and working principle are similar. It is strongly suggested you read it first.

WAVE file is used to store PCM data, with 44-byte header. Recording WAVE audio is equivalent to recording PCM audio and adding the 44-byte header in front.

We used a RandomAccessFile to write the data. We first write 44-byte header. Because some fields are not known until we finish the recording, we simply write zeros. This is shown as below.

randomAccessWriter = new RandomAccessFile(filePath, "rw");

randomAccessWriter.setLength(0); // Set file length to 0, to prevent unexpected behavior in case the file already existed

randomAccessWriter.writeBytes("RIFF");

randomAccessWriter.writeInt(0); // Final file size not known yet, write 0 

randomAccessWriter.writeBytes("WAVE");

randomAccessWriter.writeBytes("fmt ");

randomAccessWriter.writeInt(Integer.reverseBytes(16)); // Sub-chunk size, 16 for PCM

randomAccessWriter.writeShort(Short.reverseBytes((short) 1)); // AudioFormat, 1 for PCM

randomAccessWriter.writeShort(Short.reverseBytes(nChannels));// Number of channels, 1 for mono, 2 for stereo

randomAccessWriter.writeInt(Integer.reverseBytes(sRate)); // Sample rate

randomAccessWriter.writeInt(Integer.reverseBytes(sRate*nChannels*mBitsPersample/8)); // Byte rate, SampleRate*NumberOfChannels*mBitsPersample/8

randomAccessWriter.writeShort(Short.reverseBytes((short)(nChannels*mBitsPersample/8))); // Block align, NumberOfChannels*mBitsPersample/8

randomAccessWriter.writeShort(Short.reverseBytes(mBitsPersample)); // Bits per sample

randomAccessWriter.writeBytes("data");

randomAccessWriter.writeInt(0); // Data chunk size not known yet, write 0

We then write the PCM data. This is discussed in detail in post Record PCM Audio on Android.

After the recording is done, we seek to the header and update a few header fields. This is shown as below.

audioRecorder.stop();

try {

    randomAccessWriter.seek(4); // Write size to RIFF header

    randomAccessWriter.writeInt(Integer.reverseBytes(36+payloadSize));

 

    randomAccessWriter.seek(40); // Write size to Subchunk2Size field

    randomAccessWriter.writeInt(Integer.reverseBytes(payloadSize));

 

    randomAccessWriter.close();

} catch(IOException e) {

    Log.e(WavAudioRecorder.class.getName(), "I/O exception occured while closing output file");

    state = State.ERROR;

}

For the complete source code, one can refer to my github Android tutorial project.

Record PCM Audio on Android

This post discusses how to record PCM audio on Android with the android.media.AudioRecord class. If you’re not familiar with PCM, please read a previous post PCM Audio Format.

For the source code, please refer to AndroidPCMRecorder.

1. State Transition

The source code records the PCM audio with PcmAudioRecorder.java, which uses Android AudioRecord internally. Similar to the Android MediaRecorder class, PcmAudioRecorder follows a simple state machine as shown below.

Untitled drawing

Figure 1. State Transition of PcmAudioRecorder Class

As indicated in the diagram, we initialize a PcmAudioRecorder class by either calling the getInstance static method or the constructor to get into INITIALIZING state. We can then set the output file path and call prepare to get into PERPARED state. We can then start recording by calling  start method and finally call stop to stop the recording. At any state except ERROR, we can call reset to get back to INITIALIZING state. When we’re done with recording, we can call release to discard the PcmAudioRecorder object.

2. Filling the Buffer with Data and Write to File

One particular part of the code requires a bit attention is the updateListener. We register the listener with the AudioRecord object using setRecordPositionUpdateListener method. The listener is an interface with two abstract methods, namely onMarkerReached and onPeriodicNotification. We implemented the onPeriodicNotification method to pull the audio data from AudioRecord object and save it to the output file.

In order for the listener to work, we need to specify the notification period by calling AudioRecord.setPositionNotificationPeriod(int) to specify how frequently we want the listener to be triggered and pull data. The method accepts a single argument, which indicates the update period in number of frames. This leads us to next section.

3. Frame vs Sample

For PCM audio, a frame consists of the set of samples from all channels at a given point of time. In other words, the number of frames in a second is equal to sample rate.

However, when the audio is compressed (encoded further to mp3, aac etc.), a frame consists of compressed data for a whole series of samples with additional, non-sample data. For such audio formats, the sample rate and sample size refer to data after decoded to PCM, and it’s completely different from frame rate and frame size.

In our sample code, we set the update period for setPositionNotificationPeriod as number of frames in every 100 millisecond, therefore the listener will be triggered every 100 milliseconds, and we can pull data and update the recording file every 100 milliseconds.

Note that source code is modified based on ExtAudioRecorder.java.

Android Video Recording API–Illustrated with an Example

Android has made video recording very simply by providing a few high level classes, including Camera, SurfaceView and MediaRecorder. This tutorial illustrates the Video Recording API by providing a workable example as shown in the screenshots.

cam1cam2

Figure 1. Screenshots of Android Video Capturing Sample Program

The Camera class allows us to access the Camera on an Android device; the SurfaceView class provides a drawing surface, it is used to present a live preview in our application; the MediaRecoder contains the API to configure and record the video, it uses Camera class to access the hardware camera.

0. Live Preview Before Recording

Before users start to record video, they want to see what they’re capturing. That’s what the preview function does, it allows one to see what the camera is able to capture. The preview implementation is briefly illustrated as the code below,

SurfaceView prSurfaceView = (SurfaceView) findViewById(R.id.surface_camera);

SurfaceHolder prSurfaceHolder = prSurfaceView.getHolder();

prSurfaceHolder.addCallback(this);

prSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

Camera prCamera = Camera.open();        

Camera.Parameters lParam = prCamera.getParameters();

prCamera.setParameters(lParam);

prCamera.setPreviewDisplay(prSurfaceHolder);

prCamera.startPreview();

prCamera.stopPreview();

1. Video Recording and Live Preview at Recording

The state transition diagram for Android MediaRecorder class is as below,

image

Figure 2. State Transition of Android MediaRecorder

The implementation follows the state transition diagram closely. Starting from Initial state, our code sets the audio source as microphone, video source as camera, and the program enters Initialized state. The output format can be set to mpeg4 or 3gp, which indicates the container format for the video recorded. At DataSourceConfigured state, we set the audio encoder as AMR_NB (Later versions of Android supports AAC), and video encoder as MPEG4,H263 or H264 based on user configurations. We also set the frame rate, and output file here. After that, we call prepare method to transit to Prepared state, and finally a start method call will start the recording.

The description above doesn’t contain every detail but just for illustration. The actual code in feipeng.yacamcorder.Main.java contains lots of error handling and code for remembering and setting user configurations.

Note that the encoders a device supports may differ.

2. Download

You can download the entire source code for the example above from here, or from my github repo.

References:

1. http://developer.android.com/guide/appendix/media-formats.html

2. http://developer.android.com/guide/topics/media/camera.html