Sitemap
Andriesz

Andriesz

Android Audio Recording guide… Part 2 (AudioRecord)

4 min readOct 11, 2021

--

Knowing how to effectively record audio from a phone is valuable for mobile developers. Android provides several media frameworks to abstract the audio recording process, and others have developed external libraries that make it possible to incorporate high-performance audio into an application.

This article will summarize how to use Android AudioRecord framework to capture audio data on android devices with few lines of codes.

What Is Android AudioRecord ?

An android framework for capturing audio data on an android device .

AudioRecord removes a layer of abstraction between the application and a device’s audio hardware, recording uncompressed audio with no way to write directly to a file.

These APIs are the lowest level audio framework for Android that can still be used in the Java and Kotlin layer.

While MediaRecorder performs its data writing operations inside a black box, AudioRecord requires an application to periodically read the newest audio data from the AudioRecord object’s internal buffer.

Note: If you want to learn about android MediaRecorder framework please check this link.

While this lower-level framework creates more complexity, it also allows applications to build more advanced audio functionality.

How to use AudioRecorder framework ?

  1. Declare permissions (only needed once for an application)

As with all applications that need access to an audio input, declare the permission in the AndroidManifest.xml file.

( For API level 23 and above, the application should request permission to record audio the first time the user interacts with it.)

<uses-permission android:name=”android.permission.RECORD_AUDIO” />

2.Instantiate/configure AudioRecorder object

Instantiation consists of setting the following parameters .

Audio Source 
Sample rate in Hertz
Channel Configuration (stereo/mono)
Size of the internal buffer for audio data

Note: Make use of the getMinBufferSize() method when creating an AudioRecord object to ensure the internal buffer is sufficiently large given the device hardware.

Following code snippet shows how to instantiate AudioRecorder object in a real world example.

//Define AudioRecord Object and other parameters 
private lateint val = audioRecord:AudioRecord
const val RECORDER_SAMPLE_RATE = 44100
const val AUDIO_SOURCE = MediaRecorder.AudioSource.MIC
//for raw audio can use
const val RAW_AUDIO_SOURCE =
MediaRecorder.AudioSource.UNPROCESSED
const val CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_STEREO
const val AUDIO_FORMAT =
AudioFormat.ENCODING_PCM_16BIT
const val BUFFER_SIZE_RECORDING =
AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT)
//Instantiate
audioRecord = AudioRecord(AUDIO_SOURCE, RECORDER_SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, BUFFER_SIZE_RECORDING)

3.Attempt to start recording

Get Andriesz’s stories in your inbox

Join Medium for free to get updates from this writer.

Now your AudioRecorder object is ready to start on the recording job..

Let’s see how we can start recording

//First check whether the above object actually initialized 
if (audioRecord!!.state != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "error initializing AudioRecord");
return
}
//Now start the audio recording
audioRecord.startRecording()

To actually obtain the audio data, a separate thread should be dedicated to polling the AudioRecord object to avoid freezing the app’s UI.

Create a new Thread with a custom implementation of Runnable. Inside the run() method, we continuously read data from AudioRecord into a buffer until an external event (e.g., the press of a button) indicates we should stop.

In the code below, after the AudioRecord object has started recording, the writeAudioData() method is called inside the run() method of the thread we just made.

We take the raw audio bytes and write them to a file. AudioRecord uses PCM encoding, so that’s the kind of data the file will hold.

The read() method fills the passed in array with the amount of bytes requested in the third parameter, and returns the amount of bytes successfully read.

The thread will block until enough samples have been captured to deliver the requested number, meaning that whatever operations are done with the filled buffer should be completed before the next batch of samples is ready.

Here, we perform a file write for the sake of simplicity, but to ensure that no data is missed, we would ideally handle this writing in a separate thread with an independent buffer.

As with all media capture frameworks, don’t forget to release the resources used by AudioRecord when finished.

//Define AudioRecord Object and other parameters 
private lateint val = audioRecord:AudioRecord
const val RECORDER_SAMPLE_RATE = 44100
const val AUDIO_SOURCE = MediaRecorder.AudioSource.MIC
const val
CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_STEREO
const val AUDIO_FORMAT =
AudioFormat.ENCODING_PCM_16BIT
const val BUFFER_SIZE_RECORDING =
AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT)
private var recordingThread: Thread? = null
//Instantiate
audioRecord = AudioRecord(AUDIO_SOURCE, RECORDER_SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, BUFFER_SIZE_RECORDING);
//First check whether the above object actually initialized
if (audioRecord!!.state != AudioRecord.STATE_INITIALIZED) {
return
}
//Now start the audio recording
audioRecord.startRecording()
//Thread Start
recordingThread = thread(true) {
writeAudioDataToFile()
}

Now I am going to write writeAudioDataToFile() method below

var fileName =  Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath + "/$filename" + ".pcm"private fun writeAudioDataToFile() {
// assign size so that bytes are read in in chunks inferior to AudioRecord internal buffer size
val data = ByteArray(BUFFER_SIZE_RECORDING / 2)
var outputStream: FileOutputStream? = null
try {
outputStream = FileOutputStream(fileName)
} catch (e: FileNotFoundException) {
return
}
while (isRecordingAudio) {
val read = audioRecord!!.read(data, 0, data.size)
try {
outputStream!!.write(data, 0, read)
// clean up file writing operations
} catch (e: IOException) {
e.printStackTrace()
}
}
try {
outputStream.flush()
outputStream.close()
} catch (e: IOException) {
Log.e(TAG, "exception while closing output stream $e")
e.printStackTrace()
}
}

When the recording is finished, the application should release resources back to the operating system as soon as possible. The code below demonstrates how to properly end a recording.

fun stopRecording(){
if (audioRecord!= null) {
isRecordingAudio = false
audioRecord!!.stop()
audioRecord!!.release()
audioRecord = null
recordingThread = null
// triggers recordingThread to exit while loop
}
}

--

--