Audio Record Example

This example shows how to record a sound using the Android media APIs.

A screenshot of the application.

We import the classes and modules needed by our application. The most relevant classes are those from the android.media module.

from java.io import File, FileOutputStream
from java.lang import Math, Runnable, Thread
from java.nio import ByteBuffer
from java.text import SimpleDateFormat
from java.util import Date
from android.app import Activity
from android.media import AudioFormat, AudioRecord, MediaRecorder
from android.os import Environment
from android.view import View
from android.widget import Button, LinearLayout, TextView

The AudioRecordActivity class is derived from the standard Activity class and represents the application. Android will create an instance of this class when the user runs it.

class AudioRecordActivity(Activity):

    __interfaces__ = [Runnable, View.OnClickListener]

The class implements the View.OnClickListener interface, declaring this in the list of interfaces defined by the __interfaces__ attribute. Implementing this interface involves implementing the onClick method shown below.

The initialisation method simply calls the corresponding method in the base class.

    def __init__(self):
    
        Activity.__init__(self)
        
        self.recording = False

The onCreate method calls the corresponding method in the base class to help set up the activity, and we set up the user interface.

    def onCreate(self, bundle):
    
        Activity.onCreate(self, bundle)

We choose certain parameters for the audio data we want to record. If we cannot use these parameters, we simply return early and show no GUI. The parameters we use are 16-bit, little-endian, single channel (mono) audio, recorded with a sample rate of 44100 Hz.

        encoding = AudioFormat.ENCODING_PCM_16BIT
        sampleRate = 44100
        
        bufferSize = AudioRecord.getMinBufferSize(sampleRate,
            AudioFormat.CHANNEL_IN_MONO, encoding)
        
        if bufferSize == AudioRecord.ERROR_BAD_VALUE:
            return
        
        bufferSize = Math.max(bufferSize, sampleRate)
        
        self.audioBuffer = array(byte, bufferSize)

We create an AudioRecord instance, passing information about the sample data to ensure that it will be played correctly.

        self.recorder = AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate,
            AudioFormat.CHANNEL_IN_MONO, encoding, bufferSize)
        
        if self.recorder.getState() == AudioRecord.STATE_INITIALIZED:

We create a button with a "Play sound" label and register the activity as its listener for click callbacks.

            self.button = Button(self)
            self.button.setText("Start recording")
            self.button.setOnClickListener(self)

We also create a text view to show the location of the last file written.

            self.textView = TextView(self)

The button and text view are placed in a vertical layout which is used as the main content in the activity.

            layout = LinearLayout(self)
            layout.setOrientation(layout.VERTICAL)
            layout.addView(self.button)
            layout.addView(self.textView)
            
            self.setContentView(layout)

The onClick method is called whenever the button defined above is clicked. If the activity was registered as a listener with other buttons then we would distinguish between them using the View object passed to this method.

    def onClick(self, view):
    
        if not self.recording:

If we are not recording when the button is pressed then we create a file to write to, open an output stream to direct data to that file, and we start recording.

            self.button.setText("Stop recording")
            self.file = self.createFile()
            self.stream = FileOutputStream(self.file)
            self.recorder.startRecording()
            self.recording = True

We also create a thread, passing the instance of this class to it as a Runnable for it to execute, and start it.

            self.thread = Thread(self)
            self.thread.start()
        
        else:

Otherwise, we stop the recorder and interrupt the thread in order to stop processing audio data. We reset the button and print the name of the file we created to the TextView.

            self.button.setText("Start recording")
            self.recorder.stop()
            self.recording = False
            self.thread.interrupt()
            self.textView.setText("Written " + self.file.getAbsolutePath())

As a Runnable, this class provides a run method that can be executed in another context, and we use a thread to ensure that this method is run in the background while the application continues to respond to the user in the foreground. The method reads data from the audio buffer and writes it to an output stream as long as the recording flag is set and the thread is running.

    def run(self):
    
        while self.recording:
            size = self.recorder.read(self.audioBuffer, 0, len(self.audioBuffer))
            if size > 0:
                self.stream.write(self.audioBuffer[0:size])

The final method is used to conveniently create a file in the device's external storage area, returning a File object if successful or None if not.

    @args(File, [])
    def createFile(self):
    
        if Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED:
            return None

If no external storage is mounted then return None immediately.

We obtain the directory on the external storage device that is used to store music.

        storageDir = Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_MUSIC)

If possible, we create a subdirectory for this example, returning None to indicate failure.

        subdir = File(storageDir, "AudioRecord")
        if not subdir.exists():
            if not subdir.mkdirs():
                return None

Finally, we use the SimpleDateFormat class to create a file name based on the current date, then use this to create a File object which we return.

        dateString = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
        return File(subdir, dateString + ".raw")

Files