HarmonyOS Next SDK

Updated at:

This topic describes how to use the HarmonyOS Next NUI SDK for short sentence recognition, including SDK integration, the call sequence, code examples, APIs, and callbacks.

Prerequisites

Important

Obtain the NLS token on your application server and send it to the client. Do not embed or store an AccessKey ID or AccessKey secret on the client, or log tokens or initialization parameters that contain a token.

Download and integrate the SDK

  1. Download harmony_neonui_sdk.tar.gz.

  2. Extract the package and import entry/libs/neonui.har into your application. The package also provides C++ dynamic libraries and header files in native/libs and native/include, respectively.

  3. Open the sample project in DevEco Studio. Set the Appkey and token in entry/src/main/ets/Utils/UserKey.ets, then run entry/src/main/ets/pages/OneSentenceAsrPage.ets.

Item

Compatibility

System and development environment

HarmonyOS Next 5.0, API level 12; DevEco Studio 5.0.3.403.

Architecture

arm64-v8a

The SDK package supports the following features.

Feature

Supported

Short sentence recognition

Yes

Real-time speech recognition

Yes

Speech synthesis

Yes

Real-time long-text speech synthesis

Yes

Streaming text-to-speech

Yes

Offline speech synthesis

No

Flash recognition

Yes

Wake-up and command word recognition

No

Real-time audio streaming for Tingwu

Yes

Call sequence

  1. Create SDK and recorder instances and implement INativeNuiCallback.

  2. Call initialize, then call setParams to configure recognition.

  3. Call startDialog and start recording when notified by onNuiAudioStateChanged.

  4. Pass recorded audio to the SDK in order by calling updateAudio, and receive intermediate results through event callbacks.

  5. Call stopDialog to stop recognition. EVENT_ASR_RESULT returns the final recognition result.

  6. Call release when the SDK is no longer needed.

Important

SDK APIs are synchronous. Do not call them on the UI thread, where they can block the interface. To avoid deadlocks, do not call SDK APIs directly from SDK event callbacks. Schedule subsequent SDK operations outside the callback.

Code examples

The following snippets show SDK calls, not a standalone application page. For the complete page, see OneSentenceAsrPage.ets in the SDK package. The application wrapper is entry/src/main/ets/neonui/OnSentenceAsr.ets. Use resourceDir + "/resources_cloud" from the application context as the resource directory and a writable filesDir as the debug directory.

The examples create an instance with the constructor. You can also obtain the singleton by calling NativeNui.GetInstance(). Release an instance before initializing it again.

Initialize the SDK

Import the SDK types. The callback parameter of createRecognizer is the callback implementation shown below. The application provides the other parameters.

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;
}

Provide app_key, token, a unique device_id, and a readable and writable workspace. debug_path specifies the debug file directory. Enable save_log only when needed. Log files have no size limit and must be cleaned up. To save debug audio with save_wav, enable save_log and provide a valid, writable debug directory.

Configure recognition

Set recognition parameters after initialization succeeds and before starting recognition.

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));
}

You can configure customization_id, vocabulary_id, sample_rate (for example, 16000), and sr_format (for example, opus) in nls_config as needed. For parameter conditions, see the API reference. For audio encoding limits, see the FAQ below.

Start recognition

function startRecognizer(nui: NativeNui, token: string): number {
  const dialogParams: Record<string, string> = { token: token };
  return nui.startDialog(Constants.VadMode.TYPE_P2T, JSON.stringify(dialogParams));
}

Use Constants.VadMode.TYPE_P2T. To update the token or Appkey for a recognition session, pass token or app_key in dialog_params. If a subsequent session does not specify these temporary parameters, the initialization parameters are used.

The online SDK does not include a local VAD module. To detect the start and end of speech, enable cloud VAD with enable_voice_detection as shown above. Do not use TYPE_VAD, which requires an SDK with offline capabilities.

Send recorded audio

Call the following function from the recorder’s readData callback and pass audio in capture order. The SDK package provides the AudioRecorder wrapper.

function pushAudio(nui: NativeNui, buffer: ArrayBuffer): number {
  return nui.updateAudio(buffer, false);
}

Handle callbacks

The RecordingController interface below represents application recording controls and must be connected to the recorder: start starts recording and stop stops recording. Use onNuiAudioStateChanged to control capture and send the recorded data to the same SDK instance.

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 {
  }
}

The example sends audio proactively, so onNuiNeedAudioData returns 0. asrResult can contain a task_id for troubleshooting. A nonempty allResponse contains the complete JSON response. For EVENT_ASR_ERROR, check the result code, error details, and task ID. For EVENT_MIC_ERROR, check recording permissions, recording code, and whether another application is using the recorder. EVENT_DIALOG_EX requires no handling.

Note

In development environments earlier than DevEco Studio 5.0.3.403, when audio is read with AudioCapturer.on("readData", ...), calling start after stop may not trigger callbacks. If this occurs, recreate the recorder in this order: stop, release, createAudioCapturer, and start. This issue is resolved in DevEco Studio 5.0.3.403.

Stop recognition

function stopRecognizer(nui: NativeNui): number {
  return nui.stopDialog();
}

To cancel immediately without waiting for the final result, call cancelDialog() instead.

Release resources

function releaseRecognizer(nui: NativeNui): number {
  return nui.release();
}

Key APIs and parameters

For parameter descriptions and conditions, see the API reference. For return codes and troubleshooting, see the Error code reference.

Initialization and configuration

initialize(callback: INativeNuiCallback, parameters: string,
  level: number, save_log: boolean = false): number
setParams(params: string): number
GetVersion(): string

parameters and params are JSON strings. A lower level produces more logs. save_log is disabled by default; when enabled, debug_path specifies the log directory. GetVersion returns the SDK version string.

Recognition and resource management

startDialog(vad_mode: Constants.VadMode, dialog_params: string): number
updateAudio(data: ArrayBuffer, first_pack: boolean): number
stopDialog(): number
cancelDialog(): number
release(): number

startDialog starts recognition with TYPE_P2T; dialog_params contains temporary parameters for the session. updateAudio sends audio to the SDK. stopDialog requests the final result and ends recognition. cancelDialog ends the task immediately without waiting for the final result. release releases SDK resources.

Callbacks and events

Implement the following five callbacks of 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 provides the error code for EVENT_ASR_ERROR. arg2 is reserved. kwsResult is for the wake-up feature, which is not supported. asrResult contains recognition results. onNuiAudioStateChanged requests recording to open, stop, or close. In callback-based audio input mode, fill buffer in onNuiNeedAudioData and return the number of bytes written. Because ArkTS asynchronous calls are frequent, sending audio in order with updateAudio is recommended by default. The energy value in onNuiAudioRMSChanged ranges from -160 to 0 and can drive recording animations. onNuiLogTrackCallback receives logs.

Event

Description

EVENT_VAD_START

Speech start detected.

EVENT_VAD_END

Speech end detected.

EVENT_ASR_PARTIAL_RESULT

Intermediate recognition result.

EVENT_ASR_RESULT

Final recognition result.

EVENT_ASR_ERROR

Recognition error. Check the error code.

EVENT_MIC_ERROR

No audio received for 2 consecutive seconds. Check the recorder.

FAQ

Is OPUS audio supported?

The SDK supports OPUS data for short sentence recognition and flash recognition. Real-time speech recognition supports only PCM audio with 16-bit samples and one channel. For details, see the API reference.

Can audio be supplied through callbacks?

Yes. If shared data and locking ensure that audio is produced and consumed in order, set nui_config.enable_callback_recording to true in the nui.json resource file. Supply recorded audio in onNuiNeedAudioData instead of proactively sending it with updateAudio.

How do real-time and short sentence recognition calls differ?

Item

Short sentence recognition

Real-time speech recognition

service_mode

Constants.ModeAsrCloud

Constants.ModeFullCloud

service_type

Constants.kServiceTypeASR

Constants.kServiceTypeSpeechTranscriber

Cloud VAD

Enable and configure as needed.

Keep the defaults.

Result events

EVENT_ASR_PARTIAL_RESULT, EVENT_ASR_RESULT

EVENT_ASR_PARTIAL_RESULT, EVENT_SENTENCE_END, EVENT_TRANSCRIBER_COMPLETE

How do I handle an unresponsive application caused by SDK call timeouts?

Avoid synchronous SDK calls on the UI thread. For thread scheduling, see OneSentenceAsrWorker.ets in the SDK package.