之前一直想开始写技术博客,倒不是说自己有多牛,只是觉得自己弄了点东西出来想和大家分享,而且听说有利于自己技术水平的提高,就权当是一次记录吧,这是第一篇。
之前因为一次比赛App要用到语音识别,做一个通过手机输入语音指令然后根据输入内容做出响应的功能,当时请教了一下别人决定采用科大讯飞的在线命令词识别工程集成的SDK,当时先申请Appid,再下载SDK。(这里推荐大家先到讯飞官方平台上下载资料,资料上写得很详细,我这里主要写一些自己遇到的问题)
第一步:配置jar包和.so文件,在这一步卡了一下,现在网上大部分资料还是关于eclipse软件上的配置,现在大部分都使用AS,在这里推荐一篇博客文章,当时就是靠大神的方法配置完的http://blog.csdn.net/yy1300326388/article/details/46422939#comments
我的配置完是这样的,具体以官方文档为准
第二步:在工程AndroidManifest.xml文件中添加权限,参考官方下载的文件里的doc/MSC Develop Manual for Android文件
第三步:初始化,同上。我是通过先创建一个类继承自Application 如图
忘记自己申请的appid可以上官网查,接着在AndroidManifest.xml文件中配置新建的class为应用程序入口:主要是改变android:name
第四步:可以开始编写代码了,我所需要使用的功能是命令词识别功能,目前讯飞的离线语音识别功能需要收费,所以我使用的是在线的,但是两者代码相差不大。
(1)在线命令词识别需要先编写命令词语法文件,采用ABNF语法格式,官网上有详尽的资料,我们用记事本按照规定格式编写后上传到官网上就可以了,这里附上我的语法文件截图:主要修改
main和
command
(2)接下来直接贴代码,大部分都是官方demo:
//语音听写UI初始化
// private RecognizerDialog mRecognizerDialog;
//创建SpeechRecognizer,语音识别对象
private SpeechRecognizer mSpeechRecognizer;
//缓存
private SharedPreferences mSharedPreferences;
//云端语法文件
private String mCloudGrammar = null;
private static final String KEY_GRAMMAR_ABNF_ID = "grammar_abnf_id";
private static final String GRAMMAR_TYPE_ABNF = "abnf";
private static final String GRAMMAR_TYPE_BNF = "bnf";
//引擎类型
private String mEngineType = null;
//语法,词典临时变量
String mContent;
//函数调用返回值
int ret = 0 ;
接着在onCreate()中
button_voice = (Button)findViewById(R.id.button_voice);
button_voice.setOnTouchListener(this);
//语音识别引擎类型为云端
mEngineType = SpeechConstant.TYPE_CLOUD;
//创建SpeechRecognizedr对象,并初始化,注意不要导入错误的SpeechRecognizer
mSpeechRecognizer = SpeechRecognizer.createRecognizer(RgbActivity.this,mInitListener);
//初始化语法,命令词(FucUtil在下面)
mCloudGrammar = FucUtil.readFile(this,"Command.abnf","utf-8");
mContent = new String(mCloudGrammar);
//设置指定引擎类型
mSpeechRecognizer.setParameter(SpeechConstant.ENGINE_TYPE,"cloud");
mSpeechRecognizer.setParameter(SpeechConstant.SUBJECT,"asr");
ret = mSpeechRecognizer.buildGrammar(GRAMMAR_TYPE_ABNF,mContent,mCloudGrammarlistener);
if (ret != ErrorCode.SUCCESS){
LogUtil.e(TAG,"语音语法构建失败,错误码:"+ ret);
}
语音识别的按钮的触碰监听:
//语音识别按钮的触碰监听
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
//开始识别
ret = mSpeechRecognizer.startListening(mRecognizerListener);
//ToastUtils工具类,自定义Toast3500ms内显示时间
ToastUtils.makeText(getApplicationContext(), "开始识别", 500);
if(ret != ErrorCode.SUCCESS){
LogUtil.e(TAG,"识别失败,错误码:"+ret);
}
button_voice.setAlpha((float)0.5);
break;
case MotionEvent.ACTION_UP:
//停止识别
mSpeechRecognizer.stopListening();
ToastUtils.makeText(getApplicationContext(), "停止识别", 500);
button_voice.setAlpha((float) 1);
break;
case MotionEvent.ACTION_CANCEL:
ToastUtils.makeText(getApplicationContext(),"识别取消",500);
break;
case MotionEvent.ACTION_BUTTON_PRESS:
break;
}
return false;
}
初始化监听器:
//初始化监听器
private InitListener mInitListener = new InitListener() {
@Override
public void onInit(int code) {
LogUtil.e(TAG, "SpeechRecognizer init() code = " + code);
if (code != ErrorCode.SUCCESS){
LogUtil.e(TAG,"mInitListener 初始化失败,错误码:"+code);
}
}
};
云端构建语法监听器:
//云端构建语法监听器
private GrammarListener mCloudGrammarlistener = new GrammarListener() {
@Override
public void onBuildFinish(String grammarId, SpeechError speechError) {
if (speechError == null){
String grammarID = new String(grammarId);
Editor editor = mSharedPreferences.edit();
if (!TextUtils.isEmpty(grammarId)){
editor.putString(KEY_GRAMMAR_ABNF_ID,grammarID);}
editor.commit();
LogUtil.e(TAG,"mCloudGrammarlistener 语法构建成功:"+grammarId);
}else {
LogUtil.e(TAG,"语法构建失败错误码:"+speechError.getErrorCode());
}
}
};
识别监听器
//识别监听器
private RecognizerListener mRecognizerListener = new RecognizerListener() {
//音量变化回调
@Override
public void onVolumeChanged(int i, byte[] bytes) {
//在此添加动画
LogUtil.e(TAG,"返回音频数据:"+ bytes.length);
}
//开始说话
@Override
public void onBeginOfSpeech() {}
//结束说话
@Override
public void onEndOfSpeech() {}
//返回结果
@Override
public void onResult(RecognizerResult recognizerResult, boolean b) {
LogUtil.e(TAG, "调用了onResult....................");
if (recognizerResult != null){
LogUtil.e(TAG ,"recognizer Result:" + recognizerResult.getResultString());
String text;
//JsonParser:Json解析(代码在下方),text为解析出的命令词
text = JsonParser.parseGramarResult(recognizerResult.getResultString());
switch(text){
case "开灯":
break;
case "关灯":
break;
case "求救":
break;
case "安全":
break;
case "变亮":
break;
case "变暗":
break;
case "进餐":
break;
case "阅读":
break;
case "睡觉":
break;
case "复位":
break;
default:
Toast.makeText(getApplicationContext(),"无法匹配命令词,请重新发送命令",Toast.LENGTH_SHORT).show();
}
}else{
LogUtil.e(TAG,"recognizerResult为null....................");
}
}
//错误回调
@Override
public void onError(SpeechError speechError) {
LogUtil.e("mRecognizerListener","出错,错误代码:"+speechError.getErrorCode());
Toast.makeText(getApplicationContext(),"出错,错误代码:"+speechError.getErrorCode(),Toast.LENGTH_SHORT).show();
}
//事件回调
@Override
public void onEvent(int i, int i1, int i2, Bundle bundle) {}
};
FucUtil:
public class 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){
e.printStackTrace();
}
return result;
}
}
JsonParser:
public class JsonParser {
public static String TAG = "JsonParser";
public static int index = 0;
public static String parseGramarResult(String json){
StringBuffer ret = new StringBuffer() ;
try{
JSONTokener mJSONTokener = new JSONTokener(json);
JSONObject mJSONObject = new JSONObject(mJSONTokener);
JSONArray words = mJSONObject.getJSONArray("ws");
for (int i = 0 ; i < words.length(); i++){
JSONArray items = words.getJSONObject(i).getJSONArray("cw");
for (int j = 0 ; j < items.length() ; j++){
JSONObject obj = items.getJSONObject(j);
if (obj.getString("w").contains("nomatch")){
ret.append("没有匹配结果");
return ret.toString();
}
//ret=命令词 置信度 命令词 置信度
ret.append(obj.getString("w"));
ret.append(obj.getInt("sc"));
}
}
}catch (Exception e){
e.printStackTrace();
ret.append("没有匹配结果");
}
for (; index < ret.length();index++){
//判断ret的内容,找到最接近的命令词,isDigit判断字符是否是数字,是数字则跳出循环
if(Character.isDigit(ret.charAt(index)))
break;
}
//retCommand = 最匹配的命令词
String retCommand = ret.substring(0, index);
LogUtil.e(TAG, "Json解析结果: " + ret);
LogUtil.e(TAG, "retComand: " + retCommand);
return retCommand;
}
}
ToasrUtil(3500ms内自定义Toast显示时间)
public class ToastUtils{
public static void makeText(Context mContext,String mString,final int time){
final Toast mToast = Toast.makeText(mContext,mString,Toast.LENGTH_LONG);
mToast.show();
Handler mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (time <= 3500){
mToast.cancel();
}
}
},time);
}
}
LogUtil:类似于Log,设定规格,编写调试时开启,完成编译成apk时设置LEVEL为NOTHING,出自郭霖大神《第一行代码》
public class LogUtil {
public static final int VERBOSE = 1;
public static final int DEBUG = 2;
public static final int INFO = 3;
public static final int WARN = 4;
public static final int ERROR = 5;
public static final int NOTHING = 6;
//测试时LEVEL=VERBOSE,上架后LEVEL=NOTHING
public static final int LEVEL = VERBOSE;
public static void v(String tag,String msg){
if (LEVEL <= VERBOSE){
Log.v(tag,msg);
}
}
public static void d(String tag,String msg){
if (LEVEL <= DEBUG){
Log.d(tag, msg);
}
}
public static void i(String tag,String msg){
if (LEVEL <= INFO){
Log.i(tag, msg);
}
}
public static void w(String tag,String msg){
if (LEVEL <= WARN){
Log.w(tag, msg);
}
}
public static void e(String tag,String msg){
if (LEVEL <= ERROR){
Log.e(tag,msg);
}
}
}
这次记录到此已经结束了,大家转载的话说明原址,有哪里做得不好或者不对也希望大家不吝赐教。。。万分感谢