1. 概述
声纹识别技术(Voiceprint Recognition),简称VPR,是生物识别技术的一种,提取说话人的语音身份特征,提供声纹鉴别和声纹确认,也称为说话人识别,有两类,即说话人辨认和说话人确认。不同的任务和应用会使用不同的声纹识别技术,如缩小刑侦范围时可能需要辨认技术,而银行交易时则需要确认技术。声纹识别就是把声信号转换成电信号,再用计算机进行识别。
本文档旨在讲解如何快速地集成灵云VPR功能到开发者应用中。关于各服务接口更详细的说明,请参考灵云SDK开发手册。在集成过程中如有疑问,可登录灵云开发者论坛,查找答案或与其他开发者交流。
1.1 概念解释
对指南中涉及的名词作简要描述,使开发者在阅读开发指南时,对此有一些大致的了解和预备知识的了解。 如:
名称 | 说明 |
---|---|
Session | Session用来标记一个能力运行过程的上下文。一个应用最多可同时创建的Session数受到授权的限制 |
云端识别 | 由云端服务器提供识别结果的方式。使用云端能力需要加上hci_vpr_cloud_recog模块 |
本地识别 | 通过本地运算来得到识别结果的方式。使用本地能力需要加上hci_vpr_local_recog模块和本地资源文件 |
特征训练(注册) | 为提高音频的识别正确率,灵云VPR提供特征训练的方法,通过大量音频的注册训练,可以提高识别的速度和正确率。详见hci_vpr_enroll函数 |
特征确认 | 声纹模型注册和训练成功后,可以对音频数据进行校验,以判断用户唯一标识和音频数据是否相符。详见hci_vpr_verify函数 |
特征辨识 | 对音频数据校验后,需要对音频数据识别,识别结果以分数形式返回。详见hci_vpr_identity函数 |
1.2 能力定义(capkey)
VPR 支持多种能力,通过 capkey 区分不同能力的调用和配置。其中常用 capkey 如下所示:
capkey | 支持音频格式 |
---|---|
vpr.cloud.recog | alaw8k8bit ulaw8k8bit pcm8k16bit alaw16k8bit ulaw16k8bit pcm16k16bit vox6k4bit |
capkey | 模型背景类型 | 所需本地资源文件 |
---|---|---|
vpr.local.recog | iv200_8k iv200_16k iv200_digital_8k iv200_digital_16k |
gmm256_iv200.final.ubm gmm256_iv200.final.ubm.ie gmm256_iv200.final.ubm.ie.mean gmm256_iv200.final.ubm.pld gmm256_iv200.final.conf |
其中本地声纹识别资源列表:
|资源目录|支持音频格式|
|------|-----------|
|16k|pcm16k16bit
ulaw16k8bit
alaw16k8bit|
|8k|pcm8k16bit
ulaw8k8bit
alaw8k8bit|
1.3 识别流程
2. 能力使用说明
2.1 准备工作
下载声纹识别SDK并解压缩。
如果需要使用本地能力,请下载相应资源包并解压缩。
2.2 使用Android版SDK
必选模块
- libhci_curl.so
- libhci_sys.so
- libhci_sys_jni.so
- libhci_vpr.so
- libhci_vpr_jni.so
- libstlport_shared.so
云端识别
- libhci_vpr_cloud_recog.so
- libjtopus.so (根据压缩方式选择)
- libjtspeex.so (根据压缩方式选择)
本地识别
- libhci_vpr_local_recog.so
2.3 使用C++版SDK
必选模块
- libhci_curl.dll
- hci_sys.dll
- hci_vpr.dll
云端识别
- hci_vpr_cloud_recog.dll
- jtopus.dll(根据压缩方式选择)
- jtspeex.dll(根据压缩方式选择)
本地识别
- hci_vpr_local_recog.dll
- iVerifyVP.dll
- mkl_avx.dll
- mkl_avx2.dll
- mkl_core.dll
- mkl_intel_thread.dll
- mkl_p4.dll
- mkl_p4m.dll
- mkl_p4m3.dll
- mkl_p4p.dll
- mkl_rt.dll
- mkl_sequential.dll
- mkl_vml_avx.dll
- mkl_vml_avx2.dll
- mkl_vml_cmpt.dll
- mkl_vml_ia.dll
- mkl_vml_p4.dll
- mkl_vml_p4m.dll
- mkl_vml_p4m2.dll
- mkl_vml_p4m3.dll
- mkl_vml_p4p.dll
2.4 使用iOS版SDK
必选模块
- libcurl_device_simulator_iOS5.1.1.a
- libhci_sys_device_simulator_iOS5.1.1.a
- libhci_vpr_device_simulator_iOS5.1.1.a
云端识别
- libhci_vpr_cloud_recog_device_simulator_iOS5.1.1.a
- libjtspeex_device_simulator_iOS5.1.1.a
- libjtopus_device_simulator_iOS5.1.1.a
本地识别
- libhci_vpr_local_recog_device_simulator_iOS5.1.1.a
适配器选择(只选其一)
- libhci_vpr_only_cloud_adapter_device_simulator_iOS5.1.1.a(只使用云端能力)
- libhci_vpr_only_local_adapter_device_simulator_iOS5.1.1.a(只使用本地能力)
- libhci_vpr_cloud_local_adapter_device_simulator_iOS5.1.1.a(既使用云端能力,又使用本地能力)
PS:
在集成灵云的iOS版本SDK时,静态库目录中会提供一种名称中包含adapter的静态库文件。值得说明一点的是,包含adapter名称的静态库文件是作为适配器使用的,所谓适配器是指开发者使用的灵云能力,即capkey是云端能力、本地能力,亦或者是云+端能力,所以适配器文件也还会对应以上的能力范围。当开发者集成时,适配器静态库在集成到工程中,有且只能有一个适配器静态库被添加到Link目录中,如开发者使用ASR的本地自由说能力,则适配器只需要选择libhci_asr_only_local_adapter_device_simulator_iOS7.1.1.a即可。
3. 能力集成说明
在调用VPR能力之前,需要初始化灵云SDK的SYS通用模块。详见灵云SDK开发手册
3.1 通用模块初始化
Android示例代码
// 创建初始化参数辅助类
InitParam initparam = new InitParam();
// 授权文件所在路径,此项必填
String authDirPath = context.getFilesDir().getAbsolutePath();;
initparam.addParam(InitParam.AuthParam.PARAM_KEY_AUTH_PATH, authDirPath);
// 灵云云服务的接口地址,此项必填
initparam.addParam(InitParam.AuthParam.PARAM_KEY_CLOUD_URL, "http://api.hcicloud.com:8888");
// 开发者密钥,此项必填,由捷通华声提供
initparam.addParam(InitParam.AuthParam.PARAM_KEY_DEVELOPER_KEY, "01234567890");
// 应用程序序号,此项必填,由捷通华声提供
initparam.addParam(InitParam.AuthParam.PARAM_KEY_APP_KEY, "1234abcd");
// 日志的路径,可选,如果不传或者为空则不生成日志
String logDirPath = "/storage/emulated/0/sinovoice/com.sinovoice.example/log";
initparam.addParam(InitParam.LogParam.PARAM_KEY_LOG_FILE_PATH, logDirPath);
//日志等级,0=无,1=错误,2=警告,3=信息,4=细节,5=调试
//SDK将输出小于等于logLevel的日志信息
initparam.addParam(InitParam.LogParam.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: %d", errCode);
} else {
NSLog(@"hci_init success");
}
常见errcode,详见灵云SDK开发手册
3.2 授权检测
在初始化灵云SDK的通用模块后,还需要调用授权检测函数获取云端授权。
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()) {
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)\n", 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 VPR初始化
Android示例代码
// 初始化VPR
// 构造VPR初始化的帮助类的实例
VprInitParam initParam = new VprInitParam();
// 放置能力所需资源文件路径。
String sdcardState = Environment.getExternalStorageState();
String sdPath = Environment.getExternalStorageDirectory()
.getAbsolutePath();
String dataPath = sdPath + File.separator + "sinovoice"
+ File.separator + "com.sinovoice.example" + File.separator + "files"
+ File.separator;
ShowMessage(dataPath);
initParam.addParam(VprInitParam.PARAM_KEY_DATA_PATH, dataPath);
initParam.addParam(VprInitParam.PARAM_KEY_INIT_CAP_KEYS, capkey);
copyAssetsFiles(context, dataPath);
int errCode = HciCloudVpr.hciVprInit(initParam.getStringConfig());
if (errCode != HciErrorCode.HCI_ERR_NONE) {
ShowMessage("HciVprInit error:" + HciCloudSys.hciGetErrorInfo(errCode));
return;
} else {
ShowMessage("HciVprInit Success");
}
C++示例代码
//VPR 初始化
string vpr_init_config = "dataPath=";
vpr_init_config += data_path;
vpr_init_config += ",initCapkeys=";
vpr_init_config += cap_key;
err_code = hci_vpr_init(vpr_init_config.c_str());
if (err_code != HCI_ERR_NONE)
{
printf("hci_vpr_init return (%d) \n",err_code);
return;
}
iOS示例代码
//本地能力所需本地资源所在路径
NSString *dataPath = [[NSBundle mainBundle] pathForResource:@"data" ofType:Nil];
//initCapKeys为必填项。其他可配置信息请查询开发手册hci_vpr_init函数说明。
NSString *initCapKeys = @"vpr.local.recog";
NSString *config = [NSString stringWithFormat:@"initCapKeys=%@,dataPath=%@",
initCapKeys,dataPath];
HCI_ERR_CODE errCode = HCI_ERR_NONE;
errCode = hci_vpr_init(config.UTF8String);
if (errCode != HCI_ERR_NONE){
NSLog(@"hci_vpr_init failed, error:%@", errCode);
eturn NO;
}
NSLog(@"hci_vpr_init success");
return YES;
常见errcode,详见灵云SDK开发手册
对于使用本地多能力时,可以使用initCapKeys传入需要使用的所有能力,以';'隔开。 initCapKeys的作用是告知SDK,需要预先准备哪些能力并提前加载对应的库资源,提高执行效率。
以HWR能力为例:
Android示例代码
initParam.addParam(HwrInitParam.PARAM_KEY_INIT_CAP_KEYS,
"hwr.local.letter;hwr.local.freestylus;hwr.local.associateword");
C++示例代码
//HWR 初始化
string hwr_init_config = "dataPath=";
hwr_init_config += data_path;
hwr_init_config += ",initCapkeys=";
hwr_init_config += "hwr.local.letter;hwr.local.freestylus;hwr.local.associateword";
iOS示例代码
//本地能力所需本地资源所在路径
NSString *dataPath = [[NSBundle mainBundle] pathForResource:@"data" ofType:Nil];
//initCapKeys为必填项。其他可配置信息请查询开发手册hci_hwr_init函数说明。
NSString *initCapKeys = @"hwr.local.letter;hwr.local.freestylus;hwr.local.associateword";
NSString *config = [NSString stringWithFormat:@"initCapKeys = %@,dataPath = %@",
initCapKeys,dataPath];
3.4 开启会话(session)
VPR 是通过会话(session)来管理识别过程的。在VPR能力初始化成功之后,需要通过开启识别会话来完成识别。详见灵云SDK开发手册
Android示例代码
// 启动 VPR Session
VprConfig sessionConfig = new VprConfig();
sessionConfig.addParam(VprConfig.SessionConfig.PARAM_KEY_CAP_KEY,capkey);
//用户模型路径可根据实际情况指定,否则将指定授权路径为用户模型路径
sessionConfig.addParam("usermodelpath",
Environment.getExternalStorageDirectory()+"");
Session session = new Session();
ShowMessage("sessionConfig is " + sessionConfig.getStringConfig());
int errCode = HciCloudVpr.hciVprSessionStart(sessionConfig.getStringConfig(),
session);
if (HciErrorCode.HCI_ERR_NONE != errCode) {
ShowMessage("hciVprSessionStart return " + errCode);
return false;
}
C++示例代码
int session_id = -1;
//用户模型路径,默认是授权文件路径
string session_config += "capkey=vpr.local.recog,usermodelpath = e://";
HCI_ERR_CODE err_code = hci_vpr_session_start(session_config.c_str(),
&session_id);
if( err_code != HCI_ERR_NONE )
{
user_id.clear();
printf("hci_vpr_session_start return (%d) \n",err_code);
return;
}
iOS示例代码
// 开启session
HCI_ERR_CODE errCode = HCI_ERR_NONE;
NSString *config = @"capKey=vpr.local.recog";
int sessionID; // 开启会话成功后,会返回的会话ID
errCode = hci_vpr_session_start(config.UTF8String, &sessionID);
if (errCode != HCI_ERR_NONE){
NSLog(@"hci_vpr_session_start failed, error:%@", errCode);
return NO;
}
NSLog(@"hci_vpr_session_start success");
return YES;
3.5 声纹特征训练或注册
在启动会话(session)成功后,即可以进行VPR的声纹特征训练。配置参数详见灵云SDK开发手册
Android示例代码
// VPR 注册
VprEnrollVoiceData enrollVoiceData = new VprEnrollVoiceData();
VprConfig enrollConfig = new VprConfig();
enrollVoiceData.setEnrollVoiceDataCount(nEnrollDataCount);
enrollVoiceData.setEnrollVoiceDataList(enrollVoiceDataList);
//非实时识别
enrollConfig.addParam(VprConfig.UserConfig.PARM_KEY_REAL_TIME, "no");
errCode = HciCloudVpr.hciVprEnroll(session, enrollVoiceData,
enrollConfig.getStringConfig(),enrollResult);
if (errCode == HciErrorCode.HCI_ERR_PARAM_INVALID) {
ShowMessage("Param invalid return "+errCode);
return false;
}
if (HciErrorCode.HCI_ERR_NONE != errCode) {
// 启动失败
HciCloudVpr.hciVprSessionStop(session);
ShowMessage("hciVprEnroll return " + errCode);
return false;
}
C++示例代码
//VPR 注册
/*audioformat 有效值:{pcm8k16bit,ulaw8k8bit,alaw8k8bit,
pcm16k16bit, ulaw16k8bit,alaw16k8bit} */
VPR_ENROLL_VOICE_DATA enrollData;
enrollData.uiVoiceDataCount = nEnrollDataCount;
enrollData.psVoiceDataList = enrollItemArray;
string enrollConfig = "audioformat=pcm16k16bit";
if(!user_id.empty())
{
enrollConfig += ",userid=" + user_id;
}
printf("hci_vpr_enroll config [%s]\n", enrollConfig.c_str());
VPR_ENROLL_RESULT enrollResult;
//非实时识别
enrollConfig += (",realtime="+realtime);
//VPR云端非实时识别
err_code = hci_vpr_enroll(session_id,&enrollData,enrollConfig.c_str(),
&enrollResult);
if (err_code != HCI_ERR_NONE)
{
user_id.clear();
printf("hci_vpr_enroll return (%d) \n",err_code);
}
else
{
printf( "hci_vpr_enroll success\n" );
user_id = enrollResult.pszUserId;
HciExampleComon::SetSpecialConsoleTextAttribute();
printf("enroll result is userid=%s\n",user_id.c_str());
HciExampleComon::SetOriginalConsoleTextAttribute();
hci_vpr_free_enroll_result(&enrollResult);
}
iOS示例代码
//VPR声纹特征训练
//添加训练数据
int enrollDataCount = 0;
VPR_ENROLL_VOICE_DATA_ITEM enrollItemArray[4];
NSString *tmpDataPath = [NSString stringWithFormat:@"%@/tmp",
NSTemporaryDirectory()];
NSFileManager *fileMgr = [NSFileManager defaultManager];
for (int iTmp = 0;iTmp < ENROLL_DATA_COUNT;iTmp++){
NSString *path = [[NSBundle mainBundle] pathForResource:
[NSString stringWithFormat:@"enroll_%d",iTmp] ofType:@"pcm"];
NSFileHandle *readFileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
NSDictionary *attr =[fileMgr attributesOfItemAtPath:path error:nil];
NSUInteger fileSize = attr.fileSize;
NSData *data = [readFileHandle readDataOfLength:fileSize];
enrollItemArray[enrollDataCount].pvVoiceData = (void *)data.bytes;
enrollItemArray[enrollDataCount].uiVoiceLength = (unsigned int)data.length;
enrollDataCount ++;
}
VPR_ENROLL_VOICE_DATA enrollData;
enrollData.uiVoiceDataCount = enrollDataCount; //训练音频的数量
enrollData.psVoiceDataList = enrollItemArray; //待训练音频数组
NSString *userId = @"12313"; //用户唯一标识符
NSString *enrollConfig = [NSString stringWithFormat:@"userid=%@",userId];
err_code = hci_vpr_enroll(sessionID,&enrollData,enrollConfig.UTF8String,
&enrollResult);
if (err_code != HCI_ERR_NONE){
NSLog(@"hci_vpr_enroll return (%d)",err_code);
}
else{
NSLog(@"hci_vpr_enroll success");
//返回训练结果的userid
NSLog(@"enroll result is userid=%@",
[NSString stringWithFormat:@"%s",enrollResult.pszUserId]);
hci_vpr_free_enroll_result(&enrollResult);
}
常见errcode,详见灵云SDK开发手册
3.6 声纹特征确认
Android示例代码
// 开始校验
VprVerifyResult verifyResult = new VprVerifyResult();
VprConfig verifyConfig = new VprConfig();
//非实时识别
verifyConfig.addParam(VprConfig.UserConfig.PARM_KEY_REAL_TIME, "no");
errCode = HciCloudVpr.hciVprVerify(session, voiceDataVerify,
verifyConfig.getStringConfig(),verifyResult);
if (errCode == HciErrorCode.HCI_ERR_PARAM_INVALID) {
ShowMessage("Param invalid return "+errCode);
return false;
}
if (HciErrorCode.HCI_ERR_NONE != errCode) {
ShowMessage("Hcivpr hciVprVerify return " + errCode);
HciCloudVpr.hciVprSessionStop(session);
return false;
}
if (verifyResult.getStatus() == VprVerifyResult.VPR_VERIFY_STATUS_MATCH) {
ShowMessage("VprVerify success");
ShowMessage("voice data matches with user_id !");
ShowMessage("score:"+verifyResult.getScore());
} else {
ShowMessage("the score is:"+verifyResult.getScore());
ShowMessage("voice data doesn't match with user_id !");
}
C++示例代码
//非实时识别
//Verify
//audioformat
//{pcm8k16bit,ulaw8k8bit,alaw8k8bit,pcm16k16bit, ulaw16k8bit,alaw16k8bit}
string verify_config = "audioformat= pcm16k16bit";
verify_config += ",userid=" + user_id;
err_code = hci_vpr_verify(session_id, voiceData.buff_, voiceData.buff_len_,
verify_config.c_str(), &verifyResult);
if( err_code != HCI_ERR_NONE )
{
printf("hci_vpr_verify return (%d) \n",err_code);
}
else
{
printf( "hci_vpr_verify success\n" );
HciExampleComon::SetSpecialConsoleTextAttribute();
printf("the result score is :%d \n",verifyResult.uiScore);
if (verifyResult.eStatus == VPR_VERIFY_STATUS_MATCH)
{
printf( "voice data matches with user id:%s!\n",user_id.c_str() );
}
else
{
printf("voice data doesn't match with user id:%s !\n",
user_id.c_str());
}
hci_vpr_free_verify_result(&verifyResult);
HciExampleComon::SetOriginalConsoleTextAttribute();
}
iOS示例代码
//VPR声纹特征确认
//待确认音频文件
NSString *path = [[NSBundle mainBundle] pathForResource:@"verify" ofType:@"pcm"];
NSData *voiceData = [NSData dataWithContentsOfFile:path];
NSString *userId = @"12313"; //用户唯一标识符
NSString *verifyConfig = [NSString stringWithFormat:@"userid=%@",userId];
NSLog(@"hci_vpr_verify config [%@]",verifyConfig);
err_code = hci_vpr_verify(sessionID, (void *)voiceData.bytes,
(unsigned int)voiceData.length,
verifyConfig.UTF8String, &verifyResult);
if(err_code != HCI_ERR_NONE){
NSLog(@"hci_vpr_verify return (%d)",err_code);
}else{
NSLog(@"hci_vpr_verify success");
NSLog(@"the result score is :%d",verifyResult.uiScore);
if (verifyResult.eStatus == VPR_VERIFY_STATUS_MATCH){
NSLog(@"voice data matches with user id:%@!",_userId);
}
else{
NSLog(@"voice data doesn't match with user id:%@ !",_userId);
}
hci_vpr_free_verify_result(&verifyResult);
}
常见errcode,详见灵云SDK开发手册
3.7 声纹辨识确认
Android示例代码
// 辨识
VprIdentifyResult identifyResult = new VprIdentifyResult();
VprConfig identifyConfig = new VprConfig();
errCode = HciCloudVpr.hciVprIdentify(session, voiceDataVerify,
identifyConfig.getStringConfig(), identifyResult);
if (errCode == HciErrorCode.HCI_ERR_PARAM_INVALID) {
ShowMessage("Param invalid return "+errCode);
return false;
}
if (HciErrorCode.HCI_ERR_NONE != errCode) {
ShowMessage("Hcivpr hciVprIdentify return " + errCode);
HciCloudVpr.hciVprSessionStop(session);
return false;
}
ShowMessage("vprIdentify success");
ShowMessage("size: " + identifyResult.getIdentifyResultItemList().size());
PrintIdentifyResult(identifyResult);
C++示例代码
//audioformat
//{pcm8k16bit,ulaw8k8bit,alaw8k8bit,pcm16k16bit,ulaw16k8bit,alaw16k8bit}
string identify_config = "audioformat=pcm16k16bit,";
identify_config += "groupid=" + groupId;
printf("hci_vpr_identify config [%s]\n", identify_config.c_str());
//非实时识别
err_code = hci_vpr_identify(session_id, voiceData.buff_, voiceData.buff_len_,
identify_config.c_str(), &identifyResult);
if( err_code != HCI_ERR_NONE )
{
printf("hci_vpr_identify return (%d) \n",err_code);
hci_vpr_session_stop(session_id);
return;
}
printf( "hci_vpr_identify success\n" );
PrintIdentifyResult(identifyResult);
hci_vpr_free_identify_result(&identifyResult);
iOS示例代码
//待辨识音频文件
NSString *path = [[NSBundle mainBundle] pathForResource:@"verify" ofType:@"pcm"];
NSData *voiceData = [NSData dataWithContentsOfFile:path];
NSString *groupId = @"xxxxxxxxx"; //用户组id
NSString *identifyConfig = [NSString stringWithFormat:@"groupid=%@",groupId];
NSLog(@"hci_vpr_identify config [%@]",identifyConfig);
//辨识确认
NSData *voiceData = [NSData dataWithContentsOfFile:path];
err_code = hci_vpr_identify(sessionID, (void *)voiceData.bytes,
(unsigned int)voiceData.length, identifyConfig.UTF8String,
&identifyResult);
if( err_code != HCI_ERR_NONE )
{
NSLog(@"hci_vpr_identify return (%d)",err_code);
hci_vpr_session_stop(_sessionID);
return;
}
NSLog(@"hci_vpr_identify success");
[self printIdentifyResult:identifyResult];
for (int index = 0; index < (int)result.uiIdentifyResultItemCount; ++index){
VPR_IDENTIFY_RESULT_ITEM& item = result.pIdentifyResultItemList[index];
NSLog(@"index:%d", index);
NSLog(@"userid:%s",item.pszUserId);
NSLog(@"score:%d",item.uiScore);
}
hci_vpr_free_identify_result(&identifyResult);
常见errcode,详见灵云SDK开发手册
3.8 结束识别
最后我们需要反初始化,依次关闭会话,终止AFR能力,关闭灵云系统。
Android示例代码
//关闭VPR识别会话
errCode = HciCloudVpr.hciAfrSessionStop(session);
//终止VPR能力
errCode = HciCloudVpr.hciAfrRelease();
//终止灵云系统
errCode = HciCloudSys.hciRelease();
C++示例代码
//关闭VPR识别会话
errCode = hci_vpr_session_stop(nSessionId);
//终止VPR能力
errCode = hci_vpr_release();
//终止灵云系统
errCode = hci_release();
iOS示例代码
//关闭VPR识别会话
errCode = hci_vpr_session_stop(nSessionId);
//终止VPR能力
errCode = hci_vpr_release();
//终止灵云系统
errCode = hci_release();
4. 常用配置说明
VPR支持实时识别模式,即realtime=yes。可以在hci_vpr_session_start传入该参数,启动实时识别session。 以声纹辨识为例: Android示例代码
//实时识别
int nPerLen = 3200*20; //传入每段音频长度
int nLen = 0; //已传入音频的长度
VprConfig identifyConfig = new VprConfig();
identifyConfig.addParam(VprConfig.UserConfig.PARM_KEY_REAL_TIME, "yes");
ShowMessage("identifyConfig is " + identifyConfig.getStringConfig());
while (nLen+nPerLen<voiceDataVerify.length) {
int nThisLen = 0;
if (voiceDataVerify.length-nLen >= nPerLen) {
nThisLen = nPerLen;
}
else {
nThisLen =voiceDataVerify.length - nLen;
}
byte[] subVoiceData = new byte[nPerLen];
System.arraycopy(voiceDataVerify, nLen, subVoiceData, 0, nPerLen);
errCode = HciCloudVpr.hciVprIdentify(session, voiceDataVerify,
identifyConfig.getStringConfig(), identifyResult);
if (errCode == HciErrorCode.HCI_ERR_PARAM_INVALID) {
ShowMessage("Param invalid return "+errCode);
break;
}
if (errCode == HciErrorCode.HCI_ERR_NONE)
{
break;
}
nLen += nThisLen;
}
// 若未检测到端点,但数据已经传入完毕,则需要告诉引擎数据输入完毕
// 或者检测到末端了,也需要告诉引擎,获取结果
if (errCode == HciErrorCode.HCI_ERR_VPR_REALTIME_WAIT ||
errCode ==HciErrorCode.HCI_ERR_NONE) {
errCode = HciCloudVpr.hciVprIdentify(session, null,
identifyConfig.getStringConfig(), identifyResult);
ShowMessage(identifyConfig.getStringConfig());
if (errCode != HciErrorCode.HCI_ERR_NONE) {
ShowMessage("identify return "+errCode);
}
else {
ShowMessage("vprIdentify success");
ShowMessage("size: " + identifyResult.getIdentifyResultItemList().size());
PrintIdentifyResult(identifyResult);
}
}
C++示例代码
//实时识别
string identify_config = "audioformat=pcm16k16bit,";
identify_config += "groupid=" + groupId;
int nPerLen = 3200*20; //传入每段音频长度
int nLen = 0; // 当前已传入的长度
while (nLen < voiceData.buff_len_)
{
int nThisLen = 0;
if( voiceData.buff_len_ - nLen >= nPerLen )
{
nThisLen = nPerLen;
}
else
{
nThisLen = voiceData.buff_len_ - nLen;
}
err_code = hci_vpr_identify(session_id, voiceData.buff_+nLen, nThisLen,
identify_config.c_str(), &identifyResult);
if (err_code == HCI_ERR_PARAM_INVALID)
{
printf("hci_vpr_identify return (%d) \n");
break;
}
if( err_code == HCI_ERR_NONE )
{
// 检测到末端了,跳出循环
break;
}
nLen += nThisLen;
}
// 若未检测到端点,但数据已经传入完毕,则需要告诉引擎数据输入完毕
// 或者检测到末端了,也需要告诉引擎,获取结果
if( err_code == HCI_ERR_VPR_REALTIME_WAITING || err_code == HCI_ERR_NONE )
{
err_code = hci_vpr_identify(session_id, NULL, 0,
identify_config.c_str(), &identifyResult);
if (err_code != HCI_ERR_NONE)
{
printf("hci_vpr_identify return (%d) \n");
}
}
if( err_code == HCI_ERR_NONE )
{
// 输出识别结果
PrintIdentifyResult(identifyResult);
// 释放识别结果
hci_vpr_free_identify_result(&identifyResult);
if( err_code != HCI_ERR_NONE )
{
printf( "hci_vpr_free_recog_result return %d\n", err_code );
hci_vpr_session_stop(session_id);
return ;
}
printf( "hci_vpr_free_recog_result success\n" );
}
else
{
printf( "hci_vpr_recog failed with %d\n", err_code );
}
iOS示例代码
NSString *path = [[NSBundle mainBundle] pathForResource:@"verify" ofType:@"pcm"];
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSDictionary *attr = [fileMgr attributesOfItemAtPath:path error:nil];
NSUInteger fileSize = attr.fileSize;
NSUInteger offset = 3200 * 20; //送入训练的每片音频大小
//片数
NSUInteger chunks = (fileSize%offset==0)?(fileSize/offset):(fileSize/(offset)+1);
NSFileHandle *readFileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
for (int i = 0; i < chunks; ++i) {
NSUInteger currentSize;
if (i != chunks - 1) {
currentSize = offset;
} else {
currentSize = fileSize - i * offset;
}
[readFileHandle seekToFileOffset:i * offset];
NSData *subData = [readFileHandle readDataOfLength:currentSize];
err_code = hci_vpr_identify(sessionID, (void *)subData.bytes,
(unsigned int)subData.length, identifyConfig.UTF8String,
&identifyResult);
if (err_code == HCI_ERR_PARAM_INVALID){
NSLog(@"hci_vpr_identify return (%d)",err_code);
break;
}
if (err_code == HCI_ERR_NONE){
break;
}
}
//在实时模式场景,忽略HCI_ERR_VPR_REALTIME_WAITING的情况,继续注册后面的音频。
//HCI_ERR_VPR_REALTIME_WAITING (实时识别等待音频)含义是:还没有数据,或者是需要更多数据。
if( err_code == HCI_ERR_VPR_REALTIME_WAITING || err_code == HCI_ERR_NONE )
{
err_code = hci_vpr_identify(sessionID, NULL, 1,
identifyConfig.UTF8String, &identifyResult);
if (err_code != HCI_ERR_NONE)
{
NSLog(@"hci_vpr_identify return (%d)",err_code);
}
}
if( err_code == HCI_ERR_NONE )
{
//打印辨识结果
[self printIdentifyResult:identifyResult];
//释放辨识结果
hci_vpr_free_identify_result(&identifyResult);
if( err_code != HCI_ERR_NONE )
{
NSLog(@"hci_vpr_free_recog_result return %d", err_code);
hci_vpr_session_stop(_sessionID);
return ;
}
NSLog(@"hci_vpr_free_recog_result success");
}
else
{
NSLog(@"hci_vpr_recog failed with %d", err_code);
}
5. FAQ
Q: 我运行你们的 SDK 报 19 号错误:START LOG FAILED, 应该如何解决? A: 出现此问题,一般由两种原因造成。
工程文件里引入了中文路径,导致SDK启动时,读写对应的日志路径报错。 日志文件定义的路径由于权限问题不可读写,比如在 Android 平台上运行时,没有 WRITE_EXTERNAL_STORAGE的权限。
Q: 我运行你们的 SDK 报 23 号错误:LOAD_FUNCTION_FROM_DLL FAILED,应该如何解决? A: 这种情况,通常是由于缺库。最常见的场景是使用本地能力的 capkey,如 vpr.loacl.recog,没有将库文件对应组入(我们 SDK 示例程序,与 example 同层有全套库文件,示例程序内默认只组入了云端库),将相关库补齐即可使用。此外,由于 VPR能力引用了第三方的 OPENCV, MKL 库,因此如果第三方库没有拷入,也会导致此问题。
Q: 我运行你们的 SDK 报 14 号错误:LOCAL_RES_MISSING,应该如何解决? A: 本地能力常会遇到此问题,此问题是由于没有组入对应的本地资源文件。请在我们开发者社区下载需求的本地资源(VPR本地模型)等,并将资源文件组入代码 datapath 指定路径,然后再次尝试运行程序。
Q: 我运行你们的 SDK 报 11 号错误:SERVICE_RESPONSE_FAILED ,应该如何解决? A: 此问题原因较为复杂,可能是传入的参数问题与服务器不匹配,如调用私有云时,没有传入 property 等配置项。也可能是服务端内部的返回原因,请联系技术支持排查。
Q: 我运行你们的SDK报 7 号错误:CONFIG_UNSUPPORT ,应该如何解决? A: 与 11号错误类似,还是配置串问题,7 号错误原因为传入了不支持的配置串。请仔细检查各接口传入的所有配置串,并联系技术支持排查。
Q: 我运行你们的SDK报 9 号错误: SERVICE_TIMEOUT ,应该如何解决? A: 这个是因为您请求我们服务器到返回结果的时间,超过了上限。请检查网络环境相关的原因,并协助提供日志。
Q: 我运行你们的 SDK 报 16 号错误: SESSION_INVALID ,应该如何解决? A: 这个需要检查详细的代码调用过程,调用每个能力核心接口时,都是需要传入 sessionid 的,如果在 sessionstart 的时候,已经发生了错误,或者使用的id,并不是 sessionstart 时候创建的,就会返回此错误,请联系技术支持排查。
Q: 我运行你们的 SDK 报 0 号错误: ERROR NONE,应该如何解决? A: SDK 报 0 号错误, ERROR NONE,代表执行成功,请您继续后续的集成。