HarmonyOS Next SDK

本文介绍了如何使用阿里云智能语音服务提供的HarmonyOS Next NUI SDK,包括SDK下载安装、关键接口及代码示例。

前提条件

下载安装

  1. 下载harmony.zip

    说明

    下载后请在样例初始化代码中替换您的阿里云账号信息、AppkeyToken才可运行。

    类别

    兼容范围

    系统

    支持HarmonyOS Next 5.0 版本,API LEVEL 12, DevEco Studio版本号5.0.3.403

    架构

    arm64-v8a

    此SDK还包含如下功能,若未支持您想要的功能,请去对应文档获取SDK。

    功能

    是否支持

    一句话识别

    实时语音识别

    语音合成

    实时长文本语音合成

    流式文本语音合成

    离线语音合成

    录音文件识别极速版

    唤醒及命令词

    听悟实时推流

  2. 以arkts HAR包的形式进行集成。解压ZIP包,其中nuisdk-release/neonui.har 是SDK生成的HAR包文件,在用户工程项目中导入调用即可。如果需要HarmonyOS Next CPP接入方式,可在ZIP包的harmonyos_libs和harmonyos_include中获得动态库和头文件。

  3. 使用DevEco Studio打开harmony_nlsdemo目录下工程查看参考代码实现,其中流式TTS示例代码为StreamTTSClass.ets文件,替换UserKey.ets中 UserKeyStreamTTS类的Appkey和Token后,即可直接运行。(流式TTS工程测试流向为 home.ets->StreamTTSThread.ets->WorkerStreamTTSClass.ets->StreamTTSClass.ets)

关键接口

  • startStreamInputTts:开始运行流式TTS。

    /**
     * 开始运行流式TTS。请勿在UI线程调用,可能会引起阻塞。
     * @param callback:事件监听回调,参见下文具体回调。
     * @param ticket:json string形式的初始化参数,参见下方说明。
     * @param parameters:json string形式的初始化参数,参见下方说明。
     * @param session_id:当前会话的id,若客户端请求时传入则原样返回,否则由服务端自动生成32位唯一ID。
     * @param level:log打印级别,值越小打印越多。
     * @param save_log:是否保存log为文件,存储目录为ticket中的debug_path字段值。注意,log文件无上限,请注意持续存储导致磁盘存满。
     * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。
     */
    public startStreamInputTts( callback:INativeStreamInputTtsCallback,
    				ticket:string, parameters:string,
    				session_id:string, log_level:number,
    				save_log:boolean):number
    

    其中,INativeStreamInputTtsCallback类型包含如下回调。

    • onStreamInputTtsEventCallback:SDK事件回调。

      /**
       * 事件回调
       * @param event:回调事件,参见如下事件列表。
       * @param task_id:整个实时语音合成会话的任务ID,整个请求中需要保持一致,32位唯一ID。
       * @param session_id:当前会话的id,若客户端请求时传入则原样返回,否则由服务端自动生成32位唯一ID。
       * @param ret_code:参见错误码,出现STREAM_INPUT_TTS_EVENT_TASK_FAILED事件时有效,可查阅https://help.aliyun.com/document_detail/459864.html。
       * @param error_msg:详细错误信息,出现STREAM_INPUT_TTS_EVENT_TASK_FAILED事件时有效。
       * @param timestamp:合成结果中时间戳相关信息。
       * @param all_response:完整的json string格式返回消息,可从中解析需要的信息。
       */
      onStreamInputTtsEventCallback(event:StreamInputTtsEvent, task_id:string, session_id:string,
      				ret_code:number, error_msg:string, timestamp:string,
      				all_response:string):void;

      事件列表:

      名称

      说明

      STREAM_INPUT_TTS_EVENT_SYNTHESIS_STARTED

      语音合成开始,准备播放。

      STREAM_INPUT_TTS_EVENT_SENTENCE_BEGIN

      服务端检测到了一句话的开始。

      STREAM_INPUT_TTS_EVENT_SENTENCE_SYNTHESIS

      增量返回语音合成的结果,包含最新的音频和时间戳,句内全量,句间增量。

      STREAM_INPUT_TTS_EVENT_SENTENCE_END

      服务端检测到了一句话的结束,返回该句的全量时间戳。

      STREAM_INPUT_TTS_EVENT_SYNTHESIS_COMPLETE

      服务端检测到了一句话的结束,返回该句的全量时间戳。

      STREAM_INPUT_TTS_EVENT_TASK_FAILED

      语音合成发生错误,详见ret_code和error_msg。

    • onStreamInputTtsDataCallback:合成数据回调。

      /**
       * 合成数据回调。
       * @param data:合成的音频数据,写入播放器。
       */
      onStreamInputTtsDataCallback(data:ArrayBuffer|null):void

      其中,ticket初始化相关参数说明,生成示例参见下方代码示例:

      参数

      类型

      是否必选

      说明

      url

      String

      服务地址,默认使用北京服务。

      app_key

      String

      管控台创建项目的appkey。

      token

      String

      请确保该Token可以使用并在有效期内。

      debug_path

      String

      当save_log为true时,将会把运行日志存储在此路径下。

      complete_waiting_ms

      Integer

      调用stop后等待STREAM_INPUT_TTS_EVENT_SYNTHESIS_COMPLETE的超时时间,单位ms,默认10s。

      parameters任务相关参数说明,生成示例参见下方代码示例:

      参数

      类型

      是否必选

      说明

      voice

      String

      说话人音色。

      format

      String

      音频编码格式,如pcmwavmp3

      sample_rate

      Integer

      音频采样率,24000,可选择8000、16000、24000、48000。

      volume

      Integer

      朗读音量,范围是0~100,默认50。

      speech_rate

      Integer

      朗读语速,范围是-500~500,默认是0。

      pitch_rate

      Integer

      朗读语调,范围是-500~500,默认是0。

      enable_subtitle

      Boolean

      开启字级别时间戳。更多使用方法,请参见时间戳功能介绍

  • stopStreamInputTts: 停止语音合成

    /**
     * 停止语音合成,等待接收完所有合成数据直到STREAM_INPUT_TTS_EVENT_SYNTHESIS_COMPLETE。
     * @param flag_async:stop过程是否使用异步过程。 true表示stopStreamInputTts接口调用是异步的,函数调用可以快速返回,合成COMPLETE事件异步返回。false表示接口调用会block指定时间,直到合成COMPLETE事件或者FAILED事件。默认为true,避免block引起用户线程堵塞超时。
     * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。
     */
    public stopStreamInputTts(flag_async:boolean=true):number
  • sendStreamInputTts:发送待合成文本

    /**
     * 发送待合成文本
     * @param text:待合成文本,仅支持采用UTF-8编码的文本输入。单次合成推荐少于5000字,总计不超过10万字,其中1个汉字、1个英文字母、1个标点或1个句子中间空格均算作1个字符。
     * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。
     */
    public sendStreamInputTts(text:string):number

调用步骤

  1. 创建SDK类对象实例

  2. 初始化SDK和播放组件。

  3. 根据业务需求设置参数。

  4. 调用startStreamInputTts开始进行流式文本语音合成。

  5. 调用sendStreamInputTts持续发送待合成文本。在合成数据回调中,将数据写入播放器进行播放,建议使用流式播放。

  6. 调用stopStreamInputTts表示文本发送完成,等待合成完毕。

  7. 收到语音合成结束的回调或者合成出错的回调,进行对应响应。

代码示例

开始语音合成

//SDK初始化
//导入所需的SDK模块
import {Constants, NativeNui, INativeStreamInputTtsCallback, StreamInputTtsEvent} from 'neonui'

//实现回调处理函数。 具体实现见文档最下面回调代码部分
const g_ttscallback_instance:INativeStreamInputTtsCallback = {
  onStreamInputTtsEventCallback: cb_tts_event_callback,
  onStreamInputTtsDataCallback: cb_tts_user_data_callback
};

//创建SDK类对象实例
NativeNui stream_input_tts_instance = new NativeNui(Constants.ModeType.MODE_STREAM_INPUT_TTS);
//调用startStreamInputTts开始进行流式文本语音合成。
let ret:number = this.stream_input_tts_instance.startStreamInputTts(
  g_ttscallback_instance,
  genTicketTTS(), 
  genParameters(this.fontname), "", 
  Constants.LogLevel.toInt(Constants.LogLevel.LOG_LEVEL_VERBOSE), false )

其中genTicketTTS生成为String JSON串,包含用户信息。其中用户信息包含如下字段,获取方式请参考接口说明文档。

/**
 * ticket生成示例,详见Demo工程中代码示例
 */
function genTicketTTS():string {
  //郑重提示:
  //  语音交互服务需要先准备好账号,并开通相关服务。具体步骤请查看:
  //    https://help.aliyun.com/zh/isi/getting-started/start-here
  //
  //原始账号:
  //  账号(子账号)信息主要包括AccessKey ID(后续简称为ak_id)和AccessKey Secret(后续简称为ak_secret)。
  //  此账号信息一定不可存储在app代码中或移动端侧,以防账号信息泄露造成资费损失。
  //
  //STS临时凭证:
  //  由于账号信息下发给客户端存在泄露的可能,阿里云提供的一种临时访问权限管理服务STS(Security Token Service)。
  //  STS是由账号信息ak_id和ak_secret,通过请求生成临时的sts_ak_id/sts_ak_secret/sts_token
  //  (为了区别原始账号信息和STS临时凭证, 命名前缀sts_表示STS生成的临时凭证信息)
  //什么是STS:https://help.aliyun.com/zh/ram/product-overview/what-is-sts
  //STS SDK概览:https://help.aliyun.com/zh/ram/developer-reference/sts-sdk-overview
  //STS Python SDK调用示例:https://help.aliyun.com/zh/ram/developer-reference/use-the-sts-openapi-example
  //
  //账号需求说明:
  //  若使用离线功能(离线语音合成、唤醒), 则必须app_key、ak_id和ak_secret,或app_key、sts_ak_id、sts_ak_secret和sts_token
  //  若使用在线功能(语音合成、实时转写、一句话识别、录音文件转写等), 则只需app_key和token
  let str:string = "";
  //获取token方式:
  let object:object = Object({
    "appkey" : UserKeyStreamTTS.app_key,
    "token" : UserKeyStreamTTS.token,
    "url" : UserKeyStreamTTS.url
  })
  str = JSON.stringify(object);
  console.info("in genTicketTTS, UserContext:" + str);
  return str;
}

genParameters生成为String JSON串,包含参数信息,请参考接口说明文档。

function genParameters(voice:string):string {
  let str:string = "";
  let object:object = Object({
    "enable_subtitle" : "1",
    "voice" : "zhixiaoxia",
    "format" : "pcm",
    "sample_rate" : "16000",
    "volume" : "50",
    "speech_rate" : "0",
    "pitch_rate" : "0"
  })
  str = JSON.stringify(object);
  console.info("in genParameters, UserContext:" + str);
  return str;
}

流式发送合成文本

let ttstext1:string = "番茄炒蛋怎么做?";
let ttstext2:string = "必须是色香味俱全的做法哦。";
console.log(`womx ttstext1 is ${ttstext1}`);
this.stream_input_tts_instance.sendStreamInputTts(ttstext1)
console.log(`womx ttstext2 is ${ttstext2}`);
this.stream_input_tts_instance.sendStreamInputTts(ttstext2)

结束语音合成

this.stream_input_tts_instance.stopStreamInputTts()

回调处理

  • onStreamInputTtsEventCallback:流式文本语音合成事件回调,根据语音合成状态控制播放器。

    //实现回调处理函数
    let countNumber:number = 0
    function cb_tts_event_callback(event:StreamInputTtsEvent, task_id:string, session_id:string,
      ret_code:number, error_msg:string, timestamp:string,
      all_response:string):void{
      //console.info("womx cb_tts_event_callback  %d %s %d", event, task_id, code);
    
      console.info( "stream input tts event:" + event + " session id " + session_id + " session id " + task_id + " ret " + ret_code);
      if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_SYNTHESIS_STARTED) {
        console.info("STREAM_INPUT_TTS_EVENT_SYNTHESIS_STARTED");
        //waitinginit()  //返回合成开始 事件时,初始化播放器,以免播放器过早初始化引起状态异常。
    
        countNumber=0
        console.info("start play");
      } else if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_SENTENCE_SYNTHESIS) {
        console.info("STREAM_INPUT_TTS_EVENT_SENTENCE_SYNTHESIS:" + timestamp);
      } else if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_SYNTHESIS_COMPLETE || event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_TASK_FAILED) {
        /*
         * 提示: STREAM_INPUT_TTS_EVENT_SYNTHESIS_COMPLETE事件表示TTS已经合成完并通过回调传回了所有音频数据, 而不是表示播放器已经播放完了所有音频数据。
         */
    
        //合成完毕或者合成出错。进行响应
        console.info("play end");
    
        // 表示推送完数据
        //AudioRenderer.voiceEnd() //合成结束,通知播放器数据结束,等待播放完毕即可。
        is_started = false;
    
        if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_TASK_FAILED) {
          console.info("STREAM_INPUT_TTS_EVENT_TASK_FAILED error_code:" + ret_code + " errmsg:" + error_msg);
          //AudioRenderer.voiceStop(true)  //合成出错,直接停止播放器播报
        }
      } else if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_SENTENCE_BEGIN) {
        console.info("STREAM_INPUT_TTS_EVENT_SENTENCE_BEGIN:" + all_response);
      } else if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_SENTENCE_END) {
        console.info("STREAM_INPUT_TTS_EVENT_SENTENCE_END:" + all_response);
      }
    }
  • onStreamInputTtsDataCallback:语音合成数据回调,将回调中的合成数据写入播放器进行播放。

    function cb_tts_user_data_callback(buffer:ArrayBuffer|null):void{
      console.log(`womx cb_tts_user_data_callback uid[${process.uid}] pid[${process.pid}] tid[${process.tid}]`);
      countNumber+=1
      if (1==countNumber) {
        //waitingstart()  //返回合成的第一帧数据,此时启动播放器
      }
      if (buffer){
        console.info("womx cb_tts_user_data_callback %d. times=%d", buffer.byteLength, countNumber);
        if (buffer.byteLength > 0) {
          //合成的数据送到播放器模块进行实时播放。注意,合成的数据是短时间内生成较多数据,需要控制数据缓存与播放器的流式播报进度之间的匹配度
          /*if (gFlagAudioRendererUseCallback) {
            AudioRenderer.setVoiceArrayBuffer(buffer as ArrayBuffer)
          } else {
            AudioRenderer.writePlayerData(buffer)
          }*/
        }
      } else {
        console.info("womx cb_tts_user_data_callback undefined");
      }
    }