/*
 * ---------------------------------------------------------------------------
 * Recognizer.java
 * 
 * Copyright 2007 Nuance Communciations, Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the 'License'); you may not
 * use this file except in compliance with the License.
 * 
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * 
 * ---------------------------------------------------------------------------
 */


package android.speech.srec;

import android.util.Log;

import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.util.Locale;

/**
 * Simple, synchronous speech recognizer, using the Nuance SREC package.
 * Usages proceeds as follows:
 * 
 * <ul>
 * <li>Create a <code>Recognizer</code>.
 * <li>Create a <code>Recognizer.Grammar</code>.
 * <li>Setup the <code>Recognizer.Grammar</code>.
 * <li>Reset the <code>Recognizer.Grammar</code> slots, if needed.
 * <li>Fill the <code>Recognizer.Grammar</code> slots, if needed.
 * <li>Compile the <code>Recognizer.Grammar</code>, if needed.
 * <li>Save the filled <code>Recognizer.Grammar</code>, if needed.
 * <li>Start the <code>Recognizer</code>.
 * <li>Loop over <code>advance</code> and <code>putAudio</code> until recognition complete.
 * <li>Fetch and process results, or notify of failure.
 * <li>Stop the <code>Recognizer</code>.
 * <li>Destroy the <code>Recognizer</code>.
 * </ul>
 * 
 * <p>Below is example code</p>
 * 
 * <pre class="prettyprint">
 * 
 * // create and start audio input
 * InputStream audio = new MicrophoneInputStream(11025, 11025*5);
 * // create a Recognizer
 * String cdir = Recognizer.getConfigDir(null);
 * Recognizer recognizer = new Recognizer(cdir + "/baseline11k.par");
 * // create and load a Grammar
 * Recognizer.Grammar grammar = recognizer.new Grammar(cdir + "/grammars/VoiceDialer.g2g");
 * // setup the Grammar to work with the Recognizer
 * grammar.setupRecognizer();
 * // fill the Grammar slots with names and save, if required
 * grammar.resetAllSlots();
 * for (String name : names) grammar.addWordToSlot("@Names", name, null, 1, "V=1");
 * grammar.compile();
 * grammar.save(".../foo.g2g");
 * // start the Recognizer
 * recognizer.start();
 * // loop over Recognizer events
 * while (true) {
 *     switch (recognizer.advance()) {
 *     case Recognizer.EVENT_INCOMPLETE:
 *     case Recognizer.EVENT_STARTED:
 *     case Recognizer.EVENT_START_OF_VOICING:
 *     case Recognizer.EVENT_END_OF_VOICING:
 *         // let the Recognizer continue to run
 *         continue;
 *     case Recognizer.EVENT_RECOGNITION_RESULT:
 *         // success, so fetch results here!
 *         for (int i = 0; i < recognizer.getResultCount(); i++) {
 *             String result = recognizer.getResult(i, Recognizer.KEY_LITERAL);
 *         }
 *         break;
 *     case Recognizer.EVENT_NEED_MORE_AUDIO:
 *         // put more audio in the Recognizer
 *         recognizer.putAudio(audio);
 *         continue;
 *     default:
 *         notifyFailure();
 *         break;
 *     }
 *     break;
 * }
 * // stop the Recognizer
 * recognizer.stop();
 * // destroy the Recognizer
 * recognizer.destroy();
 * // stop the audio device
 * audio.close();
 * 
 * </pre>
 */
public final class Recognizer {
    static {
        System.loadLibrary("srec_jni");
    }

    private static String TAG = "Recognizer";
    
    /**
     * Result key corresponding to confidence score.
     */
    public static final String KEY_CONFIDENCE = "conf";
    
    /**
     * Result key corresponding to literal text.
     */
    public static final String KEY_LITERAL = "literal";
    
    /**
     * Result key corresponding to semantic meaning text.
     */
    public static final String KEY_MEANING = "meaning";

    // handle to SR_Vocabulary object
    private long mVocabulary = 0;
    
    // handle to SR_Recognizer object
    private long mRecognizer = 0;
    
    // Grammar currently associated with Recognizer via SR_GrammarSetupRecognizer
    private Grammar mActiveGrammar = null;
    
    /**
     * Get the pathname of the SREC configuration directory corresponding to the
     * language indicated by the Locale.
     * This directory contains dictionaries, speech models,
     * configuration files, and other data needed by the Recognizer.
     * @param locale <code>Locale</code> corresponding to the desired language,
     * or null for default, currently <code>Locale.US</code>.
     * @return Pathname of the configuration directory.
     */
    public static String getConfigDir(Locale locale) {
        if (locale == null) locale = Locale.US;
        String dir = "/system/usr/srec/config/" +
                locale.toString().replace('_', '.').toLowerCase(Locale.ROOT);
        if ((new File(dir)).isDirectory()) return dir;
        return null;
    }

    /**
     * Create an instance of a SREC speech recognizer.
     * 
     * @param configFile pathname of the baseline*.par configuration file,
     * which in turn contains references to dictionaries, speech models,
     * and other data needed to configure and operate the recognizer.
     * A separate config file is needed for each audio sample rate.
     * Two files, baseline11k.par and baseline8k.par, which correspond to
     * 11025 and 8000 hz, are present in the directory indicated by
     * {@link #getConfigDir}.
     * @throws IOException
     */
    public Recognizer(String configFile) throws IOException {
        PMemInit();
        SR_SessionCreate(configFile);
        mRecognizer = SR_RecognizerCreate();
        SR_RecognizerSetup(mRecognizer);
        mVocabulary = SR_VocabularyLoad();
    }

    /**
     * Represents a grammar loaded into the Recognizer.
     */
    public class Grammar {
        private long mGrammar = 0;

        /**
         * Create a <code>Grammar</code> instance.
         * @param g2gFileName pathname of g2g file.
         */
        public Grammar(String g2gFileName) throws IOException {
            mGrammar = SR_GrammarLoad(g2gFileName);
            SR_GrammarSetupVocabulary(mGrammar, mVocabulary);
        }

        /**
         * Reset all slots.
         */
        public void resetAllSlots() {
            SR_GrammarResetAllSlots(mGrammar);
        }

        /**
         * Add a word to a slot.
         * 
         * @param slot slot name.
         * @param word word to insert.
         * @param pron pronunciation, or null to derive from word.
         * @param weight weight to give the word.  One is normal, 50 is low.
         * @param tag semantic meaning tag string.
         */
        public void addWordToSlot(String slot, String word, String pron, int weight, String tag) {
            SR_GrammarAddWordToSlot(mGrammar, slot, word, pron, weight, tag); 
        }

        /**
         * Compile all slots.
         */
        public void compile() {
            SR_GrammarCompile(mGrammar);
        }

        /**
         * Setup <code>Grammar</code> with <code>Recognizer</code>.
         */
        public void setupRecognizer() {
            SR_GrammarSetupRecognizer(mGrammar, mRecognizer);
            mActiveGrammar = this;
        }

        /**
         * Save <code>Grammar</code> to g2g file.
         * 
         * @param g2gFileName
         * @throws IOException
         */
        public void save(String g2gFileName) throws IOException {
            SR_GrammarSave(mGrammar, g2gFileName);
        }

        /**
         * Release resources associated with this <code>Grammar</code>.
         */
        public void destroy() {
            // TODO: need to do cleanup and disassociation with Recognizer
            if (mGrammar != 0) {
                SR_GrammarDestroy(mGrammar);
                mGrammar = 0;
            }
        }

        /**
         * Clean up resources.
         */
        protected void finalize() {
            if (mGrammar != 0) {
                destroy();
                throw new IllegalStateException("someone forgot to destroy Grammar");
            }
        }
    }

    /**
     * Start recognition
     */
    public void start() {
        // TODO: shouldn't be here?
        SR_RecognizerActivateRule(mRecognizer, mActiveGrammar.mGrammar, "trash", 1);
        SR_RecognizerStart(mRecognizer);
    }
    
    /**
     * Process some audio and return the current status.
     * @return recognition event, one of:
     * <ul>
     * <li><code>EVENT_INVALID</code>
     * <li><code>EVENT_NO_MATCH</code>
     * <li><code>EVENT_INCOMPLETE</code>
     * <li><code>EVENT_STARTED</code>
     * <li><code>EVENT_STOPPED</code>
     * <li><code>EVENT_START_OF_VOICING</code>
     * <li><code>EVENT_END_OF_VOICING</code>
     * <li><code>EVENT_SPOKE_TOO_SOON</code>
     * <li><code>EVENT_RECOGNITION_RESULT</code>
     * <li><code>EVENT_START_OF_UTTERANCE_TIMEOUT</code>
     * <li><code>EVENT_RECOGNITION_TIMEOUT</code>
     * <li><code>EVENT_NEED_MORE_AUDIO</code>
     * <li><code>EVENT_MAX_SPEECH</code>
     * </ul>
     */
    public int advance() {
        return SR_RecognizerAdvance(mRecognizer);
    }
    
    /**
     * Put audio samples into the <code>Recognizer</code>.
     * @param buf holds the audio samples.
     * @param offset offset of the first sample.
     * @param length number of bytes containing samples.
     * @param isLast indicates no more audio data, normally false.
     * @return number of bytes accepted.
     */
    public int putAudio(byte[] buf, int offset, int length, boolean isLast) {
        return SR_RecognizerPutAudio(mRecognizer, buf, offset, length, isLast);
    }
    
    /**
     * Read audio samples from an <code>InputStream</code> and put them in the
     * <code>Recognizer</code>.
     * @param audio <code>InputStream</code> containing PCM audio samples.
     */
    public void putAudio(InputStream audio) throws IOException {
        // make sure the audio buffer is allocated
        if (mPutAudioBuffer == null) mPutAudioBuffer = new byte[512];
        // read some data
        int nbytes = audio.read(mPutAudioBuffer);
        // eof, so signal Recognizer
        if (nbytes == -1) {
            SR_RecognizerPutAudio(mRecognizer, mPutAudioBuffer, 0, 0, true);
        }
        // put it into the Recognizer
        else if (nbytes != SR_RecognizerPutAudio(mRecognizer, mPutAudioBuffer, 0, nbytes, false)) {
            throw new IOException("SR_RecognizerPutAudio failed nbytes=" + nbytes);
        }
    }
    
    // audio buffer for putAudio(InputStream)
    private byte[] mPutAudioBuffer = null;

    /**
     * Get the number of recognition results.  Must be called after
     * <code>EVENT_RECOGNITION_RESULT</code> is returned by
     * <code>advance</code>, but before <code>stop</code>.
     * 
     * @return number of results in nbest list.
     */
    public int getResultCount() {
        return SR_RecognizerResultGetSize(mRecognizer);
    }

    /**
     * Get a set of keys for the result.  Must be called after
     * <code>EVENT_RECOGNITION_RESULT</code> is returned by
     * <code>advance</code>, but before <code>stop</code>.
     * 
     * @param index index of result.
     * @return array of keys.
     */
    public String[] getResultKeys(int index) {
        return SR_RecognizerResultGetKeyList(mRecognizer, index);
    }

    /**
     * Get a result value.  Must be called after
     * <code>EVENT_RECOGNITION_RESULT</code> is returned by
     * <code>advance</code>, but before <code>stop</code>.
     * 
     * @param index index of the result.
     * @param key key of the result.  This is typically one of
     * <code>KEY_CONFIDENCE</code>, <code>KEY_LITERAL</code>, or
     * <code>KEY_MEANING</code>, but the user can also define their own keys
     * in a grxml file, or in the <code>tag</code> slot of
     * <code>Grammar.addWordToSlot</code>.
     * @return the result.
     */
    public String getResult(int index, String key) {
        return SR_RecognizerResultGetValue(mRecognizer, index, key);
    }

    /**
     * Stop the <code>Recognizer</code>.
     */
    public void stop() {
        SR_RecognizerStop(mRecognizer);
        SR_RecognizerDeactivateRule(mRecognizer, mActiveGrammar.mGrammar, "trash");
    }
    
    /**
     * Reset the acoustic state vectorto it's default value.
     * 
     * @hide
     */
    public void resetAcousticState() {
        SR_AcousticStateReset(mRecognizer);
    }
    
    /**
     * Set the acoustic state vector.
     * @param state String containing the acoustic state vector.
     * 
     * @hide
     */
    public void setAcousticState(String state) {
        SR_AcousticStateSet(mRecognizer, state);
    }
    
    /**
     * Get the acoustic state vector.
     * @return String containing the acoustic state vector.
     * 
     * @hide
     */
    public String getAcousticState() {
        return SR_AcousticStateGet(mRecognizer);
    }

    /**
     * Clean up resources.
     */
    public void destroy() {
        try {
            if (mVocabulary != 0) SR_VocabularyDestroy(mVocabulary);
        } finally {
            mVocabulary = 0;
            try {
                if (mRecognizer != 0) SR_RecognizerUnsetup(mRecognizer);
            } finally {
                try {
                    if (mRecognizer != 0) SR_RecognizerDestroy(mRecognizer);
                } finally {
                    mRecognizer = 0;
                    try {
                        SR_SessionDestroy();
                    } finally {
                        PMemShutdown();
                    }
                }
            }
        }
    }

    /**
     * Clean up resources.
     */
    protected void finalize() throws Throwable {
        if (mVocabulary != 0 || mRecognizer != 0) {
            destroy();
            throw new IllegalStateException("someone forgot to destroy Recognizer");
        }
    }
    
    /* an example session captured, for reference
    void doall() {
        if (PMemInit ( )
           || lhs_audioinOpen ( WAVE_MAPPER, SREC_TEST_DEFAULT_AUDIO_FREQUENCY, &audio_in_handle )
           || srec_test_init_application_data ( &applicationData, argc, argv )
           || SR_SessionCreate ( "/system/usr/srec/config/en.us/baseline11k.par" )
           || SR_RecognizerCreate ( &applicationData.recognizer )
           || SR_RecognizerSetup ( applicationData.recognizer)
           || ESR_SessionGetLCHAR ( L("cmdline.vocabulary"), filename, &flen )
           || SR_VocabularyLoad ( filename, &applicationData.vocabulary )
           || SR_VocabularyGetLanguage ( applicationData.vocabulary, &applicationData.locale )
           || (applicationData.nametag = NULL)
           || SR_NametagsCreate ( &applicationData.nametags )
           || (LSTRCPY ( applicationData.grammars [0].grammar_path, "/system/usr/srec/config/en.us/grammars/VoiceDialer.g2g" ), 0)
           || (LSTRCPY ( applicationData.grammars [0].grammarID, "BothTags" ), 0)
           || (LSTRCPY ( applicationData.grammars [0].ruleName, "trash" ), 0)
           || (applicationData.grammars [0].is_ve_grammar = ESR_FALSE, 0)
           || SR_GrammarLoad (applicationData.grammars [0].grammar_path, &applicationData.grammars [applicationData.grammarCount].grammar )
           || SR_GrammarSetupVocabulary ( applicationData.grammars [0].grammar, applicationData.vocabulary )
           || SR_GrammarSetupRecognizer( applicationData.grammars [0].grammar, applicationData.recognizer )
           || SR_GrammarSetDispatchFunction ( applicationData.grammars [0].grammar, L("myDSMCallback"), NULL, myDSMCallback )
           || (applicationData.grammarCount++, 0)
           || SR_RecognizerActivateRule ( applicationData.recognizer, applicationData.grammars [0].grammar,
                           applicationData.grammars [0].ruleName, 1 )
           || (applicationData.active_grammar_num = 0, 0)
           || lhs_audioinStart ( audio_in_handle )
           || SR_RecognizerStart ( applicationData.recognizer )
           || strl ( applicationData.grammars [0].grammar, &applicationData, audio_in_handle, &recognition_count )
           || SR_RecognizerStop ( applicationData.recognizer )
           || lhs_audioinStop ( audio_in_handle )
           || SR_RecognizerDeactivateRule ( applicationData.recognizer, applicationData.grammars [0].grammar, applicationData.grammars [0].ruleName )
           || (applicationData.active_grammar_num = -1, 0)
           || SR_GrammarDestroy ( applicationData.grammars [0].grammar )
           || (applicationData.grammarCount--, 0)
           || SR_NametagsDestroy ( applicationData.nametags )
           || (applicationData.nametags = NULL, 0)
           || SR_VocabularyDestroy ( applicationData.vocabulary )
           || (applicationData.vocabulary = NULL)
           || SR_RecognizerUnsetup ( applicationData.recognizer) // releases acoustic models
           || SR_RecognizerDestroy ( applicationData.recognizer )
           || (applicationData.recognizer = NULL)
           || SR_SessionDestroy ( )
           || srec_test_shutdown_application_data ( &applicationData )
           || lhs_audioinClose ( &audio_in_handle )
           || PMemShutdown ( )
    }
    */


    //
    // PMem native methods
    //
    private static native void PMemInit();
    private static native void PMemShutdown();


    //
    // SR_Session native methods
    //
    private static native void SR_SessionCreate(String filename);
    private static native void SR_SessionDestroy();


    //
    // SR_Recognizer native methods
    //
    
    /**
     * Reserved value.
     */
    public final static int EVENT_INVALID = 0;
    
    /**
     * <code>Recognizer</code> could not find a match for the utterance.
     */
    public final static int EVENT_NO_MATCH = 1;
    
    /**
     * <code>Recognizer</code> processed one frame of audio.
     */
    public final static int EVENT_INCOMPLETE = 2;
    
    /**
     * <code>Recognizer</code> has just been started.
     */
    public final static int EVENT_STARTED = 3;
    
    /**
     * <code>Recognizer</code> is stopped.
     */
    public final static int EVENT_STOPPED = 4;
    
    /**
     * Beginning of speech detected.
     */
    public final static int EVENT_START_OF_VOICING = 5;
    
    /**
     * End of speech detected.
     */
    public final static int EVENT_END_OF_VOICING = 6;
    
    /**
     * Beginning of utterance occured too soon.
     */
    public final static int EVENT_SPOKE_TOO_SOON = 7;
    
    /**
     * Recognition match detected.
     */
    public final static int EVENT_RECOGNITION_RESULT = 8;
    
    /**
     * Timeout occured before beginning of utterance.
     */
    public final static int EVENT_START_OF_UTTERANCE_TIMEOUT = 9;
    
    /**
     * Timeout occured before speech recognition could complete.
     */
    public final static int EVENT_RECOGNITION_TIMEOUT = 10;
    
    /**
     * Not enough samples to process one frame.
     */
    public final static int EVENT_NEED_MORE_AUDIO = 11;
    
    /**
     * More audio encountered than is allowed by 'swirec_max_speech_duration'.
     */
    public final static int EVENT_MAX_SPEECH = 12;

    /**
     * Produce a displayable string from an <code>advance</code> event.
     * @param event
     * @return String representing the event.
     */
    public static String eventToString(int event) {
        switch (event) {
            case EVENT_INVALID:
                return "EVENT_INVALID";
            case EVENT_NO_MATCH:
                return "EVENT_NO_MATCH";
            case EVENT_INCOMPLETE:
                return "EVENT_INCOMPLETE";
            case EVENT_STARTED:
                return "EVENT_STARTED";
            case EVENT_STOPPED:
                return "EVENT_STOPPED";
            case EVENT_START_OF_VOICING:
                return "EVENT_START_OF_VOICING";
            case EVENT_END_OF_VOICING:
                return "EVENT_END_OF_VOICING";
            case EVENT_SPOKE_TOO_SOON:
                return "EVENT_SPOKE_TOO_SOON";
            case EVENT_RECOGNITION_RESULT:
                return "EVENT_RECOGNITION_RESULT";
            case EVENT_START_OF_UTTERANCE_TIMEOUT:
                return "EVENT_START_OF_UTTERANCE_TIMEOUT";
            case EVENT_RECOGNITION_TIMEOUT:
                return "EVENT_RECOGNITION_TIMEOUT";
            case EVENT_NEED_MORE_AUDIO:
                return "EVENT_NEED_MORE_AUDIO";
            case EVENT_MAX_SPEECH:
                return "EVENT_MAX_SPEECH";
        }
        return "EVENT_" + event;
    }

    //
    // SR_Recognizer methods
    //
    private static native void SR_RecognizerStart(long recognizer);
    private static native void SR_RecognizerStop(long recognizer);
    private static native long SR_RecognizerCreate();
    private static native void SR_RecognizerDestroy(long recognizer);
    private static native void SR_RecognizerSetup(long recognizer);
    private static native void SR_RecognizerUnsetup(long recognizer);
    private static native boolean SR_RecognizerIsSetup(long recognizer);
    private static native String SR_RecognizerGetParameter(long recognizer, String key);
    private static native int SR_RecognizerGetSize_tParameter(long recognizer, String key);
    private static native boolean SR_RecognizerGetBoolParameter(long recognizer, String key);
    private static native void SR_RecognizerSetParameter(long recognizer, String key, String value);
    private static native void SR_RecognizerSetSize_tParameter(long recognizer,
            String key, int value);
    private static native void SR_RecognizerSetBoolParameter(long recognizer, String key,
            boolean value);
    private static native void SR_RecognizerSetupRule(long recognizer, long grammar,
            String ruleName);
    private static native boolean SR_RecognizerHasSetupRules(long recognizer);
    private static native void SR_RecognizerActivateRule(long recognizer, long grammar,
            String ruleName, int weight);
    private static native void SR_RecognizerDeactivateRule(long recognizer, long grammar,
            String ruleName);
    private static native void SR_RecognizerDeactivateAllRules(long recognizer);
    private static native boolean SR_RecognizerIsActiveRule(long recognizer, long grammar,
            String ruleName);
    private static native boolean SR_RecognizerCheckGrammarConsistency(long recognizer,
            long grammar);
    private static native int SR_RecognizerPutAudio(long recognizer, byte[] buffer, int offset,
            int length, boolean isLast);
    private static native int SR_RecognizerAdvance(long recognizer);
    // private static native void SR_RecognizerLoadUtterance(long recognizer,
    //         const LCHAR* filename);
    // private static native void SR_RecognizerLoadWaveFile(long recognizer,
    //         const LCHAR* filename);
    // private static native void SR_RecognizerSetLockFunction(long recognizer,
    //         SR_RecognizerLockFunction function, void* data);
    private static native boolean SR_RecognizerIsSignalClipping(long recognizer);
    private static native boolean SR_RecognizerIsSignalDCOffset(long recognizer);
    private static native boolean SR_RecognizerIsSignalNoisy(long recognizer);
    private static native boolean SR_RecognizerIsSignalTooQuiet(long recognizer);
    private static native boolean SR_RecognizerIsSignalTooFewSamples(long recognizer);
    private static native boolean SR_RecognizerIsSignalTooManySamples(long recognizer);
    // private static native void SR_Recognizer_Change_Sample_Rate (size_t new_sample_rate);
    
    
    //
    // SR_AcousticState native methods
    //
    private static native void SR_AcousticStateReset(long recognizer);
    private static native void SR_AcousticStateSet(long recognizer, String state);
    private static native String SR_AcousticStateGet(long recognizer);


    //
    // SR_Grammar native methods
    //
    private static native void SR_GrammarCompile(long grammar);
    private static native void SR_GrammarAddWordToSlot(long grammar, String slot,
            String word, String pronunciation, int weight, String tag);
    private static native void SR_GrammarResetAllSlots(long grammar);
    // private static native void SR_GrammarAddNametagToSlot(long grammar, String slot,
    // const struct SR_Nametag_t* nametag, int weight, String tag);
    private static native void SR_GrammarSetupVocabulary(long grammar, long vocabulary);
    // private static native void SR_GrammarSetupModels(long grammar, SR_AcousticModels* models);
    private static native void SR_GrammarSetupRecognizer(long grammar, long recognizer);
    private static native void SR_GrammarUnsetupRecognizer(long grammar);
    // private static native void SR_GrammarGetModels(long grammar,SR_AcousticModels** models);
    private static native long SR_GrammarCreate();
    private static native void SR_GrammarDestroy(long grammar);
    private static native long SR_GrammarLoad(String filename);
    private static native void SR_GrammarSave(long grammar, String filename);
    // private static native void SR_GrammarSetDispatchFunction(long grammar,
    //         const LCHAR* name, void* userData, SR_GrammarDispatchFunction function);
    // private static native void SR_GrammarSetParameter(long grammar, const
    //         LCHAR* key, void* value);
    // private static native void SR_GrammarSetSize_tParameter(long grammar,
    //         const LCHAR* key, size_t value);
    // private static native void SR_GrammarGetParameter(long grammar, const
    //         LCHAR* key, void** value);
    // private static native void SR_GrammarGetSize_tParameter(long grammar,
    //         const LCHAR* key, size_t* value);
    // private static native void SR_GrammarCheckParse(long grammar, const LCHAR*
    //         transcription, SR_SemanticResult** result, size_t* resultCount);
    private static native void SR_GrammarAllowOnly(long grammar, String transcription);
    private static native void SR_GrammarAllowAll(long grammar);


    //
    // SR_Vocabulary native methods
    //
    // private static native int SR_VocabularyCreate();
    private static native long SR_VocabularyLoad();
    // private static native void SR_VocabularySave(SR_Vocabulary* self,
    //         const LCHAR* filename);
    // private static native void SR_VocabularyAddWord(SR_Vocabulary* self,
    //         const LCHAR* word);
    // private static native void SR_VocabularyGetLanguage(SR_Vocabulary* self,
    //         ESR_Locale* locale);
    private static native void SR_VocabularyDestroy(long vocabulary);
    private static native String SR_VocabularyGetPronunciation(long vocabulary, String word);


    //
    // SR_RecognizerResult native methods
    //
    private static native byte[] SR_RecognizerResultGetWaveform(long recognizer);
    private static native int SR_RecognizerResultGetSize(long recognizer);
    private static native int SR_RecognizerResultGetKeyCount(long recognizer, int nbest);
    private static native String[] SR_RecognizerResultGetKeyList(long recognizer, int nbest);
    private static native String SR_RecognizerResultGetValue(long recognizer,
            int nbest, String key);
    // private static native void SR_RecognizerResultGetLocale(long recognizer, ESR_Locale* locale);
}
