teaching machines

Simplifying PocketSphinx demo

September 18, 2012 by . Filed under public, reader animated.

Following the PocketSphinx Android instructions left me with a demo that did more than I wanted and I didn’t really understand. I pruned away it to simplify it and eliminate the step where I had to copy over the model files with adb. Now, I put the model in my project’s assets directory and mirror that on to storage. My first change, then, was to RecognizerTask. I made it reference the app’s external storage directory where the assets are mirrored to.

public RecognizerTask(File sphinxDirectory,
                      String dictionary,
                      String lm) {
  pocketsphinx.setLogfile(sphinxDirectory.getAbsolutePath() + "/pocketsphinx.log");

  Config c = new Config();
  c.setString("-hmm", sphinxDirectory + "/hmm/en_US/hub4wsj_sc_8k");
  c.setString("-dict", sphinxDirectory + "/lm/en_US/" + dictionary);
  c.setString("-lm", sphinxDirectory + "/lm/en_US/" + lm);
  c.setString("-rawlogdir", sphinxDirectory.getAbsolutePath());
  ...

Only the constructor changed. This should allow for us to use other dictionaries and lm files fairly easily without having to muck with RecognizerTask.

The next step was to eliminate the unnecessary functionality of PocketSphinxDemo. I removed the performance calculations and edited the code. The author did something funny with making “that” references—which were final references tothis. I’m not sure the reasoning and would welcome explanation from the erudite among you. I changed the IDs of the widgets to more meaningful ones. I also triggered the asset-to-storage copy. This class was the result:

package edu.cmu.pocketsphinx.demo;

import java.io.File;
import org.twodee.android.utils.Log;
import org.twodee.android.utils.Utilities;
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.EditText;

public class PocketSphinxDemo extends Activity implements RecognitionListener {
  static {
    System.loadLibrary("pocketsphinx_jni");
  }

  /** Recognizer task, which runs in a worker thread. */
  RecognizerTask recognizer;

  /** Are we listening? */
  boolean isListening;

  /** Text box for results. */
  EditText editText;

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    // Copy out assets directory to storage. TODO: condition this on files
    // missing.
    Utilities.copyAsset(this, "pocketsphinx");

    // Set up widgets.
    editText = (EditText) findViewById(R.id.resultsText);

    Button b = (Button) findViewById(R.id.listenButton);
    b.setOnTouchListener(new OnTouchListener() {
      @Override
      public boolean onTouch(View v,
                             MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
          isListening = true;
          recognizer.start();
        } else if (event.getAction() == MotionEvent.ACTION_UP) {
          isListening = false;
          recognizer.stop();
        }
        return false;
      }
    });

    isListening = false;
    recognizer = new RecognizerTask(new File(getExternalFilesDir(null), "pocketsphinx"), "digger_dict.dic", "digger_lm.lm");
    recognizer.setRecognitionListener(this);
    new Thread(recognizer).start();
  }

  public void onPartialResults(Bundle b) {
    final String hyp = b.getString("hyp");
    editText.post(new Runnable() {
      public void run() {
        editText.setText(hyp);
      }
    });
  }

  public void onResults(Bundle b) {
    final String hyp = b.getString("hyp");
    editText.post(new Runnable() {
      public void run() {
        editText.setText(hyp);
      }
    });
  }

  @Override
  public void onError(int err) {
    Log.d("Error: %d", err);
  }
}

It works, once. Subsequent listens fail.