最近因为项目的需求,需要在无网络的情况下实现语音识别的功能,因为之前在线识别一直用的科大的,所以经理就和我说,你花半天时间简单熟悉一下,然后出一个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,jsonprivate 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() {@Overridepublic 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;// 函数调用返回值@Overridepublic void onClick(View view) {      if( null == mAsr ){// 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688this.showTip( "创建对象失败,请确认 libmsc.so 放置正确,\n 且有调用 createUtility 进行初始化" );return;}if(null == mEngineType) {showTip("请先选择识别引擎类型");return;}  switch(view.getId()){case R.id.isr_grammar:showTip("上传预设关键词/语法文件");// 本地-构建语法文件,生成语法idif (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);}}// 在线-构建语法文件,生成语法idelse { ((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() {@Overridepublic void onInit(int code) {Log.d(TAG, "SpeechRecognizer init() code = " + code);if (code != ErrorCode.SUCCESS) {showTip("初始化失败,错误码:"+code);}}};/*** 更新词典监听器。*/private LexiconListener lexiconListener = new LexiconListener() {@Overridepublic void onLexiconUpdated(String lexiconId, SpeechError error) {if(error == null){showTip("词典更新成功");}else{showTip("词典更新失败,错误码:"+error.getErrorCode());}}};/*** 构建语法监听器。*/private GrammarListener grammarListener = new GrammarListener() {@Overridepublic 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() {@Overridepublic void onContactQueryFinish(String contactInfos, boolean changeFlag) {//获取联系人mLocalLexicon = contactInfos;}     };/*** 识别监听器。*/private RecognizerListener mRecognizerListener = new RecognizerListener() {@Overridepublic void onVolumeChanged(int volume, byte[] data) {showTip("当前正在说话,音量大小:" + volume);Log.d(TAG, "返回音频数据:"+data.length);}@Overridepublic 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");}}@Overridepublic void onEndOfSpeech() {// 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入          showTip("结束说话");}@Overridepublic void onBeginOfSpeech() {// 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入showTip("开始说话");}@Overridepublic void onError(SpeechError error) {showTip("onError Code:"    + error.getErrorCode());}@Overridepublic 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() {@Overridepublic 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);// 设置云端识别使用的语法idmAsr.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);// 设置本地识别使用语法idmAsr.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();}@Overrideprotected 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 {@Overridepublic 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() {@Overridepublic void onInit(int code) {Log.d(TAG, "SpeechRecognizer init() code = " + code);if (code != ErrorCode.SUCCESS) {showTip("初始化失败,错误码:" + code);}}
};

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

/*** 识别监听器。*/
private RecognizerListener mRecognizerListener = new RecognizerListener() {@Overridepublic void onVolumeChanged(int volume, byte[] data) {showTip("当前正在说话,音量大小:" + volume);Log.d(TAG, "返回音频数据:" + data.length);}@Overridepublic 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");}}@Overridepublic void onEndOfSpeech() {// 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入showTip("结束说话");}@Overridepublic void onBeginOfSpeech() {// 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入showTip("开始说话");}@Overridepublic void onError(SpeechError error) {showTip("onError Code:" + error.getErrorCode());}@Overridepublic void onEvent(int i, int i1, int i2, Bundle bundle) {}
};

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

/*** 构建语法监听器。*/
private GrammarListener grammarListener = new GrammarListener() {@Overridepublic 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);// 设置本地识别使用语法idmAsr.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,jsonprivate 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;// 函数调用返回值@Overridepublic void onClick(View view) {if (null == mAsr) {// 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688this.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() {@Overridepublic void onInit(int code) {Log.d(TAG, "SpeechRecognizer init() code = " + code);if (code != ErrorCode.SUCCESS) {showTip("初始化失败,错误码:" + code);}}};/*** 构建语法监听器。*/private GrammarListener grammarListener = new GrammarListener() {@Overridepublic void onBuildFinish(String grammarId, SpeechError error) {if (error == null) {showTip("语法构建成功:" + grammarId);} else {showTip("语法构建失败,错误码:" + error.getErrorCode());}}};/*** 识别监听器。*/private RecognizerListener mRecognizerListener = new RecognizerListener() {@Overridepublic void onVolumeChanged(int volume, byte[] data) {showTip("当前正在说话,音量大小:" + volume);Log.d(TAG, "返回音频数据:" + data.length);}@Overridepublic 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");}}@Overridepublic void onEndOfSpeech() {// 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入showTip("结束说话");}@Overridepublic void onBeginOfSpeech() {// 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入showTip("开始说话");}@Overridepublic void onError(SpeechError error) {showTip("onError Code:" + error.getErrorCode());}@Overridepublic void onEvent(int i, int i1, int i2, Bundle bundle) {}};private void showTip(final String str) {runOnUiThread(new Runnable() {@Overridepublic 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);// 设置本地识别使用语法idmAsr.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();}@Overrideprotected 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" /><EditTextandroid:id="@+id/isr_text"android:layout_width="fill_parent"android:layout_height="0dip"android:layout_weight="1"android:gravity="top|left"android:textSize="20sp" /><LinearLayoutandroid: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><LinearLayoutandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_margin="10dip"android:layout_marginBottom="2dip"android:gravity="center_horizontal"android:orientation="horizontal" ><Buttonandroid:id="@+id/isr_recognize"android:layout_width="0dip"android:layout_height="wrap_content"android:layout_weight="1"android:text="开始识别"android:textSize="20sp" /></LinearLayout><LinearLayoutandroid: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" ><Buttonandroid:id="@+id/isr_stop"android:layout_width="0dip"android:layout_height="wrap_content"android:layout_weight="1"android:text="停止录音"android:textSize="20sp" /><Buttonandroid: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,jsonprivate 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;// 函数调用返回值@Overridepublic void onClick(View view) {if (null == mAsr) {// 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688this.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() {@Overridepublic void onClick(View v) {Toast.makeText(OffLineTestActivity.this, "取消", Toast.LENGTH_SHORT).show();dialog.cancel();}});confirmButton.setOnClickListener(new View.OnClickListener() {@Overridepublic 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() {@Overridepublic void onInit(int code) {Log.d(TAG, "SpeechRecognizer init() code = " + code);if (code != ErrorCode.SUCCESS) {showTip("初始化失败,错误码:" + code);}}};/*** 更新词典监听器。*/private LexiconListener lexiconListener = new LexiconListener() {@Overridepublic void onLexiconUpdated(String lexiconId, SpeechError error) {if (error == null) {showTip("词典更新成功");} else {showTip("词典更新失败,错误码:" + error.getErrorCode());}}};/*** 构建语法监听器。*/private GrammarListener grammarListener = new GrammarListener() {@Overridepublic void onBuildFinish(String grammarId, SpeechError error) {if (error == null) {showTip("语法构建成功:" + grammarId);} else {showTip("语法构建失败,错误码:" + error.getErrorCode());}}};/*** 识别监听器。*/private RecognizerListener mRecognizerListener = new RecognizerListener() {@Overridepublic void onVolumeChanged(int volume, byte[] data) {showTip("当前正在说话,音量大小:" + volume);Log.d(TAG, "返回音频数据:" + data.length);}@Overridepublic 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");}}@Overridepublic void onEndOfSpeech() {// 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入showTip("结束说话");}@Overridepublic void onBeginOfSpeech() {// 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入showTip("开始说话");}@Overridepublic void onError(SpeechError error) {showTip("onError Code:" + error.getErrorCode());}@Overridepublic void onEvent(int i, int i1, int i2, Bundle bundle) {}};private void showTip(final String str) {runOnUiThread(new Runnable() {@Overridepublic 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);// 设置本地识别使用语法idmAsr.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();}@Overrideprotected 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" /><EditTextandroid:id="@+id/isr_text"android:layout_width="fill_parent"android:layout_height="0dip"android:layout_weight="1"android:gravity="top|left"android:textSize="20sp" /><LinearLayoutandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_margin="10dip"android:layout_marginBottom="2dip"android:gravity="center_horizontal"android:orientation="horizontal" ><Buttonandroid:id="@+id/isr_recognize"android:layout_width="0dip"android:layout_height="wrap_content"android:layout_weight="1"android:text="开始识别"android:textSize="20sp" /><Buttonandroid: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><LinearLayoutandroid: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" ><Buttonandroid:id="@+id/isr_stop"android:layout_width="0dip"android:layout_height="wrap_content"android:layout_weight="1"android:text="停止录音"android:textSize="20sp" /><Buttonandroid: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"><TextViewandroid:layout_marginLeft="20dp"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="请编辑更新信息:"android:textColor="#000000"android:textSize="30dp" /><LinearLayoutandroid:layout_marginLeft="20dp"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="词组名称:"android:textSize="20dp" /><EditTextandroid:id="@+id/enter_word_group_name"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="30dp"android:hint="请输入词组名称" /></LinearLayout><LinearLayoutandroid:layout_marginLeft="20dp"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="词组信息:"android:textSize="20dp" /><EditTextandroid:id="@+id/enter_word_group_info"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="30dp"android:hint="请编写词组信息" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><Buttonandroid:id="@+id/register_cancle"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="350dp"android:text="取消" /><Buttonandroid: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"?>
<LinearLayoutxmlns: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"><TextViewandroid: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文档编辑指南,后续我还会对这个编辑规则进行介绍,具体就介绍到这里。

注:欢迎扫码关注

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

  1. 讯飞离线语音命令词识别

    讯飞离线语音命令词识别 强烈推荐 分享一个大神的人工智能教程.零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来! 网址:http://www.captainbed.net/yancyang ...

  2. 离线语音识别软件_6.语音板使用科大讯飞离线命令词识别

    0x00 离线命令词识别简介 语音识别技术(Auto Speech Recognize,简称ASR),就是把人的自然语言音频数据转换成文本数据的技术.理论上在线ASR是可以把所有的语音转换成对应的文本 ...

  3. 讯飞语音命令词识别的SDK配置与运用

    之前一直想开始写技术博客,倒不是说自己有多牛,只是觉得自己弄了点东西出来想和大家分享,而且听说有利于自己技术水平的提高,就权当是一次记录吧,这是第一篇. 之前因为一次比赛App要用到语音识别,做一个通 ...

  4. 【C++】QT调用VS封装的dll(以科大讯飞离线命令词识别SDK为例)

    QT调用VS封装的dll(以科大讯飞离线命令词识别SDK为例) 1.说明: 跨平台调用dll出现各种坑,谨以此文避坑. 参考博文:https://www.cnblogs.com/seer/p/4789 ...

  5. 科大讯飞离线命令词识别

    一.进入科大讯飞官网,下载自己所需要的SDK----以离线命令词识别为例: 二.分析代码-----主要有以下四大函数 const char *get_audio_file(void); //选择进行离 ...

  6. 讯飞语音——离线命令词识别

    离线命令词识别 效果图 示例源码 地址:http://download.csdn.net/detail/q4878802/9023825 步骤: 1. 下载SDK 前面文章有,就不在复述了.这里要选择 ...

  7. [转]Kaldi命令词识别

    转自: http://www.jianshu.com/p/5b19605792ab?utm_campaign=maleskine&utm_content=note&utm_medium ...

  8. 哈工大视听觉信号处理——听觉部分报告——一种智能家居命令词识别系统的设计

    题     目 听觉部分 专       业 软件工程 学    号 120L0219XX 班    级 2037101 姓       名 李启明 报 告 日 期 2022.12.20 一.基频 ( ...

  9. DTW 算法的实时语音识别——命令词识别(Python 实现)

    介绍 这是我们计算机视听觉的第三个实验,也是本学期语音部分的最后一个实验,大概花了两天才写完.上个实验做的是语音编码问题,这个实验是语音识别的事,感觉处理语音还是比较有意思的. 附上实验代码地址:实验 ...

最新文章

  1. 【Qt】pro 笔记
  2. 第二十七讲 微分方程组解的图像
  3. php-redis 下载地址
  4. 网页里如何使用js禁用控制台
  5. python中的str方法和repr方法_Python中 的 __str__ 方法和 __repr__ 方法的区别有哪些
  6. 腾讯云全面更新数据智能服务全景图!
  7. composer 检查镜像_检查N元树中的镜像
  8. matlab中D A1在哪,A1=d(1:15,:);A2=d(16:30,:);A3=
  9. python脚本 pyqt 打包成windows可执行exe文件 pyinstaller
  10. wxml代码支持js代码吗_如何取胜:代码支持者的建议
  11. PyTorch 1.0 中文文档:torch.utils.checkpoint
  12. 第 2 章 设计模式七大原则
  13. Python数据可视化之Matplotlib(饼图)
  14. C++:字符串转换成整数
  15. qq视频转码失败怎么办_视频里提取音频的几种方法
  16. 网络调试助手无法连接tcp服务器,W5500 TCP 客户端网络调试助手连不上
  17. 【计算机组成原理】地址线和数据线
  18. 上网日志留存_中国移动5G上网日志留存系统招标:最高投标总限价10亿元
  19. win10安装opcenum_如何完美的解决win10安装时遇到的问题?
  20. nginx 配置之 proxy_pass参数详解

热门文章

  1. 圣斗士星矢重生服务器维护,11月26日全服停机维护公告
  2. python鸭子类型_1
  3. 【React】React 详细教程
  4. check the manual that corresponds to your MySQL server version for the right syntax to use near
  5. matlab文件批量命名,利用MATLAB批量对文件重命名
  6. css使两个盒子并列_CSS常见面试题
  7. 【HTML+CSS+JS表白网站搭建】520七夕到了,快搭个漂亮的表白网站送给TA吧
  8. 2018年上半年信息安全工程师上午选择题及解析
  9. 大数据浪潮将催生信息产业第三极——“数据极”
  10. MIMIC-III数据库申请流程