博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
讯飞语音——离线命令词识别
阅读量:6820 次
发布时间:2019-06-26

本文共 14452 字,大约阅读时间需要 48 分钟。

离线命令词识别

  • 效果图

P1

  • 示例源码

地址:

步骤:

1. 下载SDK

前面文章有,就不在复述了。这里要选择离线命令词的服务以后,重新加载,因为需要下载离线命令词识别的资源文件

地址:

2. 集成方法

前面文章有,就不在复述了。

地址:

3. 正题,开始集成

1. 添加权限

这里用到的唤醒功能不是所有的权限都用到的,具体用到了哪些权限,可以看上面的链接,用到哪写权限就加哪些权限,这个为了快速方便测试,把讯飞用到的权限都加上了。

2. 初始化appid

我是将appid的初始化放在的Applicaiton下,具体可以下载源码

// 应用程序入口处调用,避免手机内存过小,杀死后台进程后通过历史intent进入Activity造成SpeechUtility对象为null// 如在Application中调用初始化,需要在Mainifest中注册该Applicaiton// 注意:此接口在非主进程调用会返回null对象,如需在非主进程使用语音功能,请增加参数:SpeechConstant.FORCE_LOGIN+"=true"// 参数间使用“,”分隔。// 设置你申请的应用appidStringBuffer param = new StringBuffer();param.append("appid=55d33f09");param.append(",");param.append(SpeechConstant.ENGINE_MODE + "=" + SpeechConstant.MODE_MSC);// param.append(",");// param.append(SpeechConstant.FORCE_LOGIN + "=true");SpeechUtility.createUtility(InitKqwSpeech.this, param.toString());

3. 工具类

package com.example.kqwspeechdemo2.utils;import java.io.InputStream;import android.content.Context;import android.util.Log;/** * 功能性函数扩展类 *  * @author kongqw *  */public class FucUtil {
// Log标签 private static final String TAG = "FucUtil"; /** * 读取asset目录下文件内容 * * @return content */ public static String readFile(Context mContext, String file, String code) { int len = 0; byte[] buf = null; String result = ""; try { InputStream in = mContext.getAssets().open(file); len = in.available(); buf = new byte[len]; in.read(buf, 0, len); result = new String(buf, code); } catch (Exception e) { Log.e(TAG, e.toString()); e.printStackTrace(); } return result; }}

4. 构建语法文件的工具类

package com.example.kqwspeechdemo2.engine;import com.example.kqwspeechdemo2.utils.FucUtil;import com.iflytek.cloud.ErrorCode;import com.iflytek.cloud.GrammarListener;import com.iflytek.cloud.InitListener;import com.iflytek.cloud.SpeechConstant;import com.iflytek.cloud.SpeechError;import com.iflytek.cloud.SpeechRecognizer;import com.iflytek.cloud.util.ResourceUtil;import com.iflytek.cloud.util.ResourceUtil.RESOURCE_TYPE;import android.content.Context;import android.os.Environment;import android.util.Log;import android.widget.Toast;/** * 构建离线命令词语法 *  * @author kongqw *  */public abstract class BuildLocalGrammar {
/** * 构建语法的回调 * * @param errMsg * null 构造成功 */ public abstract void result(String errMsg, String grammarId); // Log标签 private static final String TAG = "BuildLocalGrammar"; public static final String GRAMMAR_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/msc/test"; // 上下文 private Context mContext; // 语音识别对象 private SpeechRecognizer mAsr; public BuildLocalGrammar(Context context) { mContext = context; // 初始化识别对象 mAsr = SpeechRecognizer.createRecognizer(context, new InitListener() { @Override public void onInit(int code) { Log.d(TAG, "SpeechRecognizer init() code = " + code); if (code != ErrorCode.SUCCESS) { result(code + "", null); Log.d(TAG, "初始化失败,错误码:" + code); Toast.makeText(mContext, "初始化失败,错误码:" + code, Toast.LENGTH_SHORT).show(); } } }); }; /** * 构建语法 * * @return */ public void buildLocalGrammar() { try { /* * TODO 如果你要在程序里维护bnf文件,可以在这里加上你维护的一些逻辑 * 如果不嫌麻烦,要一直改bnf文件,这里的代码可以不用动,不过我个人不建议一直手动修改bnf文件 * ,内容多了以后很容易出错,不好找Bug,建议每次改之前先备份。 建议用程序维护bnf文件。 */ /* * 构建语法 */ String mContent;// 语法、词典临时变量 String mLocalGrammar = FucUtil.readFile(mContext, "kqw.bnf", "utf-8"); 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, GRAMMAR_PATH); // 使用8k音频的时候请解开注释 // mAsr.setParameter(SpeechConstant.SAMPLE_RATE, "8000"); // 设置资源路径 mAsr.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath()); // 构建语法 int ret = mAsr.buildGrammar("bnf", mContent, new GrammarListener() { @Override public void onBuildFinish(String grammarId, SpeechError error) { if (error == null) { Log.d(TAG, "语法构建成功"); result(null, grammarId); } else { Log.d(TAG, "语法构建失败,错误码:" + error.getErrorCode()); result(error.getErrorCode() + "", grammarId); } } }); if (ret != ErrorCode.SUCCESS) { Log.d(TAG, "语法构建失败,错误码:" + ret); result(ret + "", null); } } catch (Exception e) { e.printStackTrace(); } } // 获取识别资源路径 private String getResourcePath() { StringBuffer tempBuffer = new StringBuffer(); // 识别通用资源 tempBuffer.append(ResourceUtil.generateResourcePath(mContext, 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(); }}

5. 命令词识别工具类

package com.example.kqwspeechdemo2.engine;import com.iflytek.cloud.ErrorCode;import com.iflytek.cloud.InitListener;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.ResourceUtil;import com.iflytek.cloud.util.ResourceUtil.RESOURCE_TYPE;import android.content.Context;import android.os.Bundle;import android.os.Environment;import android.util.Log;import android.widget.Toast;/** * 命令词识别 *  * @author kongqw *  */public abstract class KqwSpeechRecognizer {
/** * 初始化的回调 * * @param flag * true 初始化成功 false 初始化失败 */ public abstract void initListener(boolean flag); public abstract void resultData(String data); public abstract void speechLog(String log); // Log标签 private static final String TAG = "KqwLocalCommandRecognizer"; public static final String GRAMMAR_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/msc/test"; // 上下文 private Context mContext; // 语音识别对象 private SpeechRecognizer mAsr; public KqwSpeechRecognizer(Context context) { mContext = context; // 初始化识别对象 mAsr = SpeechRecognizer.createRecognizer(context, new InitListener() { @Override public void onInit(int code) { Log.d(TAG, "SpeechRecognizer init() code = " + code); if (code != ErrorCode.SUCCESS) { initListener(false); Toast.makeText(mContext, "初始化失败,错误码:" + code, Toast.LENGTH_SHORT).show(); } else { initListener(true); } } }); } /** * 参数设置 */ public void setParam() { // 清空参数 mAsr.setParameter(SpeechConstant.PARAMS, null); // 设置识别引擎 本地引擎 mAsr.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL); // mAsr.setParameter(SpeechConstant.ENGINE_TYPE, // SpeechConstant.TYPE_MIX); // mAsr.setParameter(SpeechConstant.ENGINE_TYPE, "mixed"); // // 设置本地识别资源 mAsr.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath()); // 设置语法构建路径 mAsr.setParameter(ResourceUtil.GRM_BUILD_PATH, GRAMMAR_PATH); // 设置返回结果格式 mAsr.setParameter(SpeechConstant.RESULT_TYPE, "json"); // 设置本地识别使用语法id mAsr.setParameter(SpeechConstant.LOCAL_GRAMMAR, "kqw"); // 设置识别的门限值 mAsr.setParameter(SpeechConstant.MIXED_THRESHOLD, "60"); // 使用8k音频的时候请解开注释 // mAsr.setParameter(SpeechConstant.SAMPLE_RATE, "8000"); mAsr.setParameter(SpeechConstant.DOMAIN, "iat"); mAsr.setParameter(SpeechConstant.NLP_VERSION, "2.0"); mAsr.setParameter("asr_sch", "1"); // mAsr.setParameter(SpeechConstant.RESULT_TYPE, "json"); } // 获取识别资源路径 private String getResourcePath() { StringBuffer tempBuffer = new StringBuffer(); // 识别通用资源 tempBuffer.append(ResourceUtil.generateResourcePath(mContext, 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(); } int ret = 0;// 函数调用返回值 /** * 开始识别 */ public void startListening() { // 设置参数 setParam(); ret = mAsr.startListening(mRecognizerListener); if (ret != ErrorCode.SUCCESS) { Log.i(TAG, "识别失败,错误码: " + ret); } } /** * 识别监听器。 */ private RecognizerListener mRecognizerListener = new RecognizerListener() { StringBuffer stringBuffer = new StringBuffer(); public void onVolumeChanged(int volume) { Log.i(TAG, "当前正在说话,音量大小:" + volume); speechLog("当前正在说话,音量大小:" + volume); } @Override public void onResult(final RecognizerResult result, boolean isLast) { /* * TODO 拼接返回的数据 * * 这里返回的是Json数据,具体返回的是离线名命令词返回的Json还是语义返回的Json,需要做判断以后在对数据数据进行拼接 */ stringBuffer.append(result.getResultString()).append("\n\n"); // isLast为true的时候,表示一句话说完,将拼接后的完整的一句话返回 if (isLast) { // 数据回调 resultData(stringBuffer.toString()); } } @Override public void onEndOfSpeech() { Log.i(TAG, "结束说话"); speechLog("结束说话"); } @Override public void onBeginOfSpeech() { stringBuffer.delete(0, stringBuffer.length()); Log.i(TAG, "开始说话"); speechLog("开始说话"); } @Override public void onError(SpeechError error) { Log.i(TAG, "error = " + error.getErrorCode()); if (error.getErrorCode() == 20005) { // 本地命令词没有识别,也没有请求到网络 resultData("没有构建的语法"); speechLog("没有构建的语法"); /* * TODO * 当网络正常的情况下是不会回调20005的错误,只有当本地命令词识别不匹配,网络请求也失败的情况下,会返回20005 * 这里可以自己再做处理,例如回复“没有听清”等回复 */ } else { /* * TODO * 其他错误有很多,需要具体问题具体分析,正常在程序没有错误的情况下,只会回调一个没有检测到说话的错误,没记错的话错误码是10118 */ } } @Override public void onEvent(int eventType, int arg1, int arg2, Bundle obj) { Log.i(TAG, "eventType = " + eventType); } };}

这里的识别引擎设置的是SpeechConstant.TYPE_LOCAL,这种是本地识别引擎,只走本地识别,不走网络,如果换成SpeechConstant.TYPE_MIX,就是混合引擎,这种引擎方式,当本地没有识别到语法,返回20005错误码的时候,会直接请求语义接口,如果你语义开通了对应的场景,会走网络把你的语音转为语义,如果没有开通对应的场景,会把语音转为文字。

6. 测试类

package com.example.kqwspeechdemo2;import com.example.kqwspeechdemo2.engine.BuildLocalGrammar;import com.example.kqwspeechdemo2.engine.KqwSpeechRecognizer;import android.app.Activity;import android.os.Bundle;import android.text.TextUtils;import android.view.View;import android.widget.TextView;import android.widget.Toast;public class MainActivity extends Activity {
private TextView mTvResult; private TextView mTvLog; private BuildLocalGrammar buildLocalGrammar; private KqwSpeechRecognizer kqwSpeechRecognizer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTvResult = (TextView) findViewById(R.id.tv_result); mTvLog = (TextView) findViewById(R.id.tv_log); /** * 初始化本地语法构造器 */ buildLocalGrammar = new BuildLocalGrammar(this) { @Override public void result(String errMsg, String grammarId) { // errMsg为null 构造成功 if (TextUtils.isEmpty(errMsg)) { Toast.makeText(MainActivity.this, "构造成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, "构造失败", Toast.LENGTH_SHORT).show(); } } }; /** * 初始化离线命令词识别器 */ kqwSpeechRecognizer = new KqwSpeechRecognizer(this) { @Override public void speechLog(String log) { // 录音Log信息的回调 mTvLog.setText(log); } @Override public void resultData(String data) { // 是识别结果的回调 mTvResult.setText(data); } @Override public void initListener(boolean flag) { // 初始化的回调 if (flag) { Toast.makeText(MainActivity.this, "初始化成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, "初始化失败", Toast.LENGTH_SHORT).show(); } } }; /** * 构造本地语法文件,只有语法文件有变化的时候构造成功一次即可,不用每次都构造 */ buildLocalGrammar.buildLocalGrammar(); } /** * 开始识别按钮 * * @param view */ public void start(View view) { mTvResult.setText(null); // 开始识别 kqwSpeechRecognizer.startListening(); }}

7. 界面布局

8. BNF语法文件

#BNF+IAT 1.0 UTF-8;!grammar kqw;!slot 
;!slot
;!slot
;!slot
;!slot
;!slot
;!slot
;!start
;
:[
][
]
|[
]
[
]
|[
]
|
|
;
:庆威|小孔;
:我要|我想|我想要;
:打电话;
:给;
:前进|后退|左转|右转;
:去|到;
:厨房|卧室|阳台|客厅;

在构建语法的时候,我们不是必要在assets目录下创建一个xxx.bnf文件,构建的时候我们只要能够拿到满足BNF语法文件的字符串就行,至于这个文件内容,你存在哪都无所谓,在程序里写死、存sp、数据库、自己程序维护都OK,只要满足BNF的语法就行。

BNF语法开发指南

下载地址:

最后

  1. 如果你直接用我的Demo,我用的是测试版的离线包,只有35天的试用期,而且装机量只有3个,如果大家都用,很可能是不能正常运行的
  2. 如果是参考我的demo自己写一个,千万不要忘记替换appid和资源文件。

转载于:https://www.cnblogs.com/sesexxoo/p/6190543.html

你可能感兴趣的文章
keepalived
查看>>
EventBus
查看>>
iOS Block
查看>>
PHP错误及异常统一处理
查看>>
vim设置括号自动补全
查看>>
Get system clipboard
查看>>
windows环境eclipse开发C++程序
查看>>
svn 常用命令总结
查看>>
Linux下利用msmtp+mail+shell来发送邮件。
查看>>
Tomcat全攻略
查看>>
make: *** linux-2.6.36.4/arch/arm: Is a directo...
查看>>
android http连接阻塞超时问题
查看>>
异常处理
查看>>
线性插值针对位置量和角度量
查看>>
关于方法快的理解
查看>>
sublime text2配置
查看>>
library 'system/lib/libhoudini.so' not find
查看>>
TCP UDP socket debug tools
查看>>
网页矢量图在组态软件中的应用
查看>>
disabled by the php.ini setting phar.readonly
查看>>