Aswathy Mohan - 2023-12-22

Hi,
I have created a sample app for voice recognition using PocketSphinx. But while launching app , it got crashed. No exceptions and error showing in the logcat. And as per the troubleshooting , i found like crash happening with getRecognizer() method call. But I am unable to fix or find the error. I'm using android versions 8 to latest(13).Please see my complete code below

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.StrictMode;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Locale;

import edu.cmu.pocketsphinx.Assets;
import edu.cmu.pocketsphinx.BuildConfig;
import edu.cmu.pocketsphinx.Hypothesis;
import edu.cmu.pocketsphinx.RecognitionListener;
import edu.cmu.pocketsphinx.SpeechRecognizer;
import edu.cmu.pocketsphinx.SpeechRecognizerSetup;

public class MainActivity extends AppCompatActivity implements
RecognitionListener

{
private static final String KWS_SEARCH = "wakeup";
private static final String FORECAST_SEARCH = "forecast";
private static final String DIGITS_SEARCH = "digits";
private static final String PHONE_SEARCH = "phones";
private static final String MENU_SEARCH = "menu";

/* Keyword we are looking for to activate menu */
private static final String KEYPHRASE = "oh mighty computer";

/* Used to handle permission request */
private static final int PERMISSIONS_REQUEST_RECORD_AUDIO = 1;
private static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE =2;

private SpeechRecognizer recognizer;
private HashMap<String, Integer> captions;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (BuildConfig.DEBUG) {
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectAll()
                .penaltyLog()
                .build());
        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                .detectAll()
                .penaltyLog()
                .build());
    }
   captions = new HashMap<>();
   captions.put(KWS_SEARCH, R.string.kws_caption);
   captions.put(MENU_SEARCH, R.string.menu_caption);
   captions.put(DIGITS_SEARCH, R.string.digits_caption);
   captions.put(PHONE_SEARCH, R.string.phone_caption);
   captions.put(FORECAST_SEARCH, R.string.forecast_caption);
   ((TextView) findViewById(R.id.caption_text))
           .setText("Preparing the recognizer");
    if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {
        // Request permission to read files
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
        return;
    }
    // Check if user has given permission to record audio
    int permissionCheck = ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.RECORD_AUDIO);
    if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.RECORD_AUDIO}, PERMISSIONS_REQUEST_RECORD_AUDIO);
        return;
    }
    // Recognizer initialization is a time-consuming and it involves IO,
    // so we execute it in async task
    new SetupTask(this).execute();
}

private static class SetupTask extends AsyncTask<Void, Void, Exception> {
    WeakReference<MainActivity> activityReference;
    SetupTask(MainActivity activity) {
        this.activityReference = new WeakReference<>(activity);
    }
    @Override
    protected Exception doInBackground(Void... params) {
        try {
            Assets assets = new Assets(activityReference.get());
            File assetDir = assets.syncAssets();

            activityReference.get().setupRecognizer(assetDir);
        } catch (IOException e) {
            Log.d("print IO Ecception  ",e.getMessage());
            return e;
        }
        return null;
    }
    @Override
    protected void onPostExecute(Exception result) {
        if (result != null) {
            ((TextView) activityReference.get().findViewById(R.id.caption_text))
                    .setText("Failed to init recognizer " + result);
        } else {
            activityReference.get().switchSearch(KWS_SEARCH);
        }
    }
}
@Override
public void onRequestPermissionsResult(int requestCode,
                                       @NonNull String[] permissions, @NonNull  int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (requestCode == PERMISSIONS_REQUEST_RECORD_AUDIO) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // Recognizer initialization is a time-consuming and it involves IO,
            // so we execute it in async task
            new SetupTask(this).execute();
        } else {
            finish();
        }
    }
}

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

    if (recognizer != null) {
        recognizer.cancel();
        recognizer.shutdown();
    }
}

/**
 * In partial result we get quick updates about current hypothesis. In
 * keyword spotting mode we can react here, in other modes we need to wait
 * for final result in onResult.
 */
@Override
public void onPartialResult(Hypothesis hypothesis) {
    if (hypothesis == null)
        return;

    String text = hypothesis.getHypstr();
    if (text.equals(KEYPHRASE))
        switchSearch(MENU_SEARCH);
    else if (text.equals(DIGITS_SEARCH))
        switchSearch(DIGITS_SEARCH);
    else if (text.equals(PHONE_SEARCH))
        switchSearch(PHONE_SEARCH);
    else if (text.equals(FORECAST_SEARCH))
        switchSearch(FORECAST_SEARCH);
    else
        ((TextView) findViewById(R.id.result_text)).setText(text);
}

/**
 * This callback is called when we stop the recognizer.
 */
@Override
public void onResult(Hypothesis hypothesis) {
    ((TextView) findViewById(R.id.result_text)).setText("");
    if (hypothesis != null) {
        String text = hypothesis.getHypstr();
        Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
    }
}

@Override
public void onBeginningOfSpeech() {
}

/**
 * We stop recognizer here to get a final result
 */
@Override
public void onEndOfSpeech() {
    if (!recognizer.getSearchName().equals(KWS_SEARCH))
        switchSearch(KWS_SEARCH);
}

private void switchSearch(String searchName) {
    recognizer.stop();

    // If we are not spotting, start listening with timeout (10000 ms or 10 seconds).
    if (searchName.equals(KWS_SEARCH))
        recognizer.startListening(searchName);
    else
        recognizer.startListening(searchName, 10000);

    String caption = getResources().getString(captions.get(searchName));
    ((TextView) findViewById(R.id.caption_text)).setText(caption);
}

private void setupRecognizer(File assetsDir) throws IOException {
    // The recognizer can be configured to perform multiple searches
    // of different kind and switch between them
    File acousticModel = new File(assetsDir, "en-us-ptm");
    File dictionary = new File(assetsDir, "cmudict-en-us.dict");
    if(acousticModel.exists()) {
        Log.d("SetupRecognizer", "Acoustic Model Path: " + acousticModel.getAbsolutePath());
    }if(dictionary.exists()) {
        Log.d("SetupRecognizer", "Dictionary Path: " + dictionary.getAbsolutePath());
    }
    try {
        recognizer = SpeechRecognizerSetup.defaultSetup()
                .setAcousticModel(acousticModel)
                .setDictionary(dictionary)
                .getRecognizer();

        if (recognizer != null) {
            Log.d("SetupRecognizer", "After initializing recognizer");
            recognizer.addListener(this);
            Log.d("SetupRecognizer", "Listener attached successfully");
        } else {
            Log.e("SetupRecognizer", "Recognizer is null after initialization");
        }


} catch (IOException e) {
        Log.e("SetupRecognizer", "Error setting up recognizer", e);
        return;
    }
 }


@Override
public void onTimeout() {
    switchSearch(KWS_SEARCH);
}

@Override
public void onError(Exception e) {
    ((TextView) findViewById(R.id.caption_text)).setText(e.getMessage());

}

}