1. 双路识别
双路识别是一种特殊的识别模式,它允许开发者同时用本地引擎和云端引擎进行语音识别。双路识别是以提高响应速度为为设计初衷的,只针对实时识别模式。
2. 准备工作
3. 双路识别能力集成
3.1 通用模块初始化
在调用语音识别(ASR)能力之前,需要初始化灵云SDK的SYS通用模块。详见灵云SDK开发手册
Android代码
// 创建初始化参数辅助类 InitParam initparam = new InitParam(); // 授权文件所在路径,此项必填 String authDirPath = context.getFilesDir().getAbsolutePath();; initparam.addParam(InitParam.PARAM_KEY_AUTH_PATH, authDirPath); // 灵云云服务的接口地址,此项必填 initparam.addParam(InitParam.PARAM_KEY_CLOUD_URL, "http://api.hcicloud.com:8888"); // 开发者密钥,此项必填,由捷通华声提供 initparam.addParam(InitParam.PARAM_KEY_DEVELOPER_KEY, "01234567890"); // 应用程序序号,此项必填,由捷通华声提供 initparam.addParam(InitParam.PARAM_KEY_APP_KEY, "1234abcd"); // 日志的路径,可选,如果不传或者为空则不生成日志 String logDirPath = "/storage/emulated/0/sinovoice/com.sinovoice.example/log"; initparam.addParam(InitParam.PARAM_KEY_LOG_FILE_PATH, logDirPath); // 日志等级,0=无,1=错误,2=警告,3=信息,4=细节,5=调试,SDK将输出小于等于logLevel的日志信息 initparam.addParam(InitParam.PARAM_KEY_LOG_LEVEL, "5"); // 灵云系统初始化 // 第二个参数在Android平台下,必须为当前的Context int errCode = HciCloudSys.hciInit(initparam.getStringConfig(), this); if(errCode != HciErrorCode.HCI_ERR_NONE) { // "系统初始化失败" return; }
C++代码
HCI_ERR_CODE err_code = HCI_ERR_NONE; //配置串是由"字段=值"的形式给出的一个字符串,多个字段之间以','隔开。字段名不分大小写。 string init_config = ""; //灵云应用序号 init_config += "appKey=1234abcd"; //灵云开发者密钥 init_config += ",developerKey=1234567890"; //灵云云服务的接口地址 init_config += ",cloudUrl=http://api.hcicloud.com:8888"; //授权文件所在路径,保证可写 init_config += ",authpath=../../bin"; //日志的路径 init_config += ",logfilepath=../../bin"; //日志的等级 init_config += ",loglevel=5"; //日志文件的大小 init_config += ",logfilesize=512"; //其他配置使用默认值,不再添加,如果想设置可以参考开发手册 err_code = hci_init( init_config.c_str() );
iOS代码
HCI_ERR_CODE errCode = HCI_ERR_NONE; //appkey, developerKey, cloudUrl, authPath为必填项。其他可配置信息请查询灵云SDK开发手册hci_init函数说明。 NSString *appkey = @""; //开发者社区申请所得 NSString *developerKey = @""; //开发者社区申请所得 NSString *cloudUrl = @""; //开发者社区申请所得 NSString *authPath = @""; //授权文件所在路径,一般设置为沙盒Document路径 NSString *config = [NSString stringWithFormat:@"appkey=%@,developerKey=%@,cloudUrl=%@,authPath=%@", appkey, developerKey, cloudUrl, authPath]; errCode = hci_init(config.UTF8String); if (errCode != HCI_ERR_NONE && errCode != HCI_ERR_SYS_ALREADY_INIT) { NSLog(@"hci_init failed, error: %@", errCode); }else{ NSLog(@"hci_init success"); }
3.2 授权检测
在初始化灵云SDK的SYS通用模块后,还需要调用授权检测函数去云端获取授权。
Android代码
// 获取授权 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; } }
C++代码
//获取过期时间 int64 expire_time; int64 current_time = (int64)time( NULL ); HCI_ERR_CODE err_code = hci_get_auth_expire_time( &expire_time ); if( err_code == HCI_ERR_NONE ) { //获取成功则判断是否过期 if( expire_time > current_time ) { //没有过期 printf( "auth can use continue\n" ); return true; } } //获取过期时间失败或已经过期 //手动调用更新授权 err_code = hci_check_auth(); if( err_code == HCI_ERR_NONE ) { //更新成功 printf( "check auth success \n" ); return true; } else { //更新失败 printf( "check auth return (%d:%s)\n", err_code ,hci_get_error_info(err_code)); return false; }
iOS代码
//获取过期时间 int64 expireTime; int64 currentTime = (int64)time(NULL); HCI_ERR_CODE errCode = hci_get_auth_expire_time(&expireTime); if (errCode == HCI_ERR_NONE) { if (expireTime < currentTime) { errCode = hci_check_auth(); if(errCode == HCI_ERR_NONE){ NSLog(@"check auth success"); return YES; }else{ NSLog(@"check auth failed, error: %@", errCode); return NO; } }else{ //没有过期 NSLog(@"auth can use continue"); return YES; } } //读取授权文件出现错误 else if(errCode == HCI_ERR_SYS_AUTHFILE_INVALID){ errCode = hci_check_auth(); if(errCode == HCI_ERR_NONE){ NSLog(@"check auth success"); return YES; }else{ NSLog(@"check auth failed, error: %@", errCode); return NO; } }else{ NSLog(@"check auth failed, error: %@", errCode); return NO; }
3.3 ASR 初始化
在初始化灵云SDK的SYS通用模块和授权检测成功后,需调用 ASR 初始化函数。详见灵云SDK开发手册。
Android代码
// 创建初始化参数辅助类 AsrInitParam asrInitParam= new AsrInitParam (); // 设置本地资源库的路径 asrInitParam.addParam(AsrInitParam .PARAM_KEY_DATA_PATH, "/storage/emulated/0/sinovoice/com.sinovoice.example/data"); asrInitParam.addParam(AsrInitParam .PARAM_KEY_INIT_CAP_KEYS, "asr.local.grammar;asr.cloud.freetalk"); int errCode = HciCloudAsr.hciAsrInit(asrInitParam.getStringConfig());
C++代码
string asr_init_config = ""; //建议在initcapkeys中添加需要使用的所有能力以提高第一次识别的效率 asr_init_config += "initcapkeys=asr.local.grammar;asr.cloud.freetalk"; asr_init_config += ",datapath=../../data"; err_code = hci_asr_init(asr_init_config.c_str()); if (err_code != HCI_ERR_NONE) { printf("hci_asr_init return (%d:%s) \n",err_code,hci_get_error_info(err_code)); return; }
iOS代码
//initCapKeys为必填项,如果为本地意图识别dataPath为必填项。其他可配置信息请查询开发手册hci_asr_init函数说明。 NSString *dataPath = [[NSBundle mainBundle] pathForResource:@"data" ofType:Nil]; //本地意图识别识别所需本地资源所在路径 NSString *initCapKeys = @"asr.local.grammar;asr.cloud.freetalk"; //语音识别(ASR)所需使用的能力列表,可以包含多种能力,用';'分号隔开 NSString *config = [NSString stringWithFormat:@"dataPath =%@, initCapKeys =%@", dataPath, initCapKeys]; HCI_ERR_CODE errCode = HCI_ERR_NONE; errCode = hci_asr_init(config.UTF8String); if (errCode != HCI_ERR_NONE){ NSLog(@"hci_asr_init failed, error:%@", errCode); return NO; } NSLog(@"hci_asr_init success"); return YES;
3.4 开启会话(session)
双路识别即会话(session)同时拥有本地和云端识别功能,双路识别只支持实时识别模式,只需要在hci_asr_session_start
函数中进行相应配置
Android代码
AsrConfig config = new AsrConfig(); // 启动 ASR Session int errCode = -1; //会话开启的字符串配置 String sSessionConfig = "capKey=asr.local.grammar.v4,realtime=yes#capKey=asr.cloud.freetalk,realtime=yes"; Session nSessionId = new Session(); errCode = HciCloudAsr.hciAsrSessionStart(sSessionConfig, nSessionId);
C++代码
int nSessionId = -1; string strSessionConfig = "capKey=asr.local.grammar.v4,realtime=yes#capKey=asr.cloud.freetalk,realtime=yes"; //此处也可以传入其他配置,参见开发手册,此处其他配置采用默认值 err_code = hci_asr_session_start( strSessionConfig.c_str(), &nSessionId ); if( err_code != HCI_ERR_NONE ) { printf( "hci_asr_session_start return (%d:%s)\n", err_code ,hci_get_error_info(err_code)); return false; }
iOS代码
//开启session HCI_ERR_CODE errCode = HCI_ERR_NONE; NSString *config = @"capKey=asr.local.grammar.v4,realtime=yes#capKey=asr.cloud.freetalk,realtime=yes"; int sessionID; //开启会话成功后,会返回的会话ID errCode = hci_asr_session_start(config.UTF8String, &sessionID); if (errCode != HCI_ERR_NONE){ NSLog(@"hci_asr_session_start failed, error:%@", errCode); return NO; } NSLog(@"hci_asr_session_start success"); return YES;
3.5 语音识别
在启动会话(session)成功后,即可可以进行 ASR 识别过程。识别功能通过调用识别函数完成,如果是流式识别/实时反馈,则需要多次调用识别函数将语音数据送入 SDK。。配置参数详见灵云SDK开发手册
Android代码
String audioFile = "sinovoice.pcm"; // 从assets资源目录获取sinovoice.pcm 语音数据文件 byte[] voiceData = Tools.getAssetFileData(this,audioFile); if (null == voiceData) { // ShowMessage("Open input voice file" + audioFile + " error!"); return; } if (HciErrorCode.HCI_ERR_NONE != errCode) { // ShowMessage("hciAsrSessionStart error:" + HciCloudSys.hciGetErrorInfo(errCode)); return; } ShowMessage("hciAsrSessionStart Success"); // 识别结果 AsrRecogResult asrResult = new AsrRecogResult(); String config = "audioFormat=pcm16k16bit,encode=none,grammarType=id,grammarId=100"; while (1) { errCode = HciCloudAsr.hciAsrRecog(nSessionId, voiceData, config, null, asrResult); if (errCode == HCI_ERR_ASR_REALTIME_END) { //正常检测到端点,传入NULL和0,获取识别结果,退出循环 errCode = HciCloudAsr.hciAsrRecog(nSessionId, NULL,config, null, asrResult); break; } else if (errCode == HCI_ERR_ASR_REALTIME_WAITING){ //等待识别数据,继续循环 continue; } else { // 其它错误,一般不会出现,出现很可能是由于输入数据无效,或实时识别过程错误。 // 如果实时识别过程错误,则自动取消该次实时识别 // 否则可以手动终止本次实时识别, 然后退出循环 // 也可以继续循环,则下次hci_asr_recog()发送的数据会作为前次数据的延续 break; } }
C++代码
//每次获取音频文件后都可以传入hci_asr_recog进行识别,这里只是用while循环演示不断调用hci_asr_recog HCI_ERR_CODE errCode = HCI_ERR_NONE; ASR_RECOG_RESULT asrResult; //识别结果保存 string config = @"audioFormat=pcm16k16bit,encode=none,grammarType=id,grammarId=100"; //_sessionID:为开启会话(session)后获得的sessionID //voiceData:每次获取的音频数据文件,数据类型为NSData while (1) { errCode = hci_asr_recog(_sessionID, (void *) voiceData.bytes, (unsigned int) voiceData.length, config.c_str(), NULL, &asrResult); if (errCode == HCI_ERR_ASR_REALTIME_END) { //正常检测到端点,传入NULL和0,获取识别结果,退出循环 errCode = hci_asr_recog(_sessionID,NULL,0, config.UTF8String, NULL, &asrResult); break; } else if (errCode == HCI_ERR_ASR_REALTIME_WAITING){ //等待识别数据,继续循环 continue; } else { // 其它错误,一般不会出现,出现很可能是由于输入数据无效,或实时识别过程错误。 // 如果实时识别过程错误,则自动取消该次实时识别 // 否则可以手动终止本次实时识别, 然后退出循环 // 也可以继续循环,则下次hci_asr_recog()发送的数据会作为前次数据的延续 break; } }
iOS代码
//每次获取音频文件后都可以传入hci_asr_recog进行识别,这里只是用while循环演示不断调用hci_asr_recog HCI_ERR_CODE errCode = HCI_ERR_NONE; ASR_RECOG_RESULT asrResult; //识别结果保存 NSString *config = @"audioFormat=pcm16k16bit,encode=none,grammarType=id,grammarId=100"; //_sessionID:为开启会话(session)后获得的sessionID //voiceData:每次获取的音频数据文件,数据类型为NSData while (1) { errCode = hci_asr_recog(_sessionID, (void *) voiceData.bytes, (unsigned int) voiceData.length, config.UTF8String, NULL, &asrResult); if (errCode == HCI_ERR_ASR_REALTIME_END) { //正常检测到端点,传入NULL和0,获取识别结果,退出循环 errCode = hci_asr_recog(_sessionID,NULL,0, config.UTF8String, NULL, &asrResult); break; } else if (errCode == HCI_ERR_ASR_REALTIME_WAITING) { //等待识别数据,继续循环 continue; } else { // 其它错误,一般不会出现,出现很可能是由于输入数据无效,或实时识别过程错误。 // 如果实时识别过程错误,则自动取消该次实时识别 // 否则可以手动终止本次实时识别, 然后退出循环 // 也可以继续循环,则下次hci_asr_recog()发送的数据会作为前次数据的延续 break; } }
3.6 结束识别
Android代码
// 终止session HciCloudAsr.hciAsrSessionStop(nSessionId); //反初始化ASR HciCloudAsr.hciAsrRelease(); //ShowMessage("hciAsrRelease"); HciCloudSys.hciRelease();
C++代码
HCI_ERR_CODE errCode = HCI_ERR_NONE; //关闭会话 errCode = hci_asr_session_stop(sessionID); //sessionID:已经开启的会话的sessionID //终止ASR能力 errCode = hci_asr_release(); //关闭灵云系统 errCode = hci_release();
iOS代码
//结束session后,调用hci_asr_release和hci_release函数结束能力调用 HCI_ERR_CODE errCode = HCI_ERR_NONE; errCode = hci_asr_session_stop(sessionID); //sessionID:已经开启的会话的sessionID if (errCode != HCI_ERR_NONE){ NSLog(@"hci_asr_session_stop failed, error:%@", errCode); return NO; } NSLog(@"hci_asr_session_stop success"); errCode = hci_asr_release(); if (errCode != HCI_ERR_NONE){ NSLog(@"hci_asr_release failed, error:%@", errCode); return NO; } NSLog(@"hci_asr_release success"); errCode = hci_release(); if (errCode != HCI_ERR_NONE){ NSLog(@"hci_release failed, error:%@", errCode); return NO; } NSLog(@"hci_release success"); return YES;