本人是个未满一年经验的小新,正在进行Android修仙之路,由于没有师傅只能单兵作战,在修仙过程之中不断通过实战提高自己的修为。若对实战过程中有误的地方希望道友帮忙指正,不然本人将误入歧途最终成魔(哈哈)。在实战过程之前小辈会参考其他前辈的实战经验,这也是大多修真者都会这么做的,所以在实战过程中都不是原创。
语音识别在日常APP中很少运用到除非特殊的功能,比如短信中可以使用语音来输入要发送的内容。如果有玩过老罗的子弹短信这个功能应该都很清楚。而在机器人领域中语音识别是必不可少的,在语音识别中有很多平台提供服务,比较出名的有讯飞,百度,搜狗等。但是大多数都选择讯飞,讯飞起步的比较早,对语音这块积累了很多经验。由于本人需要做机器人应用,所以今天就来介绍如何集成科大讯飞语音识别。
这篇文章只讲了讯飞某些功能,其他功能就没有讲了,比如人脸识别,这个需要传输图片给讯飞引擎识别,对于我的业务来说这个满足不了,后面选择虹软离线识别。具体讲的内容如下。
- 前戏
- 语音合成
- 语音听写
- 语法识别
- 语音唤醒
- AIUI人机交互解决方案
- 工具类封装
前戏
注册账号与下载资源
还没注册的先进入科大讯飞官网 然后注册个账号,登入成功后点击右上方的控制台,进入控制台后点击创建应用
进入控制台 创建应用 添加功能 添加以上服务 ,这时候会弹出一个网页叫您输入唤醒名称,这里我就输入“小宝小宝”。导入SDK
把解压后的jar包与so包放入libs目录下,然后在APP build.gradle android标签内添加以下代码,当然您也可以创建jinLibs目录把so包放入此目录也行。
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
照样右击main目录,依次选择News-->Folder--<Assets Folder,把SDK中的以下资源拷贝到自己项目中
导入资源
至于这些目录是干啥用的,后面集成的时候会陆陆续续说明。
添加权限
<!--连接网络权限,用于执行云端语音能力 -->
<uses-permission android:name="android.permission.INTERNET" />
<!--获取手机录音机使用权限,听写、识别、语义理解需要用到此权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!--读取网络信息状态 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--获取当前wifi状态 -->
<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" />
<!--SD卡读写的权限(如果需要保存音频文件到本地的话)-->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
下面是科大讯飞的主要功能接口图
SDK接口图
从图可以查看到讯飞SDK提供的以下功能,但是这里注意的一点就是讯飞下载的SDK是按功能来进行分配的,您在后台选择人脸识别SDK可能不包含语音唤醒。所以在应用使用不包含的功能时,会出现崩溃或报20021的错误,这时候就要想是自己下载的SDK问题了。
SpeechUtility这个类是讯飞的SDK入门类,所以在使用SDK中的功能时首先对该对象进行初始化操作,而初始化我们通过createUtility()这个方法来完成。新建SpeechApplication并初始化SpeechUtility。
public class SpeechApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//APP_ID请到控制台进行复制
SpeechUtility.createUtility(this, "appid=" + "你的APPKEY");
// 以下语句用于设置日志开关(默认开启),设置成false时关闭语音云SDK日志打印
// Setting.setShowLog(false);
}
}
语音合成
语音合成是将一段文字转换为成语音,可根据需要合成出不同音色、语速和语调的声音,让机器像人一样开口说话。
语音合成我们使用到的是SpeechSynthesizer这个类,在使用之前我们应当去实例化它,SpeechSynthesizer这个类是不能直接new的,我们给通过它里面的静态方法createSynthesizer获取SpeechSynthesizer对象。这里需要传入两个参数,一个是Context,一个是初始化状态的回调监听,如果回调的值为ErrorCode.SUCCESS(0),就表示创建该对象成功。
// 初始化合成对象
mSpeechSynthesizer = SpeechSynthesizer.createSynthesizer(this, new InitListener() {
@Override
public void onInit(int code) {
Log.d(TAG, "InitListener init() code = " + code);
if (code != ErrorCode.SUCCESS) {
Log.d(TAG, "初始化失败,错误码:" + code);
} else {
isInitSuccess = true;
Log.d(TAG, "初始化成功");
// 初始化成功,之后可以调用startSpeaking方法
// 注:有的开发者在onCreate方法中创建完合成对象之后马上就调用startSpeaking进行合成,
// 正确的做法是将onCreate中的startSpeaking调用移至这里
}
}
});
初始化完SpeechSynthesizer对象后我们需要设置一些参数,比如语言(LANGUAGE,中文、英文等)、
方言(ACCENT,中文的普通话,粤语等)、发音人特征(性别,年龄,语气)、语速(SPEED)、音量(VOLUME)、语调(PITCH)、音频采样率(SAMPLE_RATE)
/**
* 参数设置
* @return
*/
private void initParam() {
// 清空参数
mSpeechSynthesizer.setParameter(SpeechConstant.PARAMS, null);
mSpeechSynthesizer.setParameter(SpeechConstant.TTS_DATA_NOTIFY, "1");
// 设置在线合成发音人
mSpeechSynthesizer.setParameter(SpeechConstant.VOICE_NAME, "xiaoyan");
//设置合成语速
mSpeechSynthesizer.setParameter(SpeechConstant.SPEED, "50");
//设置合成音调
mSpeechSynthesizer.setParameter(SpeechConstant.PITCH, "50");
//设置合成音量
mSpeechSynthesizer.setParameter(SpeechConstant.VOLUME, "50");
// 设置播放合成音频打断音乐播放,默认为true
mSpeechSynthesizer.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "true");
// 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
/*
mSpeechSynthesizer.setParameter(SpeechConstant.AUDIO_FORMAT, "pcm");
mSpeechSynthesizer.setParameter(SpeechConstant.TTS_AUDIO_PATH,
Environment.getExternalStorageDirectory() + "/msc/tts.pcm");
*/
}
设置参数完成之后就可以掉用startSpeaking(String text,SynthesizerListener listen)开始合成语音,这个方法需要用到两个参数,第一个就是你要合成的文本,第二个就是合成的状态回调,具体的如下面代码。
//在合成的时候需要判断当前是否初始化成功
if(!isInitSuccess){
return;
}
//根据返回值来判断状态
int code = mSpeechSynthesizer.startSpeaking("你好中国", mTtsListener);
if (code != ErrorCode.SUCCESS) {
Log.d(TAG,"语音合成失败,错误码: " + code);
}
/**
* 合成回调监听。
*/
private SynthesizerListener mTtsListener = new SynthesizerListener() {
@Override
public void onSpeakBegin() {
Log.d(TAG,"开始播放");
}
@Override
public void onSpeakPaused() {
Log.d(TAG,"暂停播放");
}
@Override
public void onSpeakResumed() {
Log.d(TAG,"继续播放");
}
@Override
public void onBufferProgress(int percent, int beginPos, int endPos,
String info) {
// 合成进度
Log.d(TAG,"合成进度 " + percent);
}
@Override
public void onSpeakProgress(int percent, int beginPos, int endPos) {
// 播放进度
Log.d(TAG,"播放进度 " + percent);
}
@Override
public void onCompleted(SpeechError error) {
if (error == null) {
Log.d(TAG,"播放完成");
} else if (error != null) {
Log.d(TAG,error.getPlainDescription(true));
}
}
@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);
// }
if (SpeechEvent.EVENT_TTS_BUFFER == eventType) {
byte[] buf = obj.getByteArray(SpeechEvent.KEY_EVENT_TTS_BUFFER);
Log.e("MscSpeechLog", "buf is =" + buf);
}
}
};
SpeechSynthesizer其他方法,根据自己的业务去使用即可,SpeechSynthesizer其他方法如下
/**
* 销毁当前对象
*/
public boolean destory(){
return mTts.destroy();
}
/**
* 获取已经创建的SpeechSynthesizer对象
* @return
*/
public SpeechSynthesizer getSynthesizer(){
return SpeechSynthesizer.getSynthesizer();
}
/**
* 获取参数 获取指定的参数的当前值。
* @param key 该值的key
* @return
*/
public String getParameter(String key){
return mTts.getParameter(key);
}
/**
* 设置语音合成的参数(下面是可以设置参数的key)
* SpeechConstant.VOICE_NAME: 发音人
* SpeechConstant.SPEED: 合成语速
* SpeechConstant.VOLUME: 合成音量
* SpeechConstant.PITCH: 合成语调
* SpeechConstant.BACKGROUND_SOUND: 背景音乐
* SpeechConstant.TTS_BUFFER_TIME: 合成音频缓冲时间
* SpeechConstant.STREAM_TYPE: 播放类型
* SpeechConstant.SAMPLE_RATE: 采样率
* SpeechConstant.TTS_AUDIO_PATH: 合成录音保存路径
* SpeechConstant.ENGINE_TYPE:引擎类型;
* ResourceUtil.TTS_RES_PATH:离线资源路径;
* ResourceUtil.ENGINE_START:启动离线引擎;
* SpeechConstant.TTS_FADING : 合成淡入淡出;
* SpeechConstant.AUDIO_FORMAT_AUE:音频流编解码格式;
* @param key
* @param value
*/
public void setParameter(String key, String value){
mTts.setParameter(key,value);
}
/**
* 停止语音
*/
public void stopSpeaking(){
mTts.stopSpeaking();
}
/**
* 对应的继续播放
* 暂停播放
*/
public void pauseSpeaking(){
mTts.pauseSpeaking();
}
/**
* 恢复播放
*/
public void resumeSpeaking(){
mTts.resumeSpeaking();
}
/**
* 是否正在播放
* @return
*/
public boolean isSpeaking(){
return mTts.isSpeaking();
}
/**
* 合成到文件 合成文本到一个音频文件,不播放
* @param text 合成的文本
* @param uri 合成的路径
* @return
*/
public int synthesizeToUri(String text, String uri,SynthesizerListener mSynthesizerListener ){
return mTts.synthesizeToUri(text,uri,mSynthesizerListener);
}
setParameter(String key,String value)这里里面应该填什么值具体的可以去看讯飞讯飞文档,最后语音合成完整代码如下
public class SpeechSynthesizerActivity extends AppCompatActivity {
private static final String TAG = SpeechSynthesizerActivity.class.getSimpleName();
// 语音合成对象
private SpeechSynthesizer mSpeechSynthesizer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_speech_synthesizer);
final EditText editText = findViewById(R.id.syn_et_text);
// 初始化合成对象
mSpeechSynthesizer = SpeechSynthesizer.createSynthesizer(this, new InitListener() {
@Override
public void onInit(int code) {
Log.d(TAG, "InitListener init() code = " + code);
if (code != ErrorCode.SUCCESS) {
Log.d(TAG, "初始化失败,错误码:" + code);
} else {
isInitSuccess = true;
Log.d(TAG, "初始化成功");
// 初始化成功,之后可以调用startSpeaking方法
// 注:有的开发者在onCreate方法中创建完合成对象之后马上就调用startSpeaking进行合成,
// 正确的做法是将onCreate中的startSpeaking调用移至这里
}
}
});
initParam();
//开始播放
findViewById(R.id.syn_btn_start_syn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(!isInitSuccess){
return;
}
String text = editText.getText().toString();
int code;
if(TextUtils.isEmpty(text)){
code = mSpeechSynthesizer.startSpeaking("请输出合成的内容",null);
}else {
code = mSpeechSynthesizer.startSpeaking(text, mTtsListener);
}
if (code != ErrorCode.SUCCESS) {
Log.d(TAG,"语音合成失败,错误码: " + code);
}
}
});
///停止播放
findViewById(R.id.syn_btn_stop_syn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mSpeechSynthesizer.stopSpeaking();
}
});
//暂停播放
findViewById(R.id.syn_btn_pause_play).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mSpeechSynthesizer.pauseSpeaking();
}
});
//继续播放
findViewById(R.id.syn_btn_resume_play).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mSpeechSynthesizer.resumeSpeaking();
}
});
}
/**
* 参数设置
* @return
*/
private void initParam() {
// 清空参数
mSpeechSynthesizer.setParameter(SpeechConstant.PARAMS, null);
// 根据合成引擎设置相应参数
mSpeechSynthesizer.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
mSpeechSynthesizer.setParameter(SpeechConstant.TTS_DATA_NOTIFY, "1");
// 设置在线合成发音人
mSpeechSynthesizer.setParameter(SpeechConstant.VOICE_NAME, "xiaoyan");
//设置合成语速
mSpeechSynthesizer.setParameter(SpeechConstant.SPEED, "50");
//设置合成音调
mSpeechSynthesizer.setParameter(SpeechConstant.PITCH, "50");
//设置合成音量
mSpeechSynthesizer.setParameter(SpeechConstant.VOLUME, "50");
// 设置播放合成音频打断音乐播放,默认为true
mSpeechSynthesizer.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "true");
// 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
/*
mSpeechSynthesizer.setParameter(SpeechConstant.AUDIO_FORMAT, "pcm");
mSpeechSynthesizer.setParameter(SpeechConstant.TTS_AUDIO_PATH,
Environment.getExternalStorageDirectory() + "/msc/tts.pcm");
*/
}
boolean isInitSuccess;
/**
* 合成回调监听。
*/
private SynthesizerListener mTtsListener = new SynthesizerListener() {
@Override
public void onSpeakBegin() {
Log.d(TAG,"开始播放");
}
@Override
public void onSpeakPaused() {
Log.d(TAG,"暂停播放");
}
@Override
public void onSpeakResumed() {
Log.d(TAG,"继续播放");
}
@Override
public void onBufferProgress(int percent, int beginPos, int endPos,
String info) {
// 合成进度
Log.d(TAG,"合成进度 " + percent);
}
@Override
public void onSpeakProgress(int percent, int beginPos, int endPos) {
// 播放进度
Log.d(TAG,"播放进度 " + percent);
}
@Override
public void onCompleted(SpeechError error) {
if (error == null) {
Log.d(TAG,"播放完成");
} else if (error != null) {
Log.d(TAG,error.getPlainDescription(true));
}
}
@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);
// }
if (SpeechEvent.EVENT_TTS_BUFFER == eventType) {
byte[] buf = obj.getByteArray(SpeechEvent.KEY_EVENT_TTS_BUFFER);
Log.e("MscSpeechLog", "buf is =" + buf);
}
}
};
@Override
protected void onDestroy() {
super.onDestroy();
if( null != mSpeechSynthesizer){
mSpeechSynthesizer.stopSpeaking();
// 退出时释放连接
mSpeechSynthesizer.destroy();
}
}
}
语音听写
将一段语音转换成文本,把语音中包含文字信息提取出来,并可以优先识别用户手机特有的联系人和个性化数据。语音识别包括听写、语法识别功能。语音识别技术(Auto Speech Recognize, 简称ASR)即把人的自然语言音频数据转换成文本数据。
语音识别对象SpeechRecognizer跟SpeechSynthesizer是一样的都是先通过.静态方法createXXX创建。
mSpeechRecognizer = SpeechRecognizer.createRecognizer(this,mInitListener);
private InitListener mInitListener = new InitListener() {
@Override
public void onInit(int code) {
Log.d(TAG, "SpeechRecognizer init() code = " + code);
if (code != ErrorCode.SUCCESS) {
Log.d(TAG,"初始化失败,错误码:" + code);
ToastUtils.showToast(SpeechRecognizerActivity.this,"初始化失败,错误码:" + code);
}else {
Log.d(TAG,"初始化成功");
ToastUtils.showToast(SpeechRecognizerActivity.this,"初始化成功");
}
}
};
初始化完成之后进行设置参数。
/**
* 参数设置
* @return
*/
public void initParam() {
// 清空参数
mSpeechRecognizer.setParameter(SpeechConstant.PARAMS, null);
mSpeechRecognizer.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);
// 设置返回结果格式
mSpeechRecognizer.setParameter(SpeechConstant.RESULT_TYPE, "json");
//此处用于设置dialog中不显示错误码信息
//mIat.setParameter("view_tips_plain","false");
// 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理
mSpeechRecognizer.setParameter(SpeechConstant.VAD_BOS, "4000");
// 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音
mSpeechRecognizer.setParameter(SpeechConstant.VAD_EOS, "1000");
// 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点
mSpeechRecognizer.setParameter(SpeechConstant.ASR_PTT, "1");
// 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
/*mSpeechRecognizer.setParameter(SpeechConstant.AUDIO_FORMAT,"wav");
mSpeechRecognizer.setParameter(SpeechConstant.ASR_AUDIO_PATH,
Environment.getExternalStorageDirectory()+"/msc/iat.wav");*/
}
具体可以设置哪些参数看下面图
参数设置
讯飞SDK内容提供了一个识别对话框RecognizerDialog来进行交互,此UI如下
正常识别
识别错误别
如果想使用讯飞这个UI前提是需要导入资源包,该资源包在SDK res目录下,如果对该UI不满意的可以导入或修改资源包下的文件即可 资源包
带对话框识别
首先实例化RecognizerDialog
RecognizerDialog mIatDialog = new RecognizerDialog(SpeechRecognizerActivity.this,
mInitListener);
mIatDialog.setListener(mRecognizerDialogListener);
mIatDialog.show();
设置听写UI监听器
private RecognizerDialogListener mRecognizerDialogListener = new RecognizerDialogListener() {
public void onResult(RecognizerResult results, boolean isLast) {
printResult(results);
}
/**
* 识别回调错误.
*/
public void onError(SpeechError error) {
Log.d( TAG,error.getPlainDescription(true));
}
};
处理识别的结果
private void handleResult(RecognizerResult results) {
String text = JsonParser.parseIatResult(results.getResultString());
String sn = null;
// 读取json结果中的sn字段
try {
JSONObject resultJson = new JSONObject(results.getResultString());
sn = resultJson.optString("sn");
} catch (JSONException e) {
e.printStackTrace();
}
mIatResults.put(sn, text);
StringBuffer resultBuffer = new StringBuffer();
for (String key : mIatResults.keySet()) {
resultBuffer.append(mIatResults.get(key));
}
Log.d(TAG,"听写结果 " + resultBuffer.toString());
}
不带对话框识别
开启监听
// 不显示听写对话框
mSpeechRecognizer.startListening(mRecognizerListener);
private RecognizerListener mRecognizerListener = new RecognizerListener() {
@Override
public void onVolumeChanged(int i, byte[] bytes) {
}
@Override
public void onBeginOfSpeech() {
ToastUtils.showToast(SpeechRecognizerActivity.this,"开始说话");
}
@Override
public void onEndOfSpeech() {
ToastUtils.showToast(SpeechRecognizerActivity.this,"结束说话");
}
@Override
public void onResult(RecognizerResult recognizerResult, boolean b) {
Log.d(TAG, recognizerResult.getResultString());
handleResult(recognizerResult);
}
@Override
public void onError(SpeechError error) {
// 错误码:10118(您没有说话),可能是录音机权限被禁,需要提示用户打开应用的录音权限。
ToastUtils.showToast(SpeechRecognizerActivity.this,error.getErrorDescription());
}
@Override
public void onEvent(int i, int i1, int i2, Bundle bundle) {
}
};
处理识别结果
private void handleResult(RecognizerResult results) {
String text = JsonParser.parseIatResult(results.getResultString());
String sn = null;
// 读取json结果中的sn字段
try {
JSONObject resultJson = new JSONObject(results.getResultString());
sn = resultJson.optString("sn");
} catch (JSONException e) {
e.printStackTrace();
}
mIatResults.put(sn, text);
StringBuffer resultBuffer = new StringBuffer();
for (String key : mIatResults.keySet()) {
resultBuffer.append(mIatResults.get(key));
}
Log.d(TAG,"听写结果 " + resultBuffer.toString());
ToastUtils.showToast(this,"听写结果 " + resultBuffer.toString());
}
更多的方法可以查看讯飞API文档,下面是该语音听写的全部代码
public class SpeechRecognizerActivity extends AppCompatActivity {
private static final String TAG = SpeechRecognizerActivity.class.getSimpleName();
// 语音听写对象
private SpeechRecognizer mSpeechRecognizer;
// 语音听写UI
private RecognizerDialog mIatDialog;
// 用HashMap存储听写结果
private HashMap<String, String> mIatResults = new LinkedHashMap<String, String>();
private String mEngineType = SpeechConstant.TYPE_CLOUD;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_speech_recognizer);
mSpeechRecognizer = SpeechRecognizer.createRecognizer(this,mInitListener);
initParam();
findViewById(R.id.recong_btn_start_recong_dialog).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mIatDialog = new RecognizerDialog(SpeechRecognizerActivity.this,
mInitListener);
mIatDialog.setListener(mRecognizerDialogListener);
mIatDialog.show();
}
});
findViewById(R.id.recong_btn_start_recong).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 不显示听写对话框
mSpeechRecognizer.startListening(mRecognizerListener);
}
});
findViewById(R.id.recong_btn_stop).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mSpeechRecognizer.stopListening();
}
});
findViewById(R.id.recong_btn_canncel).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mSpeechRecognizer.cancel();
}
});
}
/**
* 参数设置
* @return
*/
public void initParam() {
// 清空参数
mSpeechRecognizer.setParameter(SpeechConstant.PARAMS, null);
mSpeechRecognizer.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);
// 设置返回结果格式
mSpeechRecognizer.setParameter(SpeechConstant.RESULT_TYPE, "json");
//此处用于设置dialog中不显示错误码信息
//mIat.setParameter("view_tips_plain","false");
// 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理
mSpeechRecognizer.setParameter(SpeechConstant.VAD_BOS, "4000");
// 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音
mSpeechRecognizer.setParameter(SpeechConstant.VAD_EOS, "1000");
// 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点
mSpeechRecognizer.setParameter(SpeechConstant.ASR_PTT, "1");
// 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
/*mSpeechRecognizer.setParameter(SpeechConstant.AUDIO_FORMAT,"wav");
mSpeechRecognizer.setParameter(SpeechConstant.ASR_AUDIO_PATH,
Environment.getExternalStorageDirectory()+"/msc/iat.wav");*/
}
/**
* 初始化监听器。
*/
private InitListener mInitListener = new InitListener() {
@Override
public void onInit(int code) {
Log.d(TAG, "SpeechRecognizer init() code = " + code);
if (code != ErrorCode.SUCCESS) {
Log.d(TAG,"初始化失败,错误码:" + code);
ToastUtils.showToast(SpeechRecognizerActivity.this,"初始化失败,错误码:" + code);
}else {
Log.d(TAG,"初始化成功");
ToastUtils.showToast(SpeechRecognizerActivity.this,"初始化成功");
}
}
};
/**
* 听写UI监听器
*/
private RecognizerDialogListener mRecognizerDialogListener = new RecognizerDialogListener() {
public void onResult(RecognizerResult results, boolean isLast) {
handleResult(results);
}
/**
* 识别回调错误.
*/
public void onError(SpeechError error) {
Log.d( TAG,error.getErrorDescription());
}
};
/**
* 听写监听器。
*/
private RecognizerListener mRecognizerListener = new RecognizerListener() {
@Override
public void onVolumeChanged(int i, byte[] bytes) {
}
@Override
public void onBeginOfSpeech() {
ToastUtils.showToast(SpeechRecognizerActivity.this,"开始说话");
}
@Override
public void onEndOfSpeech() {
ToastUtils.showToast(SpeechRecognizerActivity.this,"结束说话");
}
@Override
public void onResult(RecognizerResult recognizerResult, boolean b) {
Log.d(TAG, recognizerResult.getResultString());
handleResult(recognizerResult);
}
@Override
public void onError(SpeechError error) {
// 错误码:10118(您没有说话),可能是录音机权限被禁,需要提示用户打开应用的录音权限。
ToastUtils.showToast(SpeechRecognizerActivity.this,error.getErrorDescription());
}
@Override
public void onEvent(int i, int i1, int i2, Bundle bundle) {
}
};
private void handleResult(RecognizerResult results) {
String text = JsonParser.parseIatResult(results.getResultString());
String sn = null;
// 读取json结果中的sn字段
try {
JSONObject resultJson = new JSONObject(results.getResultString());
sn = resultJson.optString("sn");
} catch (JSONException e) {
e.printStackTrace();
}
mIatResults.put(sn, text);
StringBuffer resultBuffer = new StringBuffer();
for (String key : mIatResults.keySet()) {
resultBuffer.append(mIatResults.get(key));
}
Log.d(TAG,"听写结果 " + resultBuffer.toString());
ToastUtils.showToast(this,"听写结果 " + resultBuffer.toString());
}
@Override
protected void onDestroy() {
super.onDestroy();
if( null != mSpeechRecognizer ){
// 退出时释放连接
mSpeechRecognizer.cancel();
mSpeechRecognizer.destroy();
}
}
}
语法识别
判断用户所说的内容是否与预定义的语法相符合,主要用于识别用户是否下达某项指令,使用语法识别前,需要先定义语法。
讯飞语法识别分为两种,一种是在线识别和离线识别,不过在线语法识别服务已下线,新用户无法使用。等后续状况再更新。
语音唤醒
通过说出特定的唤醒词(如“芝麻开门”)来唤醒处于休眠状态下的终端设备。语音唤醒的操作都是由VoiceWakeuper对象来完成。
实例化VoiceWakeuper
mVoiceWakeuper = VoiceWakeuper.createWakeuper(this, new InitListener() {
@Override
public void onInit(int code) {
if (code != ErrorCode.SUCCESS) {
Log.d(TAG,"初始化失败,错误码:" + code);
ToastUtils.showToast(WakeUpActivity.this,"初始化失败,错误码:" + code);
}else {
Log.d(TAG,"初始化成功");
ToastUtils.showToast(WakeUpActivity.this,"初始化成功");
}
}
});
设置唤醒参数
//唤醒的阈值,就相当于门限值,当用户输入的语音的置信度大于这一个值的时候,才被认定为成功唤醒。
private int curThresh = 1000;
//是否持续唤醒
private String keep_alive = "1";
/**
* 闭环优化网络模式有三种:
* 模式0:关闭闭环优化功能
*
* 模式1:开启闭环优化功能,允许上传优化数据。需开发者自行管理优化资源。
* sdk提供相应的查询和下载接口,请开发者参考API文档,具体使用请参考本示例
* queryResource及downloadResource方法;
*
* 模式2:开启闭环优化功能,允许上传优化数据及启动唤醒时进行资源查询下载;
* 本示例为方便开发者使用仅展示模式0和模式2;
*/
private String ivwNetMode = "0";
// 语音唤醒对象
private VoiceWakeuper mVoiceWakeuper;
private void initParame() {
//非空判断,防止因空指针使程序崩溃
mVoiceWakeuper = VoiceWakeuper.getWakeuper();
if(mVoiceWakeuper != null) {
// 清空参数
mVoiceWakeuper.setParameter(SpeechConstant.PARAMS, null);
// 唤醒门限值,根据资源携带的唤醒词个数按照“id:门限;id:门限”的格式传入
mVoiceWakeuper.setParameter(SpeechConstant.IVW_THRESHOLD, "0:"+ curThresh);
// 设置唤醒模式
mVoiceWakeuper.setParameter(SpeechConstant.IVW_SST, "wakeup");
// 设置持续进行唤醒
mVoiceWakeuper.setParameter(SpeechConstant.KEEP_ALIVE, keep_alive);
// 设置闭环优化网络模式
mVoiceWakeuper.setParameter(SpeechConstant.IVW_NET_MODE, ivwNetMode);
// 设置唤醒资源路径
mVoiceWakeuper.setParameter(SpeechConstant.IVW_RES_PATH, getResource());
// 设置唤醒录音保存路径,保存最近一分钟的音频
/*mVoiceWakeuper.setParameter( SpeechConstant.IVW_AUDIO_PATH,
Environment.getExternalStorageDirectory().getPath()+"/msc/ivw.wav" );
mVoiceWakeuper.setParameter( SpeechConstant.AUDIO_FORMAT, "wav" );*/
// 如有需要,设置 NOTIFY_RECORD_DATA 以实时通过 onEvent 返回录音音频流字节
//mIvw.setParameter( SpeechConstant.NOTIFY_RECORD_DATA, "1" );
}
}
在设置参数中需要获取唤醒资源的路径,在使用前确保把ivw资源目录拷贝到assets目录下,该代码如下
private String getResource() {
final String resPath = ResourceUtil.
generateResourcePath(this,
ResourceUtil.RESOURCE_TYPE.assets,
"ivw/"+ AppSetting.APP_ID+".jet");
return resPath;
}
开启唤醒监听唤醒状态
/**
* 开启唤醒功能
*/
public void startWakeuper() {
mVoiceWakeuper.startListening(new MyWakeuperListener());
}
private class MyWakeuperListener implements WakeuperListener {
//开始说话
@Override
public void onBeginOfSpeech() {
}
//错误码返回
@Override
public void onError(SpeechError arg0) {
}
@Override
public void onEvent(int arg0, int arg1, int arg2, Bundle arg3) {
}
@Override
public void onVolumeChanged(int i) {
}
@Override
public void onResult(WakeuperResult result) {
if (!"1".equalsIgnoreCase(keep_alive)) {
//setRadioEnable(true);
}
try {
String text = result.getResultString();
JSONObject object;
object = new JSONObject(text);
StringBuffer buffer = new StringBuffer();
buffer.append("【RAW】 " + text);
buffer.append("\n");
buffer.append("【操作类型】" + object.optString("sst"));
buffer.append("\n");
buffer.append("【唤醒词id】" + object.optString("id"));
buffer.append("\n");
buffer.append("【得分】" + object.optString("score"));
buffer.append("\n");
buffer.append("【前端点】" + object.optString("bos"));
buffer.append("\n");
buffer.append("【尾端点】" + object.optString("eos"));
Log.d(TAG,"唤醒结果 " + buffer.toString());
ToastUtils.showToast(WakeUpActivity.this, "唤醒结果 " + buffer.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
}
停止唤醒
/**
* 停止唤醒
*/
public void stopWakeuper() {
mVoiceWakeuper.stopListening();
}
唤醒的唤醒门限值根据自己的需要去设置,只有当超过唤醒门限值才会回调onResult()方法。使用之前确保已经设置了唤醒词,一般在创建应用选择唤醒服务的时候会弹出一个网页让您输入唤醒词,比如我设置的唤醒词"小宝小宝",还有一点就是唤醒还需要一个jet资源,这个是讯飞自动生成的在下载的sdk中可以得到,如果您是后续才添加的唤醒重新下载SDK即可得到该资源,该语音唤醒的全部代码如下
public class WakeUpActivity extends AppCompatActivity {
private static final String TAG = WakeUpActivity.class.getSimpleName();
//唤醒的阈值,就相当于门限值,当用户输入的语音的置信度大于这一个值的时候,才被认定为成功唤醒。
private int curThresh = 1000;
//是否持续唤醒
private String keep_alive = "1";
/**
* 闭环优化网络模式有三种:
* 模式0:关闭闭环优化功能
*
* 模式1:开启闭环优化功能,允许上传优化数据。需开发者自行管理优化资源。
* sdk提供相应的查询和下载接口,请开发者参考API文档,具体使用请参考本示例
* queryResource及downloadResource方法;
*
* 模式2:开启闭环优化功能,允许上传优化数据及启动唤醒时进行资源查询下载;
* 本示例为方便开发者使用仅展示模式0和模式2;
*/
private String ivwNetMode = "0";
// 语音唤醒对象
private VoiceWakeuper mVoiceWakeuper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wake_up);
mVoiceWakeuper = VoiceWakeuper.createWakeuper(this, new InitListener() {
@Override
public void onInit(int code) {
if (code != ErrorCode.SUCCESS) {
Log.d(TAG,"初始化失败,错误码:" + code);
ToastUtils.showToast(WakeUpActivity.this,"初始化失败,错误码:" + code);
}else {
Log.d(TAG,"初始化成功");
ToastUtils.showToast(WakeUpActivity.this,"初始化成功");
}
}
});
initParame();
findViewById(R.id.wake_up_open).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startWakeuper();
}
});
findViewById(R.id.wake_up_close).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stopWakeuper();
}
});
}
private void initParame() {
//非空判断,防止因空指针使程序崩溃
mVoiceWakeuper = VoiceWakeuper.getWakeuper();
if(mVoiceWakeuper != null) {
// 清空参数
mVoiceWakeuper.setParameter(SpeechConstant.PARAMS, null);
// 唤醒门限值,根据资源携带的唤醒词个数按照“id:门限;id:门限”的格式传入
mVoiceWakeuper.setParameter(SpeechConstant.IVW_THRESHOLD, "0:"+ curThresh);
// 设置唤醒模式
mVoiceWakeuper.setParameter(SpeechConstant.IVW_SST, "wakeup");
// 设置持续进行唤醒
mVoiceWakeuper.setParameter(SpeechConstant.KEEP_ALIVE, keep_alive);
// 设置闭环优化网络模式
mVoiceWakeuper.setParameter(SpeechConstant.IVW_NET_MODE, ivwNetMode);
// 设置唤醒资源路径
mVoiceWakeuper.setParameter(SpeechConstant.IVW_RES_PATH, getResource());
// 设置唤醒录音保存路径,保存最近一分钟的音频
/*mVoiceWakeuper.setParameter( SpeechConstant.IVW_AUDIO_PATH,
Environment.getExternalStorageDirectory().getPath()+"/msc/ivw.wav" );
mVoiceWakeuper.setParameter( SpeechConstant.AUDIO_FORMAT, "wav" );*/
// 如有需要,设置 NOTIFY_RECORD_DATA 以实时通过 onEvent 返回录音音频流字节
//mIvw.setParameter( SpeechConstant.NOTIFY_RECORD_DATA, "1" );
}
}
/**
* 开启唤醒功能
*/
public void startWakeuper() {
mVoiceWakeuper.startListening(new MyWakeuperListener());
}
/**
* 停止唤醒
*/
public void stopWakeuper() {
mVoiceWakeuper.stopListening();
}
@Override
protected void onDestroy() {
super.onDestroy();
// 销毁合成对象
mVoiceWakeuper = VoiceWakeuper.getWakeuper();
if (mVoiceWakeuper != null) {
mVoiceWakeuper.destroy();
}
}
/**
* 获取唤醒词功能
* @return 返回文件位置
*/
private String getResource() {
final String resPath = ResourceUtil.
generateResourcePath(this,
ResourceUtil.RESOURCE_TYPE.assets,
"ivw/"+ AppSetting.APP_ID+".jet");
return resPath;
}
private class MyWakeuperListener implements WakeuperListener {
//开始说话
@Override
public void onBeginOfSpeech() {
}
//错误码返回
@Override
public void onError(SpeechError arg0) {
}
@Override
public void onEvent(int arg0, int arg1, int arg2, Bundle arg3) {
}
@Override
public void onVolumeChanged(int i) {
}
@Override
public void onResult(WakeuperResult result) {
if (!"1".equalsIgnoreCase(keep_alive)) {
//setRadioEnable(true);
}
try {
String text = result.getResultString();
JSONObject object;
object = new JSONObject(text);
StringBuffer buffer = new StringBuffer();
buffer.append("【RAW】 " + text);
buffer.append("\n");
buffer.append("【操作类型】" + object.optString("sst"));
buffer.append("\n");
buffer.append("【唤醒词id】" + object.optString("id"));
buffer.append("\n");
buffer.append("【得分】" + object.optString("score"));
buffer.append("\n");
buffer.append("【前端点】" + object.optString("bos"));
buffer.append("\n");
buffer.append("【尾端点】" + object.optString("eos"));
Log.d(TAG,"唤醒结果 " + buffer.toString());
ToastUtils.showToast(WakeUpActivity.this, "唤醒结果 " + buffer.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
在语音听写的时候还漏了翻译这块,不过在集成这块中遇到permission denied for appid,问了讯飞管理也还没解决,由于官方并没有提供翻译这块的demo所以暂时没写上。
网友评论