Android音频记录器 - 第二次无法初始化

14

你好,我目前正在尝试使AudioRecord正常工作,因为我需要在一个大项目中使用它。但是它似乎出现了很多问题。

我已经尝试了很多方法,所以当我追踪到这个错误时,我回到了基础知识。

我正在使用我的三星Galaxy S作为我的调试设备。

我的问题是,重新启动设备后第一次可以初始化AudioRecord对象而没有问题。但是第二次运行就无法初始化AudioRecord对象了。我已经尝试了几种频率。

这是我的代码:

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);
        }
        }

经过几个小时的查看logcat信息,我找到了这个事件

    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

似乎是“释放AudioRecord的本地保持”。

因此,我尝试使用我的Audiorecord对象的finalize重写对象.release()。不过这并没有起作用...有人有什么想法吗?


我真的很想知道为什么没有人有任何答案,是因为你们无法重现这个问题,还是因为人们懒得帮忙,或者其他原因? - Anders Metnik
对于那些偶然发现这个问题的人,请确保您拥有记录权限! - NateS
1
@NateS 如果您没有记录权限,那么它甚至不会在第一次初始化时启动,正如我的问题标题所清楚表明的那样 :) 但是,如果有人只在第一次初始化尝试中遇到问题,则应检查他们在AndroidManifest.xml中的权限。 - Anders Metnik
5个回答

12

我能够复现你的问题(在一部三星手机上)。我添加了一个onDestroy()方法来释放记录:

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

添加此代码后,每次启动活动时,audioRecord似乎都能正确初始化。


1
+1 对我来说,我需要一个AsyncTask的onCancelled()方法,但如果没有这个,我不知道如何解决这个问题。谢谢 :) - Michael

6

我有同样的问题,通常audRec.release()确实有帮助,但如果您需要多次停止和启动,则以下代码更加健壮。另外,我遇到了一个问题,录制发生在一个单独的线程中,当Android运行长时间时,有时会杀死线程。因此,请查看此代码,它确保即使其他线程死亡,也会保持录制,并在以下audRec.start()停止和释放:

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();
    }
}

如果您有一个录制线程,可以按照以下方式使用它:
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;
    }
}

在您的特定情况下,您会给出什么值作为requiredSamples? - Josh

1
回答自己的问题,我发现使用AudioRecord的唯一可行方法是永远不要将其作为全局变量,不知道为什么,但如果这样做,似乎无法正确释放实例的资源。

你应该像Jordan那样尝试一下。我也是这样做的,而且它完全有效... - ThaMe90
我已经没有实现了,但是如果他复现并解决了它,我足够有信心给他一个正确的答案。感谢你让我注意到这个问题。 - Anders Metnik
我必须说,将其设置为null有点过头了。在调用release()之后,GC应该会在某个时候自动清理它,因此您可以自由地重新初始化它。 - ThaMe90
我想我当时是想表达一个观点,但是得到了很好的反馈并进行了更正。 :) - Jordan

0

我的AudioRecord没有初始化,因为它是静态的


0
你应该尝试调用audRec.stop()来释放资源。

Stop 只用于停止正在运行的录制。但 Release 也有一个 try-catch 来调用 stop,尽管 release 是释放资源而不是 stop。就我所看到的类而言。否则感谢。 - Anders Metnik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接