14

Hej, im currently trying to get AudioRecord to work. Because I need it in a bigger project. But it seems to mess up a lot. I have been trying alot of things, so I went back to basic when I traced this bug. I am using my Samsung Galaxy S as my debugdevice.

My problem is, first time after a reboot of my device I can initialize the AudioRecord object I create without problems. But the second time I run it, it won't initialize the AudioRecord object. I have tried several frequencies, fyi.

Here is my code:

package android.audiorecordtest;

import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class AudioRecordTest extends Activity {

    int frequency;
    AudioRecord audRec;
    TextView txtVw;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        txtVw = (TextView) findViewById(R.id.txtVw);


        frequency=8000;
        int bufferSize=(AudioRecord.getMinBufferSize(frequency, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT))*2;
        if (bufferSize>0) {
            audRec = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
                int status = audRec.getState();
        if (status == AudioRecord.STATE_INITIALIZED) {
            txtVw.setText("Initialized" + frequency);
        } else {
            txtVw.setText("Not Initialized i=" + frequency);
        }
        }

After a few hours of looking through logcat information i found this event

    02-28 10:46:37.048: DEBUG/dalvikvm(4477): GC_EXPLICIT freed 1801 objects / 98944 bytes in 97ms
02-28 10:46:37.048: VERBOSE/AudioRecord(4477): stop

Which seems to "release the native hold on the AudioRecord. So i tried doing an override of finalize with my Audiorecord object.release(). This didnt work though.. Anyone have any idea?

slezadav
  • 6,104
  • 7
  • 40
  • 61
Anders Metnik
  • 6,096
  • 7
  • 40
  • 79
  • I'm really wondering why no one have any answers, is it because you guys can't recreate the problem, or because people just can't be bothered to help, or ? – Anders Metnik Mar 07 '11 at 08:36
  • For those of you stumbling upon this, make sure you have the record permission! – NateS Jan 01 '12 at 05:45
  • 1
    @NateS if you dont have the record permission then it wont even initialize the first time as my question title clearly states :) But yes if someone only have the problem with first initialization attempt then they should check their permissions in AndroidManifest.xml – Anders Metnik Jan 06 '12 at 09:59

5 Answers5

12

I was able to reproduce your problem (on a Samsung phone). I added an onDestroy() method releasing the record:

@Override
public void onDestroy() { 
    super.onDestroy();
    System.out.println("OnDestroy");
    audRec.release();
}

After adding this, the audioRecord seems to initialize correctly every time the activity is started.

Jordan
  • 6,083
  • 3
  • 23
  • 30
  • 1
    +1 For me, I needed the `onCancelled()` method of an AsyncTask, but I don't know how I'd have got to the bottom of the issue without this. Thanks. :) – Michael Aug 20 '13 at 15:47
6

I had the same problem, usually the audRec.release() helps indeed, but if you need to stop and start several times the following code is more robust. Plus, I had an issue that the recording took place in a separate thread and Android sometimes kills threads when running for a long time. So take a look at this code, it makes sure the recording is held even when the other thread is dead and upon the following audRec.start() it stops and releases:

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder; 

public class RecorderSingleton {

    private static final int FREQUENCY = 16000;

    public static RecorderSingleton instance = new RecorderSingleton();
    private AudioRecord recordInstance = null;
    private int bufferSize;

    private RecorderSingleton() {
        bufferSize = AudioRecord.getMinBufferSize(FREQUENCY, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
    }

    public boolean init() {
        recordInstance = new AudioRecord(MediaRecorder.AudioSource.MIC, FREQUENCY, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
        if (recordInstance.getState() == AudioRecord.STATE_UNINITIALIZED) {
            return false;
        }
        return true;
    }

    public int getBufferSize() {
        return bufferSize;
    }
    public boolean start() {
        if (recordInstance != null && recordInstance.getState() != AudioRecord.STATE_UNINITIALIZED) {
            if (recordInstance.getRecordingState() != AudioRecord.RECORDSTATE_STOPPED) {
                recordInstance.stop();
            }
            recordInstance.release();
        }
        if (!init()) {
            return false;
        }
        recordInstance.startRecording();
        return true;
    }

    public int read(short[] tempBuffer) {
        if (recordInstance == null) {
            return AudioRecord.ERROR_INVALID_OPERATION;
        }
        int ret = recordInstance.read(tempBuffer, 0, bufferSize);
        return ret;
    }

    public void stop() {
        if (recordInstance == null) {
            return;
        }
        recordInstance.stop();
        recordInstance.release();
    }
}

Then if you have a recorder thread you can use it as follows:

import android.media.AudioRecord;

public class Recorder implements Runnable {
    private int requiredSamples;
    private int takenSamples = 0;
    private boolean cancelled = false;

    public void run() {

        // We're important...
        android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);

        int bufferRead = 0;
        int bufferSize = RecorderSingleton.instance.getBufferSize();
        short[] tempBuffer = new short[bufferSize];
        if (!RecorderSingleton.instance.start()) {
            return;
        }
        try {
            Log.d(RoomieConstants.LOG_TAG, "Recorder Started");
            while (takenSamples < requiredSamples && !cancelled) {
                bufferRead = RecorderSingleton.instance.read(tempBuffer);
                if (bufferRead == AudioRecord.ERROR_INVALID_OPERATION) {
                    throw new IllegalStateException("read() returned AudioRecord.ERROR_INVALID_OPERATION");
                } else if (bufferRead == AudioRecord.ERROR_BAD_VALUE) {
                    throw new IllegalStateException("read() returned AudioRecord.ERROR_BAD_VALUE");
                }
                takenSamples += bufferRead;
                // do something with the samples ...
                // ...
                // ...
            }
        } finally {
            // Close resources...
            stop();
        }
    }

    public void stop() {
        RecorderSingleton.instance.stop();
    }

    public void cancel() {
        cancelled  = true;
    }
}
1

To Answer my own question, the only way i found it doable to use AudioRecord, is to never have it as an global variable, dont know why, but it seems it won't let you release the resources of the instance correctly if you do so.

Anders Metnik
  • 6,096
  • 7
  • 40
  • 79
  • You should try it the way Jordan did it. I do it like that aswell, and it works just fine... – ThaMe90 Apr 26 '11 at 12:53
  • Dont have the implementation anymore, but yeah if he reproduced it, and solved it, im secure enough in it to give him a correct answer, thanks for bringing it to my attention. – Anders Metnik Apr 27 '11 at 08:30
  • I must say that setting it to null is a bit over the top, after you call release(), the GC should automatically clean it up for you at some time, so you are free to re-initialise it. – ThaMe90 Apr 27 '11 at 08:34
  • I think I was trying to make a point, but good feedback. Corrected. :) – Jordan Apr 27 '11 at 13:35
0

You should try to call audRec.stop() to release the resource.

Brian
  • 53
  • 1
  • 5
  • Stop is only for stopping a running recording. But Release also has a try-catch to call stop, though release is releasing resources, not stop. As far as i have looked through the class. Else thanks. – Anders Metnik Mar 03 '11 at 07:02
0

My AudioRecord didn't initialize because it was static