Linux SDK 集成手册

1. DDS 概述

       DDS是一个纯云端功能的sdk,必须连接网络才能正常使用,用来和dui平台进行交互,完成识别、语义、对话、合成等功能,使用之前,先要确保在dui平台上已经正确建立产品并发布。另外,压缩包中支持Ubuntu 16.04 x86_64架构的SDK , 并且含有集成手册、开发手册、FAQ,example,除此之外,设备必须连接网络才能正常使用,用来和dui平台进行交互。其它芯片所需要输出SDK,请联系商务。

2. 创建产品

DDS SDK是DUI平台针对嵌入式设备开发的对话定制服务SDK。开发者需要到DUI平台注册开发者账号,并熟悉DUI平台技能定制与产品发布。

关联阅读

注:分支号就是接口配置中的”aliasKey”

产品ID

请记住产品ID,详见下图:

Minion

aliasKey

发布下载页面的分支号即是“aliasKey”,如下图:

Minion

profile

授权信息,每一个设备对应一个授权profile文件,产品发布之后到 “授权管理” 标签页下载,如下图:

Minion
在开发时,调用dds_start函数,需要将profile文件中的内容作为配置参数deviceProfile的值传给dds,dds会完成后续授权相关操作

3. 头文件与接口描述

 

#ifndef __DDS_H__

#define __DDS_H__

 

#ifdef __cplusplus

extern "C" {

#endif

 

#if (!(defined DDS_CALL) || !(defined DDS_IMPORT_OR_EXPORT))

#if defined _WIN32

#if defined _WIN64

#define DDS_CALL __stdcall//windos系统和linux 系统,函数参数压栈方向不一样

#else

#define DDS_CALL

#endif

 

#ifdef DDS_IMPLEMENTION

#define DDS_IMPORT_OR_EXPORT __declspec(dllexport)//类似标准ANSI c的 export方法,供被调用

#else

#define DDS_IMPORT_OR_EXPORT __declspec(dllimport)

#endif

#if 0

#elif defined __ANDROID__

#define DDS_CALL

#define DDS_IMPORT_OR_EXPORT

#undef  JNIEXPORT

#define JNIEXPORT __attribute ((visibility("default")))

#endif

#elif defined __APPLE__

#define DDS_CALL

#define DDS_IMPORT_OR_EXPORT

#elif defined __unix__

#define DDS_CALL

#define DDS_IMPORT_OR_EXPORT __attribute ((visibility("default")))

#else

#define DDS_CALL

#define DDS_IMPORT_OR_EXPORT

#endif

#endif

 

#define DDS_VERSION     "DDS 0.2.26"

#define DDS_VERSION_NUM 226

 

/* callback event */

#define DDS_EV_OUT_RECORD_AUDIO                    1 //dds主动索要音频数据,一般100ms一次,在回调函数中,通过DDS_EV_IN_AUDIO_STREAM把data 上报给dds

#define DDS_EV_OUT_NATIVE_CALL                       2 //dds 查询本地数据,通过DDS_EV_IN_NATIVE_RESPONSE 消息返回给dds server

#define DDS_EV_OUT_COMMAND                             3 //向客户端发送控制命令,一般为json格式字符串,比如打开台灯

#define DDS_EV_OUT_MEDIA                                     4 //云端返回的json格式的播放列表

#define DDS_EV_OUT_STATUS                                    5 //云端返回dds状态,idle,listening,understanding

#define DDS_EV_OUT_TTS                                           6 //语音合成,返回合成的音频的url,nlg文本可选

#define DDS_EV_OUT_ERROR                                     7 //dds 异常

#define DDS_EV_OUT_ASR_RESULT                          8 //语音识别,返回识别后的文本和拼音

#define DDS_EV_OUT_DUI_RESPONSE                      9 //dui 服务返回完整结果,json格式

#define DDS_EV_OUT_DUI_LOGIN                             10 //返回login注册结果,设备名

#define DDS_EV_OUT_CINFO_RESULT                      11 //获得get的操作结果,成功失败,tts或词库的返回结果

#define DDS_EV_OUT_OAUTH_RESULT                    12 //第三方授权结果返回

#define DDS_EV_OUT_PRODUCT_CONFIG_RESULT  13 //获取产品配置信息

#define DDS_EV_OUT_WEB_CONNECT                     14 //返回和dui平台端tcp连接成功与否的消息

#define DDS_EV_OUT_DUI_DEVICENAME               15 //返回当前设备名

#define DDS_EV_OUT_REFRESH_TOKEN                  23   //refresh token更新结果

#define DDS_EV_OUT_LASR_RT_RESULT                 24   //长语音实时结果返回

#define DDS_EV_OUT_LASR_RT_RAW                       25   //长语音实时结果(服务端返回结果)

#define DDS_EV_OUT_LASR_RAW                              26   //长语音文件转写结果返回

#define DDS_EV_OUT_REQUEST_ID                           27   //request id生成通知

#define DDS_EV_OUT_VAD_RESULT                          28   //全链路云端vad结果

#define DDS_EV_OUT_TTS_MULTIPLE                      29   //tts多路复刻结果

 

/* external event */

#define DDS_EV_IN_SPEECH                                        101 //语音输入开始事件,start,end

#define DDS_EV_IN_WAKEUP                                      102 //唤醒事件,触发读取音频

#define DDS_EV_IN_NATIVE_RESPONSE                  103

#define DDS_EV_IN_RESET                                          104 //重置对话,该接口阻塞实现

#define DDS_EV_IN_EXIT                                             105 // 退出dds

#define DDS_EV_IN_CUSTOM_TTS_TEXT                106 // 需要合成的中文文本

#define DDS_EV_IN_AUDIO_STREAM                       107 //发送音频流到dds server

#define DDS_EV_IN_PLAYER_STATUS                      108 //当前播放状态,播放结束后 dds server进入listening

#define DDS_EV_IN_NLU_TEXT                                 109 //语义请求

#define DDS_EV_IN_WAKEUP_WORD                       110 //配置单个唤醒词

#define DDS_EV_IN_CINFO_OPERATE                      111 //删除终端自定义配置

#define DDS_EV_IN_OAUTH_OPERATE                    112

#define DDS_EV_IN_DM_INTENT                               113

#define DDS_EV_IN_COMMON_WAKEUP_WORD   114

#define DDS_EV_IN_PHRASE_HINTS                         115

#define DDS_EV_IN_PRODUCT_CONFIG                  116  //获取dui控制台产品配置

#define DDS_EV_IN_TTS_STOP                                  128  //停止tts多路复刻

#define DDS_EV_IN_LASR_RT_START                      129  //长语音实时识别开始接口

#define DDS_EV_IN_LASR_RT_STOP                        130  //长语音实时识别结束接口

#define DDS_EV_IN_LASR_FILE_UPLOAD              131  //长语音文件转写文件分片上传接口

#define DDS_EV_IN_LASR_TASK_CREATE             132  //长语音文件转写创建识别任务接口

#define DDS_EV_IN_LASR_TASK_QUERY               133  //长语音文件转写结果查询接口

 

/* error id */

#define DDS_ERROR_BASE 1000

#define DDS_ERROR_FATAL     (DDS_ERROR_BASE + 1)

#define DDS_ERROR_TIMEOUT   (DDS_ERROR_BASE + 2)

#define DDS_ERROR_NETWORK   (DDS_ERROR_BASE + 3)

#define DDS_ERROR_SERVER    (DDS_ERROR_BASE + 4)  

#define DDS_ERROR_LOGIC (DDS_ERROR_BASE + 5)

#define DDS_ERROR_INPUT (DDS_ERROR_BASE + 6)

 

struct dds_msg;

typedef int (*dds_ev_callback)(void *userdata, struct dds_msg *msg);//dds 回调函数,所有out 事件在此接口处理

struct dds_opt {

dds_ev_callback _handler;

void *userdata;

};

 

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_start(struct dds_msg *conf, struct dds_opt *opt);

//dds 服务启动接口,阻塞运行,需要传送产品配置参数,pID,aliaskey,profile必选和opt操作句柄,dds服务启动后会一直阻塞等待消息

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_send(struct dds_msg *msg);

//发送消息给dds 服务,dds回调函数收到回复消息并处理

 

/* message pack or unpack */

DDS_IMPORT_OR_EXPORT struct dds_msg * DDS_CALL dds_msg_new(); //新建一个消息,并分配保存消息的内存

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_msg_delete(struct dds_msg *msg); //删除一个消息,并回收内存

DDS_IMPORT_OR_EXPORT void DDS_CALL dds_msg_print(struct dds_msg *msg); //打印出start消息的地址和当前消息之间的长度

 

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_msg_set_type(struct dds_msg *msg, int value); //设置消息type 类型,DDS_EV_IN_SPEECH

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_msg_set_integer(struct dds_msg *msg, const char *key, int value); //key 变量赋值为整形value

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_msg_set_double(struct dds_msg *msg, const char *key, double value); //key 变量赋值为double 型value

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_msg_set_boolean(struct dds_msg *msg, const char *key, int value); //key 变量赋值为boolean value

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_msg_set_string(struct dds_msg *msg, const char *key, const char *value); //key 变量赋值为字符串

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_msg_set_bin(struct dds_msg *msg, const char *key, const char *value, int value_len); //设置 audio stream 数据流给key

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_msg_set_bin_p(struct dds_msg *msg, const char *key, const char *value, int value_len); //设置 value 指向的数据流文件给key

 

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_msg_get_type(struct dds_msg *msg, int *value);

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_msg_get_integer(struct dds_msg *msg, const char *key, int *value);

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_msg_get_double(struct dds_msg *msg, const char *key, double *value);

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_msg_get_boolean(struct dds_msg *msg, const char *key, int *value);

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_msg_get_string(struct dds_msg *msg, const char *key, char **value);

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_msg_get_bin(struct dds_msg *msg, const char *key, char **value, int *value_len);

DDS_IMPORT_OR_EXPORT int DDS_CALL dds_msg_get_bin_p(struct dds_msg *msg, const char *key, char **value, int *value_len);

 

#ifdef __cplusplus

}

#endif

#endif

3.1 接口描述

dds包含两个功能接口和一系列消息接口

  • 功能接口:dds_startdds_send,其中dds_start用于启动dds服务,dds_send用于发送消息。注意:dds_send接口是线程安全的,可以在不同的线程中调用,但是不要在dds的回调函数中调用
  •  消息接口:以dds_msg_xxx开头的函数,它们用于组合不同的消息类型,通过dds_send接口发送出去

3.2 输入和输出描述

dds在调用dds_start的时候会注册一个回调函数,在回调中返回各种消息,消息的类型定义是以DDS_EV_OUT_开头的宏

dds通过dds_send发送各种消息,驱动dds工作,dds支持的输入消息类型是以DDS_EV_IN_开头的宏

4. 启动与授权

    dds_start用于启动dds服务,它是一个阻塞接口,里面是一个死循环,用于接收dds_send发送的消息,并进行处理;所以该接口要运行在一个独立的线程里面。

4.1 全链路产品启动与授权

示例代码如下

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <unistd.h>

#include <pthread.h>

#include "dds.h"

 

static int dds_ev_ccb(void *userdata, struct dds_msg *msg) {//事件回调函数,dds云端返回的响应都会回调此函数

    int type;

    if (!dds_msg_get_type(msg, &type)) {//获得消息对应的事件类型

        switch (type) {

             case DDS_EV_OUT_DUI_LOGIN: {

                 printf("\n");

                 char *value;

                 if (!dds_msg_get_string(msg, "error", &value)) {//如果云端login失败,打印出错误码

                      printf("ERROR: %s\n", value);

                 } else if (!dds_msg_get_string(msg, "deviceName", &value)) {//如果无法获得错误码,打印设备名字,表示授权成功

                      printf("deviceName: %s\n", value);

                 }

                 break;

             }

            default:

                 break;

         }

    }

    return 0;

}

 

void *_run(void *arg) {//dds 线程创建成功的回调函数

    struct dds_msg *msg = dds_msg_new();//新建一个dds msg,设置对应的productId,aliasKey,savedProfile,等配置信息

    dds_msg_set_string(msg, "productId", "123456");

    dds_msg_set_string(msg, "aliasKey", "prod");

    dds_msg_set_string(msg, "savedProfile", "./xxx.profile");

    dds_msg_set_string(msg, "productKey", "1234567");

    dds_msg_set_string(msg, "productSecret", "abcdefg");

    dds_msg_set_string(msg, "devInfo", "{\"deviceName\": \"12345678\", \"platform\": \"linux\"}");

 

    struct dds_opt opt;

    opt._handler = dds_ev_ccb;//ev事件处理的函数

    opt.userdata = arg;//数据配置指针,一指向创建线程的时候传入的数据指针

    dds_start(msg, &opt);//各种配置配置完毕后,开始启动dds,创建dds服务需要的环境,设置参数等。

    dds_msg_delete(msg);

    return NULL;

}

 

int main(int argc, char **argv) {

    struct dds_msg *msg = NULL;

    pthread_t tid;//创建一个线程的 id

    pthread_create(&tid, NULL, _run, NULL);//创建一个线程,创建完毕后立即执行回调_run

 

   /*do something*/

    sleep(5);

 

    msg = dds_msg_new();

    dds_msg_set_type(msg, DDS_EV_IN_EXIT);

    dds_send(msg);

    dds_msg_delete(msg);

    pthread_join(tid, NULL);//阻塞方式等待子线程执行结束,回收线程资源

    return 0;

}

4.2 全链路产品全双工模式启动与授权

示例代码如下

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <unistd.h>

#include <pthread.h>

#include "dds.h"

 

static int dds_ev_ccb(void *userdata, struct dds_msg *msg) {//事件回调函数,dds云端返回的响应都会回调此函数

    int type;

    if (!dds_msg_get_type(msg, &type)) {//获得消息对应的事件类型

        switch (type) {

             case DDS_EV_OUT_DUI_LOGIN: {

                 printf("\n");

                 char *value;

                 if (!dds_msg_get_string(msg, "error", &value)) {//如果云端login失败,打印出错误码

                      printf("ERROR: %s\n", value);

                 } else if (!dds_msg_get_string(msg, "deviceName", &value)) {//如果无法获得错误码,打印设备名字,表示授权成功

                      printf("deviceName: %s\n", value);

                 }

                 break;

             }

            default:

                 break;

         }

    }

    return 0;

}

  

void *_run(void *arg) {//dds 线程创建成功的回调函数

    struct dds_msg *msg = dds_msg_new();//新建一个dds msg,设置对应的productId,aliasKey,savedProfile,等配置信息

    dds_msg_set_string(msg, "productId", "123456");

    dds_msg_set_string(msg, "aliasKey", "prod");

    dds_msg_set_string(msg, "savedProfile", "./xxx.profile");

    dds_msg_set_string(msg, "productKey", "1234567");

    dds_msg_set_string(msg, "productSecret", "abcdefg");

    dds_msg_set_string(msg, "devInfo", "{\"deviceName\": \"12345678\", \"platform\": \"linux\"}");

    dds_msg_set_boolean(msg, "fullDuplex", 1); //配置为全双工模式

 

    struct dds_opt opt;

    opt._handler = dds_ev_ccb;//ev事件处理的函数

    opt.userdata = arg;//数据配置指针,一指向创建线程的时候传入的数据指针

    dds_start(msg, &opt);//各种配置配置完毕后,开始启动dds,创建dds服务需要的环境,设置参数等。

    dds_msg_delete(msg);

    return NULL;

}

 

int main(int argc, char **argv) {

    struct dds_msg *msg = NULL;

    pthread_t tid;//创建一个线程的 id

    pthread_create(&tid, NULL, _run, NULL);//创建一个线程,创建完毕后立即执行回调_run

   /*do something*/

    sleep(5);

 

    msg = dds_msg_new();

    dds_msg_set_type(msg, DDS_EV_IN_EXIT);

    dds_send(msg);

    dds_msg_delete(msg);

    pthread_join(tid, NULL);//阻塞方式等待子线程执行结束,回收线程资源

    return 0;

}

正如上面的示例,dds_start的时候需要传入两个参数:

  • struct dds_msg

      该参数用于设置SDK的一些配置信息,主要包含产品配置和授权配置

      产品配置:

参数名称 是否必选 描述
productId 
必选 产品id,是在dui平台上新建的产品
asrRes 语音识别产品必选 识别模型,取值可为“aihome”、"aicomm"、"aicar"、"airobot"
aliasKey 全链路产品必选
产品分支,即产品发布的分支,取值为"test""prod"单项基础技术产品不可选。

 

      授权配置:针对不同的使用场景,dds支持两种授权方式,两种授权方式的比较如下表

 授权方式

离线预烧录授权

在线productKey授权

使用场景

没有唯一id的设备,比如MCU

有唯一id的设备,比如mac地址

使用方法

产线给每台设备烧录一个profile文件

设备启动时,上传唯一id,获取profile

获取profile

DUI平台上申请

设备运行时在线下载

配置项

savedProfile:profile文件的路径

productKey:DUI平台上生成的产品Key

productSecret:DUI平台上生成的产品Secret

savedProfile:下载的profile文件的保存路径和文件名

devInfo:JSON格式,其中deviceName为设备唯一id

 

  • struct dds_opt

       该参数用于注册回调,传递需要透传的用户指针,dds的所有输出都通过该回调返回,并透传userdata

5. 功能点

5.1 全链路

    全链路产品,包含识别、语义、对话、资源搜索等功能,输入为语音或文本或状态,输出为对话结果,对话结果包含webapicommandnative api这几种,服务返回的结果以json格式,通过DDS_EV_OUT_DUI_RESPONSE消息,回调给用户。

5.1.1 语音输入

         语音输入,一般配合本地vad使用,分为三个阶段:

         1、vad检测到开始说话,发送DDS_EV_IN_SPEECH start消息//本地的vad 检测到语音输入后,开始发送DDS_EV_IN_SPEECH start 消息通知dds server接受数据

         2、vad抛出音频,通过DDS_EV_IN_AUDIO_STREAM,给dds发送音频数据//dds 开始发送vad处理后的数据

         3、vad检测到说话结束,发送DDS_EV_IN_SPEECH end消息  // 说话结束消息

        

struct dds_msg *msg = NULL;

msg = dds_msg_new();//新建 一个消息

dds_msg_set_type(msg, DDS_EV_IN_SPEECH);//一般是有vad 发送此消息,

dds_msg_set_string(msg, "action", "start");//vad 检测到 开始说话,

dds_send(msg);//通知 给dds server 开始说话了

dds_msg_delete(msg);//销毁此消息,释放内存

msg = NULL;

 

FILE *f = fopen(wav_name, "rb");//以二进制打开一个可读文件,该文件必须存在

if(f==NULL){

    printf("cant open file ,err!");

    return -1;

}

fseek(f, 44, SEEK_SET);//跳转到文件开始第45字节处,wav文件 前面44字节保存了音频配置相关信息

char data[3200];//16bit 16k采样率,3200个字节=100ms*2Byte*1channel*16000

int len;

struct dds_msg *m;

while (1) {

    len = fread(data, 1, sizeof(data), f);

    if (len <= 0) break;

    m = dds_msg_new();

    dds_msg_set_type(m, DDS_EV_IN_AUDIO_STREAM);//通知dds server 拿数据

    dds_msg_set_bin(m, "audio", data, len);

    dds_send(m);

    dds_msg_delete(m);

    usleep(100000);//因为fread 读取很快,所以等100ms 下次读,启动下次读取数据

}

fclose(f);

 

/*告知DDS结束语音*/

msg = dds_msg_new();

dds_msg_set_type(msg, DDS_EV_IN_SPEECH);

dds_msg_set_string(msg, "action", "end");//会话结束,发送end 消息

dds_send(msg);

dds_msg_delete(msg);

msg = NULL;

5.1.2 文本输入

      文本输入,即通过一段文本来触发请求,绕过识别,直接走语义、对话、资源请求部分

struct dds_msg *msg = NULL;

msg = dds_msg_new();

dds_msg_set_type(msg, DDS_EV_IN_NLU_TEXT);

dds_msg_set_string(msg, "text", "苏州今天天气怎么样");

dds_send(msg);

dds_msg_delete(msg);

msg = NULL;

5.1.3 dm intent输入

        dm intent是绕过识别、语义、对话,直接请求技能

struct dds_msg *msg = NULL;

msg = dds_msg_new();

dds_msg_set_type(msg, DDS_EV_IN_DM_INTENT);

dds_msg_set_string(msg, "skill", "dds的example");//该技能必须在dui上创建

dds_msg_set_string(msg, "task", "车载");

dds_msg_set_string(msg, "intent", "油量不足");

dds_msg_set_string(msg, "slots", "{\"油量值\":\"4\"}");

dds_send(msg);

dds_msg_delete(msg);

5.1.4 native api输入

       该输入是在服务触发native api输出的场景下使用,客户端执行相应的动作,并将执行结果上报给dui

struct dds_msg *retmsg = dds_msg_new();

dds_msg_set_type(retmsg, DDS_EV_IN_NATIVE_RESPONSE);

dds_msg_set_string(retmsg, "temperature", "35");

dds_send(retmsg);

dds_msg_delete(retmsg);

retmsg = NULL


       以上几种输入的输出结果是一致的,都会通过回调返回;输入请求发送完成之后,dds会等待dui平台返回结果,如果15没有结果返回,会报超时错误。

5.1.5 结果输出

    dui的输出统一通过回调中的DDS_EV_OUT_DUI_RESPONSE消息返回

static int dds_ev_ccb(void *userdata, struct dds_msg *msg) {//dui 回调函数

int type;

if (!dds_msg_get_type(msg, &type)) {

   switch (type) {

           case DDS_EV_OUT_DUI_RESPONSE: {

                char *resp = NULL;

                if (!dds_msg_get_string(msg, "response", &resp)) {

                    printf("\n%s\n", resp);

                }

                break;

             }

           case DDS_EV_OUT_ERROR: {

               char *value;

               if (!dds_msg_get_string(msg, "error", &value)) {

                  printf("DDS_EV_OUT_ERROR: %s\n", value);

               }

               break;

           }

           default:

               break;

       }

    }

    return 0;

}

 

1)  web api输出示例

 web api输出,一般输出speakurl,即回复一段合成音,一般用于查询类使用场景,比如:天气、百科,如果是音乐查询,还会返回资源列表

 天气返回json

{

    "dm": {//对话结果

        "intentName": "天气",//意图名字,关于天气的

        "intentId": "5b860fcbcdd7ba00016b5ec1",//

        "widget": {

              "recommendations": ["苏州天气好不好", "你好明天的天气怎么样", "看一下深圳的天气预报"],

              "url": "https:\/\/apis.dui.ai\/webhook\/tq\/?city=苏州&lon=120.585279&lat=31.299732",//返回内容的web url 地址

              "extra": {//附加信息,由webhook或者localhook透传

                  "wind": "西北风,3级",

                  "temperature": "-2~4℃",

                   "city": "苏州",

                  "weather": "多云转阴"

                },

                "duiWidget": "web",//dui控件类型,web控件

                "name": "default",//描述控件的名称,可以编译,默认default

                "widgetName": "default",

                "type": "web"//控件类型,web控件

           },

          "nlg": "今天苏州的天气是多云转阴-2~4℃西北风,3级。",

          "task": "天气",

          "status": 0,

          "taskId": "5b860fcbcdd7ba00016b5ea3",

          "shouldEndSession": false

       },

       "skillId": "2017120200000013",//dui 服务器端 技能 id

       "recordId": "38416974bc7848d1acba45e676b5fa82",//标识一个客户端请求id,默认有dds server 生成,若dds server 请求没有 recordid,生成一个新的

       "contextId": "7bddda4196aa4b88a75afbe2947d4916",//标识dispatcher与一个skill之间的上下文,ddsserver保存dispatcher返回的contextId与sessionId, 并返回给客户端, 客户端第二次请求时可以带上contextId与sessionId

       "sessionId": "7bddda4196aa4b88a75afbe2947d4916",

        "speakUrl": "https:\/\/dds.dui.ai\/runtime\/v1\/cache\/38416974bc7848d1acba45e676b5fa82"//音频播放id,一般为mp3格式

}

 

音乐返回json

{

    "dm":{

        "intentName":"播放音乐", //客户意图

        "intentId":"5c09664a951088000116586a",

        "widget":{

            "count":18,

            "content":[

                {

                    "linkUrl":"http://47.98.45.59/298918.mp3",

                    "title":"百年孤寂",

                    "subTitle":"王菲",

                    "extra":{

                        "resType":"mp3",

                        "source":0

                    },

                    "label":"",

                    "imageUrl":""

                },

                {

                    "linkUrl":"http://47.98.36.22/528478901.mp3",

                    "title":"无问西东",

                    "subTitle":"王菲",

                    "extra":{

                        "resType":"mp3",

                        "source":0

                    },

                    "label":"",

                    "imageUrl":""

                },

                {

                    "linkUrl":"http://47.98.45.59/299445.mp3",

                    "title":"流年",

                    "subTitle":"王菲",

                    "extra":{

                        "resType":"mp3",

                        "source":0

                    },

                    "label":"",

                    "imageUrl":""

                }

            ],

            "name":"music",

            "widgetName":"music",

            "itemsPerPage":5, //每页5个音乐

            "totalPages":4, ////总共4页

            "duiWidget":"media",//媒体控件类型,返回音乐资源列表列表

            "dataSource":"api",

            "currentPage":1, //当前页

            "type":"media"

        },

        "nlg":"好听的音乐,马上就来。为您播放王菲的百年孤寂",

        "task":"音乐",

        "status":1,

        "taskId":"5c09664a9510880001165845",

        "shouldEndSession":true

    },

    "skillId":"2018112200000035",

    "recordId":"8849c8fa758b4250954313651fbd8805",

    "contextId":"3ed70560a230477889bb9ee6f5b43891",

    "sessionId":"3ed70560a230477889bb9ee6f5b43891",

    "speakUrl":"https://dds.dui.ai/runtime/v1/cache/8849c8fa758b4250954313651fbd8805"

 

}

2)  command输出示例

command一般的使用场景为,控制设备执行一个动作,比如:暂停、播放等指令

command 输出json

{

    "dm": {

         "intentName": "暂停播放",//cmd 意图,暂停播放

         "intentId": "5c096c50951088000116666c",

         "runSequence": "nlgFirst",//先播放nlg,比如下面的好的,即将暂停,然后在执行command里面的暂停方法

          "widget": {

              "duiWidget": "text",//文本控件

              "name": "default",

              "widgetName": "default",

             "type": "text"

         },

        "command": {

             "api": "mediacontrol.pause"//???应该是native本地暂停播放方法

         },

       "nlg": "好的,即将暂停",

       "task": "播放控制",//任务,暂停播放

       "status": 1,

       "taskId": "5c096c509510880001166660",

       "shouldEndSession": true

    },

    "skillId": "2018111600000003",

    "recordId": "67c555d4bd3e4a51965b19a6a7e6f738",

    "contextId": "e3388149a1904c9db969fe8382c742e3",

    "sessionId": "e3388149a1904c9db969fe8382c742e3",

    "speakUrl": "https:\/\/dds.dui.ai\/runtime\/v1\/cache\/67c555d4bd3e4a51965b19a6a7e6f738"

}


3)  native api输出示例

native api一般用于让设备,执行一个动作,并上报执行结果,dui形成一个合成音下发给设备

native api json

{

    "dm": {

           "intentName": "下一个",//主要意图,然后dds会返回下一首歌歌名url,播放完歌名后,执行本地api切换到这首歌曲

           "param": {

                "duiWidget": "text",

                "intent": "下一个"

             },

            "intentId": "5c096c509510880001166670",

             "api": "mediacontrol.next",//本地接口

             "task": "播放控制",

             "runSequence": "nlgFirst",//

             "dataFrom": "native",

             "status": 0,

             "taskId": "5c096c509510880001166660",

             "shouldEndSession": false

         },

        "skillId": "2018111600000003",

        "recordId": "3c851ea51e2a49bb8e45d2fa5b09a54a",

        "contextId": "495b99dacebe403481ccf3884780dce9",

        "sessionId": "495b99dacebe403481ccf3884780dce9"

}

5.2 语音识别

       语音识别,即只支持识别功能,支持单句识别和多句识别

5.2.1 语音输入

struct dds_msg *msg = NULL;

/*告知DDS开始语音*/

msg = dds_msg_new();

dds_msg_set_type(msg, DDS_EV_IN_SPEECH);

dds_msg_set_string(msg, "action", "start");

dds_msg_set_string(msg, "aiType", "asr");

dds_send(msg);

dds_msg_delete(msg);

msg = NULL;

 

FILE *f = fopen(wav_name, "rb");

fseek(f, 44, SEEK_SET);

char data[3200];

int len;

struct dds_msg *m;

while (1) {

    len = fread(data, 1, sizeof(data), f);

    if (len <= 0) break;

    m = dds_msg_new();

    dds_msg_set_type(m, DDS_EV_IN_AUDIO_STREAM);

    dds_msg_set_bin(m, "audio", data, len);

    dds_send(m);

    dds_msg_delete(m);

    usleep(100000);

}

fclose(f);

 

/*告知DDS结束语音*/

msg = dds_msg_new();

dds_msg_set_type(msg, DDS_EV_IN_SPEECH);

dds_msg_set_string(msg, "action", "end");

dds_send(msg);

dds_msg_delete(msg);

msg = NULL;

5.2.2 识别结果输出

static int dds_ev_ccb(void *userdata, struct dds_msg *msg) {

int type;

if (!dds_msg_get_type(msg, &type)) {

    switch (type) {

        case DDS_EV_OUT_ERROR: {

            char *value;

            if (!dds_msg_get_string(msg, "error", &value)) {

                printf("DDS_EV_OUT_ERROR: %s\n", value);

            }

            break;

        }

        case DDS_EV_OUT_ASR_RESULT: {

            char *value;

            if (!dds_msg_get_string(msg, "var", &value)) {

                printf("var: %s\n", value);//识别的中间结果

            }

            if (!dds_msg_get_string(msg, "text", &value)) {

                printf("text: %s\n", value);//识别的最终结果

            }

            if (!dds_msg_get_string(msg, "pinyin", &value)) {

                printf("pinyin: %s\n", value);//识别结果的拼音

            }

            break;

         }

         default:

            break;

     }

   }

    return 0;

}

5.3 语音合成

语音合成用于将用户的合成文本转化成音频,输入文本,输出url

 5.3.1 合成文本输入

msg = dds_msg_new();

dds_msg_set_type(msg, DDS_EV_IN_CUSTOM_TTS_TEXT);

dds_msg_set_string(msg, "text", "今天天气非常好");//需要合成的文本

dds_msg_set_string(msg, "voiceId", "zhilingfa");//发音人,必选

dds_send(msg);

dds_msg_delete(msg);

msg = NULL;

5.3.2 合成结果输出

static int dds_ev_ccb(void *userdata, struct dds_msg *msg) {

    int type;

    if (!dds_msg_get_type(msg, &type)) {

        switch (type) {

            case DDS_EV_OUT_ERROR: {

                char *value;

                if (!dds_msg_get_string(msg, "error", &value)) {

                    printf("DDS_EV_OUT_ERROR: %s\n", value);

                }

                break;

            }

            case DDS_EV_OUT_TTS: {

                char *value;

                if (!dds_msg_get_string(msg, "speakUrl", &value)) {

                    printf("speakUrl: %s\n", value);//合成的结果以url的形式返回

                    is_get_tts_url = 1;

                }

                if (!dds_msg_get_string(msg, "nlg", &value)) {

                    printf("nlg: %s\n", value);

                }

                break;

           }

           default:

               break;

        }

    }

    return 0;

}

5.4 全双工

       全双工将用户的音频全部实时上传到dui服务,本地不做vad检测,云端实时检测有效语音,完成识别、语义、对话,并实时下发识对话结果,与全链路配置相比,dds启动时只要多加一个配置,就可以设置为全双工模式

5.4.1 语音输入

       与全链路相比,音频输入,start之后,一直feed,不需要feed的时候,stop之后reset引擎,结束对话

struct dds_msg *msg = NULL;

msg = dds_msg_new();

dds_msg_set_type(msg, DDS_EV_IN_SPEECH);

dds_msg_set_string(msg, "action", "start");

dds_send(msg);

dds_msg_delete(msg);

msg = NULL;

 

FILE *f = fopen(wav_name, "rb");//以二进制打开一个可读文件,该文件必须存在

if(f==NULL){

    printf("cant open file ,err!");

    return -1;

}

fseek(f, 44, SEEK_SET);//跳转到文件开始第45字节处,wav文件 前面44字节保存了音频配置相关信息

char data[3200];//16bit 16k采样率,3200个字节=100ms*2Byte*1channel*16000

int len;

struct dds_msg *m;

while (1) {

     len = fread(data, 1, sizeof(data), f);

     if (len <= 0) break;

     m = dds_msg_new();

     dds_msg_set_type(m, DDS_EV_IN_AUDIO_STREAM);//通知dds server 拿数据

     dds_msg_set_bin(m, "audio", data, len);

     dds_send(m);

     dds_msg_delete(m);

     usleep(100000);//因为fread 读取很快,所以等100ms 下次读,启动下次读取数据

}

fclose(f);

 

//不需要上传音频的时候,告知DDS结束语音, 然后reset引擎

msg = dds_msg_new();

dds_msg_set_type(msg, DDS_EV_IN_SPEECH);

dds_msg_set_string(msg, "action", "end");

dds_send(msg);

dds_msg_delete(msg);

msg = NULL;

 

msg = dds_msg_new();

dds_msg_set_type(msg, DDS_EV_IN_RESET);

dds_send(msg);

dds_msg_delete(msg);

msg = NULL;

5.4.2 结果返回

全双工和全链路一样,都从DDS_EV_OUT_DUI_RESPONSE事件中返回json结果,json中需要关注的字段及其含义如下

//dm.rt.output

//返回的json里面,有topic字段,并且值为dm.rt.output

//实时中间结果,主要关注dm.command.api字段

{

    "dm":{

        "command":{    //全双工中间返回结果。interrupt:打断TTS; backchannel:中间接话; discardResponse:输入内容语义置信度低,服务端过滤改回复。

            "api":"interrupt"

        },

        "status":0,

        "runSequence":"commandFirst",

        "shouldEndSession":false

    },

    "topic":"dm.rt.output", //dm.rt.output,表示改消息为全双工对话中的中间反馈消息。

    "contextId":"5b82ff0f9e4448c891a282d64dcf2500",

    "recordList":{                                  //客户端实现和云端对齐策略的参数,beginTime和endTime表示改消息对应的一次会话的音频的时间偏移。

        "beginTime":1380,

        "endTime":3000,

        "expireTime":2000,

        "recordIdList":[{

                "beginTime":1380,

                "recordId":"b917dd4c0fcc4fb69c2a10a25abbae12",

                "endTime":0

            }]

    },

    "sessionId":"5b82ff0f9e4448c891a282d64dcf2500",

    "requestId":"5b49fc73b81f4944b71dc63c48780629"

}

 

//dm.output

//没有topic字段,正常对话结果

{

    "dm": {//对话结果

        "intentName": "天气",//意图名字,关于天气的

        "intentId": "5b860fcbcdd7ba00016b5ec1",//

        "widget": {

            "url": "https:\/\/apis.dui.ai\/webhook\/tq\/?city=苏州&lon=120.585279&lat=31.299732",//返回内容的web url 地址

            "duiWidget": "web",//dui控件类型,web控件

            "name": "default",//描述控件的名称,可以编译,默认default

            "widgetName": "default",

            "type": "web"//控件类型,web控件

        },

        "nlg": "今天苏州的天气是多云转阴-2~4℃西北风,3级。",

        "task": "天气",

        "status": 0,

        "taskId": "5b860fcbcdd7ba00016b5ea3",

        "shouldEndSession": false,  //该字段为true,表示全双工对话结束,停止向云端送音频;开始对话需要重新唤醒开启识别。

"simulateHalfDuplex": true, //模拟半双工。在全双工模式下,客户端收到该字段,在播放TTS时暂停向云端送音频数据,实现全双工链路模拟半双工模式的效果。

"endSkillDm": true,        。

    },

    "skillId": "2017120200000013",//dui 服务器端 技能 id

    "recordId": "38416974bc7848d1acba45e676b5fa82",//标识一个客户端请求id,默认有dds server 生成,若dds server 请求没有 recordid,生成一个新的

    "contextId": "7bddda4196aa4b88a75afbe2947d4916",//标识dispatcher与一个skill之间的上下文,ddsserver保存dispatcher返回的contextId与sessionId, 并返回给客户端, 客户端第二次请求时可以带上contextId与sessionId

    "sessionId": "7bddda4196aa4b88a75afbe2947d4916",

    "speakUrl": "https:\/\/dds.dui.ai\/runtime\/v1\/cache\/38416974bc7848d1acba45e676b5fa82"//音频播放id,一般为mp3格式

}

5.5 TTS复刻

tts多路复刻接口

5.5.1 合成文本输入

    msg = dds_msg_new();

    dds_msg_set_type(msg, DDS_EV_IN_CUSTOM_TTS_TEXT);

    dds_msg_set_string(msg, "text", "关于使用细节,必须指定复刻标志位,因为普通合成服务和复刻差别较大,无法复用全部流程。");

    dds_msg_set_string(msg, "voiceId", "770f0bf98014404a92f25875625e0915");

    dds_msg_set_integer(msg, "volume", 91);

    dds_msg_set_double(msg, "speed", 1.0);

    dds_msg_set_integer(msg, "voiceCopy", 1);

    dds_send(msg);

    dds_msg_delete(msg);

5.5.2 合成结果输出

static int dds_ev_ccb(void* userdata, struct dds_msg* msg)

{

    int type;

    if (!dds_msg_get_type(msg, &type)) {

        case DDS_EV_OUT_TTS_MULTIPLE: {

            char *value = 0, *reqID;

            if (!dds_msg_get_string(msg, "requestId", &reqID)) {

                printf("requestId: %s\n", reqID);

            }

            if (!dds_msg_get_bin(msg, "stream", &value, &len)) {

                printf("stream len: %d\n", len);

                if(len == 0){

                   //TODO::合成结束

                }else{

                   //TODO::stream数据指针,长度len

                }

            }

            break;

        }

        default:

            break;

        }

    }

    return 0;

}

6. 错误描述

dds的错误统一在回调中返回,消息类型是DDS_EV_OUT_ERROR编译宏,具体的错误码如下

6.1 DDS_ERROR_FATAL

致命错误,出现该错误,一般表示dds客户端处于不可用状态,原因是因为授权未通过

6.2 DDS_ERROR_TIMEOUT

超时错误,表示未在指定时间内(15秒)返回对话结果

6.3 DDS_ERROR_NETWORK

网络错误,在网络连接、数据传输、websocket 握手,过程中出错;具体错误原因,在error字段中描述,error的常见描述如下

1 tcp connect error ,网络连接失败

2auth failed, websocket授权失败

3bad requestwebsocket 参数错误

4network error,网络异常断开

6.4 DDS_ERROR_SERVER

服务返回的结果异常,非json格式或缺少字段

7. 日志调试开关

dds_msg_set_boolean(msg, "logEnable", 1);

dds_msg_set_string(msg, "logFile", "./dds.log");