Lesson 130. Media. Record audio using AudioRecorder

Lesson 130. Media. Record audio using AudioRecorder


In this lesson:

– Write audio using AudioRecorder

MediaRecorder reviewed last lesson recorded audio directly to a file. AudioRecorder does not write data, but allows us to retrieve it from within the application. that is, the mediator between the app and the microphone. When we start recording, AudioRecorder starts to receive data from the microphone and stores it in its internal buffer. When creating an AudioRecorder, we can specify the desired size of this buffer and then request read data from it.

That is, AudioRecorder will be useful if you want to somehow process the data before writing to a file, or if you want to send data not to a file, but somewhere else.

Let’s write a program in which we will consider the basic methods of work with AudioRecorder.

Let’s create a project:

Project name: P1301_AudioRecorder
Build Target: Android 2.3.3
Application name: AudioRecorder
Package name: en.startandroid.develop.p1301audiorecorder
Create Activity: MainActivity

Add rows to strings.xml:

Start record
Stop record
Start read
Stop read

we draw a screen main.xml:



	
		
		
	
	
		
		
	

The upper buttons start / stop recording audio from the microphone in AudioRecorder, the lower ones read data from AudioRecorder into our clipboard.

In the manifest, add permission to record the sound: android.permission.RECORD_AUDIO.

MainActivity.java:

package ru.startandroid.develop.p1301audiorecorder;

import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends Activity {

  final String TAG = "myLogs";

  int myBufferSize = 8192;
  AudioRecord audioRecord;
  boolean isReading = false;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    createAudioRecorder();

    Log.d(TAG, "init state = " + audioRecord.getState());
  }

  void createAudioRecorder() {
    int sampleRate = 8000;
    int channelConfig = AudioFormat.CHANNEL_IN_MONO;
    int audioFormat = AudioFormat.ENCODING_PCM_16BIT;

    int minInternalBufferSize = AudioRecord.getMinBufferSize(sampleRate,
        channelConfig, audioFormat);
    int internalBufferSize = minInternalBufferSize * 4;
    Log.d(TAG, "minInternalBufferSize = " + minInternalBufferSize
        + ", internalBufferSize = " + internalBufferSize
        + ", myBufferSize = " + myBufferSize);

    audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
        sampleRate, channelConfig, audioFormat, internalBufferSize);
  }

  public void recordStart(View v) {
    Log.d(TAG, "record start");
    audioRecord.startRecording();
    int recordingState = audioRecord.getRecordingState();
    Log.d(TAG, "recordingState = " + recordingState);
  }

  public void recordStop(View v) {
    Log.d(TAG, "record stop");
    audioRecord.stop();
  }

  public void readStart(View v) {
    Log.d(TAG, "read start");
    isReading = true;
    new Thread(new Runnable() {
      @Override
      public void run() {
        if (audioRecord == null)
          return;

        byte[] myBuffer = new byte[myBufferSize];
        int readCount = 0;
        int totalCount = 0;
        while (isReading) {
          readCount = audioRecord.read(myBuffer, 0, myBufferSize);
          totalCount += readCount;
          Log.d(TAG, "readCount = " + readCount + ", totalCount = "
              + totalCount);
        }
      }
    }).start();
  }

  public void readStop(View v) {
    Log.d(TAG, "read stop");
    isReading = false;
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();

    isReading = false;
    if (audioRecord != null) {
      audioRecord.release();
    }
  }
}

We look at the code.

IN onCreate we call our AudioRecorder creation method and log the state of the created object. The status can be obtained by the getState method. There can be only two states: STATE_INITIALIZED and STATE_UNINITIALIZED. Do they mean AudioRecorder is ready or not ready?

IN createAudioRecorder we create AudioRecorder. To do this, we will need several input parameters:
– sound source
– sample rate
– Mono / stereo channel mode
– audio format
– buffer size

We set the sample rate to 8000. Mono mode. Audio format – 16 bit. To know the size of the buffer, there is a method getMinBufferSize. It, based on the audio format data transmitted to it, returns the minimum buffer size that AudioRecorder can work with. We get the minimum size and place this variable multiplied by 4 in the internalBufferSize variable. This buffer size will be in the AudioRecord created.

Next we create AudioRecord. We specify the microphone as the sound source. We also specify the sample rate, channel mode, buffer format and size.

In the method recordStart start recording with the startRecording method. With the getRecordingState method, we get the status – whether or not there is a recording. There are two options: RECORDSTATE_RECORDING (recording is in progress) and RECORDSTATE_STOPPED (recording is suspended).

IN recordStop stop recording by the stop method.

IN readStart set the isReading label to true. It will mean that we are now in data read mode with AudioRecorder. Next, create a new thread and read in it so that it does not occupy the main stream. We create our buffer the size of myBufferSize and read data from it by the read method. This happens in a loop that checks that we are in read mode. The read input method accepts an array (in which data will be placed), an indentation (if you need to read the data not from the beginning, but from some position), and the portion size of the received data. In readCount, the read method returns the number of bytes it gave us. In totalCount, we sum up the total number of bytes received.

By the way, the read method has a few more implementations, if this doesn’t work for you. See the help for more details.

In the method readStop we disable read mode by assigning the isReading variable false. The readStart thread will read this value, exit the loop, and exit.

IN onDestroy disable read mode and release the resources occupied by AudioRecord using the release method.

We save everything, launch the application.

We see in logs

minInternalBufferSize = 1024, internalBufferSize = 4096, myBufferSize = 8192
init state = 1

We see that the minimum size of the AudioRecorder buffer was 1024. The internal buffer will be 4096. The size of our buffer will be 8192. The recorder status = 1, this value is variable STATE_INITIALIZED, so everything is ok, ready to go.

press Start record.

record start
recordingState = 3

The record state of the recorder = 3, this is the value of the RECORDSTATE_RECORDING variable, so everything is ok, the recording is going.

AudioRecorder now receives data from the microphone and holds it in its clipboard. Let’s try to read this data. press Start read, And after a few seconds Stop read

List:

14: 03: 48.519: D / myLogs (14361): read start
14: 03: 48.519: D / myLogs (14361): readCount = 4096, totalCount = 4096
14: 03: 48.779: D / myLogs (14361): readCount = 4096, totalCount = 8192
14: 03: 49.039: D / myLogs (14361): readCount = 4096, totalCount = 12288
14: 03: 49.289: D / myLogs (14361): readCount = 4096, totalCount = 16384
14: 03: 49.549: D / myLogs (14361): readCount = 4096, totalCount = 20480
14: 03: 49.809: D / myLogs (14361): readCount = 4096, totalCount = 24576
14: 03: 50.069: D / myLogs (14361): readCount = 4096, totalCount = 28672
14: 03: 50.319: D / myLogs (14361): readCount = 4096, totalCount = 32768
14: 03: 50.569: D / myLogs (14361): readCount = 4096, totalCount = 36864
14: 03: 50.829: D / myLogs (14361): readCount = 4096, totalCount = 40960
14: 03: 51.079: D / myLogs (14361): readCount = 4096, totalCount = 45056
14: 03: 51.179: D / myLogs (14361): read stop
14: 03: 51.339: D / myLogs (14361): readCount = 4096, totalCount = 49152

We see that several buffer readings have passed. So we were getting data from the microphone with AudioRecorder.

How many bytes of sound per second

Let’s figure out how to calculate how much space a sound takes when you write it. Remember the parameters we set when creating AudioRecorder: sample rate (how often the sound is read) = 8000 Hz, format (which memory occupies one record) = 16 bits, channel = mono. These options mean that one second of sound will take up 8000 * 16 * 1 = 128,000 bits = 16,000 bytes.

Now let’s look at the logs. The first reading of the sound was at 14: 03: 48.519, and the last at 14: 03: 51.339. That is, we wrote the sound for about three seconds. Three seconds is 16,000 * 3 = 48,000 bytes.

Again we look at the logs, the last value of totalCount = 49 152. As you can see, the calculated and real numbers are similar.

Let’s change the parameters in the createAudioRecorder method:

    int sampleRate = 22050;
    int channelConfig = AudioFormat.CHANNEL_IN_STEREO;

and again we will count:

22 050 (sample rate) * 16 (format) * 2 (stereo) = 705 600 bits = 88 200 bytes now takes a second of sound. As you can see, the quality has increased in size.

Save everything, launch the application. press Start record, then Start read and three seconds later Stop read.

14: 15: 29.959: D / myLogs (14567): read start
14: 15: 29.969: D / myLogs (14567): readCount = 8192, totalCount = 8192
14: 15: 29.969: D / myLogs (14567): readCount = 8192, totalCount = 16384
14: 15: 30.069: D / myLogs (14567): readCount = 8192, totalCount = 24576

14: 15: 32.479: D / myLogs (14567): readCount = 8192, totalCount = 237568
14: 15: 32.579: D / myLogs (14567): readCount = 8192, totalCount = 245760
14: 15: 32.659: D / myLogs (14567): read stop
14: 15: 32.669: D / myLogs (14567): readCount = 8192, totalCount = 253952

We see that in about 3 seconds we counted 253 952 bytes. We look at our calculations 88 200 * 3 = 264 600. The figures are again close.

It was a slight departure from the topic of the lesson to give you an idea of ​​the relationship of such parameters as time, sample rate, format, channels, bytes.

Listener of dedicated frames

When AudioRecorder gives us data, it calculates the frames given. A frame is a unit of sound recording. How many bytes does one frame take? We already considered this a little earlier – format * channel mode. That is, if we write sound in the format of 16 bits / mono, then the frame will take 16 * 1 = 16 bits = 2 bytes.

We can hang a listener with a couple of methods on AudioRecorder. One method will work periodically for each specified number of given frames, and the other one at a time upon reaching a certain number of delivered frames.

Let’s rewrite createAudioRecorder:

  void createAudioRecorder() {
    int sampleRate = 8000;
    int channelConfig = AudioFormat.CHANNEL_IN_MONO;
    int audioFormat = AudioFormat.ENCODING_PCM_16BIT;

    int minInternalBufferSize = AudioRecord.getMinBufferSize(sampleRate,
        channelConfig, audioFormat);
    int internalBufferSize = minInternalBufferSize * 4;
    Log.d(TAG, "minInternalBufferSize = " + minInternalBufferSize
        + ", internalBufferSize = " + internalBufferSize
        + ", myBufferSize = " + myBufferSize);

    audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
        sampleRate, channelConfig, audioFormat, internalBufferSize);

    audioRecord.setPositionNotificationPeriod(1000);

    audioRecord.setNotificationMarkerPosition(10000);
    audioRecord
        .setRecordPositionUpdateListener(new OnRecordPositionUpdateListener() {
          public void onPeriodicNotification(AudioRecord recorder) {
            Log.d(TAG, "onPeriodicNotification");
          }

          public void onMarkerReached(AudioRecord recorder) {
            Log.d(TAG, "onMarkerReached");
            isReading = false;
          }
        });
  }

The setRecordPositionUpdateListener method sets the listener with the onPeriodicNotification and onMarkerReached methods I described above. The setPositionNotificationPeriod method sets the number of frames to trigger onPeriodicNotification, and setNotificationMarkerPosition sets the number of frames to trigger onMarkerReached.

That is, the onPeriodicNotification method will work every 1000 frames given. And onMarkerReached – upon reaching 10,000 dedicated frames. OnMarkerReached we will stop reading.

Save everything, run the program and click Start record, then Start read. This time, we may not manually stop the reading process because it will stop on reaching 10,000 frames itself.

We look at the logs:

read start
readCount = 4096, totalCount = 4096
onPeriodicNotification
onPeriodicNotification
onPeriodicNotification
readCount = 4096, totalCount = 8192
onPeriodicNotification
onPeriodicNotification
onPeriodicNotification
readCount = 4096, totalCount = 12288
onPeriodicNotification
onPeriodicNotification
readCount = 4096, totalCount = 16384
onPeriodicNotification
onMarkerReached
onPeriodicNotification
readCount = 4096, totalCount = 20480

We see that onPeriodicNotification triggered approximately every 2,000 bytes, and onMarkerReached worked around receiving 20,000 bytes.

Remember that one frame occupies two bytes in current settings. So, as we indicated, the onPeriodicNotification method worked every 1000 frames, and onMarkerReached once 10,000 frames were received

In the next lesson:

– Get photos and videos using the system app




Discuss in the forum [11 replies]

Leave a Comment