本文介紹了如何使用阿里云智能語音服務提供的HarmonyOS Next NUI SDK,包括SDK下載安裝、關鍵接口及代碼示例。
前提條件
下載安裝
下載V1.5.016.napi.001.003_5d87aea0fa4f42e1b5bb254573fbbcc6af546c6b.tar.gz。
重要請下載后在樣例初始化代碼中替換您的阿里云賬號信息、Appkey和Token才可運行。
類別
兼容范圍
系統
支持HarmonyOS Next 5.0 版本,API LEVEL 12, DevEco Studio版本號5.0.3.403
架構
arm64-v8a
此SDK還包含如下功能:
功能
是否支持
一句話識別
是
實時語音識別
是
語音合成
是
實時長文本語音合成
是
流式文本語音合成
是
離線語音合成
否
錄音文件識別極速版
是
喚醒及命令詞
否
聽悟實時推流
是
以arkts HAR包的形式進行集成。解壓壓縮包,其中entry/libs/neonui.har 是SDK生成的HAR包文件,在用戶工程項目中導入調用即可。如果需要HarmonyOS Next CPP接入方式,可在壓縮包的native/libs和native/include中獲得動態庫和頭文件。
使用DevEco Studio打開工程,其中一句話識別示例代碼為OneSentenceAsrPage.ets文件,替換UserKey.ets中 UserKey類的Appkey和Token后,即可直接運行。
SDK關鍵接口
initialize:初始化SDK。
/**
* 初始化SDK,SDK可多實例,請先釋放后再次進行初始化。請勿在UI線程調用,可能會引起阻塞。
* @param callback:事件監聽回調,參見下文具體回調。
* @param parameters:json string形式的初始化參數,參見下方說明或接口說明:http://bestwisewords.com/document_detail/173298.html。
* @param level:log打印級別,值越小打印越多。
* @param save_log:是否保存log為文件,存儲目錄為ticket中的debug_path字段值。注意,log文件無上限,請注意持續存儲導致磁盤存滿。
* @return:參見錯誤碼:http://bestwisewords.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_RESULT
語音識別最終結果。
EVENT_ASR_ERROR
根據錯誤碼信息判斷出錯原因。
EVENT_MIC_EEROR
錄音錯誤,表示SDK連續2秒未收到任何音頻,可檢查錄音系統是否正常。
setParams:以JSON格式設置SDK參數。
/** * 以JSON格式設置參數 * @param params:參見接口說明:http://bestwisewords.com/document_detail/173298.html。 * @return:參見錯誤碼:http://bestwisewords.com/document_detail/459864.html。 */ public setParams(params:string):number
startDialog:開始識別。
/** * 開始識別 * @param vad_mode:多種模式,對于識別場景,請使用P2T。 * @param dialog_params:json string形式的對話參數,參見接口說明:http://bestwisewords.com/document_detail/173298.html。 * @return:參見錯誤碼:http://bestwisewords.com/document_detail/459864.html。 */ public startDialog(vad_mode:Constants.VadMode, dialog_params:string):number
stopDialog:結束識別。
/** * 結束識別,調用該接口后,服務端將返回最終識別結果并結束任務。 * @return:參見錯誤碼:http://bestwisewords.com/document_detail/459864.html。 */ public stopDialog():number
cancelDialog:立即結束識別。
/** * 立即結束識別,調用該接口后,不等待服務端返回最終識別結果就立即結束任務。 * @return:參見錯誤碼:http://bestwisewords.com/document_detail/459864.html。 */ public cancelDialog():number
release:釋放SDK。
/** * 釋放SDK資源 * @return:參見錯誤碼:http://bestwisewords.com/document_detail/459864.html。 */ public release():number
GetVersion:獲得當前SDK版本信息。
/** * 獲得當前SDK版本信息 * @return: 字符串形式的SDK版本信息 */ public GetVersion():string
調用步驟
創建SDK類對象實例
初始化SDK、錄音實例。
根據業務需求設置參數。
調用startDialog開始識別。
根據音頻狀態回調onNuiAudioStateChanged中的事件,打開錄音機。
調用 updateAudio()提供錄音數據給SDK。
在EVENT_ASR_PARTIAL_RESULT事件回調中獲取識別中間結果。
調用stopDialog結束識別,并從EVENT_ASR_RESULT事件回調中獲得最終識別結果。
結束調用,使用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如何獲得,請查看http://bestwisewords.com/document_detail/72138.html
object.set("app_key", "用戶自己的app_key"); // 必填
//方法1:
// 首先ak_id ak_secret app_key如何獲得,請查看http://bestwisewords.com/document_detail/72138.html
// 然后請看 http://bestwisewords.com/document_detail/466615.html 使用其中方案一獲取臨時憑證
// 此方案簡介: 遠端服務器生成具有有效時限的臨時憑證, 下發給移動端進行使用, 保證賬號信息ak_id和ak_secret不被泄露
// 獲得Token方法(運行在APP服務端): http://bestwisewords.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.ModeAsrCloud); // 必填
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);
//參數可根據實際業務進行配置
//接口說明可見: http://bestwisewords.com/document_detail/173298.html
//查看 2.開始識別
//由于對外的SDK不帶有本地VAD模塊(僅帶有喚醒功能的SDK具有VAD模塊),
//若要使用VAD模式,則需要設置nls_config參數啟動在線VAD模式(見genParams())
if (this.vadMode) {
nls_config.set("enable_voice_detection", true);
nls_config.set("max_start_silence", 10000);
nls_config.set("max_end_silence", 800);
} else {
nls_config.set("enable_voice_detection", false);
}
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.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.kServiceTypeASR); // 必填
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_ASR_RESULT) { // 例如展示識別結果 showText(asrView, asrResult.asrResult); } else if (event == Constants.NuiEvent.EVENT_ASR_PARTIAL_RESULT) { // 例如展示識別中間結果 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();