2019-01-11 15:56:24 weixin_44328479 阅读数 6238
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5922 人正在学习 去看看 杨波

简介

本文记录百度离线识别与讯飞离线语音识别,针对的是应用本身级别的。

百度语音离线识别集成

  1. 首先下载开发架包:bdasr_V3_20180801_d6f298a.jar,这个是离在线融合的SDK ;

  2. 导入so库:可在下载的demo中找到,复制到自己工程中同名的路径下
    在这里插入图片描述

  3. 需要百度开放平台上去申请 API Key 和 Secret Key,这是认证权限的必备信息,只有认证通过才可以继续使用语音功能,如语音唤醒,在线识别,离线识别等等。首先需要百度开放平台上去申请 API Key 和 Secret Key,这是认证权限的必备信息,只有认证通过才可以继续使用语音功能,如语音唤醒,在线识别,离线识别等等。
    在这里插入图片描述
    而鉴权认证就是要成功获取到AccessToken(Access Token 是用户身份验证和授权的凭证),而这个认证过程需要自己在应用中编写,之后会提到。
    将获取到的AppID、API Key 和 Secret Key添加到项目的配置文件中去;

  4. AndroidManifest.xml 文件的配置:
    设置权限(如果是Android 6.0 以上还需要获取动态权限,之后会说到)

    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

设置APP_ID, APP_KEY, APP_SECRET

    <meta-data android:name="com.baidu.speech.APP_ID"
        android:value="9788136" />
    <meta-data
        android:name="com.baidu.speech.API_KEY"
        android:value="0GjQNO5H4pGPf9HyA3AmZEbz" />
    <meta-data
        android:name="com.baidu.speech.SECRET_KEY"
        android:value="db981ef3ec647ba8a09b599ad7447a24" />
  1. 注意事项:在申请API Key时需要设置应用包名,并且保证官网申请时的与app/bulid.gradle里的applicationId一致,否则会导致离线命令词或是唤醒报“no licenece”的错误。

离线识别实现

1.可以先准备本地词库和唤醒词库

2.基于SDK的使用:

  • 初始化EventManager类
 EventManager  asr = EventManagerFactory.create(context, "asr");//语音识别器
 EventManager  wp = EventManagerFactory.create(context, "wp");//语音唤醒器
  • 定义输出事件类
public class RecogResultManager implements EventListener{
    ...........
    
     /**
     * 回调事件
     */
    @Override
    public void onEvent(String name, String params, byte[] data, int offset, int length) {
        String logMessage = "name:" + name + "; params:" + params;
        Log.d(TAG, logMessage);
        switch (name) {
            case SpeechConstant.CALLBACK_EVENT_ASR_READY:
                // 引擎准备就绪,可以开始说话              
                break;
            case SpeechConstant.CALLBACK_EVENT_ASR_BEGIN:
                // 检测到用户的已经开始说话
                break;
            case SpeechConstant.CALLBACK_EVENT_ASR_END:
                // 检测到用户的已经停止说话
                break;
            case SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL:
                // 临时识别结果, 长语音模式需要从此消息中取出结果
                break;
              case SpeechConstant.CALLBACK_EVENT_ASR_FINISH:
                // 识别结束, 最终识别结果或可能的错误
                break;
            case SpeechConstant.CALLBACK_EVENT_ASR_LONG_SPEECH:
                // 长语音
                break;
            case SpeechConstant.CALLBACK_EVENT_ASR_VOLUME:
                //音量值 
                break;
            case SpeechConstant.CALLBACK_EVENT_ASR_AUDIO:
                if (data.length != length) {
                 //可能出错的地方:回调返回的数据有问题
                   Log.d(TAG, "internal error: asr.audio" +
                            " callback data length is not equal to length param");
                 .....
                }
                break;
            case SpeechConstant.CALLBACK_EVENT_WAKEUP_SUCCESS:
                //语音唤醒成功 
                break;
            case SpeechConstant.CALLBACK_EVENT_WAKEUP_ERROR:
                //语音唤醒失败
                break;
            .......

            default:
                break;
        }
    }
}    


  • 注册自己的输出事件类
asr.registerListener(eventListener);
wp.registerListener(eventListener);
  • 加载唤醒词库与离线词库
 /**
 - 加载唤醒词库
  */
    private void loadWakeup() {
        Map<String, Object> params = new HashMap<>();
        params.put(SpeechConstant.WP_WORDS_FILE, "assets://WakeUp.bin");
        //开始识别语音唤醒
        mWakeup.start(params);
    }
    
 /**
 - 加载离线词库
  */
    private void loadWakeup() {
       Map<String, Object> params = new LinkedHashMap<>();
       //设置此参数使用与语音唤醒后进入语音识别模式
        params.put(SpeechConstant.VAD, SpeechConstant.VAD_DNN);
        params.put(SpeechConstant.DECODER, 2);
        params.put(SpeechConstant.ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH, "assets://baidu_speech_grammar.bsg");
       //设置唤醒到识别的停顿时间
        if (backTrackInMs > 0) {
            params.put(SpeechConstant.AUDIO_MILLS, System.currentTimeMillis() - backTrackInMs);
        }
        mRecognizer.cancel();
        //开始进行识别
        mRecognizer.start(params);
    }
     注:对于语音唤醒到识别有两种方案:
     方案1: backTrackInMs > 0,唤醒词说完后,直接接句子,中间没有停顿,
            开启回溯,连同唤醒词一起整句识别,推荐4个字 1500ms,backTrackInMs 最大 15000,即15s.
     方案2: backTrackInMs = 0,唤醒词说完后,中间有停顿,不开启回溯。唤醒词识别回调后,正常开启识别。
            官方demo里采用默认设置的是1500,本人demo中选择的是方案2,因为测试结果方案2的识别效果好一些,详见下面测试结果。
  • 查询权限,获取AccessToken
public void check() {
        appendLogMessage("try to check appId " + appId + " ,appKey=" + appKey + " ,secretKey" + secretKey);
        if (appId == null || appId.isEmpty()) {
              errorMessage = "appId 为空";
              fixMessage = "填写appID";
          }
        if (appKey == null || appKey.isEmpty()) {
                errorMessage = "appKey 为空";
                fixMessage = "填写appID";
          }
        if (secretKey == null || secretKey.isEmpty()) {
                 errorMessage = "secretKey 为空";
                  fixMessage = "secretKey";
         }
        try {
                checkOnline();
        } catch (UnknownHostException e) {
                infoMessage = "无网络或者网络不连通,忽略检测 : " + e.getMessage();
        } catch (Exception e) {
                errorMessage = e.getClass().getCanonicalName() + ":" + e.getMessage();
                fixMessage = " 重新检测appId, appKey, appSecret是否正确";
         }
 }

/**
 *  获取并校验token
 */
  public void checkOnline() throws Exception {
            String urlpath = "http://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id="
                    + appKey + "&client_secret=" + secretKey;
            URL url = new URL(urlpath);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(1000);
            InputStream is = conn.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder result = new StringBuilder();
            String line = "";
            do {
                line = reader.readLine();
                if (line != null) {
                    result.append(line);
                }
            } while (line != null);
           String res = result.toString();
           if (!res.contains("audio_voice_assistant_get")) {
                errorMessage = "appid:" + appId + ",没有audio_voice_assistant_get 权限,请在网页上开通\"语音识别\"能力";
                fixMessage = "secretKey";
                return;
            }
            appendLogMessage("openapi return " + res);
            JSONObject jsonObject = new JSONObject(res);
            String error = jsonObject.optString("error");
            if (error != null && !error.isEmpty()) {
                errorMessage = "appkey secretKey 错误" + ", error:" + error + ", json is" + result;
                fixMessage = " 重新检测appId对应的 appKey, appSecret是否正确";
                return;
            }
           String token = jsonObject.getString("access_token");
           if (token == null || !token.endsWith("-" + appId)) {
                errorMessage = "appId 与 appkey及 appSecret 不一致。
                appId = " + appId + " ,token = " + token;
                fixMessage = " 重新检测appId对应的 appKey, appSecret是否正确";
            }
        }
    }
  • 加载动态权限(Android 6.0以上需要)
    在主Activity中添加:

 private void initPermission() {
        String[] permissions = {Manifest.permission.RECORD_AUDIO,
                Manifest.permission.ACCESS_NETWORK_STATE,
                Manifest.permission.INTERNET,
                Manifest.permission.READ_PHONE_STATE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
        };
        ArrayList<String> toApplyList = new ArrayList<>();
       for (String perm : permissions) {
            if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, perm)) {
                toApplyList.add(perm);
                // 进入到这里代表没有权限.
            }
        }
        String[] tmpList = new String[toApplyList.size()];
        if (!toApplyList.isEmpty()) {
            ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123);
        }
    }
    
@Override
   public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        // 此处为android 6.0以上动态授权的回调,用户自行实现。
    }
  • 控制识别/唤醒
   /**
    * 语音唤醒开始
    */
    public void start(Map<String, Object> params) {
        String json = new JSONObject(params).toString();
        wp.send(SpeechConstant.WAKEUP_START, json, null, 0, 0);
    }
    
    /**
     * 停止语音唤醒
     */
    public void stop() {
        wp.send(SpeechConstant.WAKEUP_STOP, null, null, 0, 0);
    }

    /**
     * 开始识别
     */
    public void start(Map<String, Object> params) {
        String json = new JSONObject(params).toString();
        asr.send(SpeechConstant.ASR_START, json, null, 0, 0);
    }
    
    /**
     * 提前结束录音等待识别结果。
     */
    public void stop() {
        if (!isInited) {
            throw new RuntimeException("release() was called");
        }
        asr.send(SpeechConstant.ASR_CANCEL, "{}", null, 0, 0);
    }
    
    /**
     * 取消本次识别,取消后将立即停止不会返回识别结果。
     * cancel 与stop的区别是 cancel在stop的基础上,完全停止整个识别流程,
     */
    public void cancel() {
        if (!isInited) {
            throw new RuntimeException("release() was called");
        }
        asr.send(SpeechConstant.ASR_CANCEL, "{}", null, 0, 0);
    }
  • 事件管理器退出,回收资源,不能一直占用着mic资源。
    /**
     * 释放语音唤醒资源
     */
    public void release() {
       if (wp== null) {
            return;
        }
        stop();
        wp.unregisterListener(eventListener);
        wp = null;
        isInited = false;
    }
   
    /**
     * 释放语音识别资源
     */
      public void release() {
        if (asr == null) {
            return;
        }
        cancel();
        asr.send(SpeechConstant.ASR_KWS_UNLOAD_ENGINE, null, null, 0, 0);
        asr.unregisterListener(eventListener);
        asr = null;
        isInited = false;
    }

添加动作识别:

之前考虑是用数据库匹配识别结果,来找到相应的组件,但是demo里做得着急,只是通过管理activity,以activity和name为媒介来找到相应的组件,从而执行相应的动作。

public abstract class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        VrApplication.getInstance().addActivity(this);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        VrApplication.getInstance().removeActivity(this);
    }
}
public class VrApplication extends Application {

    private static final VrApplication INSTANCE = new VrApplication();

    private  List<BaseActivity> mActivities;

    private VrApplication() {}

    public static VrApplication getInstance() {
        return VrApplication.INSTANCE;
    }

    public  void addActivity(BaseActivity activity){
        if(mActivities == null){
            mActivities = new ArrayList<>();
        }
        mActivities.add(activity);
    }
    
      public  void removeActivity(BaseActivity activity){
        mActivities.remove(activity);
    }

    public List<BaseActivity> getActivities() {
        return mActivities;
    }

    /**
     * 关闭应用后需要清空管理
     */
     public  void finishAll(){
        for(BaseActivity activity : mActivities){
            if(! activity.isFinishing()){
                activity.finish();
            }
        }
    }
}

所有的界面activity需要继承BaseActivity,初始化组件时保存起来,界面销毁时clear()。

 private void initView() {
        mViews = new HashMap<>();
        EditText etCode = (EditText) findViewById(R.id.tv_point_code);
        Button action = (Button) findViewById(R.id.btn_action);
        mViews.put(etCode.getHint().toString(), etCode);
        mViews.put(action.getText().toString(), action);
    }

回调监听处理结果,进行确认组件去执行相应的动作:

public class RecogResultManager implements EventListener, IStatus {
     ...............
     
    /**
     * 回调事件
     */
    @Override
    public void onEvent(String name, String params, byte[] data, int offset, int length) {
           switch (name) {
                ........
                case SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL:
                     // 临时识别结果, 长语音模式需要从此消息中取出结果
                     handlePartial(params, data, offset, length);
                     break;
                case SpeechConstant.CALLBACK_EVENT_ASR_FINISH:
                     // 识别结束, 最终识别结果或可能的错误
                     handleFinish(params);
                     break;
                ........
                case SpeechConstant.CALLBACK_EVENT_WAKEUP_SUCCESS:
                     //语音唤醒成功
                     handleWpSuccess(name, params);
                     break;
                case SpeechConstant.CALLBACK_EVENT_WAKEUP_ERROR:
                     handleWpErro(name, params);
                     break;
          }
    } 
    ........
}               

处理语音唤醒,语音唤醒后,要切换开始进行语音识别:

private void handleWpMsg() {
        Map<String, Object> mParams = new LinkedHashMap<>();
        mParams.put(SpeechConstant.VAD, SpeechConstant.VAD_DNN);
        mParams.put(SpeechConstant.DECODER, 2);
        mParams.put(SpeechConstant.ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH, BSGPATH);
         if (mBackTrackInMs > 0) {
            mParams.put(SpeechConstant.AUDIO_MILLS, System.currentTimeMillis() - mBackTrackInMs);
        }
        mRecognizer.cancel();
        mRecognizer.start(mParams);
 }

处理语音识别结果:

private void analysData(RecogResult recogResult) {
        String results = recogResult.getBestResult();
        //从缓存中获取当前的Activity
        BaseActivity activityInstance = UiUtil.getActivityInstance(mContext);
        if (activityInstance == null) {
            return;
        }
        Map<String, View> views = activityInstance.getViews();
        for (Map.Entry<String, View> entry : views.entrySet()) {
            if (results.contains(entry.getKey())) {
                action(entry.getValue(), results);
            }
        }
  }

执行动作:

 private void action(View value, String results) {
        if (value instanceof Button) {
            value.performClick();
        } else if (value instanceof EditText) {
            ((EditText) value).setText(results);
        }
 }

注意事项

1.即使只进行离线操作,第一次也需要进行联网进行认证授权才可以进行接下来的语音识别。
2. 不用时要及时释放,不能一直占用mic资源,影响其他使用。
3. 环境嘈杂时,识别率会很低,需要依赖降噪算法。
4.本demo针对一个应用来说,通过语音识别命令,并执行操作,跳转界面,点击操作,选择操作等,有一定的局限性,因为离线时,只能识别词库中的词,所以EditText或是TextView填写值的时候,可能没有用户所说的那个值,目前只是依赖在线识别来控制这块,还有待进一步研究。

demo下载地址:

链接: https://download.csdn.net/download/weixin_44328479/10910474.
Alt

2016-11-23 13:46:32 u014418171 阅读数 1837
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5922 人正在学习 去看看 杨波

本文将使用PocketSphinx来实现Android平台的离线语音识别

优点: 离线,不用联网 识别较准(大家都说99%  我觉得只有80%)
缺点: 自定义语音命令麻烦  只能小范围识别 

网上看了很多例子 我跟你说 那都是坑 根本就是错误的教程…
不废话 开始教程:

1.先确定要识别的词组

我的需求是实现离线识别 以下词组

中文:  向前 向后 向左 向右
英文: FORWARD BACKWARD TURNLEFT TURNRIGHT

(很多这样的教程 好像基本没教怎么识别英文来着…)

2.生成并修改lm和dic文件

先生成中文的 新建一个txt文件 命名为 text.txt
并把 向前 向后 向左 向右这四个词写上去,然后保存..
如图

注意很多文章都说这样的格式 < s>向前< /s>
这样的格式 我只想跟你说 你被坑了… 这样做出来的语音识别demo是没有任何识别反应的 真害人..

打开 http://www.speech.cs.cmu.edu/tools/lmtool-new.html 在线转换lm文件 , 点击选择文件选择刚才的text.txt文件

然后点击 COMPILE KNOWLEDGE BASE 提交
然后分别打开这两个文件 查看内容 并分别复制内容到新建的text.dic和text.lm文件里, 保存 (如果乱码 请使用谷歌浏览器 或 修改浏览器显示编码)

例如 我的 text.dic文件文本内容为:(我本来是写向前 向后 向左 向右 现在你看 顺序乱了 我不知道这样的目的 我建议保留转换后的顺序 可能与优先级有关吧)

向前
向右
向后
向左

而text.lm的内容为

Language model created by QuickLM on Tue Nov 22 03:42:55 EST 2016
Copyright (c) 1996-2010 Carnegie Mellon University and Alexander I. Rudnicky
The model is in standard ARPA format, designed by Doug Paul while he was at MITRE.
The code that was used to produce this language model is available in Open Source.
Please visit http://www.speech.cs.cmu.edu/tools/ for more information
The (fixed) discount mass is 0.5. The backoffs are computed using the ratio method.
This model based on a corpus of 4 sentences and 6 words
\data\
ngram 1=6
ngram 2=8
ngram 3=4

\1-grams:

-0.7782 -0.3010

-0.7782 -0.2218

-1.3802 向前 -0.2218

-1.3802 向右 -0.2218

-1.3802 向后 -0.2218

-1.3802 向左 -0.2218

\2-grams:

-0.9031 向前 0.0000

-0.9031 向右 0.0000

-0.9031 向后 0.0000

-0.9031 向左 0.0000

-0.3010 向前 -0.3010

-0.3010 向右 -0.3010

-0.3010 向后 -0.3010

-0.3010 向左 -0.3010

\3-grams:

-0.3010 向前

-0.3010 向右

-0.3010 向后

-0.3010 向左

\end\

还有的说有什么UTF-8编码问题 我只想说 我没遇到 我用的Sublime Text3文本编辑器 你如果有问题 你换我这个文本编辑器试试
完成上面的然后 脑残的一步来了 手动找字典(拼音)
哪里来的拼音字典? 先去这里下载一个压缩文件

https://sourceforge.net/projects/cmusphinx/files/pocketsphinx/0.7/ 

然后找到 pocketsphinx-0.7.tar.gz 点击下载(下不动用迅雷)

下载好后解压

pocketsphinx-0.7\pocketsphinx-0.7\model\hmm\zh\tdt_sc_8k //为中文语言模型文件
pocketsphinx-0.7\model\lm\zh_CN\xxxx.dic//为拼音读音字典
//同理
pocketsphinx-0.7\model\hmm\en_US\hub4wsj_sc_8k //为英文语言模型文件
pocketsphinx-0.7\model\lm\en_US\xxxx.dic //为英文读音字典

你还会发现有台湾的等等

我目录的中文语言文件字典为mandarin_notone.dic 打开 你会发现里面有很多文字对应读音(拼音)

这时 我们回到刚才的text.dic和text.lm文件 打开text.dic文件
一个词一个词 在mandarin_notone.dic字典中快捷键查找 (词找不到就单个字找) 然后复制拼音到相应的词语右边 注意拼音和词语要至少留一个空格 然后拼音和拼音之间也要留一个空格
编辑text.dic文件,我的找好了 如下:

向前  x iang q ian
向右  x iang y ou
向后  x iang h ou
向左  x iang z uo

好了 保存…

英文的也一样步骤 字典的话,要从英文字典上查 英文最好都大写 并且多个单词时最好不留空格

3.Demo修改

代码太多 略.. 但我文章下面直接放demo
网上其他教程很多都是打开demo就闪退 其实原因是
你还要手动复制模型和字典文件到sd卡上

我写的这个demo也是基于它, 但我解决了这个问题 我把文件放在 assets里 并且我把模型文件和字典都整合放到一起了 ,如下

这样 就能自动复制文件到临时路径 并让你们快速实现离线语音功能 程序员不需要担心其他操作.
实际情况下那demo遗留了一个 严重的bug 就是过100s左右会c库异常闪退 这个问题我也解决了
还有个 文字不断重复 和回调一直回调同一个识别答案这个问题也解决了…

我做了判断 , 文件都放在data/data/com.packagename.xxx/file/
并且根据手机语言切换识别中文还是英文…

public RecognizerTask(Context context) {

String dataPath = context.getFilesDir().getAbsolutePath();

File zhPath =newFile(dataPath +"/voice/zh");

if(!zhPath.exists()) {

zhPath.mkdirs();

}

File enPath =newFile(dataPath +"/voice/en");

if(!enPath.exists()) {

enPath.mkdirs();

}

pocketsphinx

.setLogfile(dataPath +"/voice/pocketsphinx.log");

String rootPath = isZh(context) ? zhPath.getPath() : enPath.getPath();//根据环境选择中英文识别

String dicPath = rootPath +"/text.dic";

String imPath = rootPath +"/text.lm";

if(!newFile(dicPath).exists()) {

releaseAssets(context,"/", dataPath);

}

Config c =newConfig();

c.setString("-hmm", rootPath);

c.setString("-dict", dicPath);

c.setString("-lm", imPath);

c.setFloat("-samprate",8000.0);

c.setInt("-maxhmmpf",2000);

c.setInt("-maxwpf",10);

c.setInt("-pl_window",2);

c.setBoolean("-backtrace",true);

c.setBoolean("-bestpath",false);

this.ps =newDecoder(c);

this.audio =null;

this.audioq =newLinkedBlockingQueue();

this.use_partials =false;

this.mailbox = Event.NONE;

}

代码很简单的 放Demo吧
—安卓离线语音识别 PocketSphinx Demo.zip—
http://download.csdn.net/detail/u014418171/9690129

安卓开发者交流群欢迎您加入
418263790

2018-08-27 22:30:29 hfut_why 阅读数 7772
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5922 人正在学习 去看看 杨波

      最近因为项目的需求,需要在无网络的情况下实现语音识别的功能,因为之前在线识别一直用的科大的,所以经理就和我说,你花半天时间简单熟悉一下,然后出一个Demo,下午有人过来看;因为之前科大在线SR也是别人做的,准确的说我只是了解过一点,也写过相关的blog——百度语音识别结合云知声离线TTSDemo(AS)Android原生TTS的基本使用以及配合中文语音包实现中文TTS等,但是就半天不到的时间写一个Demo还是很赶的,比较不熟悉。下面就来简单的总结一下这半天的经历。   

源码下载地址

 

第一阶段    基础准备

第一步:找到科大讯飞开发平台官网,注册账户

平台地址

第二步:点击右上角“控制台”进入个人控制台

第三步:创建应用,根据选择的服务生成SDK并下载

 

      这里我们添加离线命令词识别服务,获取了对应SDK之后,也就完成的最基本的准备工作了,生成的APPID很重要哟,这个不用说你也应该知道。我们的第一阶段就算完成了

 

第二阶段    Demo导入

第四步:打开AS,创建一个和上图同名的应用

第五步:导入SDK解压文件夹下的sample目录里面的的mscV5PlusDemomodule

 

这里面需要实现在AS项目中导入module操作,如下图所示:

 

选择上面sample下面对应的mscV5PlusDemo即可,如果有需要调整sdk版本的就按照错误提示调整就好了,比较简单;至此,我们就把SDK中的Demo(mscV5PlusDemo)导入到了我们的项目中:

 

第六步:这个时候选择导入的module,在arm机上运行,发现并不能正常运行,那么你需要考虑以下几个问题

(1)Demo中的离线命令词识别的commen.jet文件位置错误

在解压文件夹的res目录下找到asr文件夹,将其copy到Demo里面的assets目录下:

 

(2)一定要在arm机上测试,因为这个Demo里面只有armeabi的so文件

(3)如果可以运行,进入如下界面,发现里面不仅仅只有我们需要的离线命令词识别,还有在线识别等等:

 

我们点击“立刻体验语法识别”,关闭设备网络,选择下图中的“本地”,然后点击“构建语法”,再点击“开始识别”;

这个时候很有可能再报错误,查看错误码发现原来是没有录音权限等权限问题,这个时候你就纳闷了,明明Demo代码中已经添加了权限:

   <uses-permission android:name="android.permission.RECORD_AUDIO" />
   <uses-permission android:name="android.permission.INTERNET" />
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
   <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
   <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
   <uses-permission android:name="android.permission.READ_PHONE_STATE" />
   <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
   <uses-permission android:name="android.permission.READ_CONTACTS" />
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
   <uses-permission android:name="android.permission.WRITE_SETTINGS" />
   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
   <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
   <uses-permission android:name="android.permission.BLUETOOTH" />
   <uses-permission android:name="android.permission.BROADCAST_STICKY" />

为什么还有问题,这个时候你再进入到Demo代码里面查看,里面并没有做6.0以及以上版本的动态权限申请处理,所以怎么办了,要么我们自己加上,要么换一个低一点的机子测试一下。

// 开始识别,没有权限判断
case R.id.isr_recognize:
   ((EditText)findViewById(R.id.isr_text)).setText(null);// 清空显示内容
   // 设置参数
   if (!setParam()) {
      showTip("请先构建语法。");
      return;
   };
   
   ret = mAsr.startListening(mRecognizerListener);
   if (ret != ErrorCode.SUCCESS) {
      showTip("识别失败,错误码: " + ret);   
   }
   break;

这里我们就不深究了,因为后面还有好多内容了,假设这个时候你能够正常运行了,也能在Demo中完成离线命令词识别了。那么下一阶段就是瘦身处理了。

 

第三阶段    功能瘦身

第七步:提取离线命令词识别功能

      不得不说,这个Demo对于我们只使用离线命令词识别来说有一点冗余,太多了;下面我们就来把离线命令词功能抽取出来,如下图:

 

实现离线命令词识别的功能实现主要是上图中红色框中AsrDemo中的逻辑,其源码如下:

package com.iflytek.mscv5plusdemo;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;
import android.widget.Toast;

import com.iflytek.cloud.ErrorCode;
import com.iflytek.cloud.GrammarListener;
import com.iflytek.cloud.InitListener;
import com.iflytek.cloud.LexiconListener;
import com.iflytek.cloud.RecognizerListener;
import com.iflytek.cloud.RecognizerResult;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechRecognizer;
import com.iflytek.cloud.util.ContactManager;
import com.iflytek.cloud.util.ContactManager.ContactListener;
import com.iflytek.cloud.util.ResourceUtil;
import com.iflytek.cloud.util.ResourceUtil.RESOURCE_TYPE;
import com.iflytek.speech.util.FucUtil;
import com.iflytek.speech.util.JsonParser;
import com.iflytek.speech.util.XmlParser;

public class AsrDemo extends Activity implements OnClickListener{
   private static String TAG = AsrDemo.class.getSimpleName();
   // 语音识别对象
   private SpeechRecognizer mAsr;
   private Toast mToast;  
   // 缓存
   private SharedPreferences mSharedPreferences;
   // 本地语法文件
   private String mLocalGrammar = null;
   // 本地词典
   private String mLocalLexicon = null;
   // 云端语法文件
   private String mCloudGrammar = null;
   // 本地语法构建路径    
   private String grmPath = Environment.getExternalStorageDirectory()
                        .getAbsolutePath() + "/msc/test";
   // 返回结果格式,支持:xml,json
   private String mResultType = "json";
   
   private  final String KEY_GRAMMAR_ABNF_ID = "grammar_abnf_id";
   private  final String GRAMMAR_TYPE_ABNF = "abnf";
   private  final String GRAMMAR_TYPE_BNF = "bnf";

   private String mEngineType = "cloud";
   @SuppressLint("ShowToast")
   public void onCreate(Bundle savedInstanceState)
   {
      super.onCreate(savedInstanceState);
      this.requestWindowFeature(Window.FEATURE_NO_TITLE);
      setContentView(R.layout.isrdemo);
      initLayout();
      
      // 初始化识别对象
      mAsr = SpeechRecognizer.createRecognizer(this, mInitListener);    

      // 初始化语法、命令词
      mLocalLexicon = "张海羊\n刘婧\n王锋\n";
      mLocalGrammar = FucUtil.readFile(this,"call.bnf", "utf-8");
      mCloudGrammar = FucUtil.readFile(this,"grammar_sample.abnf","utf-8");
      
      // 获取联系人,本地更新词典时使用
      ContactManager mgr = ContactManager.createManager(AsrDemo.this, mContactListener); 
      mgr.asyncQueryAllContactsName();
      mSharedPreferences = getSharedPreferences(getPackageName(),    MODE_PRIVATE);
      mToast = Toast.makeText(this,"",Toast.LENGTH_SHORT);   
      
   }
   
   /**
    * 初始化Layout。
    */
   private void initLayout(){
      findViewById(R.id.isr_recognize).setOnClickListener(this);
      
      findViewById(R.id.isr_grammar).setOnClickListener(this);
      findViewById(R.id.isr_lexcion).setOnClickListener(this);
      
      findViewById(R.id.isr_stop).setOnClickListener(this);
      findViewById(R.id.isr_cancel).setOnClickListener(this);

      //选择云端or本地
      RadioGroup group = (RadioGroup)this.findViewById(R.id.radioGroup);
      group.setOnCheckedChangeListener(new OnCheckedChangeListener() {
         @Override
         public void onCheckedChanged(RadioGroup group, int checkedId) {
            if(checkedId == R.id.radioCloud)
            {
               ((EditText)findViewById(R.id.isr_text)).setText(mCloudGrammar);
               findViewById(R.id.isr_lexcion).setEnabled(false);
               mEngineType = SpeechConstant.TYPE_CLOUD;
            }else if(checkedId == R.id.radioLocal)
            {
               ((EditText)findViewById(R.id.isr_text)).setText(mLocalGrammar);
               findViewById(R.id.isr_lexcion).setEnabled(true);
               mEngineType =  SpeechConstant.TYPE_LOCAL;
            }
         }
      });
   }
    
   
   String mContent;// 语法、词典临时变量
    int ret = 0;// 函数调用返回值
   @Override
   public void onClick(View view) {      
      if( null == mAsr ){
         // 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688
         this.showTip( "创建对象失败,请确认 libmsc.so 放置正确,\n 且有调用 createUtility 进行初始化" );
         return;
      }
      
      if(null == mEngineType) {
         showTip("请先选择识别引擎类型");
         return;
      }  
      switch(view.getId())
      {
         case R.id.isr_grammar:
            showTip("上传预设关键词/语法文件");
            // 本地-构建语法文件,生成语法id
            if (mEngineType.equals(SpeechConstant.TYPE_LOCAL)) {
               ((EditText)findViewById(R.id.isr_text)).setText(mLocalGrammar);
               mContent = new String(mLocalGrammar);
               mAsr.setParameter(SpeechConstant.PARAMS, null);
               // 设置文本编码格式
               mAsr.setParameter(SpeechConstant.TEXT_ENCODING,"utf-8");
               // 设置引擎类型
               mAsr.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);
               // 设置语法构建路径
               mAsr.setParameter(ResourceUtil.GRM_BUILD_PATH, grmPath);
               //使用8k音频的时候请解开注释
//             mAsr.setParameter(SpeechConstant.SAMPLE_RATE, "8000");
               // 设置资源路径
               mAsr.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath());
               ret = mAsr.buildGrammar(GRAMMAR_TYPE_BNF, mContent, grammarListener);
               if(ret != ErrorCode.SUCCESS){
                  showTip("语法构建失败,错误码:" + ret);
               }
            }
            // 在线-构建语法文件,生成语法id
            else { 
               ((EditText)findViewById(R.id.isr_text)).setText(mCloudGrammar);
               mContent = new String(mCloudGrammar);
               // 指定引擎类型
               mAsr.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);
               // 设置文本编码格式
               mAsr.setParameter(SpeechConstant.TEXT_ENCODING,"utf-8");
                ret = mAsr.buildGrammar(GRAMMAR_TYPE_ABNF, mContent, grammarListener);
               if(ret != ErrorCode.SUCCESS)
                  showTip("语法构建失败,错误码:" + ret);
            }
            break;
         // 本地-更新词典
         case R.id.isr_lexcion: 
            ((EditText)findViewById(R.id.isr_text)).setText(mLocalLexicon);
            mContent = new String(mLocalLexicon);
            mAsr.setParameter(SpeechConstant.PARAMS, null);
            // 设置引擎类型
            mAsr.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
            // 设置资源路径
            mAsr.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath());
            //使用8k音频的时候请解开注释
//          mAsr.setParameter(SpeechConstant.SAMPLE_RATE, "8000");
            // 设置语法构建路径
            mAsr.setParameter(ResourceUtil.GRM_BUILD_PATH, grmPath);
            // 设置语法名称
            mAsr.setParameter(SpeechConstant.GRAMMAR_LIST, "call");
            // 设置文本编码格式
            mAsr.setParameter(SpeechConstant.TEXT_ENCODING,"utf-8");
            ret = mAsr.updateLexicon("contact", mContent, lexiconListener);
            if(ret != ErrorCode.SUCCESS){
               showTip("更新词典失败,错误码:" + ret);
            }
            break;
         // 开始识别
         case R.id.isr_recognize:
            ((EditText)findViewById(R.id.isr_text)).setText(null);// 清空显示内容
            // 设置参数
            if (!setParam()) {
               showTip("请先构建语法。");
               return;
            };
            
            ret = mAsr.startListening(mRecognizerListener);
            if (ret != ErrorCode.SUCCESS) {
               showTip("识别失败,错误码: " + ret);   
            }
            break;
         // 停止识别
         case R.id.isr_stop:
            mAsr.stopListening();
            showTip("停止识别");
            break;
         // 取消识别
         case R.id.isr_cancel:
            mAsr.cancel();
            showTip("取消识别");
            break;
      }
   }
   
   /**
     * 初始化监听器。
     */
    private InitListener mInitListener = new InitListener() {

      @Override
      public void onInit(int code) {
         Log.d(TAG, "SpeechRecognizer init() code = " + code);
         if (code != ErrorCode.SUCCESS) {
              showTip("初始化失败,错误码:"+code);
           }
      }
    };
       
   /**
     * 更新词典监听器。
     */
   private LexiconListener lexiconListener = new LexiconListener() {
      @Override
      public void onLexiconUpdated(String lexiconId, SpeechError error) {
         if(error == null){
            showTip("词典更新成功");
         }else{
            showTip("词典更新失败,错误码:"+error.getErrorCode());
         }
      }
   };
   
   /**
     * 构建语法监听器。
     */
   private GrammarListener grammarListener = new GrammarListener() {
      @Override
      public void onBuildFinish(String grammarId, SpeechError error) {
         if(error == null){
            if (mEngineType.equals(SpeechConstant.TYPE_CLOUD)) {
               Editor editor = mSharedPreferences.edit();
               if(!TextUtils.isEmpty(grammarId))
                  editor.putString(KEY_GRAMMAR_ABNF_ID, grammarId);
               editor.commit();
            }
            showTip("语法构建成功:" + grammarId);
         }else{
            showTip("语法构建失败,错误码:" + error.getErrorCode());
         }        
      }
   };
   /**
    * 获取联系人监听器。
    */
   private ContactListener mContactListener = new ContactListener() {
      @Override
      public void onContactQueryFinish(String contactInfos, boolean changeFlag) {
         //获取联系人
         mLocalLexicon = contactInfos;
      }     
   };
   /**
     * 识别监听器。
     */
    private RecognizerListener mRecognizerListener = new RecognizerListener() {
        
        @Override
        public void onVolumeChanged(int volume, byte[] data) {
           showTip("当前正在说话,音量大小:" + volume);
           Log.d(TAG, "返回音频数据:"+data.length);
        }
        
      @Override
      public void onResult(final RecognizerResult result, boolean isLast) {
         if (null != result && !TextUtils.isEmpty(result.getResultString())) {
            Log.d(TAG, "recognizer result:" + result.getResultString());
            String text = "";
            if (mResultType.equals("json")) {
               text = JsonParser.parseGrammarResult(result.getResultString(), mEngineType);
            } else if (mResultType.equals("xml")) {
               text = XmlParser.parseNluResult(result.getResultString());
            }
            // 显示
            ((EditText) findViewById(R.id.isr_text)).setText(text);
         } else {
            Log.d(TAG, "recognizer result : null");
         }
      }
        
        @Override
        public void onEndOfSpeech() {
           // 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入          
         showTip("结束说话");
        }
        
        @Override
        public void onBeginOfSpeech() {
           // 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入
           showTip("开始说话");
        }

      @Override
      public void onError(SpeechError error) {
         showTip("onError Code:"    + error.getErrorCode());
      }

      @Override
      public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
         // 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
         // 若使用本地能力,会话id为null
         // if (SpeechEvent.EVENT_SESSION_ID == eventType) {
         //    String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);
         //    Log.d(TAG, "session id =" + sid);
         // }
      }

    };
    
   

   private void showTip(final String str) {
      runOnUiThread(new Runnable() {
         @Override
         public void run() {
            mToast.setText(str);
            mToast.show();
         }
      });
   }

   /**
    * 参数设置
    * @param
    * @return 
    */
   public boolean setParam(){
      boolean result = false;
      // 清空参数
      mAsr.setParameter(SpeechConstant.PARAMS, null);
      // 设置识别引擎
      mAsr.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);
      if("cloud".equalsIgnoreCase(mEngineType))
      {
         String grammarId = mSharedPreferences.getString(KEY_GRAMMAR_ABNF_ID, null);
         if(TextUtils.isEmpty(grammarId))
         {
            result =  false;
         }else {
            // 设置返回结果格式
            mAsr.setParameter(SpeechConstant.RESULT_TYPE, mResultType);
            // 设置云端识别使用的语法id
            mAsr.setParameter(SpeechConstant.CLOUD_GRAMMAR, grammarId);
            result =  true;
         }
      }
      else
      {
         // 设置本地识别资源
         mAsr.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath());
         // 设置语法构建路径
         mAsr.setParameter(ResourceUtil.GRM_BUILD_PATH, grmPath);
         // 设置返回结果格式
         mAsr.setParameter(SpeechConstant.RESULT_TYPE, mResultType);
         // 设置本地识别使用语法id
         mAsr.setParameter(SpeechConstant.LOCAL_GRAMMAR, "call");
         // 设置识别的门限值
         mAsr.setParameter(SpeechConstant.MIXED_THRESHOLD, "30");
         // 使用8k音频的时候请解开注释
//       mAsr.setParameter(SpeechConstant.SAMPLE_RATE, "8000");
         result = true;
      }
      
      // 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
      // 注:AUDIO_FORMAT参数语记需要更新版本才能生效
      mAsr.setParameter(SpeechConstant.AUDIO_FORMAT,"wav");
      mAsr.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory()+"/msc/asr.wav");
      return result;
   }
   
   //获取识别资源路径
   private String getResourcePath(){
      StringBuffer tempBuffer = new StringBuffer();
      //识别通用资源
      tempBuffer.append(ResourceUtil.generateResourcePath(this, RESOURCE_TYPE.assets, "asr/common.jet"));
      //识别8k资源-使用8k的时候请解开注释
//    tempBuffer.append(";");
//    tempBuffer.append(ResourceUtil.generateResourcePath(this, RESOURCE_TYPE.assets, "asr/common_8k.jet"));
      return tempBuffer.toString();
   }
   
   @Override
   protected void onDestroy() {
      super.onDestroy();
      if( null != mAsr ){
         // 退出时释放连接
         mAsr.cancel();
         mAsr.destroy();
      }
   }
   
}

看着还是比较多的,我之所以说多而没有说难就是因为它并不难;下面的介绍中我们会对其进行再次瘦身。

 

第八步:为自己的Demo做准备工作

(1)把assets目录copy到我们的module中

(2)把jniLibs目录copy到我们的module中

这里是在Project视图下完成的,这里在Android视图下展示效果更好一下

(3)打开Project视图,把libs目录中的内容复制到我们的module中

(4)在build.gradle(Module:app)中的depandencies下添加依赖:

compile files('libs/Msc.jar')

 

(5)把Demo中的工具类copy到我们的module中

 

截止到现在,我们还在准备阶段,下面就进入正题,来对我们的需要的功能的实现做一个简要的梳理

 

第九步:提取离线命令词识别功能到我们的项目

定义一个activity,CallStepActivity,把AsrDemo中的逻辑代码copy到CallStepActivty中,把对应的布局文件也对应copy进来

 

第十步:梳理逻辑,继续瘦身

上面也说了,AsrDemo中的Demo还是有点冗余,因为好多我们用不上或者暂时用不上,比如在线的命令词识别等肯定用不上,比如词典更新我们暂时用不上,下面就来分析一下单纯使用离线命令词识别的实现(下面是重点

(1)根据应用ID初始化SpeechUtility,通常在程序入口Application中完成

package com.hfut.offlinerecongnizer.activity.util;

import android.app.Application;

import com.hfut.offlinerecongnizer.R;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechUtility;

/**
 * author:why
 * created on: 2018/8/27 11:10
 * description:
 */
public class MyApplication extends Application {

    @Override
    public void onCreate() {
        // 应用程序入口处调用,避免手机内存过小,杀死后台进程后通过历史intent进入Activity造成SpeechUtility对象为null
        // 注意:此接口在非主进程调用会返回null对象,如需在非主进程使用语音功能,请增加参数:SpeechConstant.FORCE_LOGIN+"=true"
        // 参数间使用“,”分隔。
        // 设置你申请的应用appid
        // 注意: appid 必须和下载的SDK保持一致,否则会出现10407错误
        StringBuffer param = new StringBuffer();
        param.append("appid=" + getString(R.string.app_id));
        param.append(",");
        // 设置使用v5+
        param.append(SpeechConstant.ENGINE_MODE + "=" + SpeechConstant.MODE_MSC);
        SpeechUtility.createUtility(MyApplication.this, param.toString());
        super.onCreate();
    }
}

(2)在Activity中初始化初始化监听器,用于初始化语音识别引擎

/**
 * 初始化监听器。
 */
private InitListener mInitListener = new InitListener() {

    @Override
    public void onInit(int code) {
        Log.d(TAG, "SpeechRecognizer init() code = " + code);
        if (code != ErrorCode.SUCCESS) {
            showTip("初始化失败,错误码:" + code);
        }
    }
};

(3)初始化语音识别监听器

/**
 * 识别监听器。
 */
private RecognizerListener mRecognizerListener = new RecognizerListener() {

    @Override
    public void onVolumeChanged(int volume, byte[] data) {
        showTip("当前正在说话,音量大小:" + volume);
        Log.d(TAG, "返回音频数据:" + data.length);
    }

    @Override
    public void onResult(final RecognizerResult result, boolean isLast) {
        if (null != result && !TextUtils.isEmpty(result.getResultString())) {
            Log.d(TAG, "recognizer result:" + result.getResultString());
            String text = "";
            if (mResultType.equals("json")) {
                text = JsonParser.parseGrammarResult(result.getResultString(), SpeechConstant.TYPE_LOCAL);
            } else if (mResultType.equals("xml")) {
                text = XmlParser.parseNluResult(result.getResultString());
            }
            // 显示
            ((EditText) findViewById(R.id.isr_text)).setText(text);
        } else {
            Log.d(TAG, "recognizer result : null");
        }
    }

    @Override
    public void onEndOfSpeech() {
        // 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入
        showTip("结束说话");
    }

    @Override
    public void onBeginOfSpeech() {
        // 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入
        showTip("开始说话");
    }

    @Override
    public void onError(SpeechError error) {
        showTip("onError Code:" + error.getErrorCode());
    }

    @Override
    public void onEvent(int i, int i1, int i2, Bundle bundle) {

    }
};

(4)初始化语法文件构建监听器

/**
 * 构建语法监听器。
 */
private GrammarListener grammarListener = new GrammarListener() {
    @Override
    public void onBuildFinish(String grammarId, SpeechError error) {
        if (error == null) {
            showTip("语法构建成功:" + grammarId);
        } else {
            showTip("语法构建失败,错误码:" + error.getErrorCode());
        }
    }
};

(5)初始化语音识别引擎并完成参数设置

// 初始化识别引擎
mAsr = SpeechRecognizer.createRecognizer(this, mInitListener);
//设置识别引擎参数
setParam();

其中setPatam():

public void setParam() {
    boolean result = true;
    // 清空参数
    mAsr.setParameter(SpeechConstant.PARAMS, null);
    // 设置识别引擎
    mAsr.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
    // 设置本地识别资源
    mAsr.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath());
    // 设置语法构建路径
    mAsr.setParameter(ResourceUtil.GRM_BUILD_PATH, grmPath);
    // 设置返回结果格式
    mAsr.setParameter(SpeechConstant.RESULT_TYPE, mResultType);
    // 设置本地识别使用语法id
    mAsr.setParameter(SpeechConstant.LOCAL_GRAMMAR, "call");
    // 设置识别的门限值
    mAsr.setParameter(SpeechConstant.MIXED_THRESHOLD, "30");

}

(6)完成语法构建

private void  buildGrammer() {
        mLocalGrammar = FucUtil.readFile(this, "call.bnf", "utf-8");
        // 本地-构建语法文件,生成语法id
        ((EditText) findViewById(R.id.isr_text)).setText(mLocalGrammar);
        mContent = new String(mLocalGrammar);
        mAsr.setParameter(SpeechConstant.PARAMS, null);
        // 设置文本编码格式
        mAsr.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8");
        // 设置引擎类型
        mAsr.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
        // 设置语法构建路径
        mAsr.setParameter(ResourceUtil.GRM_BUILD_PATH, grmPath);
        // 设置资源路径
        mAsr.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath());
        ret = mAsr.buildGrammar(GRAMMAR_TYPE_BNF, mContent, grammarListener);
        if (ret != ErrorCode.SUCCESS) {
            showTip("语法构建失败,错误码:" + ret);
        } else {
            showTip("语法构建成功");
        }

    }

(7)开启识别,停止识别,取消识别分别是:

 mAsr.startListening(mRecognizerListener);
 mAsr.stopListening();
 mAsr.cancel();

第十一步:最简单的功能实现代码

所以最后组合起来,我们实现剥离了所有其他功能的只是实现离线命令词识别的代码,CallStepActivity代码如下:

package com.hfut.offlinerecongnizer.activity.activity;

import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.SharedPreferences;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.hfut.offlinerecongnizer.R;
import com.hfut.offlinerecongnizer.activity.util.FucUtil;
import com.hfut.offlinerecongnizer.activity.util.JsonParser;
import com.hfut.offlinerecongnizer.activity.util.XmlParser;
import com.iflytek.cloud.ErrorCode;
import com.iflytek.cloud.GrammarListener;
import com.iflytek.cloud.InitListener;
import com.iflytek.cloud.LexiconListener;
import com.iflytek.cloud.RecognizerListener;
import com.iflytek.cloud.RecognizerResult;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechRecognizer;
import com.iflytek.cloud.util.ContactManager;
import com.iflytek.cloud.util.ResourceUtil;

/**
 * @author why
 * @date 2018-8-27 15:09:38
 */
public class CallStepActivity extends AppCompatActivity implements View.OnClickListener {

    private static String TAG = OffLineTestActivity.class.getSimpleName();
    // 语音识别对象
    private SpeechRecognizer mAsr;
    private Toast mToast;
    // 本地语法文件
    private String mLocalGrammar = null;
    // 本地语法构建路径
    private String grmPath = Environment.getExternalStorageDirectory()
            .getAbsolutePath() + "/msc/call";
    // 返回结果格式,支持:xml,json
    private String mResultType = "json";
    private final String GRAMMAR_TYPE_BNF = "bnf";

    @SuppressLint("ShowToast")
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_call_step);
        initLayout();
        mToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
        // 初始化识别引擎
        mAsr = SpeechRecognizer.createRecognizer(this, mInitListener);

        //构建本地语法
        buildGrammer();
    }

    /**
     * 初始化Layout。
     */
    private void initLayout() {
        findViewById(R.id.isr_recognize).setOnClickListener(this);
        findViewById(R.id.isr_stop).setOnClickListener(this);
        findViewById(R.id.isr_cancel).setOnClickListener(this);
    }

    String mContent;// 语法、词典临时变量
    int ret = 0;// 函数调用返回值

    @Override
    public void onClick(View view) {
        if (null == mAsr) {
            // 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688
            this.showTip("创建对象失败,请确认 libmsc.so 放置正确,\n 且有调用 createUtility 进行初始化");
            return;
        }
        switch (view.getId()) {

            // 开始识别
            case R.id.isr_recognize:
                ((EditText) findViewById(R.id.isr_text)).setText(null);// 清空显示内容
                //设置识别引擎参数
                setParam();
                ret = mAsr.startListening(mRecognizerListener);
                if (ret != ErrorCode.SUCCESS) {
                    showTip("识别失败,错误码: " + ret);
                }
                break;

            // 停止识别
            case R.id.isr_stop:
                mAsr.stopListening();
                showTip("停止识别");
                break;

            // 取消识别
            case R.id.isr_cancel:
                mAsr.cancel();
                showTip("取消识别");
                break;
        }
    }

    /**
     * 初始化监听器。
     */
    private InitListener mInitListener = new InitListener() {

        @Override
        public void onInit(int code) {
            Log.d(TAG, "SpeechRecognizer init() code = " + code);
            if (code != ErrorCode.SUCCESS) {
                showTip("初始化失败,错误码:" + code);
            }
        }
    };


    /**
     * 构建语法监听器。
     */
    private GrammarListener grammarListener = new GrammarListener() {
        @Override
        public void onBuildFinish(String grammarId, SpeechError error) {
            if (error == null) {
                showTip("语法构建成功:" + grammarId);
            } else {
                showTip("语法构建失败,错误码:" + error.getErrorCode());
            }
        }
    };

    /**
     * 识别监听器。
     */
    private RecognizerListener mRecognizerListener = new RecognizerListener() {

        @Override
        public void onVolumeChanged(int volume, byte[] data) {
            showTip("当前正在说话,音量大小:" + volume);
            Log.d(TAG, "返回音频数据:" + data.length);
        }

        @Override
        public void onResult(final RecognizerResult result, boolean isLast) {
            if (null != result && !TextUtils.isEmpty(result.getResultString())) {
                Log.d(TAG, "recognizer result:" + result.getResultString());
                String text = "";
                if (mResultType.equals("json")) {
                    text = JsonParser.parseGrammarResult(result.getResultString(), SpeechConstant.TYPE_LOCAL);
                } else if (mResultType.equals("xml")) {
                    text = XmlParser.parseNluResult(result.getResultString());
                }
                // 显示
                ((EditText) findViewById(R.id.isr_text)).setText(text);
            } else {
                Log.d(TAG, "recognizer result : null");
            }
        }

        @Override
        public void onEndOfSpeech() {
            // 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入
            showTip("结束说话");
        }

        @Override
        public void onBeginOfSpeech() {
            // 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入
            showTip("开始说话");
        }

        @Override
        public void onError(SpeechError error) {
            showTip("onError Code:" + error.getErrorCode());
        }

        @Override
        public void onEvent(int i, int i1, int i2, Bundle bundle) {

        }
    };

    private void showTip(final String str) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mToast.setText(str);
                mToast.show();
            }
        });
    }

    /**
     * 参数设置
     *
     * @param
     * @return
     */
    public void setParam() {
        boolean result = true;
        // 清空参数
        mAsr.setParameter(SpeechConstant.PARAMS, null);
        // 设置识别引擎
        mAsr.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
        // 设置本地识别资源
        mAsr.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath());
        // 设置语法构建路径
        mAsr.setParameter(ResourceUtil.GRM_BUILD_PATH, grmPath);
        // 设置返回结果格式
        mAsr.setParameter(SpeechConstant.RESULT_TYPE, mResultType);
        // 设置本地识别使用语法id
        mAsr.setParameter(SpeechConstant.LOCAL_GRAMMAR, "call");
        // 设置识别的门限值
        mAsr.setParameter(SpeechConstant.MIXED_THRESHOLD, "30");
        // 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
        // 注:AUDIO_FORMAT参数语记需要更新版本才能生效
        mAsr.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
        mAsr.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/asr.wav");
    }

    //获取识别资源路径
    private String getResourcePath() {
        StringBuffer tempBuffer = new StringBuffer();
        //识别通用资源
        tempBuffer.append(ResourceUtil.generateResourcePath(this, ResourceUtil.RESOURCE_TYPE.assets, "asr/common.jet"));
        return tempBuffer.toString();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (null != mAsr) {
            // 退出时释放连接
            mAsr.cancel();
            mAsr.destroy();
        }
    }


    private void buildGrammer() {

        mLocalGrammar = FucUtil.readFile(this, "call.bnf", "utf-8");
        // 本地-构建语法文件,生成语法id
        ((EditText) findViewById(R.id.isr_text)).setText(mLocalGrammar);
        mContent = new String(mLocalGrammar);
        mAsr.setParameter(SpeechConstant.PARAMS, null);
        // 设置文本编码格式
        mAsr.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8");
        // 设置引擎类型
        mAsr.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
        // 设置语法构建路径
        mAsr.setParameter(ResourceUtil.GRM_BUILD_PATH, grmPath);
        //使用8k音频的时候请解开注释
//             mAsr.setParameter(SpeechConstant.SAMPLE_RATE, "8000");
        // 设置资源路径
        mAsr.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath());
        ret = mAsr.buildGrammar(GRAMMAR_TYPE_BNF, mContent, grammarListener);
        if (ret != ErrorCode.SUCCESS) {
            showTip("语法构建失败,错误码:" + ret);
        } else {
            showTip("语法构建成功");
        }

    }
}

activity_call_step.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:padding="10dip" >
    <include layout="@layout/title" />

    <EditText
        android:id="@+id/isr_text"
        android:layout_width="fill_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:gravity="top|left"
        android:textSize="20sp" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dip"
        android:layout_marginBottom="2dip"
        android:gravity="center_horizontal"
        android:orientation="horizontal" >
    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dip"
        android:layout_marginBottom="2dip"
        android:gravity="center_horizontal"
        android:orientation="horizontal" >
        <Button
            android:id="@+id/isr_recognize"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="开始识别"
            android:textSize="20sp" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="2dip"
        android:layout_marginLeft="10dip"
        android:layout_marginRight="10dip"
        android:layout_marginTop="2dip"
        android:gravity="center_horizontal"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/isr_stop"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="停止录音"
            android:textSize="20sp" />

        <Button
            android:id="@+id/isr_cancel"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="取消"
            android:textSize="20sp" />
    </LinearLayout>

</LinearLayout>

如果不出意外的话,运行应该没有任何问题的。至此,最难的最复杂的第三阶段已经结束了,下面就来看看第四阶段的工作任务:

 

第四阶段    提高

第十二步:丰富我们的功能

因为API里面提供了更新词典的功能(从这里我们也可以推出来后面介绍的bnf文件中词槽的定义也可以通过代码来实现):

mAsr.updateLexicon(groupName, mLocalLexicon, lexiconListener);

所以我们就该利用起来,毕竟如果我想修改某一个词槽的定义时,不能每次都是通过编辑bnf文件,然后在运行程序来实现,太麻烦了。这里我通过一个自定义的AlertDialog来实现对词槽的重新赋值,并列的同义词用“,”隔开即可,类似于bnf文件中的  |  符号;下面直接给出OffLineTestActivity代码:

package com.hfut.offlinerecongnizer.activity.activity;

import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.Toast;

import com.hfut.offlinerecongnizer.R;
import com.hfut.offlinerecongnizer.activity.util.FucUtil;
import com.hfut.offlinerecongnizer.activity.util.JsonParser;
import com.hfut.offlinerecongnizer.activity.util.XmlParser;
import com.iflytek.cloud.ErrorCode;
import com.iflytek.cloud.GrammarListener;
import com.iflytek.cloud.InitListener;
import com.iflytek.cloud.LexiconListener;
import com.iflytek.cloud.RecognizerListener;
import com.iflytek.cloud.RecognizerResult;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechRecognizer;
import com.iflytek.cloud.util.ContactManager;
import com.iflytek.cloud.util.ContactManager.ContactListener;
import com.iflytek.cloud.util.ResourceUtil;
import com.iflytek.cloud.util.ResourceUtil.RESOURCE_TYPE;

/**
 * @author why
 * @date 2018-8-27 13:20:58
 */
public class OffLineTestActivity extends AppCompatActivity implements View.OnClickListener {

    private static String TAG = OffLineTestActivity.class.getSimpleName();
    // 语音识别对象
    private SpeechRecognizer mAsr;
    private Toast mToast;
    // 缓存
    //private SharedPreferences mSharedPreferences;
    // 本地语法文件
    private String mLocalGrammar = null;
    // 本地词典
    private String mLocalLexicon = null;
    // 本地语法构建路径
    private String grmPath = Environment.getExternalStorageDirectory()
            .getAbsolutePath() + "/msc/call";
    // 返回结果格式,支持:xml,json
    private String mResultType = "json";
    private final String GRAMMAR_TYPE_BNF = "bnf";

    private String groupName;
    private String groupInfo;

    @SuppressLint("ShowToast")
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_off_line_test);
        initLayout();

        // 初始化识别引擎对象
        mAsr = SpeechRecognizer.createRecognizer(this, mInitListener);
        mToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);

        //构建本地语法
        buildGrammer();
    }

    /**
     * 初始化Layout
     */
    private void initLayout() {
        findViewById(R.id.isr_recognize).setOnClickListener(this);
        findViewById(R.id.isr_lexcion).setOnClickListener(this);
        findViewById(R.id.isr_stop).setOnClickListener(this);
        findViewById(R.id.isr_cancel).setOnClickListener(this);
    }

    String mContent;// 语法、词典临时变量
    int ret = 0;// 函数调用返回值

    @Override
    public void onClick(View view) {
        if (null == mAsr) {
            // 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688
            this.showTip("创建对象失败,请确认 libmsc.so 放置正确,\n 且有调用 createUtility 进行初始化");
            return;
        }
        switch (view.getId()) {
            // 本地-更新词典
            case R.id.isr_lexcion:

                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                LayoutInflater inflater = LayoutInflater.from(this);
                final View v = inflater.inflate(R.layout.user_info_editor, null);
                final EditText wordGroupName = v.findViewById(R.id.enter_word_group_name);
                final EditText wordGroupInfo = v.findViewById(R.id.enter_word_group_info);
                Button cancleButton = v.findViewById(R.id.register_cancle);
                Button confirmButton = v.findViewById(R.id.register_confirm);
                final Dialog dialog = builder.create();
                //点击EditText弹出软键盘
                cancleButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(OffLineTestActivity.this, "取消", Toast.LENGTH_SHORT).show();
                        dialog.cancel();
                    }
                });


                confirmButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                        if (!wordGroupName.getText().toString().equals("")) {
                            groupName= wordGroupName.getText().toString();
                        }
                        if (!wordGroupInfo.getText().toString().equals("")) {
                            groupInfo = wordGroupInfo.getText().toString();
                        }
                        mLocalLexicon=getUpdateInfo(groupInfo);
                        ((EditText) findViewById(R.id.isr_text)).setText(mLocalLexicon);
                        mAsr.setParameter(SpeechConstant.PARAMS, null);
                        // 设置引擎类型
                        mAsr.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
                        // 设置资源路径
                        mAsr.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath());
                        // 设置语法构建路径
                        mAsr.setParameter(ResourceUtil.GRM_BUILD_PATH, grmPath);
                        // 设置语法名称
                        mAsr.setParameter(SpeechConstant.GRAMMAR_LIST, "call");
                        // 设置文本编码格式
                        mAsr.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8");
                        //执行更新操作
                        ret = mAsr.updateLexicon(groupName, mLocalLexicon, lexiconListener);
                        if (ret != ErrorCode.SUCCESS) {
                            showTip("更新词典失败,错误码:" + ret);
                        }
                        else{
                            showTip("更新词典成功" );
                        }
                        dialog.cancel();
                    }
                });

                dialog.show();
                dialog.getWindow().setContentView(v);//自定义布局应该在这里添加,要在dialog.show()的后面
                dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
                dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
                break;
            // 开始识别
            case R.id.isr_recognize:
                //设置识别引擎参数
                setParam();
                ((EditText) findViewById(R.id.isr_text)).setText(null);// 清空显示内容
                ret = mAsr.startListening(mRecognizerListener);
                if (ret != ErrorCode.SUCCESS) {
                    showTip("识别失败,错误码: " + ret);
                }
                break;
            // 停止识别
            case R.id.isr_stop:
                mAsr.stopListening();
                showTip("停止识别");
                break;
            // 取消识别
            case R.id.isr_cancel:
                mAsr.cancel();
                showTip("取消识别");
                break;
        }
    }

    private String getUpdateInfo(String groupInfo) {
        String[] wordList=groupInfo.split(",");
        StringBuilder builder=new StringBuilder();
        for(int i=0;i<wordList.length;i++){
            if(i==wordList.length-1) {
                builder.append(wordList[i] );
                Log.d(TAG, "getUpdateInfo: "+wordList[i]);
            }else{
                builder.append(wordList[i] + "\n");
            }
        }
        return builder.toString();
    }

    /**
     * 初始化监听器。
     */
    private InitListener mInitListener = new InitListener() {

        @Override
        public void onInit(int code) {
            Log.d(TAG, "SpeechRecognizer init() code = " + code);
            if (code != ErrorCode.SUCCESS) {
                showTip("初始化失败,错误码:" + code);
            }
        }
    };

    /**
     * 更新词典监听器。
     */
    private LexiconListener lexiconListener = new LexiconListener() {
        @Override
        public void onLexiconUpdated(String lexiconId, SpeechError error) {
            if (error == null) {
                showTip("词典更新成功");
            } else {
                showTip("词典更新失败,错误码:" + error.getErrorCode());
            }
        }
    };

    /**
     * 构建语法监听器。
     */
    private GrammarListener grammarListener = new GrammarListener() {
        @Override
        public void onBuildFinish(String grammarId, SpeechError error) {
            if (error == null) {
                showTip("语法构建成功:" + grammarId);
            } else {
                showTip("语法构建失败,错误码:" + error.getErrorCode());
            }
        }
    };

    /**
     * 识别监听器。
     */
    private RecognizerListener mRecognizerListener = new RecognizerListener() {

        @Override
        public void onVolumeChanged(int volume, byte[] data) {
            showTip("当前正在说话,音量大小:" + volume);
            Log.d(TAG, "返回音频数据:" + data.length);
        }

        @Override
        public void onResult(final RecognizerResult result, boolean isLast) {
            if (null != result && !TextUtils.isEmpty(result.getResultString())) {
                Log.d(TAG, "recognizer result:" + result.getResultString());
                String text = "";
                if (mResultType.equals("json")) {
                    text = JsonParser.parseGrammarResult(result.getResultString(), SpeechConstant.TYPE_LOCAL);
                } else if (mResultType.equals("xml")) {
                    text = XmlParser.parseNluResult(result.getResultString());
                }
                // 显示
                ((EditText) findViewById(R.id.isr_text)).setText(text);
            } else {
                Log.d(TAG, "recognizer result : null");
            }
        }

        @Override
        public void onEndOfSpeech() {
            // 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入
            showTip("结束说话");
        }

        @Override
        public void onBeginOfSpeech() {
            // 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入
            showTip("开始说话");
        }

        @Override
        public void onError(SpeechError error) {
            showTip("onError Code:" + error.getErrorCode());
        }

        @Override
        public void onEvent(int i, int i1, int i2, Bundle bundle) {

        }
    };

    private void showTip(final String str) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mToast.setText(str);
                mToast.show();
            }
        });
    }

    /**
     * 参数设置
     *
     * @param
     * @return
     */
    public void setParam() {
        // 清空参数
        mAsr.setParameter(SpeechConstant.PARAMS, null);
        // 设置识别引擎
        mAsr.setParameter(SpeechConstant.ENGINE_TYPE,SpeechConstant.TYPE_LOCAL);
        // 设置本地识别资源
        mAsr.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath());
        // 设置语法构建路径
        mAsr.setParameter(ResourceUtil.GRM_BUILD_PATH, grmPath);
        // 设置返回结果格式
        mAsr.setParameter(SpeechConstant.RESULT_TYPE, mResultType);
        // 设置本地识别使用语法id
        mAsr.setParameter(SpeechConstant.LOCAL_GRAMMAR, "call");
        // 设置识别的门限值
        mAsr.setParameter(SpeechConstant.MIXED_THRESHOLD, "30");
        // 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
        // 注:AUDIO_FORMAT参数语记需要更新版本才能生效
        mAsr.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
        mAsr.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/asr.wav");

    }

    //获取识别资源路径
    private String getResourcePath() {
        StringBuffer tempBuffer = new StringBuffer();
        //识别通用资源
        tempBuffer.append(ResourceUtil.generateResourcePath(this, RESOURCE_TYPE.assets, "asr/common.jet"));
        return tempBuffer.toString();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (null != mAsr) {
            // 退出时释放连接
            mAsr.cancel();
            mAsr.destroy();
        }
    }


    private boolean buildGrammer() {

             mLocalGrammar = FucUtil.readFile(this, "call.bnf", "utf-8");
            // 本地-构建语法文件,生成语法id
            ((EditText) findViewById(R.id.isr_text)).setText(mLocalGrammar);
            mContent = new String(mLocalGrammar);
            mAsr.setParameter(SpeechConstant.PARAMS, null);
            // 设置文本编码格式
            mAsr.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8");
            // 设置引擎类型
            mAsr.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
            // 设置语法构建路径
            mAsr.setParameter(ResourceUtil.GRM_BUILD_PATH, grmPath);
            //使用8k音频的时候请解开注释
//             mAsr.setParameter(SpeechConstant.SAMPLE_RATE, "8000");
            // 设置资源路径
            mAsr.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath());
            ret = mAsr.buildGrammar(GRAMMAR_TYPE_BNF, mContent, grammarListener);
            if (ret != ErrorCode.SUCCESS) {
                showTip("语法构建失败,错误码:" + ret);
            }
            else{
                showTip("语法构建成功");
            }
        return true;
    }
}

activity_off_line_test.xml代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:padding="10dip" >
    <include layout="@layout/title" />

    <EditText
        android:id="@+id/isr_text"
        android:layout_width="fill_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:gravity="top|left"
        android:textSize="20sp" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dip"
        android:layout_marginBottom="2dip"
        android:gravity="center_horizontal"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/isr_recognize"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="开始识别"
            android:textSize="20sp" />

        <Button
            android:id="@+id/isr_lexcion"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="更新词典"
            android:textSize="20sp"
            android:enabled="true" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="2dip"
        android:layout_marginLeft="10dip"
        android:layout_marginRight="10dip"
        android:layout_marginTop="2dip"
        android:gravity="center_horizontal"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/isr_stop"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="停止录音"
            android:textSize="20sp" />

        <Button
            android:id="@+id/isr_cancel"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="取消"
            android:textSize="20sp" />
    </LinearLayout>

</LinearLayout>

 

word_info_editor.xml代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#B0C4DE"
    android:orientation="vertical">

    <TextView
        android:layout_marginLeft="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="请编辑更新信息:"
        android:textColor="#000000"
        android:textSize="30dp" />

    <LinearLayout
        android:layout_marginLeft="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="词组名称:"
            android:textSize="20dp" />

        <EditText
            android:id="@+id/enter_word_group_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30dp"
            android:hint="请输入词组名称" />

    </LinearLayout>

    <LinearLayout
        android:layout_marginLeft="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="词组信息:"
            android:textSize="20dp" />

        <EditText
            android:id="@+id/enter_word_group_info"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30dp"
            android:hint="请编写词组信息" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/register_cancle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="350dp"
            android:text="取消" />

        <Button
            android:id="@+id/register_confirm"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:text="确定" />

    </LinearLayout>

</LinearLayout>

其中还有一个所有布局都用到的title.xml代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:layout_gravity="top"
  android:gravity="center">
  <TextView
          android:text="@string/app_name"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:gravity="center"
          android:textSize="30sp"
          android:layout_margin="10dip"
    />
</LinearLayout>

上面介绍的离线命令词识别都是基于我们自己编辑的bnf文件中的规则来识别,下面给出一个文件示例:

#BNF+IAT 1.0 UTF-8;
!grammar call;

//通用词槽
!slot <want>;
!slot <deal>;
!slot <how>;

//联系相关词槽声明
!slot <contact>;//联系人
!slot <callPhone>;//联系方式
!slot <callTo>;//联系动作

//巡游相关词槽声明
!slot <destination>;//巡游点
!slot <goTo>;//去
!slot <goToPre>;//准备去

/*
专业语料相关
 */
 //办卡业务
 !slot <cardType>;

 //公积金业务
 !slot <percent>;
 !slot <wagesDeal>;
 !slot <wages>;

!start <commands>;
<commands>:<callRule>|<guideRule>|<dealCardRule>|<wagesDealRule>;

//通用语料
<want>:我想|我要|我准备;
<how>:如何|怎么|怎样;
<deal>:办理|解决|处理;

//测试语料
<contact>:黄老板|王华洋|齐带华|火警!id(119);
<callPhone>:打电话|发微信|发短信;
<callTo>:给;
<callRule>:<callTo><contact><callPhone>|<callPhone><callTo><contact>;//联系语料相关规则

//巡游语料
<destination>:卫生间|饮水机|现金柜台|取款机|充电器|大堂经理;
<goTo>:去|到|找;
<goToPre>:带我|请带我|我想;
<guideRule>:[<goToPre>]<goTo><destination>;//巡游语料相关规则

//办卡语料
<cardType>:卡|信用卡|儿童卡|储蓄卡;//卡片类型
<dealCardRule>:[<want>]<deal><cardType>;

//公积金业务
<percent>:比例;//公积金比例
<wagesDeal>:转移|提取;//处理公积金
<wages>:公积金;
<wagesDealRule>:[<how>]<wagesDeal><wages>;


具体的编辑规则请参考bnf文档编辑指南,后续我还会对这个编辑规则进行介绍,具体就介绍到这里。

注:欢迎扫码关注

 

2018-10-15 20:37:57 TZYZlpx123456789 阅读数 3270
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5922 人正在学习 去看看 杨波

最近Boss让我开发一个离线语音听写功能 , 可是我发现科大讯飞的离线版本下架了 , 也看了很多地方如百度语音.......

      都没发现有离线版本 , 然而今天突然发现科大讯飞的离线听写又上架了  这就让我很开心 . 我就立马把它下载了下来,集成到我的项目中去 . 然后经过一个小时的努力 , 我把它简单的封装了一下 , 然后很方便的就可以到处调用了 !

      下面我就把我的那个类发上来你们可以直接拷过去用 . 具体有哪些不懂 , 里面传的东西不清楚可以在下方留言 , 我会给你们讲解.

    emmmm~很久没发博客了 , 现在给大家带来些福利 .

下面就是这个类:

 

public class LatYuYin {
    private static String TAG = "IatDemo";
    // 语音听写对象
    private SpeechRecognizer mIat;
    // 语音听写UI
    private RecognizerDialog mIatDialog;

    // 用HashMap存储听写结果
    private HashMap<String, String> mIatResults = new LinkedHashMap<String, String>();

    private Toast mToast;

    private Context mContext;
    private SharedPreferences mSharedPreferences;
    private boolean mTranslateEnable = false;
    private EditText mEditText;


    public LatYuYin(Context context) {
        mContext = context;
        // 初始化识别无UI识别对象
        // 使用SpeechRecognizer对象,可根据回调消息自定义界面;
        mIat = SpeechRecognizer.createRecognizer(mContext, mInitListener);
        // 初始化听写Dialog,如果只使用有UI听写功能,无需创建SpeechRecognizer
        // 使用UI听写功能,请根据sdk文件目录下的notice.txt,放置布局文件和图片资源
        mIatDialog = new RecognizerDialog(mContext, mInitListener);
        mToast = Toast.makeText(context, "", Toast.LENGTH_SHORT);
        mSharedPreferences = mContext.getSharedPreferences(IatSettings.PREFER_NAME, Activity.MODE_PRIVATE);
    }

    public void cancelmLat() {
        mIat.cancel();
        showTip("取消听写");
    }

    public void stopmLat() {
        mIat.stopListening();
        showTip("停止听写");
    }

    int ret = 0;// 函数调用返回值

    //开始听写
    public void startmLat(View view) {
        mEditText = (EditText) view.findViewById(R.id.activity_chat_et_text);
        mEditText.setText(null);
        mIatResults.clear();
        // 设置参数
        setParam();
        boolean isShowDialog = mSharedPreferences.getBoolean(mContext.getString(R.string.pref_key_iat_show), true);
        if (isShowDialog) {
            // 显示听写对话框
            mIatDialog.setListener(mRecognizerDialogListener);
            mIatDialog.show();
            showTip(mContext.getString(R.string.text_begin));
        } else {
            // 不显示听写对话框
            ret = mIat.startListening(mRecognizerListener);
            if (ret != ErrorCode.SUCCESS) {
                showTip("听写失败,错误码:" + ret);
            } else {
                showTip(mContext.getString(R.string.text_begin));
            }
        }

    }

    private void showTip(final String str) {
        Toast.makeText(mContext, str, Toast.LENGTH_SHORT).show();
    }

    /**
     * 参数设置
     *
     * @return
     */
    public void setParam() {
        // 清空参数
        mIat.setParameter(SpeechConstant.PARAMS, null);
        String lag = mSharedPreferences.getString("iat_language_preference", "mandarin");
        // 设置引擎
        mIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
        // 设置返回结果格式
        mIat.setParameter(SpeechConstant.RESULT_TYPE, "json");

        this.mTranslateEnable = mSharedPreferences.getBoolean(mContext.getString(R.string.pref_key_translate), false);
        if (mTranslateEnable) {
            Log.i(TAG, "translate enable");
            mIat.setParameter(SpeechConstant.ASR_SCH, "1");
            mIat.setParameter(SpeechConstant.ADD_CAP, "translate");
            mIat.setParameter(SpeechConstant.TRS_SRC, "its");
        }

        if (lag.equals("en_us")) {
            // 设置语言
            mIat.setParameter(SpeechConstant.LANGUAGE, "en_us");
            mIat.setParameter(SpeechConstant.ACCENT, null);


            if (mTranslateEnable) {
                mIat.setParameter(SpeechConstant.ORI_LANG, "en");
                mIat.setParameter(SpeechConstant.TRANS_LANG, "cn");
            }
        } else {
            // 设置语言
            mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
            // 设置语言区域
            mIat.setParameter(SpeechConstant.ACCENT, lag);

            if (mTranslateEnable) {
                mIat.setParameter(SpeechConstant.ORI_LANG, "cn");
                mIat.setParameter(SpeechConstant.TRANS_LANG, "en");
            }
        }

        // 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理
        mIat.setParameter(SpeechConstant.VAD_BOS, mSharedPreferences.getString("iat_vadbos_preference", "4000"));

        // 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音
        mIat.setParameter(SpeechConstant.VAD_EOS, mSharedPreferences.getString("iat_vadeos_preference", "1000"));

        // 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点
        mIat.setParameter(SpeechConstant.ASR_PTT, mSharedPreferences.getString("iat_punc_preference", "1"));

        // 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
        mIat.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
        mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/iat.wav");
        //此处engineType为“local”
        mIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
        if (SpeechConstant.TYPE_LOCAL.equals(SpeechConstant.TYPE_LOCAL)) {
            // 设置本地识别资源
            mIat.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath());
        }
    }

    private String getResourcePath() {
        StringBuffer tempBuffer = new StringBuffer();
        //识别通用资源
        tempBuffer.append(ResourceUtil.generateResourcePath(mContext, ResourceUtil.RESOURCE_TYPE.assets, "iat/common.jet"));
        tempBuffer.append(";");
        tempBuffer.append(ResourceUtil.generateResourcePath(mContext, ResourceUtil.RESOURCE_TYPE.assets, "iat/sms_16k.jet"));
        //识别8k资源-使用8k的时候请解开注释
        return tempBuffer.toString();
    }

    /**
     * 初始化监听器。
     */
    private InitListener mInitListener = new InitListener() {

        @Override
        public void onInit(int code) {
            Log.d(TAG, "SpeechRecognizer init() code = " + code);
            if (code != ErrorCode.SUCCESS) {
                showTip("初始化失败,错误码:" + code);
            }
        }
    };

    /**
     * 听写UI监听器
     */
    private RecognizerDialogListener mRecognizerDialogListener = new RecognizerDialogListener() {
        public void onResult(RecognizerResult results, boolean isLast) {
            Log.d(TAG, "recognizer result:" + results.getResultString());
            if (mTranslateEnable) {
                printTransResult(results);
            } else {
                String text = JsonParser.parseIatResult(results.getResultString());
                mEditText.append(text);
                mEditText.setSelection(mEditText.length());
            }
        }


        /**
         * 识别回调错误.
         */
        public void onError(SpeechError error) {
            if (mTranslateEnable && error.getErrorCode() == 14002) {
                showTip(error.getPlainDescription(true) + "\n请确认是否已开通翻译功能");
            } else {
                showTip(error.getPlainDescription(true));
            }
        }

    };
    /**
     * 听写监听器。
     */
    private RecognizerListener mRecognizerListener = new RecognizerListener() {

        @Override
        public void onBeginOfSpeech() {
            // 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入
            showTip("开始说话");
        }

        @Override
        public void onError(SpeechError error) {
            // Tips:
            // 错误码:10118(您没有说话),可能是录音机权限被禁,需要提示用户打开应用的录音权限。
            if (mTranslateEnable && error.getErrorCode() == 14002) {
                showTip(error.getPlainDescription(true) + "\n请确认是否已开通翻译功能");
            } else {
                showTip(error.getPlainDescription(true));
            }
        }

        @Override
        public void onEndOfSpeech() {
            // 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入
            showTip("结束说话");
        }

        @Override
        public void onResult(RecognizerResult results, boolean isLast) {
            if (mTranslateEnable) {
                printTransResult(results);
            } else {
                String text = JsonParser.parseIatResult(results.getResultString());
            }

            if (isLast) {
                //TODO 最后的结果
            }
        }

        @Override
        public void onVolumeChanged(int volume, byte[] data) {
            showTip("当前正在说话,音量大小:" + volume);
            Log.d(TAG, "返回音频数据:" + data.length);
        }

        @Override
        public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
            // 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
            // 若使用本地能力,会话id为null
            // if (SpeechEvent.EVENT_SESSION_ID == eventType) {
            //    String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);
            //    Log.d(TAG, "session id =" + sid);
            // }
        }
    };

    private void printTransResult(RecognizerResult results) {
        String trans = JsonParser.parseTransResult(results.getResultString(), "dst");
        String oris = JsonParser.parseTransResult(results.getResultString(), "src");

        if (TextUtils.isEmpty(trans) || TextUtils.isEmpty(oris)) {
            showTip("解析结果失败,请确认是否已开通翻译功能。");
        }

    }
}

以上就是这个类的代码 , 基本你们拷过去就能用 .  同时你们附加了这两个类你们需要科大讯飞里面的开发包里面拷出来.

然后具体调用很简单如下:

以上就是调用的代码简单吧  然后你们不知道还要考哪些东西就把这几个文件都拷进去 , 或者看官方文档

好了  .........基本就是这些 , 如果还是没成功就在底下留言 , 我看到我会很快的回复你的!

          希望大家工作顺利!!!!!!!

2018-09-28 17:25:03 weixin_43112746 阅读数 2911
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5922 人正在学习 去看看 杨波

上一篇的地址手把手的操作——用java调用科大讯飞的离线语音识别dll实现离线识别(JNA实现)(一)

上一篇讲到了最难的地方,参数的转换,这里单独写出来
**

三、参数的转换(难点)

**
注:本文是以讯飞提供的C语言例子作为模板改写,语音来源于文件
1、先分析提供的例子
本人使用的是VS2010
下载链接链接:https://pan.baidu.com/s/1CZX3k6nhsbLkuzB3mocyww 提取码:6r5g
2.45G大小,需要安装一段时间,因为用JNA只用看,这个版本够了
【为了给我一样对C/C++不了解的初阶,大神轻喷】

运行之后
【文件】–【打开】–【项目/解决方案】–自己找文件位置–【asr_offline_sample.vcxproj】
在这里插入图片描述

同样的方法打开头文件
【文件】–【打开】–【文件】–自己找文件位置–【头文件.h结尾的】
在这里插入图片描述

主要看例子asr_sample.c
在java中主要就是将这个文件进行改写,所以要先看懂
我自己看了很久,讲下结构吧:
上面几个是各项功能的函数,最下面是主函数main,跟java的结构很像吧
几大主要功能
【登录】
【建立语法】
【建立词典】
【语音识别】
【退出】

看上去很简单啊,接下来就是一个一个在java中实现
我先建立了一个lib接口,里面放调用的东西
(先把动态库加载进来)

public interface VoiceLib extends Library{
	VoiceLib instance = (VoiceLib)Native.loadLibrary("msc_x64", VoiceLib.class);
	}

#1登录
这个功能在(一)中已经有例子了,这里重写一遍就好

public interface VoiceLib extends Library{
	VoiceLib instance = (VoiceLib)Native.loadLibrary("msc_x64", VoiceLib.class);
	int MSPLogin(String usr, String pwd, String params);
	}

新建主类xMsc
改写登录功能

	public static void main(String[] args) {
		String login_config = "appid=5ba4***"; //登录参数
		UserData asr_data=null ;
		int ret             = 0;
		ret = VoiceLib.instance.MSPLogin(null, null, login_config); 
		//第一个参数为用户名,第二个参数为密码,传null即可,第三个参数是登录参数
		if (MSP_SUCCESS!= ret) {
			System.out.println("登录失败!");
			exit();
		}
		}

上面先改写下几个常量:

public class xMsc {

public static final int SAMPLE_RATE_16K = 16000;
	public static final int SAMPLE_RATE_8K = 8000;
	public static final int MAX_GRAMMARID_LEN = 32;
	public static final int MAX_PARAMS_LEN = 1024;

	public static final int MSP_SUCCESS = 0;
	public static final int MSP_FAILED = 1;
	private static int MSP_AUDIO_SAMPLE_FIRS = 1;
	private static int MSP_AUDIO_SAMPLE_CONTINUE = 2;
	private static int MSP_EP_LOOKING_FOR_SPEECH = 0;
	private static int MSP_REC_STATUS_INCOMPLETE = 2;
	private static int MSP_EP_AFTER_SPEECH = 3;
	private static int MSP_AUDIO_SAMPLE_LAST = 4;
	private static int MSP_REC_STATUS_COMPLETE = 5;
	
	private static File f_pcm 		 = null;
	private static byte[] pcm_data	 =null;
	private static int errcode		 = -1;
	private static String session_id = "";
	private static IntByReference ep_status  = new IntByReference(MSP_EP_LOOKING_FOR_SPEECH);
	private static IntByReference rec_status = new IntByReference(MSP_REC_STATUS_INCOMPLETE);
	private static IntByReference rss_status1 = new IntByReference(MSP_AUDIO_SAMPLE_FIRS);
	private static IntByReference err 		  = new IntByReference(errcode);

	public static final String ASR_RES_PATH = "fo|common.jet"; // 离线语法识别资源路径
	public static final String GRM_BUILD_PATH = "./msc/GrmBuilld_x64"; // 构建离线语法识别网络生成数据保存路径
	public static final String GRM_FILE = "./source/szx.bnf"; // 构建离线识别语法网络所用的语法文件
	public static final String LEX_NAME = "contact"; // 更新离线识别语法的contact槽(语法文件为此示例中使用的call.bnf)
	


里面的路径必须要跟我一样,尤其是 ASR_RES_PATH ,前面必须加"fo|"
有几个文件要拷贝过来啊,语法文件之类,我建立了一个source文件夹,都放里面了
现在的结构
在这里插入图片描述
【先一口气把main里面的全部改写完吧!】

public static void main(String[] args) {
		String login_config = "appid=5ba4*****"; //登录参数,要改成自己的
		asr_data = new UserData();
		int ret = 0;
		ret = VoiceLib.instance.MSPLogin(null, null, login_config); // 第一个参数为用户名,第二个参数为密码,传null即可,第三个参数是登录参数
		if (MSP_SUCCESS != ret) {
			System.out.println("登录失败!");
			exit();
		}
		System.out.println("登录成功!");
		System.out.println("构建离线识别语法网络...\n");
		ret = build_grammar(asr_data); // 第一次使用某语法进行识别,需要先构建语法网络,获取语法ID,之后使用此语法进行识别,无需再次构建

		if (MSP_SUCCESS != ret) {
			System.out.println("构建语法调用失败!\n");
			exit();
		}
		while (MSP_FAILED != asr_data.build_fini) {
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		if (MSP_SUCCESS != asr_data.errcode) {
			exit();
		}

		System.out.println("离线识别语法网络构建完成,开始识别...\n");
		ret = run_asr(asr_data);
		if (MSP_SUCCESS != ret) {
			System.out.println("离线语法识别出错: \n");
			exit();
		}
	}

【注】词典我删掉了,和构建语法一样,需要的自己写喽

¥¥¥¥¥退出功能先封装一下¥¥¥¥¥¥

public static void exit(){
		VoiceLib.instance.MSPLogout();
		System.out.println("请按任意键退出...\n");
		
	}

【再来一口气】把要用的函数全部封装到VoiceLib中,等下直接调用


import com.sun.jna.Library;

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.szxinfo.recognizer.CallbackUtil.GrammarCallBack;



public interface VoiceLib extends Library{
	String filePath = "mscx64";
	VoiceLib instance = (VoiceLib)Native.loadLibrary(filePath, VoiceLib.class);
	
	int MSPLogin(String usr, String pwd, String params);
	
    int QISRBuildGrammar (String cs2, String grm_content, int grm_cnt_len, String grm_build_params, GrammarCallBack grammarCallBack, Pointer pointer);
    
    String QISRSessionBegin(String grammarList, String asr_params, IntByReference errcode);
    
    int QISRAudioWrite(String session_id, byte[] every, int waveLen, int audioStatus, IntByReference ep_status1, IntByReference rec_status1);
	
    String QISRGetResult(String session_id, IntByReference rss_status1, int waitTime, IntByReference err);
	
    int  QISRSessionEnd(String sessionID, String hints);
    
   // int GrammarCallBack(int ecode, String info, UserData udata);
    
	
    int  MSPLogout();

	



	
	

	
	
	
}

【这里的参数我都已经改好啦,等下到具体功能再解释】

然后写语法的

//重要分割线*********************************//
在讯飞提供的例子中,有一个回调函数和一个构建语法函数,都要改写,怎么办呢?
回调函数我封装到一个Util中(后面的词典回调一起了)

package com.xinzhi;


import com.sun.jna.Callback;
import com.sun.jna.Pointer;

public  class CallbackUtil {	

	//语法回调函数的接口
	 public static interface GrammarCallBack extends Callback{
		 
	    	int  build_grm_cb(int ecode, String info, Pointer udata);
	    }
	 //语法回调函数的实现
	 public static class GrammarCallBack_Realize implements GrammarCallBack{
		 
		@Override
		public int build_grm_cb(int ecode, String info,Pointer udata) {
			UserData g_udata=Util.fromPointer(udata, 0);
			if (null != g_udata) {
				
				xMsc.asr_data.build_fini = com.szxinfo.recognizer.xMsc.MSP_FAILED;
				xMsc.asr_data.errcode = ecode;
			} 
			if (com.szxinfo.recognizer.xMsc.MSP_SUCCESS == ecode && null != info) {
				System.out.println("构建语法成功! 语法ID:"+info);
				if (null != g_udata) {
					xMsc.asr_data.grammar_id=info;
				}
			}
			else {
				System.out.println("构建语法失败!错误代码:"+ ecode);
			}			
			return 0;
		}
		 
	 }
	 
}


里面还有错误啊,回头来改!
【终板ps】这里面的错误已经全部修改完成

其中void,我使用Pointer来模拟*

再来写语法的

//构建离线识别语法网络
	@SuppressWarnings("finally")
	public static int build_grammar(UserData asr_data2) {
		File grm_file = null;
		String grm_content = null;
		int grm_cnt_len = 0;
		String grm_build_params = null;
		int ret = MSP_SUCCESS;

		grm_file = new File(GRM_FILE);

		if (!grm_file.exists()) {
			System.out.println("打开语法文件失败!");
			return MSP_FAILED;
		}

		// 按照字节读取语法文件,并将字节添加到grm_content中
		try {
			FileInputStream in = new FileInputStream(grm_file);
			InputStreamReader input = new InputStreamReader(in);
			char[] cbuf = new char[(int) grm_file.length()];
			@SuppressWarnings("unused")
			int data=0;
			data = input.read(cbuf);
			// 构建语法的内容传的是字符串,将char[]转成String
			StringBuilder builder = new StringBuilder();
			for (char c : cbuf) {
				builder.append(c);
			}
			grm_content = builder.toString();
			// 释放资源
			in.close();
			input.close();
			grm_file = null;

			// 初始化构建语法参数
			grm_build_params = "engine_type=local,asr_res_path=" + ASR_RES_PATH 
					+ ",sample_rate=" + SAMPLE_RATE_16K
					+ ",grm_build_path=" + GRM_BUILD_PATH + ",";

			// 实例化回调函数对象
			CallbackUtil.GrammarCallBack callback = new CallbackUtil.GrammarCallBack_Realize();
			// 语法长度为内容的长度
			grm_cnt_len = grm_content.length();
			// 传入参数,构建语法
			ret = VoiceLib.instance.QISRBuildGrammar("bnf", grm_content, grm_cnt_len, grm_build_params, callback,
					asr_data2.getPointer());

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			return ret;
		}
	}

QISRBuildGrammar(“bnf”, grm_content, grm_cnt_len, grm_build_params, callback, udata1);
主要是给这个函数配参数,现在还没有测通,有测通的告诉我一声!

【终板ps】已经全部测通!这里得到的语法id必须是call,如果你是一串字符串,说明读的不对!

解释:
第一个 是文件类型,不用说固定的
第二个 是语法内容,我用了文件缓冲读字符,按行读取字符,注意用的是StringBuilder,因为要拼接,你直接用String卡死了不要找我哈。
第三个 是文件长度,length一下就好
第四个 是参数,这里初始化直接赋值就好,注意字符串凭借的引号问题
第五个就是回调函数
第六个是那个无定向指针

现在报参数无效,错误码23002,估计是后面两个还不行,需要继续调整,知道的指导一下,我继续调试了!
【终板PS】没有正确的读call.bnf,终板更新已解决

最后是识别,这里坑太多,不会的下面留言

// 进行离线语法识别
	public static int run_asr(UserData udata) {
		String asr_params;
		String rec_rslt = null;
		String asr_audiof = null;

		long pcm_count = 0;
		long pcm_size;
		@SuppressWarnings("unused")
		int last_audio = 0;
		int aud_stat = 1;

		asr_audiof = get_audio_file();

		f_pcm = new File(asr_audiof);

		if (!f_pcm.exists()) {
			System.out.println("打开语音文件失败!\n");
			run_error();
		}
		pcm_size = f_pcm.length();

		try {
			@SuppressWarnings("resource")
			InputStream in = new FileInputStream(f_pcm);
			pcm_data = new byte[(int) pcm_size];
			@SuppressWarnings("unused")
			int n = 0;
			n = in.read(pcm_data);

		} catch (Exception e1) {
			e1.printStackTrace();
		}

		// 离线语法识别参数设置
		asr_params = "engine_type=local,asr_res_path=" + ASR_RES_PATH + ",sample_rate=" + SAMPLE_RATE_16K
				+ ",grm_build_path=" + GRM_BUILD_PATH + ",local_grammar=" + udata.grammar_id
				+ ",result_type=plain,result_encoding=GB2312,";

		session_id = VoiceLib.instance.QISRSessionBegin(null, asr_params, err);
		if (null == session_id) {
			run_error();
		}

		System.out.println("开始识别...\n");

		while (true) {
			int len = 6400;

			if (pcm_size < 12800) {
				len = (int) pcm_size;
				last_audio = 1;
			}
			aud_stat = MSP_AUDIO_SAMPLE_CONTINUE;// 0x02
			if (0 == pcm_count) {
				aud_stat = MSP_AUDIO_SAMPLE_FIRS;// 0x01
			}
			if (len <= 0) {
				break;
			}
			System.out.print(">");
		

			errcode = VoiceLib.instance.QISRAudioWrite(session_id, pcm_data, len, aud_stat, ep_status, rec_status);

			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}


			if (MSP_SUCCESS != errcode) {
				run_error();
				break;
			}
			pcm_count += (long) len;
			pcm_size -= (long) len;

			// 检测到音频结束
			if (MSP_EP_AFTER_SPEECH == ep_status.getValue()) {
				break;
			}
		}

		// 主动点击音频结束
		VoiceLib.instance.QISRAudioWrite(session_id, null, 0, MSP_AUDIO_SAMPLE_LAST, ep_status, rec_status);
		pcm_data = null;


		// 获取识别结果
		while (MSP_REC_STATUS_COMPLETE != rss_status1.getValue() && MSP_SUCCESS == err.getValue()) {

			rec_rslt = VoiceLib.instance.QISRGetResult(session_id, rss_status1, 0, err);
		}

		System.out.println("\n识别结束:\n");
		System.out.println("=============================================================\n");
		if (null != rec_rslt) {

			String str = rec_rslt.substring(rec_rslt.indexOf("input=") + 6);
			System.out.println("识别结果为:\n" + str);
		} else {
			System.out.println("没有识别结果!");
			System.out.println("=============================================================\n");
			run_exit();
		}
		return MSP_SUCCESS;
	}

附加几个小方法:

	public static void run_error() {
		if (null != pcm_data) {
			pcm_data = null;
		}
		if (null != f_pcm) {
			f_pcm = null;
		}
	}

	public static int run_exit() {
		VoiceLib.instance.QISRSessionEnd(session_id, null);
		return errcode;
	}

至此,全部完工!
识别结果如下:
在这里插入图片描述

**

代码是plain解析的,截图是xml的,改下参数就好,编码别动,GB2312,不要utf-8

**

觉得不错的就点个赞吧!

--------------------------------2018年12月19日补充-------------Util中的内容

import com.sun.jna.Pointer;

public class Util {
	 public static UserData fromPointer(Pointer pointer, int offset) {
		  UserData inst = new UserData();
		  inst.build_fini = pointer.getInt(offset);
		  offset += 4;
		  inst.update_fini = pointer.getInt(offset);
		  offset += 4;
		  inst.errcode = pointer.getInt(offset);
		  offset+=4;
		  inst.grammar_id=pointer.getString(offset);
		  return inst;
		}

}

--------2018年12月20日补充-------------UserData中的内容

package com.xinzhi;

import com.sun.jna.Structure;


public class UserData extends Structure{
	public 	int     build_fini;    		 //标识语法构建是否完成 
	public   int     update_fini;   		//标识更新词典是否完成
	public   int     errcode; 	   //记录语法构建或更新词典回调错误码
	public 	String  grammar_id;   //保存语法构建返回的语法ID	


}
*************************************

¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥主要识别类¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥

package com.xinzhi;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.sql.Ref;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.sun.jna.StringArray;
import com.sun.jna.ptr.ByteByReference;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;
import com.xinzhi.CallbackUtil.GrammarCallBack;

public class xMsc {

	public static final int SAMPLE_RATE_16K = 16000;
	public static final int SAMPLE_RATE_8K = 8000;
	public static final int MAX_GRAMMARID_LEN = 32;
	public static final int MAX_PARAMS_LEN = 1024;

	public static final int MSP_SUCCESS = 0;
	public static final int MSP_FAILED = 1;
	private static int MSP_AUDIO_SAMPLE_FIRS = 0x01;
	private static int MSP_AUDIO_SAMPLE_CONTINUE =0x02;
	private static int MSP_EP_LOOKING_FOR_SPEECH = 0;
	private static int MSP_REC_STATUS_INCOMPLETE = 2;
	private static int MSP_EP_AFTER_SPEECH = 3;
	private static int MSP_AUDIO_SAMPLE_LAST = 4;
	private static int MSP_REC_STATUS_COMPLETE = 5;

	public static final String ASR_RES_PATH   = "fo|D:\\msc\\common.jet"; // 离线语法识别资源路径
	public static final String GRM_BUILD_PATH = "D:\\msc\\GrmBuilld_x64"; // 构建离线语法识别网络生成数据保存路径
	public static final String GRM_FILE 	  = xMsc.class.getClassLoader().getResource("call.bnf").getPath(); // 构建离线识别语法网络所用的语法文件
	public static final String LEX_NAME 	  = "contact"; // 更新离线识别语法的contact槽(语法文件为此示例中使用的call.bnf)

	
	// 选择进行离线语法识别的语音文件
	public static String get_audio_file()  {
		int key = 0;
		// while(key != 27) //按Esc则退出
		// {
		System.out.println("请选择音频文件:\n");
		System.out.println("1.打电话给丁伟\n");
		System.out.println("2.打电话给黄辣椒\n");
	/*	Scanner scan = new Scanner(System.in);
		key = scan.nextInt();
		 
		switch (key) {
		case 1:
			System.out.println("\n1.打电话给丁伟\n");
			return "./source/ddhgdw.pcm";
		case 2:
			System.out.println("\n2.打电话给黄辣椒\n");
			return "./source/ddhghlj.pcm";
		// default:
		// continue;
		}*/
		// }
		// exit(0);
		return "source/ddhgdw.pcm";
	}
	

	// 构建离线识别语法网络
	public static int build_grammar(UserData asr_data2) {
		File grm_file = null;
		String grm_content = null;
		int grm_cnt_len = 0;
		String grm_build_params = null;
		int ret = MSP_SUCCESS;

		grm_file = new File(GRM_FILE);
		if (!grm_file.exists()) {
			System.out.println("打开语法文件失败!");
			return MSP_FAILED;
		}

		grm_cnt_len = (int) grm_file.length();
		// 按照字节读取语法文件,并将字节添加到grm_content中
		
		try {
			FileInputStream	in = new FileInputStream(grm_file);
			/*BufferedInputStream bis = new BufferedInputStream(in);
			byte[] buf = new byte[grm_cnt_len];
			bis.read(buf, 0, grm_cnt_len);*/
			//buf[grm_cnt_len]='\0';
			
			/*	Charset cs = Charset.forName ("UTF-8");
				ByteBuffer bb = ByteBuffer.allocate (buf.length); 
				bb.put (buf); 
				bb.flip (); 
				CharBuffer cb = cs.decode (bb);
				char[] getChars = cb.array();*/
				
		//	BufferedReader buf2 = new BufferedReader(new FileReader(grm_file));
			InputStreamReader input=new InputStreamReader(in);
			//StringBuilder builder = new StringBuilder();
			int data;
			char[] cbuf=new char[(int) grm_file.length()];
			data = input.read(cbuf);
				
			System.out.println("data="+data);
			StringBuilder builder=new StringBuilder();
			for(char c:cbuf) {
				builder.append(c);
			}
			grm_content = builder.toString();
			
			
			in.close();
			//grm_file = null;
			
		
		// 初始化构建语法参数
		grm_build_params = "engine_type=local,asr_res_path="+ASR_RES_PATH +",sample_rate="+SAMPLE_RATE_16K
				+ ",grm_build_path=" +GRM_BUILD_PATH+",";
		
		// 实例化回调函数对象
		 CallbackUtil.GrammarCallBack callback=new CallbackUtil.GrammarCallBack_Realize();
		grm_cnt_len=grm_content.length();
		System.out.println(grm_cnt_len+"wwwww");
		System.out.println("grm_content:\n"+grm_content);
		System.out.println("grm_build_params"+grm_build_params);
		System.out.println("callback:" + callback);
		// 传入参数,构建语法
		System.out.println("识别之前的userData"+asr_data2);
		
		
		
		ret = VoiceLib.instance.QISRBuildGrammar("bnf",grm_content,grm_cnt_len,grm_build_params,callback,asr_data2.getPointer());
		//asr_data2.grammar_id="call";
		//System.out.println("grammar_id="+asr_data2.getClass().getName());
		// grm_content = null;

		System.out.println("ret:" + ret);
		return ret;
		} catch (Exception e) {
			e.printStackTrace();
			return ret;
		}
	}

	// 更新离线识别语法词典
	public static int update_lexicon(UserData udata) {
		String lex_content = "丁伟\n黄辣椒";
		int lex_cnt_len = lex_content.length();
		String update_lex_params = null;

		update_lex_params = "engine_type = local, text_encoding = GB2312, asr_res_path =" + ASR_RES_PATH
				+ ", sample_rate = " + SAMPLE_RATE_16K + ",grm_build_path =" + GRM_BUILD_PATH + ", grammar_list ="
				+ udata.grammar_id;

		CallbackUtil.LexiconCallBack callback = new CallbackUtil.LexiconCallBack_Realize();
		String udata1 = "";
		System.out.println(LEX_NAME);
		System.out.println(lex_content);
		System.out.println(lex_cnt_len);
		System.out.println(update_lex_params);
		System.out.println(callback);
		System.out.println(udata1);
		return VoiceLib.instance.QISRUpdateLexicon(LEX_NAME.toCharArray(), lex_content.toCharArray(), lex_cnt_len,
				update_lex_params.toCharArray(), callback, udata1.toCharArray());
	}
	
	
	static File f_pcm = null;
	static byte[] pcm_data;
	static int errcode =-1;
	static String session_id ="";
	
	static byte[] buf ;
	static IntByReference ep_status1=new IntByReference(0);
	static IntByReference rec_status1=new IntByReference(2);
	static IntByReference rss_status1=new IntByReference(1);
	static Pointer pointer;
	static String pcm_data_s;
	// static ByteByReference b;
	static ByteByReference b=new ByteByReference();
	static IntByReference i=new IntByReference();
	static PointerByReference p=new PointerByReference(Pointer.NULL);
	
	// 进行离线语法识别
	public static int run_asr(UserData udata) {
		String asr_params ;
		String rec_rslt =null;
		String asr_audiof = null;

		long pcm_count = 0;
		long pcm_size;
		int last_audio = 0;
		int aud_stat = 1;

		
		asr_audiof = get_audio_file();
		System.out.println("asr_audiof:" + asr_audiof);
		
		f_pcm = new File(asr_audiof);
		
		if (!f_pcm.exists()) {
			System.out.println("打开语音文件失败!\n");
			run_error();
		}
		pcm_size = f_pcm.length();
		System.out.println("f_pcm_size"+pcm_size);


		try {
			InputStream in=new FileInputStream(f_pcm);
			//ByteArrayOutputStream bos=new ByteArrayOutputStream();
			pcm_data=new byte[(int) pcm_size];
			
			int n=0;
			//while((n=in.read(pcm_data))!=-1) {
			//bos.write(pcm_data,0,n);
			//}
			//pcm_data=bos.toByteArray();
			n=in.read(pcm_data);
			System.out.println("n="+n);
			System.out.println("pcm_size="+pcm_size);
			System.out.printf("%x\n",pcm_data[0]);
			System.out.printf("%x\n",pcm_data[1]);
			System.out.printf("%x\n",pcm_data[2]);
			System.out.printf("%x\n",pcm_data[3]);
			System.out.println("*I*********");
			System.out.printf("%x\n",pcm_data[(int) (pcm_size-4)]);
			System.out.printf("%x\n",pcm_data[(int) (pcm_size-3)]);
			System.out.printf("%x\n",pcm_data[(int) (pcm_size-2)]);
			System.out.printf("%x\n",pcm_data[(int) (pcm_size-1)]);
			
			
			//InputStreamReader input=new InputStreamReader(in);
			//StringBuilder builder = new StringBuilder();
			/*int data;
			char[] cbuf=new char[(int) pcm_size];
			data = input.read(cbuf);
			
			System.out.println("data="+data);
			System.out.printf("char%x",(int)cbuf[0]);*/
			/*pointer=new Pointer(135662L);
			for(int i=0;i<135662;i++) {
				pointer.setByte(i, pcm_data[i]);
			}*/
			
			/*pointer=new Memory(pcm_size);
			pointer.read(0, pcm_data, 0,(int) pcm_size);
			System.out.println("SIZE"+pointer.SIZE);*/
			
			
			//int offset = 0;
			//int numRead = 0;
			/*while (offset < pcm_data.length&& (numRead = in.read(pcm_data, offset, pcm_data.length - offset)) >= 0) {
				offset += numRead;
			}*/
			//offset=in.read(pcm_data);
			
			
		} catch (Exception e1) {
			e1.printStackTrace();
		}

		// 离线语法识别参数设置
		asr_params = "engine_type=local,asr_res_path=" + ASR_RES_PATH 
					+ ",sample_rate=" + SAMPLE_RATE_16K
					+ ",grm_build_path=" + GRM_BUILD_PATH 
					+ ",local_grammar=" +udata.grammar_id
					+ ",result_type=plain,result_encoding=GB2312,";
		/*asr_params="engine_type = local,                asr_res_path = fo|res/asr/common.jet, sample_rate = 16000," + 
				"grm_build_path = res/asr/GrmBuilld_x64, local_grammar = call,           result_type =plain, result_encoding =GB2312, ";*/
		System.out.println("asr_param="+asr_params);
		IntByReference err=new IntByReference(errcode);
		//int[] errcode2= {0};
		session_id= VoiceLib.instance.QISRSessionBegin(null, asr_params, err);
		System.out.println("session_err="+err.getValue());
		System.out.println("session_id:"+session_id);
		System.out.println("session_id_length:"+session_id.length());
		if (null == session_id) {
			run_error();
		}
		
		
		System.out.println("开始识别...\n");
		
		
		while (true) {
			int len =6400;

			if (pcm_size < 12800) {
				len = (int) pcm_size;
				last_audio = 1;
			}
			aud_stat = MSP_AUDIO_SAMPLE_CONTINUE;//0x02
			if (0 == pcm_count) {
				aud_stat =MSP_AUDIO_SAMPLE_FIRS;//0x01
			}
			if (len <= 0) {
				break;
			}
			System.out.print(">");
			byte[] every=new byte[len];
			System.arraycopy(pcm_data, (int) pcm_count, every, 0, len);			
				errcode = VoiceLib.instance.QISRAudioWrite(session_id,every,len, aud_stat,ep_status1,rec_status1);
				
			
 			try {
				Thread.sleep(15);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			/*System.out.print("pcmsize====="+pcm_size);
			System.out.print(" errcode="+errcode);
 			System.out.print(" aut_stat="+aud_stat);
 			System.out.print(" len="+len);
 			System.out.print(" pcm_count="+pcm_count);
			System.out.print(" ep_status1="+ep_status1.getValue());
 			System.out.println(" rec_status="+rec_status1.getValue());*/
			
			if (MSP_SUCCESS != errcode) {
				run_error();
				break;
			}
			pcm_count += (long) len;
			pcm_size -= (long) len;
			
			// 检测到音频结束
			if (MSP_EP_AFTER_SPEECH == ep_status1.getValue()) {
				break;
			}
			try {
				Thread.sleep(150);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} // 模拟人说话时间间隙
	}
		
		// 主动点击音频结束
		VoiceLib.instance.QISRAudioWrite(session_id, null, 0, MSP_AUDIO_SAMPLE_LAST, ep_status1,rec_status1);
		pcm_data = null;
		

		//MSP_REC_STATUS_COMPLETE = 5
		//rss_status = MSP_REC_STATUS_INCOMPLETE;//2
		//System.out.println("rss_status="+rss_status);
		 
		
		// 获取识别结果
		while (MSP_REC_STATUS_COMPLETE != rss_status1.getValue() && MSP_SUCCESS==err.getValue()) {
			/*System.out.println("rss_status="+rss_status1.getValue());
			System.out.println("errValue1="+err.getValue());*/
			rec_rslt = VoiceLib.instance.QISRGetResult(session_id, rss_status1,0, err);
			try {
				Thread.sleep(150);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			//System.out.println("session_id_result="+session_id);
//			System.out.println("rss_status="+rss_status1.getValue());
//			System.out.println("errValue="+err.getValue());
//			System.out.println("rec_rslt="+rec_rslt);
			
		}
		System.out.println("\n识别结束:\n");
		System.out.println("=============================================================\n");
		if (null != rec_rslt) {
			System.out.println("识别结果为:\n" + rec_rslt);
		} else {
			System.out.println("没有识别结果!");
			System.out.println("=============================================================\n");
			run_exit();
		}
		return MSP_SUCCESS;
	}

	public static void run_error() {
		if (null != pcm_data) {
			pcm_data = null;
		}
		if (null != f_pcm) {
			f_pcm = null;
		}
	}

	public static int run_exit() {
		VoiceLib.instance.QISRSessionEnd(session_id, null);
		return errcode;
	}

	static UserData asr_data;
	public static void main(String[] args) {
		String login_config = "appid=5ba********"; // 登录参数换成你的
		asr_data = new UserData();
		asr_data.build_fini=2;
		//asr_data.grammar_id ="12345678901234567890123456789012";
		/*Integer zero=new Integer(0);
		Arrays.fill(asr_data.toArray(asr_data.size()), zero);*/
		
		int ret = 0;
		ret = VoiceLib.instance.MSPLogin(null, null, login_config); // 第一个参数为用户名,第二个参数为密码,传null即可,第三个参数是登录参数
		if (MSP_SUCCESS != ret) {
			System.out.println("登录失败!");
			exit();
		}
		System.out.println("登录成功!");
		System.out.println("构建离线识别语法网络...\n");
		ret = build_grammar(asr_data); // 第一次使用某语法进行识别,需要先构建语法网络,获取语法ID,之后使用此语法进行识别,无需再次构建
		System.out.println("RET============================================" + ret);
		
		if (MSP_SUCCESS != ret) {
			System.out.println("构建语法调用失败!\n");
			exit();
		}
		
		 while (MSP_FAILED!= asr_data.build_fini) {
			  try { 
				  Thread.sleep(1); 
				  } catch(InterruptedException e) { // TODO Auto-generated catch block
		  e.printStackTrace(); 
		  }
		  }
		
		
		
		 

		if (MSP_SUCCESS != asr_data.errcode) {
			exit();
		}
		
		System.out.println("离线识别语法网络构建完成,开始识别...\n");
		ret = run_asr(asr_data);
		if (MSP_SUCCESS != ret) {
			System.out.println("离线语法识别出错: %d \n");// +ret
			exit();
		}

		System.out.println("请按任意键继续\n");
/*		// _getch();
		System.out.println("更新离线语法词典...\n");
		ret = update_lexicon(asr_data); // 当语法词典槽中的词条需要更新时,调用QISRUpdateLexicon接口完成更新
		if (MSP_SUCCESS != ret) {
			System.out.println("更新词典调用失败!\n");
			exit();
		}
		while (MSP_FAILED != asr_data.update_fini)
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				// TODO Auto xinzhi-generated catch block
				e.printStackTrace();
			}
		if (MSP_SUCCESS != asr_data.errcode)
			exit();
		System.out.println("更新离线语法词典完成,开始识别...\n");
		ret = run_asr(asr_data);
		if (MSP_SUCCESS != ret) {
			System.out.println("离线语法识别出错: %d \n");
			exit();
		}*/

	}

	public static void exit() {
		VoiceLib.instance.MSPLogout();
		System.out.println("请按任意键退出...\n");
		// _getch();

	}
}

配套源码地址:https://download.csdn.net/download/weixin_43112746/10908947
记得更换成自己的资源
帮助有需要的人。觉得有用,记得给我个赞!

没有更多推荐了,返回首页