源码
在线语法识别
SDK下载
SDK集成
下载SDK以后,将jar和so导入工程
权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
封装
灵云配置类
package kong.qingwei.kqwhcidemo;
/**
* Created by kqw on 2016/8/12.
* 灵云配置信息
*/
public final class ConfigUtil {
/**
* 灵云APP_KEY
*/
public static final String APP_KEY = "3d5d5466";
/**
* 开发者密钥
*/
public static final String DEVELOPER_KEY = "eca643ff7b3c758745d7cf516e808d34";
/**
* 灵云云服务的接口地址
*/
public static final String CLOUD_URL = "test.api.hcicloud.com:8888";
/**
* 需要运行的灵云能力
*/
// 离线语音合成
public static final String CAP_KEY_TTS_LOCAL = "tts.local.synth";
// 云端语音合成
public static final String CAP_KEY_TTS_CLOUD = "tts.cloud.wangjing";
// 云端语义识别
public static final String CAP_KEY_NUL_CLOUD = "nlu.cloud";
// 云端自由说
public static final String CAP_KEY_ASR_CLOUD_FREETALK = "asr.cloud.freetalk";
// 离线自由说
public static final String CAP_KEY_ASR_LOCAL_FREETALK = "asr.local.freetalk";
// 云端语音识别+语义
public static final String CAP_KEY_ASR_CLOUD_DIALOG = "asr.cloud.dialog";
// 离线命令词
public static final String CAP_KEY_ASR_LOCAL_GRAMMAR = "asr.local.grammar.v4";
// 在线命令词
public static final String CAP_KEY_ASR_CLOUD_GRAMMAR = "asr.cloud.grammar";
}
初始化灵云语音能力的工具类
package kong.qingwei.kqwhcidemo;
import android.app.Activity;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;
import com.sinovoice.hcicloudsdk.api.HciCloudSys;
import com.sinovoice.hcicloudsdk.common.AuthExpireTime;
import com.sinovoice.hcicloudsdk.common.HciErrorCode;
import com.sinovoice.hcicloudsdk.common.InitParam;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* Created by kqw on 2016/8/12.
* 初始化灵云语音
*/
public class HciUtil {
private static final String TAG = "HciUtil";
private Activity mActivity;
private final String mConfigStr;
public HciUtil(Activity activity) {
mActivity = activity;
// 加载信息,返回InitParam, 获得配置参数的字符串
InitParam initParam = getInitParam();
mConfigStr = initParam.getStringConfig();
}
public boolean initHci() {
// 初始化
int errCode = HciCloudSys.hciInit(mConfigStr, mActivity);
if (errCode != HciErrorCode.HCI_ERR_NONE && errCode != HciErrorCode.HCI_ERR_SYS_ALREADY_INIT) {
Toast.makeText(mActivity, "hciInit error: " + HciCloudSys.hciGetErrorInfo(errCode), Toast.LENGTH_SHORT).show();
return false;
}
// 获取授权/更新授权文件 :
errCode = checkAuthAndUpdateAuth();
if (errCode != HciErrorCode.HCI_ERR_NONE) {
// 由于系统已经初始化成功,在结束前需要调用方法hciRelease()进行系统的反初始化
Toast.makeText(mActivity, "CheckAuthAndUpdateAuth error: " + HciCloudSys.hciGetErrorInfo(errCode), Toast.LENGTH_SHORT).show();
HciCloudSys.hciRelease();
return false;
}
return true;
}
/**
* 释放
*/
public void hciRelease(){
HciCloudSys.hciRelease();
}
/**
* 加载初始化信息
*
* @return 系统初始化参数
*/
private InitParam getInitParam() {
String authDirPath = mActivity.getFilesDir().getAbsolutePath();
// 前置条件:无
InitParam initparam = new InitParam();
// 授权文件所在路径,此项必填
initparam.addParam(InitParam.AuthParam.PARAM_KEY_AUTH_PATH, authDirPath);
// 是否自动访问云授权,详见 获取授权/更新授权文件处注释
initparam.addParam(InitParam.AuthParam.PARAM_KEY_AUTO_CLOUD_AUTH, "no");
// 灵云云服务的接口地址,此项必填
initparam.addParam(InitParam.AuthParam.PARAM_KEY_CLOUD_URL, ConfigUtil.CLOUD_URL);
// 开发者Key,此项必填,由捷通华声提供
initparam.addParam(InitParam.AuthParam.PARAM_KEY_DEVELOPER_KEY, ConfigUtil.DEVELOPER_KEY);
// 应用Key,此项必填,由捷通华声提供
initparam.addParam(InitParam.AuthParam.PARAM_KEY_APP_KEY, ConfigUtil.APP_KEY);
// 配置日志参数
String sdcardState = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(sdcardState)) {
String sdPath = Environment.getExternalStorageDirectory().getAbsolutePath();
String packageName = mActivity.getPackageName();
String logPath = sdPath + File.separator + "sinovoice" + File.separator + packageName + File.separator + "log" + File.separator;
// 日志文件地址
File fileDir = new File(logPath);
if (!fileDir.exists()) {
fileDir.mkdirs();
}
// 日志的路径,可选,如果不传或者为空则不生成日志
initparam.addParam(InitParam.LogParam.PARAM_KEY_LOG_FILE_PATH, logPath);
// 日志数目,默认保留多少个日志文件,超过则覆盖最旧的日志
initparam.addParam(InitParam.LogParam.PARAM_KEY_LOG_FILE_COUNT, "5");
// 日志大小,默认一个日志文件写多大,单位为K
initparam.addParam(InitParam.LogParam.PARAM_KEY_LOG_FILE_SIZE, "1024");
// 日志等级,0=无,1=错误,2=警告,3=信息,4=细节,5=调试,SDK将输出小于等于logLevel的日志信息
initparam.addParam(InitParam.LogParam.PARAM_KEY_LOG_LEVEL, "5");
}
return initparam;
}
/**
* 获取授权
*
* @return 授权结果
*/
private int checkAuthAndUpdateAuth() {
// 获取系统授权到期时间
int initResult;
AuthExpireTime objExpireTime = new AuthExpireTime();
initResult = HciCloudSys.hciGetAuthExpireTime(objExpireTime);
if (initResult == HciErrorCode.HCI_ERR_NONE) {
// 显示授权日期,如用户不需要关注该值,此处代码可忽略
Date date = new Date(objExpireTime.getExpireTime() * 1000);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);
Log.i(TAG, "expire time: " + sdf.format(date));
if (objExpireTime.getExpireTime() * 1000 > System.currentTimeMillis()) {
// 已经成功获取了授权,并且距离授权到期有充足的时间(>7天)
Log.i(TAG, "checkAuth success");
return initResult;
}
}
// 获取过期时间失败或者已经过期
initResult = HciCloudSys.hciCheckAuth();
if (initResult == HciErrorCode.HCI_ERR_NONE) {
Log.i(TAG, "checkAuth success");
return initResult;
} else {
Log.e(TAG, "checkAuth failed: " + initResult);
return initResult;
}
}
}
语法识别的类
和之前的语音识别一样,只不过配置了语法
在原来的基础上添加了initGrammar
方法
package kong.qingwei.kqwhcidemo;
import android.app.Activity;
import android.util.Log;
import com.sinovoice.hcicloudsdk.android.asr.recorder.ASRRecorder;
import com.sinovoice.hcicloudsdk.common.asr.AsrConfig;
import com.sinovoice.hcicloudsdk.common.asr.AsrGrammarId;
import com.sinovoice.hcicloudsdk.common.asr.AsrInitParam;
import com.sinovoice.hcicloudsdk.common.asr.AsrRecogResult;
import com.sinovoice.hcicloudsdk.recorder.ASRCommonRecorder;
import com.sinovoice.hcicloudsdk.recorder.ASRRecorderListener;
import com.sinovoice.hcicloudsdk.recorder.RecorderEvent;
import java.io.IOException;
import java.io.InputStream;
/**
* Created by kqw on 2016/8/15.
* 语音识别类
*/
public class AsrUtil {
private static final String TAG = "AsrUtil";
private Activity mActivity;
private ASRRecorder mAsrRecorder;
private AsrConfig asrConfig;
private OnAsrRecogListener mOnAsrRecogListener;
private String mGrammar = null;
private String mCapKey = ConfigUtil.CAP_KEY_ASR_CLOUD_GRAMMAR;
public AsrUtil(Activity activity) {
mActivity = activity;
initAsr();
initGrammar(mCapKey);
}
/**
* 初始化语音识别
*/
private void initAsr() {
Log.i(TAG, "initAsr: ");
// 初始化录音机
mAsrRecorder = new ASRRecorder();
// 配置初始化参数
AsrInitParam asrInitParam = new AsrInitParam();
String dataPath = mActivity.getFilesDir().getPath().replace("files", "lib");
asrInitParam.addParam(AsrInitParam.PARAM_KEY_INIT_CAP_KEYS, mCapKey);
asrInitParam.addParam(AsrInitParam.PARAM_KEY_DATA_PATH, dataPath);
asrInitParam.addParam(AsrInitParam.PARAM_KEY_FILE_FLAG, AsrInitParam.VALUE_OF_PARAM_FILE_FLAG_ANDROID_SO);
Log.v(TAG, "init parameters:" + asrInitParam.getStringConfig());
// 设置初始化参数
mAsrRecorder.init(asrInitParam.getStringConfig(), new ASRResultProcess());
// 配置识别参数
asrConfig = new AsrConfig();
// PARAM_KEY_CAP_KEY 设置使用的能力
asrConfig.addParam(AsrConfig.SessionConfig.PARAM_KEY_CAP_KEY, mCapKey);
// PARAM_KEY_AUDIO_FORMAT 音频格式根据不同的能力使用不用的音频格式
asrConfig.addParam(AsrConfig.AudioConfig.PARAM_KEY_AUDIO_FORMAT, AsrConfig.AudioConfig.VALUE_OF_PARAM_AUDIO_FORMAT_PCM_16K16BIT);
// PARAM_KEY_ENCODE 音频编码压缩格式,使用OPUS可以有效减小数据流量
asrConfig.addParam(AsrConfig.AudioConfig.PARAM_KEY_ENCODE, AsrConfig.AudioConfig.VALUE_OF_PARAM_ENCODE_SPEEX);
// 其他配置,此处可以全部选取缺省值
asrConfig.addParam("intention", "weather");
}
/**
* 初始化语法
*
* @param capKey CapKey
*/
public void initGrammar(String capKey) {
// 语法相关的配置,若使用自由说能力可以不必配置该项
if (capKey.contains("local.grammar")) {
mGrammar = loadGrammar("stock_10001.gram");
// 加载本地语法获取语法ID
AsrGrammarId id = new AsrGrammarId();
ASRCommonRecorder.loadGrammar("capkey=" + capKey + ",grammarType=jsgf", mGrammar, id);
Log.d(TAG, "grammarid=" + id);
// PARAM_KEY_GRAMMAR_TYPE 语法类型,使用自由说能力时,忽略以下此参数
asrConfig.addParam(AsrConfig.GrammarConfig.PARAM_KEY_GRAMMAR_TYPE, AsrConfig.GrammarConfig.VALUE_OF_PARAM_GRAMMAR_TYPE_ID);
asrConfig.addParam(AsrConfig.GrammarConfig.PARAM_KEY_GRAMMAR_ID, "" + id.getGrammarId());
} else if (capKey.contains("cloud.grammar")) {
mGrammar = loadGrammar("stock_10001.gram");
// PARAM_KEY_GRAMMAR_TYPE 语法类型,使用自由说能力时,忽略以下此参数
asrConfig.addParam(AsrConfig.GrammarConfig.PARAM_KEY_GRAMMAR_TYPE, AsrConfig.GrammarConfig.VALUE_OF_PARAM_GRAMMAR_TYPE_JSGF);
}
}
/**
* 开始语音识别
*/
public void start(OnAsrRecogListener listener) {
mOnAsrRecogListener = listener;
if (mAsrRecorder.getRecorderState() == ASRRecorder.RECORDER_STATE_IDLE) {
asrConfig.addParam(AsrConfig.SessionConfig.PARAM_KEY_REALTIME, "no");
mAsrRecorder.start(asrConfig.getStringConfig(), mGrammar);
} else {
Log.i(TAG, "start: 录音机未处于空闲状态,请稍等");
}
}
private class ASRResultProcess implements ASRRecorderListener {
@Override
public void onRecorderEventError(RecorderEvent event, int errorCode) {
Log.i(TAG, "onRecorderEventError: errorCode = " + errorCode);
if (null != mOnAsrRecogListener) {
mOnAsrRecogListener.onError(errorCode);
}
}
@Override
public void onRecorderEventRecogFinsh(RecorderEvent recorderEvent, final AsrRecogResult arg1) {
if (recorderEvent == RecorderEvent.RECORDER_EVENT_RECOGNIZE_COMPLETE) {
Log.i(TAG, "onRecorderEventRecogFinsh: 识别结束");
}
if (null != mOnAsrRecogListener) {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
mOnAsrRecogListener.onAsrRecogResult(arg1);
}
});
}
}
@Override
public void onRecorderEventStateChange(RecorderEvent recorderEvent) {
if (recorderEvent == RecorderEvent.RECORDER_EVENT_BEGIN_RECORD) {
Log.i(TAG, "onRecorderEventStateChange: 开始录音");
} else if (recorderEvent == RecorderEvent.RECORDER_EVENT_BEGIN_RECOGNIZE) {
Log.i(TAG, "onRecorderEventStateChange: 开始识别");
} else if (recorderEvent == RecorderEvent.RECORDER_EVENT_NO_VOICE_INPUT) {
Log.i(TAG, "onRecorderEventStateChange: 无音频输入");
} else {
Log.i(TAG, "onRecorderEventStateChange: recorderEvent = " + recorderEvent);
}
}
@Override
public void onRecorderRecording(byte[] volumedata, int volume) {
if (null != mOnAsrRecogListener) {
mOnAsrRecogListener.onVolume(volume);
}
}
@Override
public void onRecorderEventRecogProcess(RecorderEvent recorderEvent, AsrRecogResult arg1) {
if (recorderEvent == RecorderEvent.RECORDER_EVENT_RECOGNIZE_PROCESS) {
Log.i(TAG, "onRecorderEventRecogProcess: 识别中间反馈");
}
if (arg1 != null) {
if (arg1.getRecogItemList().size() > 0) {
Log.i(TAG, "onRecorderEventRecogProcess: 识别中间结果结果为:" + arg1.getRecogItemList().get(0).getRecogResult());
} else {
Log.i(TAG, "onRecorderEventRecogProcess: 未能正确识别,请重新输入");
}
}
}
}
/**
* 读取语法
*
* @param fileName 文件名
* @return 语法
*/
private String loadGrammar(String fileName) {
String grammar = "";
InputStream is = null;
try {
is = mActivity.getAssets().open(fileName);
byte[] data = new byte[is.available()];
is.read(data);
grammar = new String(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return grammar;
}
/**
* 语音识别的回调接口
*/
public interface OnAsrRecogListener {
// 识别结果
void onAsrRecogResult(AsrRecogResult asrRecogResult);
// 识别错误码
void onError(int errorCode);
// 录音音量
void onVolume(int volume);
}
}
使用
使用和语音识别完全一致
初始化灵云的语音能力和语法识别
// 灵云语音工具类
mInitTts = new HciUtil(this);
// 初始化灵云语音
boolean isInitHci = mInitTts.initHci();
if (isInitHci) { // 初始化成功
……
// 语音识别
mAsrUtil = new AsrUtil(this);
}
语法识别
/**
* 语音识别(语音转文字)
*
* @param view view
*/
public void asr(View view) {
mAsrUtil.start(new AsrUtil.OnAsrRecogListener() {
@Override
public void onAsrRecogResult(AsrRecogResult asrRecogResult) {
StringBuilder stringBuffer = new StringBuilder();
ArrayList<AsrRecogItem> asrRecogItemArrayList = asrRecogResult.getRecogItemList();
for (AsrRecogItem asrRecogItem : asrRecogItemArrayList) {
String result = asrRecogItem.getRecogResult();
Log.i(TAG, "onAsrRecogResult: " + result);
stringBuffer.append(result).append("\n");
}
showDialog("识别结果", stringBuffer.toString());
}
@Override
public void onError(int errorCode) {
Log.i(TAG, "onError: " + errorCode);
}
@Override
public void onVolume(int volume) {
Log.i(TAG, "onVolume: " + volume);
}
});
}
离线语音识别
在在线语法识别的基础上,离线的语法识别只需要将CapKey替换,并且添加离线资源即可
离线资源的下载
下载完解压离线资源
将里面所有的文件重命名,前面加lib
,后面加.so
,然后导入工程
修改CapKey为asr.local.grammar.v4
注意,灵云的离线语音功能第一次使用需要联网激活,激活以后才可以使用离线功能。