一、引言
demo下载地址:https://fir.im/jy28
demo源码查看:https://github.com/wapchief/android-CollectionDemo
效果图:
选这几个平台的主要也是从多方面考虑。
大概从这几个
- 识别精准度
- 免费限制
- 稳定程度
- 包体积
- 拓展性
目前只有百度的语音服务支持长语音(60秒以上),和离线语音识别。
当然语音的库(.so)文件也是最大的。足足10多M。
二、申请应用
在集成之前,需要去相应的平台去申请应用。
拿到key等密钥。目前这几个平台都在测试免费层限制中。
百度是完全免费,讯飞有免费层日调用次数限制。
三、集成
首先去各大平台下载对于的语音文件库
一般都包含.so和libs两部分。这两部分都需要加入到项目中。
不过这里只需要注意一点是,每个平台的文档不一样,有些是使用libs集成第三方库,有些是使用jnilibs加载。
如果都按照第三方文档来集成,必定会出错。
这里统一使用jniLibs来集成(只适用于AndroidStudio)。如果是eclipse,则需要使用libs集成
如果在集成中遇到了问题,可以参考我以前写过的解决方案
Android关于libs,jniLibs库的基本使用说明及冲突解决
1、(必须)将下载后的所有so库文件(以.so结尾的文件)拷贝至项目下/app/src/main目录,如果没有则创建一个,里面的名字是固定的,注意不能修改,必须按照这样的架构目录集成。
2、(必须)将jar文件拷贝到libs目录中
然后将jar添加到项目。
可以使用右键对应的jar包,add-libs手动添加
3、(必须)在AndroidManifest.xml添加权限
以讯飞的权限文档说明为例
<!--连接网络权限,用于执行云端语音能力 -->
<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"/>
<!--读取联系人权限,上传联系人需要用到此权限 -->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<!--外存储写权限,构建语法需要用到此权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!--外存储读权限,构建语法需要用到此权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!--配置权限,用来记录应用配置信息 -->
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<!--手机定位信息,用来为语义等功能提供定位,提供更精准的服务-->
<!--定位信息是敏感信息,可通过Setting.setLocationEnable(false)关闭定位请求 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!--如需使用人脸识别,还要添加:摄相头权限,拍照需要用到 -->
<uses-permission android:name="android.permission.CAMERA" />
如果SDK版本在6.0以上需要在项目中手动获取录音权限
/*动态权限申请*/
private void initPermission() {
String permission[] = {Manifest.permission.RECORD_AUDIO,
Manifest.permission.ACCESS_NETWORK_STATE,
Manifest.permission.INTERNET,
Manifest.permission.WRITE_EXTERNAL_STORAGE};
ArrayList<String> applyList = new ArrayList<>();
for (String per : permission) {
if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, per)) {
applyList.add(per);
}
}
String tmpList[] = new String[applyList.size()];
if (!applyList.isEmpty()) {
ActivityCompat.requestPermissions(this, applyList.toArray(tmpList), 123);
}
}
四、初始化服务
各个平台的初始化方法都不一样。
但大多数都是在自己项目的Application中初始化。
//初始化讯飞语音
SpeechUtility.createUtility(mContext, SpeechConstant.APPID +"=59daecea," + SpeechConstant.FORCE_LOGIN +"=true");
//讯飞调试日志开启
Setting.setShowLog(true);
//初始化阿里语音
NlsClient.openLog(true);
NlsClient.configure(mContext);
有些是在AndroidManifest.xml中初始化
<!--******************************百度语音**********************************-->
<meta-data android:name="com.baidu.speech.APP_ID"
android:value="8172882" />
<meta-data
android:name="com.baidu.speech.API_KEY"
android:value="R3crsZhvpqQSrLGUvG7kuG0pCnpTbXvb" />
<meta-data
android:name="com.baidu.speech.SECRET_KEY"
android:value="6MafqZkSLoNYUML1YduHPDYBg1kkPLHj" />
<service android:name="com.baidu.speech.VoiceRecognitionService" android:exported="false"/>
五、开启识别服务
以讯飞为例:
在开始识别前需要初始化识别对象,
SpeechRecognizer recognizer = SpeechRecognizer.createRecognizer(this, null);
然后配置听写参数
/**
* 参数设置
*/
public void setParam() {
//2.设置听写参数,详见《科大讯飞MSC API手册(Android)》SpeechConstant类
recognizer.setParameter(SpeechConstant.DOMAIN, "iat");
recognizer.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
recognizer.setParameter(SpeechConstant.ACCENT, "mandarin ");
//设置音频保存路径
recognizer.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
recognizer.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/iat.wav");
}
开始识别的开始和终止,及识别过程的内容回调
//开启
private void startXF() {
setParam();
recognizer.startListening(recognizerListener);
}
//停止
private void stopXF() {
recognizer.stopListening();
}
/*监听*/
private RecognizerListener recognizerListener = new RecognizerListener() {
@Override
public void onVolumeChanged(int i, byte[] bytes) {
//音量变化
}
@Override
public void onBeginOfSpeech() {
//开始说话
Log.e(TAG, "XF开始说话");
}
@Override
public void onEndOfSpeech() {
//结束说话
Log.e(TAG, "XF结束说话");
}
@Override
public void onResult(RecognizerResult recognizerResult, boolean b) {
//返回结果需要判断null
text = JsonParser.parseIatResult(recognizerResult.getResultString());
Log.e(TAG, "XFResult:" + text + "\n" + recognizerResult.getResultString());
mVoiceTv.setText(recognizerResult.getResultString());
printResult(recognizerResult);
}
@Override
public void onError(SpeechError speechError) {
//错误回调
Log.e(TAG, "XFError:" + speechError.toString());
}
@Override
public void onEvent(int i, int i1, int i2, Bundle bundle) {
//事件拓展
}
};
解析语音识别的结果。
//储存听写结果
private HashMap<String, String> mIatResults = new LinkedHashMap<String, String>();
/*解析器*/
private void printResult(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));
}
mVoiceEt.setText("讯飞识别结果:" + resultBuffer.toString());
mVoiceEt.setSelection(mVoiceEt.length());
// mVoiceTv.setText(resultBuffer.toString());
}
六、异常及其他
SecurityException异常:
如果是在6.0以上版本的sdk可能会出现该问题,主要是临时权限调用,比如调用系统的录音文件播放,或者调用系统的图库。
可以参考调用图库的解决方案,原理一样
Android版本相机适配问题集合(不断整理更新中)
demo下载地址:
https://fir.im/jy28
demo源码:
https://github.com/wapchief/android-CollectionDemo