本文介绍了如何使用阿里云智能语音服务提供的HarmonyOS Next NUI SDK,包括SDK下载安装、关键接口及代码示例。
前提条件
下载安装
下载harmony.zip。
重要请下载后在样例初始化代码中替换您的阿里云账号信息、Appkey和Token才可运行。
类别
兼容范围
系统
支持HarmonyOS Next 5.0 版本,API LEVEL 12, DevEco Studio版本号5.0.3.403
架构
arm64-v8a
此SDK还包含如下功能:
功能
是否支持
一句话识别
是
实时语音识别
是
语音合成
是
实时长文本语音合成
是
流式文本语音合成
是
离线语音合成
否
录音文件识别极速版
是
唤醒及命令词
否
听悟实时推流
是
以arkts HAR包的形式进行集成。解压ZIP包,其中nuisdk-release/neonui.har 是SDK生成的HAR包文件,在用户工程项目中导入调用即可。如果需要HarmonyOS Next CPP接入方式,可在ZIP包的harmonyos_libs和harmonyos_include中获得动态库和头文件。
使用DevEco Studio打开harmony_nlsdemo目录下工程查看参考代码实现,其中一句话识别示例代码为SpeechTranscriber.ets文件,替换UserKey.ets中 UserKey类的Appkey和Token后,即可直接运行。
SDK关键接口
initialize:初始化SDK。
/**
* 初始化SDK,SDK可多实例,请先释放后再次进行初始化。请勿在UI线程调用,可能会引起阻塞。
* @param callback:事件监听回调,参见下文具体回调。
* @param parameters:json string形式的初始化参数,参见下方说明或接口说明:https://help.aliyun.com/document_detail/173298.html。
* @param level:log打印级别,值越小打印越多。
* @param save_log:是否保存log为文件,存储目录为ticket中的debug_path字段值。注意,log文件无上限,请注意持续存储导致磁盘存满。
* @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。
*/
public initialize(callback:INativeNuiCallback ,
parameters:string ,
level:number ,
save_log:boolean=false ):number
其中,INativeNuiCallback接口类型包含如下回调。
onNuiAudioStateChanged:根据音频状态进行录音功能的开关。
/** * 当start/stop/cancel等接口调用时,SDK通过此回调通知App进行录音的开关操作。 * @param state:录音需要的状态(打开/停止/关闭) */ onNuiAudioStateChanged:(state:Constants.AudioState)=>void
onNuiNeedAudioData:在回调中提供音频数据。注意:由于ArkTS中异步接口调用频繁,因此建议不使用此回调提供录音数据,用户应通过主动调用updateAudio()顺序地给SDK中传入录音数据。
/** * 开始识别时,此回调被连续调用,App需要在回调中进行语音数据填充。 * @param buffer:填充语音的存储区。 * @return:实际填充的字节数。 */ onNuiNeedAudioData:(buffer:ArrayBuffer)=>number;
onNuiEventCallback:SDK事件回调。
/** * SDK主要事件回调 * @param event:回调事件,参见如下事件列表。 * @param resultCode:参见错误码,在出现EVENT_ASR_ERROR事件时有效。 * @param arg2:保留参数。 * @param kwsResult:语音唤醒功能(暂不支持)。 * @param asrResult:语音识别结果。 */ onNuiEventCallback:(event:Constants.NuiEvent, resultCode:number, arg2:number, kwsResult:KwsResult, asrResult:AsrResult)=>void;
onNuiAudioRMSChanged:音频能量值回调。
/** * 音频能量值回调 * @param val: 音频数据能量值回调,范围-160至0,一般用于UI展示语音动效 */ onNuiAudioRMSChanged:(val:number)=>number;
事件列表:
名称
说明
EVENT_VAD_START
检测到人声起点。
EVENT_VAD_END
检测到人声尾点。
EVENT_ASR_PARTIAL_RESULT
语音识别中间结果。
EVENT_ASR_ERROR
根据错误码信息判断出错原因。
EVENT_MIC_ERROR
录音错误,表示SDK连续2秒未收到任何音频,可检查录音系统是否正常。
EVENT_SENTENCE_START
实时语音识别事件,表示检测到一句话开始。
EVENT_SENTENCE_END
实时语音识别事件,表示检测到一句话结束,返回一句完整的结果。
EVENT_SENTENCE_SEMANTICS
暂不使用。
EVENT_TRANSCRIBER_COMPLETE
停止语音识别后最终事件。
setParams:以JSON格式设置SDK参数。
/** * 以JSON格式设置参数 * @param params:参见接口说明:https://help.aliyun.com/document_detail/173298.html。 * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。 */ public setParams(params:string):number
startDialog:开始识别。
/** * 开始识别 * @param vad_mode:多种模式,对于识别场景,请使用P2T。 * @param dialog_params:json string形式的对话参数,参见接口说明:https://help.aliyun.com/document_detail/173298.html。 * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。 */ public startDialog(vad_mode:Constants.VadMode, dialog_params:string):number
stopDialog:结束识别。
/** * 结束识别,调用该接口后,服务端将返回最终识别结果并结束任务。 * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。 */ public stopDialog():number
cancelDialog:立即结束识别。
/** * 立即结束识别,调用该接口后,不等待服务端返回最终识别结果就立即结束任务。 * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。 */ public cancelDialog():number
release:释放SDK。
/** * 释放SDK资源 * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。 */ public release():number
GetVersion:获得当前SDK版本信息。
/** * 获得当前SDK版本信息 * @return: 字符串形式的SDK版本信息 */ public GetVersion():string
调用步骤
创建SDK类对象实例
初始化SDK、录音实例。
根据业务需求设置参数。
调用startDialog开始识别。
根据音频状态回调onNuiAudioStateChanged中的事件,打开录音机。
调用 updateAudio()提供录音数据给SDK。
在EVENT_SENTENCE_START事件回调中表示当前开始识别一个句子,在EVENT_ASR_PARTIAL_RESULT事件回调中获取识别中间结果,在EVENT_SENTENCE_END事件回调中获得这句话完整的识别结果和各相关信息。
调用stopDialog结束识别。并从EVENT_TRANSCRIBER_COMPLETE事件回调确认已停止识别。
结束调用,使用release接口释放SDK资源。
代码示例
您如果有多个需求,也可以直接new对象进行使用。也可采用GetInstance获得单例。
NUI SDK初始化
//定义类NativeNuiCallbackHandle 实现回调接口INativeNuiCallback
class NativeNuiCallbackHandle implements INativeNuiCallback{
//内部实现INativeNuiCallback中的5个接口函数
//此处省略
}
let context = getContext(this) as common.UIAbilityContext;
this.filesDir = context.filesDir;
this.resourceDir = context.resourceDir;
//这里获得资源路径, 由于资源文件存放在工程的resfiles目录下,所以使用沙箱路径下的resfiles目录
let asset_path:string = this.resourceDir+"/resources_cloud"
//由于用户无法直接操作设备目录,因此调试路径设置为APP所在的沙箱路径下的公共目录filesDir
let debug_path:string = this.filesDir
//初始化SDK,注意用户需要在genInitParams中填入相关ID信息才可以使用。
cbhandle:NativeNuiCallbackHandle = new NativeNuiCallbackHandle()
g_asrinstance:NativeNui = new NativeNui(Constants.ModeType.MODE_DIALOG, "asr")
let ret:number = this.g_asrinstance.initialize(this.cbhandle, this.genInitParams(asset_path,debug_path), Constants.LogLevel.LOG_LEVEL_VERBOSE, false);
console.info("result = " + ret);
if (ret == Constants.NuiResultCode.SUCCESS) {
console.error(`call g_asrinstance.initialize() return success`);
} else {
//抛出错误异常信息。
console.error(`call g_asrinstance.initialize() return error:${ret}`);
}
其中,genInitParams生成String JSON字符串,包含资源目录和用户信息。其中用户信息包含如下字段。
genInitParams(workpath:string, debugpath:string):string {
let str:string = "";
//获取token方式:
//使用Map类型实现JSON格式的数据存储。用户也可以使用自有的JSON实现。
let object:Map<string, string|number|boolean|object> = new Map();
//账号和项目创建
// ak_id ak_secret app_key如何获得,请查看https://help.aliyun.com/document_detail/72138.html
object.set("app_key", "用户自己的app_key"); // 必填
//方法1:
// 首先ak_id ak_secret app_key如何获得,请查看https://help.aliyun.com/document_detail/72138.html
// 然后请看 https://help.aliyun.com/document_detail/466615.html 使用其中方案一获取临时凭证
// 此方案简介: 远端服务器生成具有有效时限的临时凭证, 下发给移动端进行使用, 保证账号信息ak_id和ak_secret不被泄露
// 获得Token方法(运行在APP服务端): https://help.aliyun.com/document_detail/450255.html?spm=a2c4g.72153.0.0.79176297EyBj4k
object.set("token", "用户自己的token"); // 必填
//方法2:
// STS获取临时凭证方法暂不支持
//方法3:(强烈不推荐,存在阿里云账号泄露风险)
// 参考Auth类的实现在端上访问阿里云Token服务获取SDK进行获取。请勿将ak/sk存在本地或端侧环境。
// 此方法优点: 端侧获得Token, 无需搭建APP服务器。
// 此方法缺点: 端侧获得ak/sk账号信息, 极易泄露。
// JSONObject object = Auth.getAliYunTicket();
object.set("device_id", "用户设备所具有的唯一ID信息"); // 必填, 推荐填入具有唯一性的id, 方便定位问题
object.set("url", "wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1"); // 默认
object.set("workspace", workpath); // 必填, 且需要有读写权限
//当初始化SDK时的save_log参数取值为true时,该参数生效。表示是否保存音频debug,该数据保存在debug目录中,需要确保debug_path有效可写。
// object.put("save_wav", "true");
//debug目录,当初始化SDK时的save_log参数取值为true时,该目录用于保存中间音频文件。
object.set("debug_path", debugpath);
// FullMix = 0 // 选用此模式开启本地功能并需要进行鉴权注册
// FullCloud = 1
// FullLocal = 2 // 选用此模式开启本地功能并需要进行鉴权注册
// AsrMix = 3 // 选用此模式开启本地功能并需要进行鉴权注册
// AsrCloud = 4
// AsrLocal = 5 // 选用此模式开启本地功能并需要进行鉴权注册
//一句话识别
console.log("init asr for 实时语音识别")
object.set("service_mode", Constants.ModeFullCloud); // 必填。 此处是实时语音识别功能与一句话识别功能配置3个差异之1
str = MapToJson(object) //JSON格式转为字符串
console.info("configinfo genInitParams:" + str);
return str;
}
function MapToJson(map:Map<string, string|number|boolean|object>):string {
let obj:object = Object({});
map.forEach( (value, key) => {
obj[key] = value;
});
return JSON.stringify(obj)
}
参数设置
以JSON字符串形式进行设置。
//设置相关识别参数,具体参考API文档
// initialize()之后startDialog之前调用
nui_instance.setParams(genParams());
genParams():string {
let params:string = "";
let nls_config:Map<string, string|number|boolean|object> = new Map();
nls_config.set("enable_intermediate_result", true);
// 参数可根据实际业务进行配置
// 接口说明可见https://help.aliyun.com/document_detail/173528.html
// 查看 2.开始识别
// 此处是实时语音识别功能与一句话识别功能配置3个差异之3,无需设置vad相关参数
nls_config.set("enable_punctuation_prediction", true);
nls_config.set("enable_inverse_text_normalization", true);
// nls_config.set("customization_id", "test_id");
// nls_config.set("vocabulary_id", "test_id");
// nls_config.put("enable_words", false);
// nls_config.set("sample_rate", 16000);
// nls_config.set("sr_format", "opus");
let parameters:Map<string, string|number|boolean|object> = new Map();
parameters.set("nls_config", Object( JSON.parse(MapToJson(nls_config)) ) );
//一句话识别
console.log("start asr for 实时语音识别")
parameters.set("service_type", Constants.kServiceTypeSpeechTranscriber); // 必填 此处是实时语音识别功能与一句话识别功能配置3个差异之2
params = MapToJson(parameters);//parameters.toString();
console.log("configinfo genParams" + params)
return params;
}
开始识别
通过startDialog接口开启监听。
//默认使用Constants.VadMode.TYPE_P2T。
//Constants.VadMode.TYPE_VAD只在具有离线功能的SDK中支持,若想要启动VAD,请设置参数enable_voice_detection。
nui_instance.startDialog(Constants.VadMode.TYPE_P2T, genDialogParams());
genDialogParams():string {
let params:string = "";
let dialog_param:Map<string, string|number|boolean|object> = new Map();
// 运行过程中可以在startDialog时更新临时参数,尤其是更新过期token
// 注意: 若下一轮对话不再设置参数,则继续使用初始化时传入的参数
// dialog_param.put("app_key", "");
// dialog_param.put("token", "");
params = MapToJson(dialog_param);
console.info("configinfo dialog params: " + params);
return params;
}
推送录音数据
updateAudio:在AudioCapturer的on('readData',)注册的回调函数中,直接调用updateAudio接口把录音数据送入SDK内部。
//g_asrinstance.updateAudio(buffer,false) /*AudioCapturer中注册的'readData'接口是AudioCapturer.readDataCallback *AudioCapturer.audioCapturer.on('readData', AudioCapturer.readDataCallback); */ class AudioCapturer{ static readDataCallback = (buffer: ArrayBuffer) => { console.log(`${TAG} read data bytelength is ${buffer.byteLength}. uid[${process.uid}] pid[${process.pid}] tid[${process.tid}]`); AudioCapturer.g_asrinstance.updateAudio(buffer,false) } }
回调处理
onNuiAudioStateChanged:录音状态回调,SDK内部维护录音状态,调用时根据该状态的回调进行录音机的开关操作。
/* 对于鸿蒙开发环境IDE版本5.0.3.403 以前的版本,AudioCapturer模块如果使用注册回调[on("readData",)]的方式读取录音数据, 存在AudioCapturer.stop后直接start不会触发回调的情况。此时必须按照 (stop,realease)再(createAudioCapturer,start)的流程才能正常工作。 升级为 IDE版本5.0.3.403版本后,以上问题已经解决。所以以下示例代码中注释掉了create/release相关接口的调用。 */ onNuiAudioStateChanged(state:Constants.AudioState):void { console.info(`womx onUsrNuiAudioStateChanged(${state})`) if (state === Constants.AudioState.STATE_OPEN){ console.info(`womx onUsrNuiAudioStateChanged(${state}) audio recorder start`) //AudioCapturer.init(g_asrinstance) AudioCapturer.start() console.info(`womx onUsrNuiAudioStateChanged(${state}) audio recorder start done`) } else if (state === Constants.AudioState.STATE_CLOSE){ console.info(`womx onUsrNuiAudioStateChanged(${state}) audio recorder close`) AudioCapturer.stop() //AudioCapturer.release() console.info(`womx onUsrNuiAudioStateChanged(${state}) audio recorder close done`) } else if (state === Constants.AudioState.STATE_PAUSE){ console.info(`womx onUsrNuiAudioStateChanged(${state}) audio recorder pause`) AudioCapturer.stop() //AudioCapturer.release() console.info(`womx onUsrNuiAudioStateChanged(${state}) audio recorder pause done`) } }
onNuiNeedAudioData:录音数据回调,在该回调中填充录音数据。
public int onNuiNeedAudioData(byte[] buffer, int len) { console.info(`warning,this callback should not be called in HarmonyOS Next`) return 0; }
onNuiEventCallback:NUI SDK事件回调,请勿在事件回调中调用SDK的接口,可能引起死锁。
onNuiEventCallback(event:Constants.NuiEvent, resultCode:number, arg2:number, kwsResult:KwsResult, asrResult:AsrResult):void { console.log("onUsrNuiEventCallback event is " + event); // asrResult包含task_id,task_id有助于排查问题,请用户进行记录保存。 // // 新版本新增asrResult.allResponse,若为非nullptr和非空,则给出json格式字符串的完整信息。 if (event == Constants.NuiEvent.EVENT_TRANSCRIBER_COMPLETE) { // 例如展示识别结果 showText(asrView, asrResult.asrResult); } else if (event == Constants.NuiEvent.EVENT_ASR_PARTIAL_RESULT || event === Constants.NuiEvent.EVENT_SENTENCE_END) { if (event === Constants.NuiEvent.EVENT_ASR_PARTIAL_RESULT ) { // 例如展示当前句子的识别中间结果 this.message = "EVENT_ASR_PARTIAL_RESULT" } else if(event === Constants.NuiEvent.EVENT_SENTENCE_END){ // 例如展示当前句子的完整识别结果 this.message = "EVENT_SENTENCE_END" } showText(asrView, asrResult.asrResult); } else if (event == Constants.NuiEvent.EVENT_ASR_ERROR) { // asrResult在EVENT_ASR_ERROR中为错误信息,搭配错误码resultCode和其中的task_id更易排查问题,请用户进行记录保存。 } else if (event == Constants.NuiEvent.EVENT_MIC_ERROR) { // EVENT_MIC_ERROR表示2s未传入音频数据,请检查录音相关代码、权限或录音模块是否被其他应用占用。 } else if (event == Constants.NuiEvent.EVENT_DIALOG_EX) { /* unused */ // 此事件可不用关注 } //解析asr识别结果 if (asrResult) { let asrinfo:string = "" asrinfo = asrResult.asrResult if (asrinfo) { try { let asrresult_json:object|null = JSON.parse(asrResult.asrResult) if (asrresult_json) { let payload:object|null = asrresult_json["payload"]; if (payload) { //console.log(JSON.stringify(payload)) let asrmessage:string = payload["result"]; //解析到云端返回的识别结果 } } } catch (e){ console.error("got asrinfo not json, so donot fresh asrinfo." + JSON.stringify(e)) } } } }
结束识别
nui_instance.stopDialog();
释放SDK
nui_instance.release();