Android修真传之讯飞语音识别第二篇
本人是个未满一年经验的小新,正在进行Android修仙之路,由于没有师傅只能单兵作战,在修仙过程之中不断通过实战提高自己的修为。若对实战过程中有误的地方希望道友帮忙指正,不然本人将误入歧途最终成魔(哈哈)。在实战过程之前小辈会参考其他前辈的实战经验,这也是大多修真者都会这么做的,所以在实战过程中都不是原创。
什么是AIUI
AIUI 是科大讯飞提供的一套人机智能交互解决方案, 旨在实现人机交互无障碍,使人与机器之间可以通过语音、图像、手势等自然交互方式,进行持续,双向,自然地沟通。现阶段 AIUI 提供以语音交互为核心的交互解决方案,全链路聚合了语音唤醒、语音识别、语义理解、内容(信源)平台、语音合成等模块。可以应用于智能手机(终端)、机器人、音箱、车载、智能家居、智能客服等多种领域,让产品不仅能听会说,而且能理解会思考。AIUI 开放平台主要包含了语义技能(Skill)、问答库(Q&A)编辑以及AIUI 应用(硬件)云端配置的能力,并为不同形态产品提供了不同的接入方式
为什么要使用AIUI
比如我现在做一个机器人,这个机器人能和用户进行交流,比如我说查询今天天气,机器人会回答今天的天气状态。如果您没有接入AIUI会怎么做?是不是首先获取用户说出的内容,这个由语音听写SpeechRecognizer对象来完成,获取到内容后还需要进行分词,判断分词结果是否包含天气,然后再根据时间去查询天气。这样的话太麻烦,而且在解决这个问题上还需要更好的算法来支撑。不过AIUI帮我们解决了这个问题,首先AIUI官方提供了很多技能,比如天气技能,我们只需要添加该技能后通过监听AIUI的事件,在AIUIConstant.EVENT_RESULT中接收天气的结果即可。
什么是技能呢?这个技能就是专门解决一类的问题?比如上面的天气这个问题,这个技能只能回答和天气相关的,除非你自己自定义技能。下面让我们看看讯飞提供了哪些技能给我们开发者使用,当然技能可以进行分享让其他人使用您的技能。 技能商店集成AIUI
首先AIUI相关的操作都是由AIUIAgent这个类来完成的,我们第一步就是需要获取该对象。
//创建AIUIAgent
final AIUIAgent mAIUIAgent = AIUIAgent.createAgent(this,
getAIUIParams(),mAIUIListener);
第一个参数不解释,第二个参数主要是获取我们assest目录下的cfg文件的内容,如果你下载的SDK中没有改文件的话说明您还没引入AIUI服务,该getAIUIParams方法如下
private String getAIUIParams() {
String params = "";
AssetManager assetManager = getResources().getAssets();
try {
InputStream ins = assetManager.open( "cfg/aiui_phone.cfg" );
byte[] buffer = new byte[ins.available()];
ins.read(buffer);
ins.close();
params = new String(buffer);
} catch (IOException e) {
e.printStackTrace();
}
return params;
}
第三个就是接受AIUI SDK抛出事件的监听器
private int mAIUIState;
private AIUIListener mAIUIListener = new AIUIListener() {
@Override
public void onEvent(AIUIEvent event) {
switch (event.eventType) {
case AIUIConstant.EVENT_WAKEUP:
//唤醒事件
Log.d( TAG, "on event: "+ event.eventType );
break;
case AIUIConstant.EVENT_RESULT: {
//结果解析事件
try {
JSONObject bizParamJson = new JSONObject(event.info);
JSONObject data = bizParamJson.getJSONArray("data").getJSONObject(0);
JSONObject params = data.getJSONObject("params");
JSONObject content = data.getJSONArray("content").getJSONObject(0);
if (content.has("cnt_id")) {
String cnt_id = content.getString("cnt_id");
JSONObject cntJson = new JSONObject(new String(event.data.getByteArray(cnt_id), "utf-8"));
String sub = params.optString("sub");
if ("nlp".equals(sub)) {
// 解析得到语义结果
String resultStr = cntJson.optString("intent");
Log.d( TAG, "解析得到语义结果 " + resultStr );
JSONObject obj = new JSONObject(resultStr);
JSONObject user = obj.getJSONObject("answer");
String result = user.getString("text");
Log.d( TAG, "获取结果内容 " + result );
ToastUtils.showToast(AiuiActivity.this,"获取结果内容" + result);
}
}
} catch (Throwable e) {
e.printStackTrace();
}
} break;
case AIUIConstant.EVENT_ERROR: {
//错误事件
Log.d( TAG, "on event: "+ event.eventType );
Log.d(TAG, "错误: "+event.arg1+"\n"+event.info );
} break;
case AIUIConstant.EVENT_VAD: {
if (AIUIConstant.VAD_BOS == event.arg1) {
//语音前端点
} else if (AIUIConstant.VAD_EOS == event.arg1) {
//语音后端点
}
} break;
case AIUIConstant.EVENT_START_RECORD: {
Log.d( TAG, "on event: "+ event.eventType );
//开始录音
} break;
case AIUIConstant.EVENT_STOP_RECORD: {
Log.d( TAG, "on event: "+ event.eventType );
// 停止录音
} break;
case AIUIConstant.EVENT_STATE: {
mAIUIState = event.arg1;
if (AIUIConstant.STATE_IDLE == event.arg1) {
// 闲置状态,AIUI未开启
} else if (AIUIConstant.STATE_READY == event.arg1) {
// AIUI已就绪,等待唤醒
} else if (AIUIConstant.STATE_WORKING == event.arg1) {
// AIUI工作中,可进行交互
}
} break;
default:
break;
}
}
};
最终在AIUIConstant.EVENT_RESULT中解析最终的结果,接下来就是需要获取用户输入的内容然后发送给AIUI,但是这里需要注意一点,在进行语音识别前首先开启唤醒改变AIUI内部状态,只有唤醒状态AIUI才能接收语音输入
// 先发送唤醒消息,改变AIUI内部状态,只有唤醒状态才能接收语音输入
if( AIUIConstant.STATE_WORKING != mAIUIState ){
AIUIMessage wakeupMsg = new AIUIMessage(
AIUIConstant.CMD_WAKEUP,
0,
0,
"",
null);
mAIUIAgent.sendMessage(wakeupMsg);
这里我就用讯飞自带的RecognizerDialog,首先实例化RecognizerDialog并设置监听
private RecognizerDialog mRecognizerDialog;
// 用HashMap存储听写结果
private HashMap<String, String> mIatResults = new LinkedHashMap<String, String>();
mRecognizerDialog = new RecognizerDialog(this,null);
mRecognizerDialog.setListener(new RecognizerDialogListener() {
@Override
public void onResult(RecognizerResult recognizerResult, boolean b) {
handleResult(recognizerResult);
}
@Override
public void onError(SpeechError speechError) {
}
});
mRecognizerDialog.show();
处理听写结果
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());
sendData(resultBuffer.toString());
发送数据给AIUI
//发送数据
public void sendData(String text){
try {
// 在输入参数中设置tag,则对应结果中也将携带该tag,可用于关联输入输出
String params = "data_type=text,tag=text-tag";
byte[] textData = text.getBytes("utf-8");
AIUIMessage write = new AIUIMessage(AIUIConstant.CMD_WRITE, 0, 0, params, textData);
mAIUIAgent.sendMessage(write);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
最后我们需要在后台添加我们感兴趣的技能即可
进入详情配置
添加技能
测试结果
测试结果
这里偷了懒并没有把结果解析成对象
问答QA
创建问答库 填写问答内容 从图中可以得知问题与答案成多对多的关系,这里回答的答案可以进行设置如下图 回答设置 ,每当填写完新的问答后应用必须重新开启。 测试结果自定义技能
自定义技能看文档需要Node.JS 就不介绍了。
工具类封装
这里我就简单封装了下语音合成,识别,唤醒以及AIUI
初始化各个对象
/**
* 初始化引擎
* @param context 上下文
* @param appid appid不解释
*/
public void initEngine(final Context context, final String appid){
SpeechUtility.createUtility(context, "appid=" + appid);
mSpeechSynthesizer = SpeechSynthesizer.createSynthesizer(context, new InitListener() {
@Override
public void onInit(int code) {
if (code != ErrorCode.SUCCESS) {
Log.d(TAG, "语音合成初始化失败,错误码:" + code);
} else {
Log.d(TAG, "语音合成初始化成功");
}
}
});
mSpeechRecognizer = SpeechRecognizer.createRecognizer(context, new InitListener() {
@Override
public void onInit(int code) {
if (code != ErrorCode.SUCCESS) {
Log.d(TAG, "语音识别初始化失败,错误码:" + code);
} else {
Log.d(TAG, "语音识别初始化成功");
}
}
});
mVoiceWakeuper = VoiceWakeuper.createWakeuper(context, new InitListener() {
@Override
public void onInit(int code) {
if (code != ErrorCode.SUCCESS) {
Log.d(TAG,"唤醒初始化失败,错误码:" + code);
}else {
Log.d(TAG,"唤醒初始化成功");
}
}
});
//创建AIUIAgent
mAiuiAgent = AIUIAgent.createAgent(context,
getAIUIParams(context),mAIUIListener);
}
语音合成
/**
* 语音合成对象
*/
private SpeechSynthesizer mSpeechSynthesizer;
/**
* 设置合成的参数
* @param key 参数key
* @param value 参数值
*/
public void setSynthesizerParameter(String key,String value){
Log.d(TAG,"setSynthesizerParameter " + key + " " + value);
if(null == mSpeechSynthesizer){
return;
}
mSpeechSynthesizer.setParameter(key,value);
}
/**
* 开始合成
* @param text 合成的文本
*/
public void startSpeak(String text){
Log.d(TAG,"startSpeak " + text);
if(null == mSpeechSynthesizer){
return;
}
mSpeechSynthesizer.startSpeaking(text,null);
}
/**
* 开始合成
* @param text 合成的文本
* @param synthesizerListener 合成的状态
*/
public void startSpeak(String text, SynthesizerListener synthesizerListener){
Log.d(TAG,"startSpeak " + text);
if(null == mSpeechSynthesizer){
return;
}
mSpeechSynthesizer.startSpeaking(text,synthesizerListener);
}
/**
* 是否正在合成
* @return
*/
public boolean isSpeaking(){
Log.d(TAG,"isSpeaking ");
if(null == mSpeechSynthesizer){
return false;
}
return mSpeechSynthesizer.isSpeaking();
}
/**
* 停止语音
*/
public void stopSpeaking(){
Log.d(TAG,"stopSpeaking ");
if(null == mSpeechSynthesizer){
return;
}
mSpeechSynthesizer.stopSpeaking();
}
/**
* 对应的继续播放
* 暂停播放
*/
public void pauseSpeaking(){
Log.d(TAG,"pauseSpeaking ");
if(null == mSpeechSynthesizer){
return;
}
mSpeechSynthesizer.pauseSpeaking();
}
/**
* 恢复播放
*/
public void resumeSpeaking(){
Log.d(TAG,"resumeSpeaking ");
if(null == mSpeechSynthesizer){
return;
}
mSpeechSynthesizer.resumeSpeaking();
}
/**
* 合成到文件 合成文本到一个音频文件,不播放
* @param text 合成的文本
* @param uri 合成的路径
* @return
*/
public int synthesizeToUri(String text, String uri,SynthesizerListener mSynthesizerListener ){
Log.d(TAG,"synthesizeToUri ");
if(null == mSpeechSynthesizer){
return 0;
}
return mSpeechSynthesizer.synthesizeToUri(text,uri,mSynthesizerListener);
}
语音识别
/**
* 语音听写对象
*/
private SpeechRecognizer mSpeechRecognizer;
/**
* 语音听写对话框
*/
private RecognizerDialog mRecognizerDialog;
/**
* 用HashMap存储听写结果
*/
private HashMap<String, String> mIatResults = new LinkedHashMap<String, String>();
/**
* 设置语音听写参数
* @param key
* @param value
*/
public void setRecognizerParameter(String key,String value){
Log.d(TAG,"setmRecognizerParameter " + key + " " + value);
if(null == mSpeechRecognizer){
return;
}
mSpeechRecognizer.setParameter(key,value);
}
/**
* 开始听写(不带识别框)
*/
public void startRecognizer(RecognizerListener recognizerListener){
Log.d(TAG,"startRecognizer " );
if(null == mSpeechRecognizer){
return;
}
chageAiuiState();
mSpeechRecognizer.startListening(recognizerListener);
}
/**
* 开始听写(带识别框)
*/
public void startRecognizerDialog(Context context, final SpeechRecongnizerResult recongnizerResult){
Log.d(TAG,"startRecognizerDialog " );
chageAiuiState();
mRecognizerDialog = new RecognizerDialog(context, null);
mRecognizerDialog.setListener(new RecognizerDialogListener() {
@Override
public void onResult(RecognizerResult recognizerResult, boolean b) {
String result = handleResult(recognizerResult);
if(null != recongnizerResult){
recongnizerResult.onResult(result);
mRecognizerDialog.dismiss();
}
}
@Override
public void onError(SpeechError speechError) {
if(null != recongnizerResult){
recongnizerResult.onError(speechError.getErrorDescription());
}
}
});
mRecognizerDialog.show();
}
/**
* 停止识别
*/
public void stopRecognizer(){
Log.d(TAG,"stopRecognizer " );
if(null == mSpeechRecognizer){
return;
}
mSpeechRecognizer.stopListening();
}
/**
* 是否正在识别
* @return
*/
public boolean isRecognizer(){
Log.d(TAG,"isRecognizer " );
if(null == mSpeechRecognizer){
return false;
}
return mSpeechRecognizer.isListening();
}
/**
* 处理识别结果
* @param results
* @param
*/
public String handleResult(RecognizerResult results) {
Log.d(TAG,"handleResult " + 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());
//向AIUI写入数据
try {
// 在输入参数中设置tag,则对应结果中也将携带该tag,可用于关联输入输出
String params = "data_type=text,tag=text-tag";
byte[] textData = resultBuffer.toString().getBytes("utf-8");
AIUIMessage write = new AIUIMessage(AIUIConstant.CMD_WRITE, 0, 0, params, textData);
mAiuiAgent.sendMessage(write);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return resultBuffer.toString();
}
语音唤醒
/**
* 语音唤醒对象
*/
private VoiceWakeuper mVoiceWakeuper;
/**
* 设置唤醒参数
* @param key
* @param value
*/
public void setWakeParame(String key,String value){
if(null == mVoiceWakeuper){
return;
}
mVoiceWakeuper.setParameter(key,value);
}
/**
* 获取唤醒资源
* @param context
* @param appid
* @return
*/
public String getResource(Context context,String appid){
String resPath = ResourceUtil.
generateResourcePath(context,
ResourceUtil.RESOURCE_TYPE.assets,
"ivw/"+ appid+".jet");
return resPath;
}
/**
* 开启唤醒功能
*/
public void startWakeuper(WakeuperListener wakeuperListener) {
if(null == mVoiceWakeuper){
return;
}
mVoiceWakeuper.startListening(wakeuperListener);
}
/**
* 停止唤醒
*/
public void stopWakeuper() {
mVoiceWakeuper.stopListening();
}
/**
* 处理唤醒结果
* @param wakeuperResult
*/
public WakeResult handleWakeResult(WakeuperResult wakeuperResult){
Log.d(TAG,"handleWakeResult " + wakeuperResult);
WakeResult wakeResult = new WakeResult();
String text = wakeuperResult.getResultString();
JSONObject object;
try {
object = new JSONObject(text);
wakeResult.setSst(object.optString("sst"));
wakeResult.setId(object.optString("id"));
wakeResult.setScore(object.optString("score"));
wakeResult.setBos(object.optString("bos"));
wakeResult.setEos(object.optString("eos"));
return wakeResult;
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
AIUI
private AIUIAgent mAiuiAgent;
private String getAIUIParams(Context context) {
String params = "";
AssetManager assetManager = context.getResources().getAssets();
try {
InputStream ins = assetManager.open( "cfg/aiui_phone.cfg" );
byte[] buffer = new byte[ins.available()];
ins.read(buffer);
ins.close();
params = new String(buffer);
} catch (IOException e) {
e.printStackTrace();
}
return params;
}
private AIUIListener mAIUIListener = new AIUIListener() {
@Override
public void onEvent(AIUIEvent event) {
if(null == mAiuiResultListn){
return;
}
switch (event.eventType) {
case AIUIConstant.EVENT_WAKEUP:
//唤醒事件
Log.d( TAG, "on event: "+ event.eventType );
mAiuiResultListn.onWakeUp();
break;
case AIUIConstant.EVENT_RESULT: {
//结果解析事件
try {
JSONObject bizParamJson = new JSONObject(event.info);
JSONObject data = bizParamJson.getJSONArray("data").getJSONObject(0);
JSONObject params = data.getJSONObject("params");
JSONObject content = data.getJSONArray("content").getJSONObject(0);
if (content.has("cnt_id")) {
String cnt_id = content.getString("cnt_id");
JSONObject cntJson = new JSONObject(new String(event.data.getByteArray(cnt_id), "utf-8"));
String sub = params.optString("sub");
if ("nlp".equals(sub)) {
// 解析得到语义结果
String resultStr = cntJson.optString("intent");
Log.d( TAG, "解析得到语义结果 " + resultStr );
JSONObject obj = new JSONObject(resultStr);
JSONObject user = obj.getJSONObject("answer");
String result = user.getString("text");
Log.d( TAG, "获取结果内容 " + result );
mAiuiResultListn.onResult(result);
}
}
} catch (Throwable e) {
e.printStackTrace();
mAiuiResultListn.onResult("我还不知道您说什么,您可以到技能商店添加应用哦");
}
} break;
case AIUIConstant.EVENT_ERROR: {
//错误事件
Log.d( TAG, "on event: "+ event.eventType );
Log.d(TAG, "错误: "+event.arg1+"\n"+event.info );
mAiuiResultListn.onError(event.arg1+"\n"+event.info );
} break;
case AIUIConstant.EVENT_VAD: {
if (AIUIConstant.VAD_BOS == event.arg1) {
//语音前端点
} else if (AIUIConstant.VAD_EOS == event.arg1) {
//语音后端点
}
} break;
case AIUIConstant.EVENT_START_RECORD: {
Log.d( TAG, "on event: "+ event.eventType );
//开始录音
} break;
case AIUIConstant.EVENT_STOP_RECORD: {
Log.d( TAG, "on event: "+ event.eventType );
// 停止录音
} break;
case AIUIConstant.EVENT_STATE: {
mAiuiResultListn.onAiuiState(event.arg1);
if (AIUIConstant.STATE_IDLE == event.arg1) {
// 闲置状态,AIUI未开启
} else if (AIUIConstant.STATE_READY == event.arg1) {
// AIUI已就绪,等待唤醒
} else if (AIUIConstant.STATE_WORKING == event.arg1) {
// AIUI工作中,可进行交互
}
} break;
default:
break;
}
}
};
private AiuiResultListn mAiuiResultListn;
public void setAiuiResultListn(AiuiResultListn aiuiResultListn){
this.mAiuiResultListn = aiuiResultListn;
}
/**
* 改变AIUI状态,使其接收写入的内容,
* 但是到了指定的时间后自动关闭,可再
* 设置参数中设置超时时间
*/
private void chageAiuiState(){
AIUIMessage wakeupMsg = new AIUIMessage(
AIUIConstant.CMD_WAKEUP,
0,
0,
"",
null);
mAiuiAgent.sendMessage(wakeupMsg);
}
释放资源
/**
* 释放资源
*/
public void destory(){
if(null != mSpeechSynthesizer){
mSpeechSynthesizer.stopSpeaking();
mSpeechSynthesizer.destroy();
mSpeechSynthesizer = null;
}
if(null != mSpeechRecognizer){
mSpeechRecognizer.stopListening();
mSpeechRecognizer.destroy();
mSpeechRecognizer = null;
}
if (mVoiceWakeuper != null) {
mVoiceWakeuper.destroy();
mVoiceWakeuper = null;
}
if(null != mAiuiAgent){
mAiuiAgent.destroy();
mAiuiAgent = null;
}
}
涉及的接口以及实体类
public interface AiuiResultListn {
void onWakeUp();
void onError(String errorMsg);
void onAiuiState(int state);
void onResult(String result);
}
public interface SpeechRecongnizerResult {
void onResult(String result);
void onError(String errorMsg);
}
public class WakeResult {
//操作类型
private String sst;
//唤醒词id
private String id;
//得分
private String score;
//前端点
private String bos;
//尾端点
private String eos;
}
主界面
测试界面
项目地址把自己的jar包与so包替换并在AppSetting中设置您的APPID