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.

WAVE Audio File Format

WAV (Waveform Audio File Format, also known as WAVE) is a commonly used audio file format for storing raw audio samples on Windows. It follows the RIFF (Resource Interchange File Format) generic format.

0. The Format

A wave file is a RIFF file with a “WAVE” chunk. The WAVE chunk consists of two subchunks, namely “fmt ” and “data”. (Note that there’s a space in “fmt ”). Below we list out the meanings of each field in a WAVE file.

field

length

endian

Note

Chunk Tag

4

big

“RIFF”

RIFF chunk descriptor

Chunk Size

4

little

type

——-

4

——

big

——

“WAVE”

———

subchunk id (fmt )

4

big

“fmt ”

fmt subchunk: format information about the audio data

subchunk size

4

little

audio format

2

little

1=>PCM, other values => data compressed

num of channels

2

little

1=> mono, 2=>stereo

sample rate

4

little

8000, 16000, 22050, 44100 etc.

byte rate

4

little

Sample rate*num of channels*bits per sample/8

block align

2

little

num of channels*bits per sample/8

bits per sample

——

2

 

——-

little

 

——

8=>8bits, 16=>16bits

 

———–

subchunk id (data)

4

big

“data”

Data subchunk: contains the raw audio data

subchunk size

4

little

Num of samples * num of channels * bits per sample/8

data

little

Audio data

Note that there’re totally 44 bytes (12 + 24 + 8) before the actual audio data.

1. Byte-by-Byte example

Below is a screenshot of a wave file shown in vim hex mode. We’ll go through the bytes one by one.

wave

Figure 1. Bytes of a wave file recorded on Android 

5249 4646: RIFF

74d8 0400: 04d874 = 317556 bytes. I used “ls -l test.wav” to get the file size as 317564 bytes, which is equal to 317556 + 4 (size field) + 4 (RIFF field).

5741 5645: WAVE

666d 7420: fmt<I’m a space>

1000 0000: 00000010 = 16 bytes.

0100: 0001, which corresponds to PCM, values other than 1 indicate data is compressed.

0100: 0001, only one channel.

44ac 0000: 0000 ac44 = 44100, the sample rate is 44100Hz.

8858 0100: 0001 5888 = 88200 = sample rate * number of channel * bits per sample/8 = 44100 * 1 * 16 / 8 = 44100 * 2

0200: 0002, block align. 02 = number of channel * bits per sample / 8 = 1 * 16 / 8 = 2

1000: 0010 = 16, bits per sample

6461 7461: data

50d8 0400: 0004 d850 = 317520, data size. 317520 + 44 (total header bytes) = 317564, which matches with the total file size obtained using “ls -al”.

References:

1. wikipedia page WAVE: http://en.wikipedia.org/wiki/WAV

2. WAVE PCM soundfile format: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/

Android Video2Gif Released

Several months ago, my friend asks my help to convert a video to an animated gif so it can be shared on a social network. I googled a little bit and managed to do it with a few command line tools on Linux. It’s not difficult but it can be troublesome, especially for non-tech people.
Then I searched for Android gif making apps. There are plenty of them, but none are made for converting video to gif. I decided to write one.
I got busy with my new job and a few other stuff, the development is delayed. When I resume, there’s already an app that does video to gif, and it takes the “video to gif” app name. 🙂
So here is the app, Android Video2Gif. You can convert a video to gif by three clicks.

  1. click a video from Videos tab or Browser tab, this goes to video extract screen.
  2. click extract on top right corner of the screen, this might take a while to sample frames from the video. After it’s done, it goes to gif edit screen.
  3. click ok to start gif generation.

Below are a few screenshots.

sc

Pic 1. Video2Gif Screenshots

The app is still in beta, so I expect there will be issues on some devices. As always, all feedback are welcome.