1

我正在开发一个 Android 语音识别应用程序,它利用 Android 的 AudioRecord 类,除了这个缺陷之外,一切都很顺利。记录器(AudioRecord 的一个实例)在停止后无法再次重新启动,并导致 GC_CONCURRENT 跳入要求垃圾,之后程序退出。我怀疑有一些内存泄漏,但我无法理解它。

下面是我的代码:

package edu.cmu.pocketsphinx.demo;

import static edu.cmu.pocketsphinx.SphinxUtil.syncAssets;
import static edu.cmu.pocketsphinx.sphinxbase.setLogFile;

import java.io.File;
import java.io.IOException;

import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.ToggleButton;
import edu.cmu.pocketsphinx.Config;
import edu.cmu.pocketsphinx.Decoder;
import edu.cmu.pocketsphinx.Hypothesis;


public class PocketSphinxAndroidDemo extends Activity {

    private class RecognitionTask
            extends AsyncTask<AudioRecord, Void, Hypothesis> {

        private final Decoder decoder;

        public RecognitionTask() {
            File root = null;

            try {
                root = syncAssets(getApplicationContext(), "models");
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            File rootLog = new File(root.getParentFile(), "pocketsphinx.log");
            setLogFile(rootLog.getPath());

            Config config = Decoder.defaultConfig();

            config.setString("-lm",  new File(root, "lm/hub4.5000.DMP").getPath());
            config.setString("-hmm", new File(root, "hmm/hub4wsj_sc_8k").getPath());
            config.setString("-dict",new File(root, "lm/hub4.5000.dic").getPath());
            config.setString("-rawlogdir", root.getPath());

            config.setString("-rawlogdir", root.getPath());
            config.setFloat("-samprate", SAMPLE_RATE);
            config.setInt("-maxhmmpf", 10000);
            config.setBoolean("-backtrace", true);
            config.setBoolean("-bestpath", false);
            config.setBoolean("-remove_noise", false);

            decoder = new Decoder(config);
        }

        protected Hypothesis doInBackground(AudioRecord... recorder) {
            int nread;
            short[] buf = new short[1024];
            decoder.startUtt(null);


            while ((nread = recorder[0].read(buf, 0, buf.length)) > 0){
                decoder.processRaw(buf, nread, false, false);
            }
            decoder.endUtt();
            return decoder.hyp();
        }

        protected void onPostExecute(Hypothesis hypothesis) {
            if (null != hypothesis)
                speechResult.append("\n" + hypothesis.getHypstr());
            else
                speechResult.append("\n<no speech>");
        }
    }

    private static final int SAMPLE_RATE = 8000;
    private static final String TAG="PocketSphinxAndroidDemo";
    static {
        System.loadLibrary("pocketsphinx_jni");
    }

    private TextView speechResult;
    private AudioRecord recorder;
    private RecognitionTask recTask;
    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        speechResult = (TextView) findViewById(R.id.SpeechResult);

        recorder = new AudioRecord(MediaRecorder.AudioSource.VOICE_RECOGNITION,
                                   SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO,
                                   AudioFormat.ENCODING_PCM_16BIT, 204800);
        recTask = new RecognitionTask();
    }

    public void onToggleRecognition(View view) {
        Log.i(TAG, "I in ToggleRecognition");
        if (!(view instanceof ToggleButton))
            return;

        if (((ToggleButton) view).isChecked()) {
            recorder.startRecording();
            recTask.execute(recorder);
        } else {
            recorder.stop();
        }
    }

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

2 回答 2

1

您可能想查看您的主题...

在 Main 上,您似乎在执行“recorder.start”和“recorder.stop”

但是,您可以使用 'decoder.start..' 、 'decoder.end..' 在后台线程中控制解码器

IMO,您应该在后台线程中管理所有记录器和编码/解码的控制方法,而不是从主线程启动/停止记录器并在后台启动/停止解码,而没有任何线程之间的通信/协调方式。

这是一个更复杂的应用程序,它可以工作,你可能想看看AudioBoo以及它控制我上面提到的录音机/编码器的地方(全部在后台,都在同一个线程中)

请参阅 audioboo 类“ FLACRecorder ”和“BooRecorder”,并仔细查看这 2 个类中的“开始/停止”相关方法,以及它们如何与各自的线程交互。线程的使用与您的代码完全不同。

或者

IMO,这不太可能,但是您正在加载的“sphinx”库中可能存在一些问题,这些问题会阻止它在不进行明确的“卸载”、“重新加载”循环的情况下被第二次调用。您可以查看那里的论坛,以查看是否存在两次调用库而不卸载、重新加载的问题。

于 2013-10-12T16:12:22.017 回答
0

请注意,当stop()您创建AudioRecord对象时,您还应该调用,release()否则您将无法重新启动它。

这可能不是您的全部问题,但肯定会引起问题。

于 2014-03-18T03:14:47.603 回答