科大讯飞离线语音命令词识别的使用说明

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

源码下载地址

 

第一阶段    基础准备

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

平台地址

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

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

科大讯飞离线语音命令词识别的使用说明_第1张图片

 

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

 

第二阶段    Demo导入

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

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

科大讯飞离线语音命令词识别的使用说明_第2张图片

 

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

科大讯飞离线语音命令词识别的使用说明_第3张图片

 

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

科大讯飞离线语音命令词识别的使用说明_第4张图片

 

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

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

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

科大讯飞离线语音命令词识别的使用说明_第5张图片

科大讯飞离线语音命令词识别的使用说明_第6张图片

 

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

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

科大讯飞离线语音命令词识别的使用说明_第7张图片

 

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

科大讯飞离线语音命令词识别的使用说明_第8张图片

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

   
   
   
   
   
   
   
   
   
   
   
   
   
   

为什么还有问题,这个时候你再进入到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对于我们只使用离线命令词识别来说有一点冗余,太多了;下面我们就来把离线命令词功能抽取出来,如下图:

科大讯飞离线语音命令词识别的使用说明_第9张图片

 

实现离线命令词识别的功能实现主要是上图中红色框中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中

科大讯飞离线语音命令词识别的使用说明_第10张图片

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

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

科大讯飞离线语音命令词识别的使用说明_第11张图片

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

compile files('libs/Msc.jar')

科大讯飞离线语音命令词识别的使用说明_第12张图片

 

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

科大讯飞离线语音命令词识别的使用说明_第13张图片

 

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

 

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

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

科大讯飞离线语音命令词识别的使用说明_第14张图片

 

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

上面也说了,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文件:



    

    

    
    

    
        

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

 

第四阶段    提高

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

因为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

activity_off_line_test.xml代码:



    

    

    

        

 

word_info_editor.xml代码:




    

    

        

        

    

    

        

        

    

    

        

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



  

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

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

//通用词槽
!slot ;
!slot ;
!slot ;

//联系相关词槽声明
!slot ;//联系人
!slot ;//联系方式
!slot ;//联系动作

//巡游相关词槽声明
!slot ;//巡游点
!slot ;//去
!slot ;//准备去

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

 //公积金业务
 !slot ;
 !slot ;
 !slot ;

!start ;
:|||;

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

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

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

//办卡语料
:卡|信用卡|儿童卡|储蓄卡;//卡片类型
:[];

//公积金业务
:比例;//公积金比例
:转移|提取;//处理公积金
:公积金;
:[];


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

注:欢迎扫码关注

科大讯飞离线语音命令词识别的使用说明_第15张图片

 

你可能感兴趣的:(科大讯飞,离线命令词,语音识别,离线语音识别,Android,Android基础,随笔,Android,Studio,AI)