An Android Studio Audio Recording Tutorial

This chapter will provide an overview of the MediaRecorder class and explain how this class can be used to record audio or video. The use of the MediaPlayer class to play back audio will also be covered. Having covered the basics, an example application will be created to demonstrate these techniques. In addition to looking at audio and video handling, this chapter will also touch on saving files to the SD card.

Playing Audio

In terms of audio playback, most implementations of Android support AAC LC/LTP, HE-AACv1 (AAC+), HEAACv2 (enhanced AAC+), AMR-NB, AMR-WB, MP3, MIDI, Ogg Vorbis, and PCM/WAVE formats.

Audio playback can be performed using either the MediaPlayer or the AudioTrack classes. AudioTrack is a more advanced option that uses streaming audio buffers and provides greater control over the audio. The MediaPlayer class, on the other hand, provides an easier programming interface for implementing audio playback and will meet the needs of most audio requirements.

The MediaPlayer class has associated with it a range of methods that can be called by an application to perform certain tasks. A subset of some of the key methods of this class is as follows:

  • create() – Called to create a new instance of the class, passing through the Uri of the audio to be played.
  • setDataSource() – Sets the source from which the audio is to play.
  • prepare() – Instructs the player to prepare to begin playback.
  • start() – Starts the playback.
  • pause() – Pauses the playback. Playback may be resumed via a call to the resume() method.
  • stop() – Stops playback.
  • setVolume() – Takes two floating-point arguments specifying the playback volume for the left and right channels.
  • resume() – Resumes a previously paused playback session.
  • reset() – Resets the state of the media player instance. Essentially sets the instance back to the uninitialized state. At a minimum, a reset player will need to have the data source set again, and the prepare() method called.
  • release() – To be called when the player instance is no longer needed. This method ensures that any resources held by the player are released.

In a typical implementation, an application will instantiate an instance of the MediaPlayer class, set the source of the audio to be played, and then call prepare() followed by start(). For example:

 

You are reading a sample chapter from Android Studio Giraffe Essentials – Kotlin Edition.

Buy the full book now in eBook (PDF) or Print format.

The full book contains 91 chapters and over 810 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

val mediaPlayer = MediaPlayer()
 
mediaPlayer?.setDataSource("https://www.yourcompany.com/myaudio.mp3")
mediaPlayer?.prepare()
mediaPlayer?.start()Code language: Kotlin (kotlin)

Recording Audio and Video using the MediaRecorder Class

As with audio playback, recording can be performed using several different techniques. One option is to use the MediaRecorder class, which, as with the MediaPlayer class, provides several methods that are used to record audio:

  • setAudioSource() – Specifies the audio source to be recorded (typically, this will be MediaRecorder. AudioSource.MIC for the device microphone).
  • setVideoSource() – Specifies the source of the video to be recorded (for example MediaRecorder.VideoSource.CAMERA).
  • setOutputFormat() – Specifies the format into which the recorded audio or video is to be stored (for example MediaRecorder.OutputFormat.AAC_ADTS).
  • setAudioEncoder() – Specifies the audio encoder for the recorded audio (for example MediaRecorder. AudioEncoder.AAC).
  • setOutputFile() – Configures the path to the file into which the recorded audio or video will be stored.
  • prepare() – Prepares the MediaRecorder instance to begin recording.
  • start() – Begins the recording process.
  • stop() – Stops the recording process. Once a recorder has been stopped, it must be completely reconfigured and prepared before restarting.
  • reset() – Resets the recorder. The instance will need to be completely reconfigured and prepared before being restarted.
  • release() – Should be called when the recorder instance is no longer needed. This method ensures that all resources held by the instance are released.

A typical implementation using this class will set the source, output, encoding format, and output file. Calls will then be made to the prepare() and start() methods. The stop() method will then be called when the recording ends, followed by the reset() method. When the application no longer needs the recorder instance, a call to the release() method is recommended:

val mediaRecorder = MediaRecorder(context)
 
mediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC)
mediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
mediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
mediaRecorder?.setOutputFile(audioFilePath)
mediaRecorder?.prepare()
mediaRecorder?.start()
.
.
mediaRecorder?.stop()
mediaRecorder?.reset()
mediaRecorder?.release()Code language: Kotlin (kotlin)

To record audio, the manifest file for the application must include the android.permission.RECORD_AUDIO permission:

<uses-permission android:name="android.permission.RECORD_AUDIO" />Code language: HTML, XML (xml)

As outlined in the chapter entitled An Android Permission Requests Tutorial, access to the microphone falls into the category of dangerous permissions. To support Android 6, therefore, a specific request for microphone access must also be made when the application launches, the steps for which will be covered later in this chapter.

About the Example Project

The remainder of this chapter will create an example application to demonstrate the use of the MediaPlayer and MediaRecorder classes to implement the recording and playback of audio on an Android device.

 

You are reading a sample chapter from Android Studio Giraffe Essentials – Kotlin Edition.

Buy the full book now in eBook (PDF) or Print format.

The full book contains 91 chapters and over 810 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

When developing applications that use specific hardware features, the microphone being a case in point, it is important to check the feature’s availability before attempting to access it in the application code. The application created in this chapter will, therefore, also include code to detect the presence of a microphone on the device.

Once completed, this application will provide a straightforward interface allowing the user to record and play audio. The recorded audio will be stored within an audio file on the device. That being the case, this tutorial will also briefly explore the mechanism for using SD Card storage.

Creating the AudioApp Project

Select the New Project option from the welcome screen and, within the resulting new project dialog, choose the Empty Views Activity template before clicking on the Next button.

Enter AudioApp into the Name field and specify com.ebookfrenzy.audioapp as the package name. Before clicking on the Finish button, change the Minimum API level setting to API 31: Android 12.0 and the Language menu to Kotlin. Add view binding support to the project using the steps outlined in section 18.8 Migrating a Project to View Binding.

Designing the User Interface

Once the new project has been created, select the activity_main.xml file from the Project tool window, and with the Layout Editor tool in Design mode, select the “Hello World!” TextView and delete it from the layout.

 

You are reading a sample chapter from Android Studio Giraffe Essentials – Kotlin Edition.

Buy the full book now in eBook (PDF) or Print format.

The full book contains 91 chapters and over 810 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

Drag and drop three Button views onto the layout. The positioning of the buttons is not paramount to this example, though Figure 76-1 shows a suggested layout using a vertical chain.

Configure the buttons to display string resources that read Play, Record, and Stop and give them view IDs of playButton, recordButton, and stopButton, respectively.

Select the Play button and, within the Attributes panel, configure the onClick property to call a method named playAudio when selected by the user. Repeat these steps to configure the remaining buttons to call methods named recordAudio and stopAudio, respectively.

Figure 76-1

Checking for Microphone Availability

Attempting to record audio on a device without a microphone will cause the Android system to throw an exception. It is vital, therefore, that the code checks for the presence of a microphone before making such an attempt. There are several ways of doing this, including checking for the physical presence of the device. An easier approach that is more likely to work on different Android devices is to ask the Android system if it has a package installed for a particular feature. This involves creating an instance of the Android PackageManager class and then calling the object’s hasSystemFeature() method. PackageManager.FEATURE_MICROPHONE is the feature of interest in this case.

For this example, we will create a method named hasMicrophone() that may be called upon to check for the presence of a microphone. Within the Project tool window, locate and double-click on the MainActivity.kt file and modify it to add this method:

 

You are reading a sample chapter from Android Studio Giraffe Essentials – Kotlin Edition.

Buy the full book now in eBook (PDF) or Print format.

The full book contains 91 chapters and over 810 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

package com.ebookfrenzy.audioapp
.
.
import android.content.pm.PackageManager
 
class MainActivity : AppCompatActivity() {
.
.
    private fun hasMicrophone(): Boolean {
        val pmanager = this.packageManager
        return pmanager.hasSystemFeature(
                PackageManager.FEATURE_MICROPHONE)
    }
}
Code language: Kotlin (kotlin)

Initializing the Activity

The next step is to modify the activity to perform several initialization tasks. Remaining within the MainActivity.kt file, modify the code as follows:

.
.
import android.media.MediaRecorder
import android.os.Environment
import android.view.View
import android.media.MediaPlayer
 
import java.io.File
.
.
class MainActivity : AppCompatActivity() {
 
    private lateinit var binding: ActivityMainBinding 
    private var mediaRecorder: MediaRecorder? = null
    private var mediaPlayer: MediaPlayer? = null
 
    private var audioFilePath: String? = null
    private var isRecording = false
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        audioSetup()
    }
 
    private fun audioSetup() {
 
        if (!hasMicrophone()) {
            binding.stopButton.isEnabled = false
            binding.playButton.isEnabled = false
            binding.recordButton.isEnabled = false
        } else {
            binding.playButton.isEnabled = false
            binding.stopButton.isEnabled = false
        }
 
        val audioFile = File(this.filesDir, "myaudio.3gp")
        audioFilePath = audioFile.absolutePath
    }
.
.Code language: Kotlin (kotlin)

The added code calls hasMicrophone() method to ascertain whether the device includes a microphone. If it does not, all the buttons are disabled; otherwise, only the Stop and Play buttons are disabled. The next line of code needs a little more explanation:

val audioFile = File(this.filesDir, "myaudio.3gp")
audioFilePath = audioFile.absolutePathCode language: Kotlin (kotlin)

This code creates a new file named myaudio.3gp within the app’s internal storage to store the audio recording.

Implementing the recordAudio() Method

The recordAudio() method will be called when the user touches the Record button. This method will need to turn the appropriate buttons on and off and configure the MediaRecorder instance with information about the source of the audio, the output format and encoding, and the file’s location into which the audio is to be stored. Finally, the prepare() and start() methods of the MediaRecorder object will need to be called. Combined, these requirements result in the following method implementation in the MainActivity.kt file:

fun recordAudio(view: View) {
    isRecording = true
    binding.stopButton.isEnabled = true
    binding.playButton.isEnabled = false
    binding.recordButton.isEnabled = false
 
    try {
        mediaRecorder = MediaRecorder(this)
        mediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC)
        mediaRecorder?.setOutputFormat(
                MediaRecorder.OutputFormat.THREE_GPP)
        mediaRecorder?.setOutputFile(audioFilePath)
        mediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
        mediaRecorder?.prepare()
    } catch (e: Exception) {
        e.printStackTrace()
    }
    mediaRecorder?.start()
}Code language: Kotlin (kotlin)

Implementing the stopAudio() Method

The stopAudio() method enables the Play button, turning off the Stop button, and then stopping and resetting the MediaRecorder instance. The code to achieve this reads as outlined in the following listing and should be added to the MainActivity.kt file:

 

You are reading a sample chapter from Android Studio Giraffe Essentials – Kotlin Edition.

Buy the full book now in eBook (PDF) or Print format.

The full book contains 91 chapters and over 810 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

fun stopAudio(view: View) {
 
    binding.stopButton.isEnabled = false
    binding.playButton.isEnabled = true
 
    if (isRecording) {
        binding.recordButton.isEnabled = false
        mediaRecorder?.stop()
        mediaRecorder?.release()
        mediaRecorder = null
        isRecording = false
    } else {
        mediaPlayer?.release()
        mediaPlayer = null
        binding.recordButton.isEnabled = true
    }
}Code language: Kotlin (kotlin)

Implementing the playAudio() method

The playAudio() method will create a new MediaPlayer instance, assign the audio file located on the SD card as the data source and then prepare and start the playback:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
 
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
 
    <application
.
.Code language: HTML, XML (xml)

Configuring and Requesting Permissions

Before testing the application, the appropriate permissions must be requested within the manifest file for the application. Specifically, the application will require permission to access the microphone. Within the Project tool window, locate and double-click on the AndroidManifest.xml file to load it into the editor and modify the XML to add the permission tags:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
 
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
 
    <application
.
.Code language: HTML, XML (xml)

The above steps will be adequate to ensure that the user enables microphone access permission when the app is installed on devices running versions of Android predating Android 6.0. Microphone access is categorized in Android as being a dangerous permission because it allows the app to compromise the user’s privacy. For the example app to function on Android 6 or later devices, code needs to be added to request permission at app runtime.

Edit the MainActivity.kt file and begin by adding some additional import directives and a constant to act as request identification codes for the permissions being requested:

.
.
import android.Manifest
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
.
.
class MainActivity : AppCompatActivity() {
.
.
    private val RECORD_REQUEST_CODE = 101
.
.Code language: Kotlin (kotlin)

Next, a method needs to be added to the class, the purpose of which is to take as arguments the permission to be requested and the corresponding request identification code. Remaining with the MainActivity.kt class file, implement this method as follows:

 

You are reading a sample chapter from Android Studio Giraffe Essentials – Kotlin Edition.

Buy the full book now in eBook (PDF) or Print format.

The full book contains 91 chapters and over 810 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

private fun requestPermission(permissionType: String, requestCode: Int) {
    val permission = ContextCompat.checkSelfPermission(this,
            permissionType)
 
    if (permission != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this,
                arrayOf(permissionType), requestCode
        )
    }
}Code language: Kotlin (kotlin)

Using the steps outlined in the An Android Permission Requests Tutorial chapter of this book, the above method verifies that the specified permission has not already been granted before making the request, passing through the identification code as an argument.

When the request has been handled, the onRequestPermissionsResult() method will be called on the activity, passing through the identification code and the request results. The next step, therefore, is to implement this method within the MainActivity.kt file as follows:

override fun onRequestPermissionsResult(requestCode: Int,
                permissions: Array<String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
 
    when (requestCode) {
        RECORD_REQUEST_CODE -> {
            if (grantResults.isEmpty() || grantResults[0]
                != PackageManager.PERMISSION_GRANTED
            ) {
 
                binding.recordButton.isEnabled = false
 
                Toast.makeText(
                    this,
                    "Record permission required",
                    Toast.LENGTH_LONG
                ).show()
            }
        }
    }
}Code language: Kotlin (kotlin)

The above code checks the request identifier code to identify which permission request has returned before checking whether or not the corresponding permission was granted. If permission is denied, a message is displayed to the user indicating that the app will not function and the record button is disabled.

Before testing the app, all that remains is to call the newly added requestPermission() method for microphone access when the app launches. Remaining in the MainActivity.kt file, modify the audioSetup() method as follows:

private fun audioSetup() {
.
. 
    audioFilePath = audioFile.absolutePath
 
    requestPermission(Manifest.permission.RECORD_AUDIO,
            RECORD_REQUEST_CODE)
}Code language: Kotlin (kotlin)

Testing the Application

Compile and run the application on an Android device containing a microphone, allow microphone access, and tap the Record button. After recording, touch Stop followed by Play. At this point, the recorded audio should play back through the device speakers.

 

You are reading a sample chapter from Android Studio Giraffe Essentials – Kotlin Edition.

Buy the full book now in eBook (PDF) or Print format.

The full book contains 91 chapters and over 810 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

Summary

The Android SDK provides several mechanisms to implement audio recording and playback. This chapter has looked at two of these: the MediaPlayer and MediaRecorder classes. Having covered the theory of using these techniques, this chapter worked through creating an example application designed to record and then play back audio. While working with audio in Android, this chapter also looked at the steps involved in ensuring that the device on which the application is running has a microphone before attempting to record audio.