本文介绍如何使用 HarmonyOS Next NUI SDK 实现一句话识别,包括 SDK 集成、调用流程、代码示例、接口和回调。
前提条件
已了解一句话识别的参数和使用限制,详情请参见一句话识别移动端 SDK 接口说明。
已获取项目 Appkey,详情请参见管理项目。
已获取有效的 NLS Token(Access Token),详情请参见获取 Token。
由应用服务端获取 NLS Token,再下发到客户端。不要将 AccessKey ID、AccessKey Secret 写入客户端代码或保存在设备端,也不要在日志中输出 Token 或包含 Token 的初始化参数。
下载安装与集成
解压 SDK 包,将
entry/libs/neonui.har导入应用工程。SDK 包同时提供 C++ 动态库和头文件,分别位于native/libs和native/include。使用 DevEco Studio 打开示例工程,在
entry/src/main/ets/Utils/UserKey.ets中填写 Appkey 和 Token,再运行entry/src/main/ets/pages/OneSentenceAsrPage.ets。
|
类别 |
兼容范围 |
|
系统及开发环境 |
HarmonyOS Next 5.0,API level 12;DevEco Studio 5.0.3.403。 |
|
架构 |
arm64-v8a |
SDK 包的功能支持情况如下。
|
功能 |
是否支持 |
|
一句话识别 |
是 |
|
实时语音识别 |
是 |
|
语音合成 |
是 |
|
实时长文本语音合成 |
是 |
|
流式文本语音合成 |
是 |
|
离线语音合成 |
否 |
|
录音文件识别极速版 |
是 |
|
唤醒及命令词 |
否 |
|
听悟实时推流 |
是 |
调用流程
创建 SDK 实例和录音实例,实现
INativeNuiCallback。调用
initialize初始化 SDK,再调用setParams设置识别参数。调用
startDialog开始识别,根据onNuiAudioStateChanged通知启动录音。顺序调用
updateAudio向 SDK 传入录音数据,通过事件回调获取中间结果。调用
stopDialog结束识别,EVENT_ASR_RESULT返回最终识别结果。不再使用时调用
release释放 SDK 资源。
SDK 接口为同步调用,不要在 UI 线程调用,以免阻塞界面。不要在 SDK 的事件回调中直接调用 SDK 接口,以免死锁;需要继续调用时,将操作调度到回调之外执行。
代码示例
以下示例展示 SDK 调用片段,不是独立的应用页面。完整页面为 SDK 包中的 OneSentenceAsrPage.ets,业务封装为 entry/src/main/ets/neonui/OnSentenceAsr.ets。资源目录使用应用上下文的 resourceDir + "/resources_cloud",调试目录使用可写的 filesDir。
示例通过构造函数创建实例,也可使用 NativeNui.GetInstance() 获取单例。对同一实例重新初始化前,先释放其资源。
初始化
导入 SDK 类型。createRecognizer 的 callback 为下文的回调实现,其他参数由应用提供。
import { NativeNui, Constants, INativeNuiCallback, KwsResult, AsrResult } from 'neonui';
function createRecognizer(callback: INativeNuiCallback, resourceDir: string,
filesDir: string, appkey: string, token: string, deviceId: string): NativeNui {
const nui = new NativeNui(Constants.ModeType.MODE_DIALOG, 'asr');
const parameters: Record<string, string> = {
app_key: appkey,
token: token,
device_id: deviceId,
url: 'wss://nls-gateway.cn-shanghai.aliyuncs.com/ws/v1',
workspace: resourceDir + '/resources_cloud',
debug_path: filesDir,
service_mode: Constants.ModeAsrCloud
};
const ret = nui.initialize(callback, JSON.stringify(parameters),
Constants.LogLevel.LOG_LEVEL_VERBOSE, false);
if (ret !== Constants.NuiResultCode.SUCCESS) {
throw new Error('initialize failed: ' + ret);
}
return nui;
}
app_key、token、唯一的 device_id 和可读写的 workspace 为必填项。debug_path 用于保存调试文件。仅在需要时开启 save_log;开启后,日志持续写入且没有大小上限,需及时清理。save_wav 可用于保存音频调试数据,需要开启 save_log 并提供有效、可写的调试目录。
设置识别参数
在初始化成功后、开始识别前设置参数。
function configureRecognizer(nui: NativeNui, vadMode: boolean): number {
const nlsConfig: Record<string, Object> = {
enable_intermediate_result: true,
enable_punctuation_prediction: true,
enable_inverse_text_normalization: true,
enable_voice_detection: vadMode
};
if (vadMode) {
nlsConfig['max_start_silence'] = 10000;
nlsConfig['max_end_silence'] = 800;
}
const parameters: Record<string, Object> = {
nls_config: nlsConfig,
service_type: Constants.kServiceTypeASR
};
return nui.setParams(JSON.stringify(parameters));
}
可按需在 nls_config 中配置customization_id、vocabulary_id、sample_rate(如 16000)和 sr_format(如 opus)。参数适用条件见接口说明,音频编码限制见下文常见问题。
开始识别
function startRecognizer(nui: NativeNui, token: string): number {
const dialogParams: Record<string, string> = { token: token };
return nui.startDialog(Constants.VadMode.TYPE_P2T, JSON.stringify(dialogParams));
}
使用 Constants.VadMode.TYPE_P2T。需要在开始一轮识别时更新 Token 或 Appkey,可在 dialog_params 中传入 token 或 app_key;某轮未传入临时参数时,继续使用初始化参数。
当前在线 SDK 不包含本地 VAD 模块。需要自动检测语音起止时,使用上例的 enable_voice_detection 开启云端 VAD,不使用仅适用于带离线功能 SDK 的 TYPE_VAD。
推送录音数据
在录音模块的 readData 回调中调用以下函数,按采集顺序传入音频。SDK 包中的录音封装为 AudioRecorder。
function pushAudio(nui: NativeNui, buffer: ArrayBuffer): number {
return nui.updateAudio(buffer, false);
}
处理回调
以下 RecordingController 表示应用的录音控制接口,需连接到实际录音模块:start 启动录音,stop 停止录音。通过 onNuiAudioStateChanged 控制采集,将录音数据送给同一个 SDK 实例。
interface RecordingController {
start(): void;
stop(): void;
}
interface RecognitionPayload {
result: string;
}
interface RecognitionResponse {
payload: RecognitionPayload;
}
class NativeNuiCallbackHandle implements INativeNuiCallback {
private recorder: RecordingController;
constructor(recorder: RecordingController) {
this.recorder = recorder;
}
onNuiAudioStateChanged(state: Constants.AudioState): void {
if (state === Constants.AudioState.STATE_OPEN) {
this.recorder.start();
} else if (state === Constants.AudioState.STATE_CLOSE ||
state === Constants.AudioState.STATE_PAUSE) {
this.recorder.stop();
}
}
onNuiNeedAudioData(buffer: ArrayBuffer): number {
return 0;
}
onNuiEventCallback(event: Constants.NuiEvent, resultCode: number, arg2: number,
kwsResult: KwsResult, asrResult: AsrResult): void {
if (event === Constants.NuiEvent.EVENT_ASR_PARTIAL_RESULT ||
event === Constants.NuiEvent.EVENT_ASR_RESULT) {
if (asrResult && asrResult.asrResult) {
try {
const response = JSON.parse(asrResult.asrResult) as RecognitionResponse;
if (response.payload) {
console.info(response.payload.result);
}
} catch (e) {
console.error('Invalid recognition response');
}
}
} else if (event === Constants.NuiEvent.EVENT_ASR_ERROR ||
event === Constants.NuiEvent.EVENT_MIC_ERROR) {
console.error('recognition error: ' + resultCode);
}
}
onNuiAudioRMSChanged(val: number): number {
return 0;
}
onNuiLogTrackCallback(level: Constants.LogLevel, log: string): void {
}
}
示例采用主动推送音频的方式,因此 onNuiNeedAudioData 返回 0。asrResult 可包含 task_id,可记录用于排查问题;allResponse 非空时包含完整 JSON 响应。出现 EVENT_ASR_ERROR 时,结合 resultCode、错误信息和任务 ID 排查;出现 EVENT_MIC_ERROR 时,检查录音权限、录音代码及录音模块是否被其他应用占用。EVENT_DIALOG_EX 无需处理。
在 DevEco Studio 5.0.3.403 之前的开发环境中,使用 AudioCapturer.on("readData", ...) 读取录音时,stop 后直接 start 可能不再触发回调。遇到该问题时,按 stop、release、createAudioCapturer、start 的顺序重新创建录音实例。DevEco Studio 5.0.3.403 已解决该问题。
结束识别
function stopRecognizer(nui: NativeNui): number {
return nui.stopDialog();
}
需要立即取消、不等待最终结果时,改为调用 cancelDialog()。
释放资源
function releaseRecognizer(nui: NativeNui): number {
return nui.release();
}
关键接口与参数
参数含义和使用条件请参见接口说明。接口返回码及错误处理请参见错误码查询。
初始化与参数设置
initialize(callback: INativeNuiCallback, parameters: string,
level: number, save_log: boolean = false): number
setParams(params: string): number
GetVersion(): string
parameters 和 params 为 JSON 字符串。level 为日志级别,值越小输出越多。save_log 默认关闭;开启时,日志目录由 debug_path 指定。GetVersion 返回 SDK 版本字符串。
识别与资源管理
startDialog(vad_mode: Constants.VadMode, dialog_params: string): number
updateAudio(data: ArrayBuffer, first_pack: boolean): number
stopDialog(): number
cancelDialog(): number
release(): number
startDialog 启动识别,使用 TYPE_P2T,dialog_params 为本轮临时参数。updateAudio 向 SDK 送入音频。stopDialog 通知服务端结束识别并返回最终结果;cancelDialog 不等待最终结果,立即结束任务。release 释放 SDK 资源。
回调和事件
实现 INativeNuiCallback 的以下五个回调。
onNuiEventCallback(event: Constants.NuiEvent, resultCode: number, arg2: number,
kwsResult: KwsResult, asrResult: AsrResult): void
onNuiAudioStateChanged(state: Constants.AudioState): void
onNuiNeedAudioData(buffer: ArrayBuffer): number
onNuiAudioRMSChanged(val: number): number
onNuiLogTrackCallback(level: Constants.LogLevel, log: string): void
resultCode 在 EVENT_ASR_ERROR 时提供错误码。arg2 为保留参数;kwsResult 用于暂不支持的唤醒功能;asrResult 为识别结果。onNuiAudioStateChanged 通知打开、停止或关闭录音。回调供数模式下,onNuiNeedAudioData 填充 buffer 并返回实际字节数;由于 ArkTS 异步调用较频繁,建议默认使用 updateAudio 主动顺序推送。onNuiAudioRMSChanged 的能量值范围为 -160~0,可用于录音动效。onNuiLogTrackCallback 用于日志回调。
|
事件 |
说明 |
|
EVENT_VAD_START |
检测到人声起点。 |
|
EVENT_VAD_END |
检测到人声尾点。 |
|
EVENT_ASR_PARTIAL_RESULT |
识别中间结果。 |
|
EVENT_ASR_RESULT |
识别最终结果。 |
|
EVENT_ASR_ERROR |
识别错误,根据错误码排查。 |
|
EVENT_MIC_ERROR |
连续 2 秒未收到音频,检查录音系统。 |
常见问题
是否支持 OPUS 音频?
本 SDK 的一句话识别和录音文件识别极速版支持 OPUS 数据;实时语音识别仅支持 PCM 编码、16 bit 采样位数和单声道。详情请参见接口说明。
是否可以通过回调提供录音数据?
可以。若已通过共享数据和加锁保证录音数据生产、消费有序,可将资源文件 nui.json 中 nui_config.enable_callback_recording 设为 true,在 onNuiNeedAudioData 中填充录音数据,而不使用主动 updateAudio 推送。
实时语音识别与一句话识别的调用有何区别?
|
配置项 |
一句话识别 |
实时语音识别 |
|
service_mode |
Constants.ModeAsrCloud |
Constants.ModeFullCloud |
|
service_type |
Constants.kServiceTypeASR |
Constants.kServiceTypeSpeechTranscriber |
|
云端 VAD |
按需配置是否启用及相关参数。 |
保持默认。 |
|
结果事件 |
EVENT_ASR_PARTIAL_RESULT、EVENT_ASR_RESULT |
EVENT_ASR_PARTIAL_RESULT、EVENT_SENTENCE_END、EVENT_TRANSCRIBER_COMPLETE |
接口调用超时导致应用无响应,如何处理?
避免在 UI 线程执行同步 SDK 调用。线程调度方式可参考 SDK 包中的 OneSentenceAsrWorker.ets。