# 一. 集成准备 ## 1.1 获取产品ID 集成SDK之前,您首先需要[到DUI控制台](https://www.duiopen.com/console/product/index.html)创建产品,获取如下的产品ID。 ![Minion](https://aispeech-dui-doc.oss-cn-hangzhou.aliyuncs.com/images/ct_common_Andriod_SDK/%E5%AE%89%E5%8D%93SDK1.png) ## 1.2 下载和导入SDK * 目前Android SDK只支持[Android Studio](https://developer.android.com/studio/index.html)开发环境 * Android Demo 存放在 gitee:[dui-dds-android-sdk-samples](https://gitee.com/szaispeech/dui-dds-android-sdk-samples) 项目 * 可以前往[SDK下载页](https://www.duiopen.com/openSource/sdkDownload)下载最新的SDK包,sdk 包内包含 **aar duicore javadoc** 等 * java doc 还可在线查看 [javadoc在线文档](https://www.duiopen.com/duilite-doc/dds) * 如需SDK历史版本,请前往[Android_SDK下载页](https://www.duiopen.com/docs/ct_sdk_android) ![SDK 下载页](https://aispeech-dui-doc.oss-cn-hangzhou.aliyuncs.com/images/ct_common_Andriod_SDK/Android+SDK%E4%B8%8B%E8%BD%BD.png) **sdk 不同版本 duicore.zip、product.zip 获取方式差异:** * V2.X.X.X 版本 duicore.zip 在 sdk 里一同发布,product.zip 仍在产品发布页下载。**!!! 不要使用错误的 duicore.zip** * v1.X.X.X 版本通过产品发布页下载 duicore.zip 和 product.zip ## 1.3 集成 ***SDK要求Android 4.1及其以上的系统且 /data分区剩余空间至少200MB*** 将SDK包中的aar文件( 如 lib-dds-2.0.1.1-release.aar )导入到现有的工程中,放入到libs目录下,并添加配置: ***build.gradle*** ```java repositories{ flatDir { dirs 'libs' } } ``` 在build.gradle中配置引用sync工程即可。 ***build.gradle*** ```java dependencies { implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) //在线授权必须引入 implementation 'com.squareup.okhttp3:okhttp:3.9.0' } ``` ## 1.4 代码混淆 如果您的应用使用了混淆, **proguard-rules.pro** 内请添加: * 2.0.0.1 及以上版本: ``` -keep class com.aispeech.**{*;} -keep class com.aispeech.**$**{*;} -keep class ai.dui.sdk.**{*;} -keep class ai.dui.sdk.**$**{*;} ``` * 1.X.X.X 版本: ``` -keep com.aispeech.** -keep public class com.aispeech.lasa{*;} -keep public class com.aispeech.dui.dds.luaBug{ *; } -keep class ai.dui.sdk.**{*;} -keep class ai.dui.sdk.**$**{*;} ``` ## 1.5 权限说明 SDK需要的权限如下表,请在AndroidManifest.xml 文件中添加。 | 权限 | 用途 | | ----------------------------------------- | -------------------------------------- | | android.permission.INTERNET | 允许接入互联网,以便访问我们的云服务 | | android.permission.RECORD_AUDIO | 允许采集音频 | | | android.permission.ACCESS_WIFI_STATE | 获取WIFI状态 | | android.permission.ACCESS_NETWORK_STATE | 获取网络状态 | | android.permission.WRITE_EXTERNAL_STORAGE | (非必须)写入外部存储数据,用于存储一些缓存文件 | | android.permission.READ_EXTERNAL_STORAGE | (非必须)读取外部存储数据 | ## 1.6 精简 sdk sdk 包含所有的功能及资源,客户可根据自己的需要裁剪不需要的功能,可减少 app 或者下载的资源包的大小。 sdk 包里有 mini 文件夹,内含: 1. mini.py // 精简脚本 2. mini.config // 精简脚本的配置文件 脚本使用方法: 1. 修改 mini/mini.config 配置文件,0表示不使用, 1表示使用 2. 在mini目录下执行python脚本: python3 mini.py 3. 在mini目录下生成精减的 aar 与 duicore ***!!!强烈建议客户使用匹配的 duicore 和 aar, 支持客户自己根据需要裁剪,一般可以减少一半大小*** # 二. 初始化 SDK 初始化包含 1. 授权 2. 资源、产品的初始化 ## 2.1 构造 DDSConfig 初始化时需要 DDSConfig 参数配置。以下说明核心的初始化参数,更多参数说明请见 [javadoc在线文档](https://www.duiopen.com/duilite-doc/dds) * DDSConfigBuilder 方式构造 DDSConfig ```java // DDSConfig 新的方式构造 2.0.1.1 及以上版本支持 DDSConfigBuilder ddsConfigBuilder = new DDSConfigBuilder( "productId", // 产品ID "aliasKey", // 产品分支 prod/test/(自定义分支) "apiKey", // 产品的 apiKey,一个API Key只被指定的客户端使用,来源:DUI控制台-产品接入-授权管理 "productKey", // 产品的 productKey,用于设备注册请求签名,来源:DUI控制台-产品接入-授权管理 "productSecret", // 产品的 productSecret,用于设备注册请求签名,来源:DUI控制台-产品接入-授权管理 "duicoreZip" // 资源包,用法详见下方"duicore 配置规则" ); ddsConfigBuilder.createWakeupBuilder() .setMicType(0); ddsConfigBuilder.createCommonBuilder() .setCustomZip("product.zip") .setDeviceName("XXX") .setDeviceId("XXX") // 设备ID,授权时认证的原始设备ID,请务必确保设备ID唯一。若不填,则SDK自己收集设备ID。可以通过DDS.getInstance().getDeviceName()获取授权认证后的唯一ID .setUserId("123456"); // 用户ID DDSConfig ddsConfig = ddsConfigBuilder.build(); // 之后 2.2 init 初始化 ``` * ~~DDSConfig 旧的方式构造~~ ```java // DDSConfig 旧的方式构造 DDSConfig ddsConfig = new DDSConfig() .addConfig(DDSConfig.K_DEVICE_ID, deviceId) // 设备ID字符串,授权时认证的原始设备ID,请务必确保设备ID唯一。若不填,则SDK自己收集设备ID。可以通过DDS.getInstance().getDeviceName()获取授权认证后的唯一ID .addConfig(DDSConfig.K_PRODUCT_ID, productId) // 产品ID字符串 .addConfig(DDSConfig.K_USER_ID, userId) // 用户ID字符串 .addConfig(DDSConfig.K_ALIAS_KEY, aliasKey) // 产品分支 prod/test/(自定义分支) .addConfig(DDSConfig.K_API_KEY, apiKey) // 产品的 apiKey字符串,一个API Key只被指定的客户端使用,来源:DUI控制台-产品接入-授权管理 .addConfig(DDSConfig.K_PRODUCT_KEY, productKey) // 产品的 productKey字符串,用于设备注册请求签名,来源:DUI控制台-产品接入-授权管理 .addConfig(DDSConfig.K_PRODUCT_SECRET, productSecret) // 产品的 productSecret字符串,用于设备注册请求签名,来源:DUI控制台-产品接入-授权管理 .addConfig(DDSConfig.K_DUICORE_ZIP, "duicore.zip") // 资源包,用法详见下方"duicore 配置规则" // 之后 2.2 init 初始化 ``` **duicore 配置规则:** 1. 如果duicore.zip的放置位置在assets目录下,那么就可以使用如代码中的写法,直接使用文件名:duicore.zip 2. 如果duicore.zip没有放置在assets目录下,就必须使用duicore.zip文件的绝对路径:/sdcard/dds/duicore.zip 3. 在 duicore.zip 所在的文件夹中,必须包含与 duicore.zip 文件相应的 **duicore.zip.md5sum** 文件(2.16.0.1以上版本非必须),文件内容是 duicore.zip 文件的 md5 值 在收到 onInitComplete 回调之后,就可以使用DDS来完成对应的操作。 isFull参数如果为false,则表示不完全初始化,缺失资源包,您需要等待资源包的更新下载完成或者[内置一份资源包](https://www.duiopen.com/docs/ct_ziyuan); 如果为true,则表示完全初始化,所有功能就绪。 ## 2.2 授权和初始化 [官网产品授权操作说明](https://cloud.aispeech.com/docs/2045) * **2.0.0.1 及以上版本会先回调收授权状态,之后回调 init 状态。即 onInitComplete 回调 true 即表示初始化和授权都成功** ***init 时会进行授权操作,必须授权成功后才能正常使用sdk*** v2.X.X.X 版本授权流程: 1. 授权操作需要连接到互联网, 在 SDK 初始化时设置 DDSAuthListener 来监听授权的情况 2. SDK 初始化完毕后,您可以使用 ```DDS.getInstance().isAuthSuccess()``` 来判断当前是否授权成功。 3. 授权成功会回调 ```DDSAuthListener.onAuthSuccess()```,授权失败会收到回调```DDSAuthListener.onAuthFailed()```,需要重新调用DDS.getInstance().doAuth() 4. 授权错误码从 "070601“ – "070622",详见 五、错误码描述。 * **v1.X.X.X 版本的授权和 init 回调没有顺序关系** 1. 在SDK初始化时,可以设置 DDSAuthListener 来监听授权的情况。 2. SDK初始化完毕后,您可以使用 DDS.getInstance().isAuthSuccess() 来判断当前是否授权成功。 3. 授权成功会回调 DDSAuthListener.onAuthSuccess,授权失败会收到回调DDSAuthListener.onAuthFailed,需要重新调用DDS.getInstance().doAuth()。 4. Profile文件的错误码范围从 "070601“ – "070622",详见 五、错误码描述。 ```java DDS.getInstance().init( getApplicationContext(), ddsConfig, new DDSInitListener() { // 资源、产品的初始化回调 @Override public void onInitComplete(boolean isFull) { AILog.d(TAG, "onInitComplete: " + isFull); } @Override public void onError(int what, String msg) { AILog.d(TAG, "onError: " + what + ", error: " + msg); } }, new DDSAuthListener() { // 授权回调 @Override public void onAuthSuccess() { AILog.d(TAG, "onAuthSuccess"); } @Override public void onAuthFailed(String errId, String error) { AILog.d(TAG, "onAuthFailed: " + errId + ", error:" + error); } }); ``` ## 2.3 初始化状态检查 除了通过监听 onInitComplete 回调外,还可以直接通过判断 ```DDS.getInstance().getInitStatus()```的返回值来检查初始化是否完毕。 **当未初始化完成时,尝试调用一些方法时,会抛出 DDSNotInitCompleteException 异常** ```java /** * 获取当前初始化的状态 * * @return DDS.INIT_COMPLETE_NONE 还未初始化完成,表示DDS正在初始化 * DDS.INIT_COMPLETE_NOT_FULL 部分初始化完成,表示DDS已经初始化完成,但还没有完成更新 * DDS.INIT_COMPLETE_FULL 完全初始化完成,已经完成更新 */ DDS.getInstance().getInitStatus(); ``` ## 2.4 release SDK init 回调 INIT_COMPLETE_FULL之后即可使用sdk的功能。 如果不需要 sdk ,可以通过以下方法释放 sdk 资源 ```java DDS.getInstance().release(); ``` **注意事项** • DDS.getInstance().init()和DDS.getInstance().release() 方法**不会**阻塞应用程序的主线程,也**不会影响应用程序性能** • 当调用DDS.getInstance().release()之后,之前注册的 observers 会失效,再次 DDS.getInstance().init() 后,您需要对之前的 observers 重新注册 ## 2.5 更新产品 2.0.0.1 及以上版本不支持更新 duicore 资源,但可以更新产品配置 如果您想对发布的产品持续进行免安装的更新,您需要做以下几个操作: * 请求更新 您可以选择在合适的时候发起更新请求。当sdk在检查到有更新后,会自动下载资源并进行热更新,结果会以回调的方式通知。 ```java DDS.getInstance().getUpdater().update(new DDSUpdateListener() { @Override public void onUpdateFound(String detail) { AILog.d(TAG, "onUpdateFound"); } @Override public void onUpdateFinish() { AILog.d(TAG, "onUpdateFinish"); } @Override public void onDownloadProgress(float progress) { AILog.d(TAG, "onDownloadProgress :" + progress); } @Override public void onError(int what, String error) { AILog.d(TAG, "onError :" + what + ", error: " + error); } @Override public void onUpgrade(String version) { AILog.d(TAG, "onUpgrade :" + version); } }; ``` * 处理更新推送 在您成功发布产品之后,若您已经订阅了消息 sys.resource.updated,则会收到资源更新的推送,此时您可以选择直接请求更新,以保持产品一直处于最新状态(推荐)。 ```java DDS.getInstance().getAgent().subscribe("sys.resource.updated", new MessageObserver() { @Override public void onMessage(String message, String data) { try { DDS.getInstance().getUpdater().update(ddsUpdateListener); } catch (DDSNotInitCompleteException e) { e.printStackTrace(); } } }); ``` SDK中内置一套资源更新时的通知的UI实现,您可以在DDSConfig中设置 DDSConfig.K_USE_UPDATE_NOTIFICATION 来开启或关闭。 ## 2.6 Debug #### 2.6.1 日志 2.0.0.1 及以上版本默认日志级别是 4 (同 Log.INFO)。修改日志级别: ```java DDS.getInstance().setDebugMode(2); // 日志默认保存路径: /sdcard/Android/data/包名/cache/alog/ ``` 日志压缩成xlog形式,默认保存2天的日志,以下是日志需要的大概大小 * 单麦D级别日志24小时保存日志 150M * 多麦D级别日志24小时保存日志 400M #### 2.6.2 音频调试 初始化后可动态设置保存 debug 音频 ```java DDS.getInstance().setAudioDebug(true); // 音频默认保存路径: /sdcard/Android/data/包名/cache/ ``` 动态音频调试接口可替代静态调试配置: K_VAD_DEBUG、K_TTS_DEBUG、K_WAKEUP_DEBUG、K_ASR_DEBUG、K_ASRPP_DEBUG #### 2.6.3 动态Debug ```java DDS.getInstance().startDebug(); // 开启调试模式 日志级别调为V 保存调试音频 DDS.getInstance().stopDebug(); // 关闭调试模式 日志级别修改为E 停止保存调试音频 ``` #### 2.6.4 缓存配置 缓存目录的配置需要在初始化时设置,如不设置,默认 /sdcard/Android/data/包名/cache ```java ddsConfigBuilder.createCommonBuilder() .setCachePath("/sdcard/cache") // 缓存文件保存目录,默认 /sdcard/Android/data/包名/cache .setCacheSize(500000) // 缓存文件大小限制。自定义缓存目录时必需 ``` # 三. 功能列表 ## 3.1 响应command 1. 客户端动作的执行 详细的command编写说明,请参见[客户端动作(command)编写说明](https://www.duiopen.com/docs/ct_skill_command) ![Minion](http://aispeech-dui-doc.oss-cn-hangzhou.aliyuncs.com/upload/f14a9ee0-8bcf-11e7-88e4-b7f5b8a39b76/%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%8A%A8%E4%BD%9C20171128.png) 2. 快捷唤醒中的命令响应 ![Minion](http://aispeech-dui-doc.oss-cn-hangzhou.aliyuncs.com/upload/f14a9ee0-8bcf-11e7-88e4-b7f5b8a39b76/%E5%BF%AB%E6%8D%B7%E5%94%A4%E9%86%92%E4%B8%AD%E7%9A%84%E5%91%BD%E4%BB%A4%E5%93%8D%E5%BA%9420171128.png) 当您在DUI平台上定制了command之后,可以使用如下的方式在您的工程中做实现。 类似于Android的广播接收器,您可以在需要的地方注册和注销CommandObserver,同一个CommandObserver可以处理多个commands。 ```java // 注册 DDS.getInstance().getAgent().subscribe(new String[]{"open_window", "increase.volume"}, commandObserver); // 注销 DDS.getInstance().getAgent().unSubscribe(commandObserver); private CommandObserver commandObserver = new CommandObserver() { @Override public void onCall(final String command, final String data) { if (command.equals("open_window")){ JSONObject jsonData = new JSONObject(data); String intentName = jsonData.optString("intentName"); String w = jsonData.optString("w"); // TODO 依据w的值,执行打开窗户操作 }else if (command.equals("increase.volume")) { // TODO 处理调大音量的快捷唤醒指令 } } }; ``` ## 3.2 响应native api ![Minion](http://aispeech-dui-doc.oss-cn-hangzhou.aliyuncs.com/upload/f14a9ee0-8bcf-11e7-88e4-b7f5b8a39b76/%E5%93%8D%E5%BA%94native+api20171128.png) 当您在DUI平台上定制了native api之后,可以使用如下的方式在您的工程中做实现。 类似于Android的广播接收器,您可以在需要的地方注册和注销NativeApiObserver,同一个NativeApiObserver可以处理多个native api。 ```java // 注册 DDS.getInstance().getAgent().subscribe(new String[]{"query_battery"}, nativeApiObserver); // 注销 DDS.getInstance().getAgent().unSubscribe(nativeApiObserver); private NativeApiObserver nativeApiObserver = new NativeApiObserver() { @Override public void onQuery(final String nativeApi, final String data) { if (nativeApi.equals("query_battery")){ JSONObject jsonData = new JSONObject(data); String intentName = jsonData.optString("intentName"); // 执行查询电量操作 String battery = DeviceUtil.getBattery(); DDS.getInstance().getAgent().feedbackNativeApiResult(nativeApi, new TextWidget().setText(battery)); } } }; ``` nativeCommand支持异步写法 ```java String mCommand; public void doNativeCommand() { String[] mTopic = new String[]{ "sys.car.crl" }; NativeCommandObserver nativeCommandObserver = new NativeCommandAsyncObserver() { @Override public void onNativeCommandAsync(String command, String data) { mCommand = command; AILog.d(TAG, "command = " + command); AILog.d(TAG, "data = " + data); // 异步执行操作,这里不能再有耗时操作,应用层保证这里切异步 } }; DDS.getInstance().getAgent().subscribeSync(mTopic, nativeCommandObserver); } ``` **注:** • 每个onQuery方法执行时,需要调用 **feedbackNativeApiResult**() 来向DUI平台返回执行结果,表示一个native api执行结束。 • native api的执行超时时间为10s **更多详细使用请查看:** **https://www.duiopen.com/docs/skill_FAQ** ## 3.3 响应内置消息 内置消息列表: | message | data | 说明 | | -------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | sys.kernel.ready | 无 | SDK加载完成,所有功能就绪 | | sys.dialog.start | { reason:"wakeup.major",//唤醒词触发对话 } | 对话开始,及开始原因: <br>1.wakeup.major,唤醒词唤醒 <br/>2.wakeup.command,快捷命令唤醒 <br/>3.api.startDialog,调用startDialog接口 <br/>4.api.sendText,调用sendText接口(对话Idle时) <br/>5.api.triggerIntent,调用triggerIntent接口(对话Idle时) <br/>6.api.avatarClick,调用avatarClick接口(对话Idle时) <br/>7.api.avatarPress,调用avatarPress接口(对话Idle时) | | sys.dialog.error | { errId:071304, errMsg:"asr null" recordId:"70dcef50b8b31a6c02dbac1acee31de7" } | 对话中发生的错误 | | sys.dialog.end | { reason:"normal",//对话正常结束 skillId:"100001246" } { reason:"error",//对话发生错误; errId:071304, errMsg:"asr null", skillId:"100001246" } { reason:"interrupt",//对话被打断。强制结束对话时出现。 skillId:"100001246" } | 对话结束,及结束原因: 1.normal,对话正常结束 2.error,对话发生错误退出 3.interrupt,对话被打断退出 | | sys.dialog.continue | 无 | 对话恢复 | | sys.resource.updated | 无 | 收到线上资源变更的推送 | | sys.wakeup.result | { "type": "major", "word": "你好小驰", "greeting":"主人你好" } | 语音唤醒 | | sys.vad.begin | 无 | VAD触发 | | sys.vad.end | 无 | VAD结束 | | sys.upload.result | { "result":false, "reqId":"xxxx", "errId":71801, "errMsg":"Network Invalid" } | 上传词库、设备信息的结果 | 类似于Android的广播接收器,您可以在需要的地方注册和注销MessageObserver,同一个MessageObserver可以处理多个message。 您可以通过注册消息接收器来接收需要UI展示的数据,具体UI消息数据见: **[UI事件及数据定义](https://www.duiopen.com/docs/ct_UI)** ```java // 注册 DDS.getInstance().getAgent().subscribe(new String[]{"sys.dialog.start","sys.dialog.end","context.input.text"}, messageObserver); // 注销 DDS.getInstance().getAgent().unSubscribe(messageObserver); private MessageObserver messageObserver = new MessageObserver() { @Override public void onMessage(final String message, final String data) { if (message.equals("sys.dialog.start")){ runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, "对话开始了", Toast.LENGTH_LONG).show(); } }); }else if(message.equals("context.input.text")){ final JSONObject dataJson = new JSONObject(data); runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, dataJson.optString("text"), Toast.LENGTH_LONG).show(); } }); } } }; ``` ## 3.4 开启/终止对话 您可以在任何时刻主动开启/终止对话,通过以下接口实现。 ***dialog*** ```java // 开启对话。如果当前已经在对话中,则重新开启新一轮对话。 DDS.getInstance().getAgent().startDialog(); // 关闭对话 DDS.getInstance().getAgent().stopDialog(); // 支持在开启对话时传入一个播报文本, 先播报文本,后开启对话 Agent.startDialog(JSONObject jsonObject) // 支持在关闭对话时传入一个播报文本, 文本播报结束后自动结束对话 Agent.stopDialog(JSONObject jsonObject) 使用示例: JSONObject jsonObject = new JSONObject(); jsonObject.put("speakText", "播报文本"); DDS.getInstance().getAgent().startDialog(jsonObject); // 多模态事件同步,该接口用于客户端给对话中控发送一个事件 DDS.getInstance().getAgent().updateDispatchEvent(JSONObject obj) 使用示例: JSONObject obj = new JSONObject(); // obj.put("endSkill", "true"); // 不支持 endSkill DDS.getInstance().getAgent().updateDispatchEvent(obj); ``` ### 3.4.1 提示音 **半双工** 使用 ```startDialog``` 后默认会有一个提示音,提示音可以自由打开或者关闭 ```java DDS.getInstance().getAgent().openTip(); // 打开tip提示音 DDS.getInstance().getAgent().closeTip(); // 关闭tip提示音 ``` ## 3.5 按键事件 如果您有相关的软硬件按键,您需要关注以下接口,将用户的行为反馈给SDK。 ```java // 点击唤醒/停止识别/打断播报 操作接口 DDS.getInstance().getAgent().avatarClick(); // 点击唤醒/停止识别/打断播报 操作接口 , 并附带一则欢迎语,当此次是唤醒时,播报这则欢迎语 DDS.getInstance().getAgent().avatarClick("有什么可以帮你"); // 按下按键接口(调用此接口需关闭VAD,VAD在创建产品时可配置) DDS.getInstance().getAgent().avatarPress(); // 释放按键接口(调用此接口需关闭VAD,VAD在创建产品时可配置) DDS.getInstance().getAgent().avatarRelease(); ``` ## 3.6 语音播报 使用 DDS.getInstance().getAgent().getTTSEngine().speak() 接口可以主动播放 TTS 。 参数说明: 1)**text** 播报文本,支持[SSML](https://www.duiopen.com/docs/ct_ssml)。 2)**priority** 优先级 提供4个优先级播报。 ①优先级0:保留,与DDS语音交互同级,仅限内部使用; ②优先级1:正常,默认选项,同级按序播放; ③优先级2:重要,可以插话<优先级1>,同级按序播放,播报完毕后继续播报刚才被插话的<优先级1>; ④优先级3:紧急,可以打断当前正在播放的<优先级1|优先级2>,同级按序播放,播报完毕后不再继续播报刚才被打断的<优先级1|优先级2>。 3)**ttsId** 用于追踪该次播报的id,建议使用UUID。 4)**audioFocus** 该次播报的音频焦点,默认值: ①优先级0:android.media.AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE ②优先级非0:android.media.AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK * Text 播报 ```java // 请求语音播报 DDS.getInstance().getAgent().getTTSEngine().speak("这是一段测试文本", 1, "100", AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); ``` * SSML 播报 ```java String test1 = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<speak xml:lang=\"cn\">\n" + " 测试SSML播报\n" + "</speak>"; DDS.getInstance().getAgent().getTTSEngine().speak(test1, 1, "10004", AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, TTSEngine.SSML); ``` 停止播报接口如下: 1)ttsId与speak接口的ttsId一致,则停止或者移除该播报; 2)ttsId为空, 停止所有播报; 3)ttsId为"0",停止当前播报。 ```java // 停止播报 DDS.getInstance().getAgent().getTTSEngine().shutup("100"); ``` ## 3.7 内置H5 2.0.0.1 及以上版本基于性能考虑默认关闭H5通信节点, 如有需要, 可以通过配置打开: ```java // 新的配置方式 ddsConfigBuilder.createNodeBuilder() .enableH5(true); // 旧的配置方式 config.addConfig(DDSConfig.K_H5_NODE_ENABLE, "true"); ``` 您如果使用和上传了h5资源,可以通过以下方法来获取h5资源包的 index.html 文件的路径用来在webview中加载 ```java // 获取资源包中h5的index.html文件的绝对路径 DDS.getInstance().getAgent().getValidH5Path(); ``` ## 3.8 外部录音机拾音 您如果有自己的录音机,可以通过以下方法将录音数据传给DDS。 ```java // 注意:需要将DDSConfig.K_RECORDER_MODE设置为"external",用来关闭DDS内置录音机,该接口才会生效 // 音频长度:无限制。内置录音机是3200(单麦) // 调用间隔:无限制。内置录音机是100ms // 调用时机:无限制。DDS会根据对话状态将接受的音频送入识别或者丢弃。 DDS.getInstance().getAgent().feedPcm(byte[] pcm); ``` ## 3.9 外部TTS引擎 您如果使用第三方的TTS引擎,可以通过以下方法来将第三方TTS引擎注册到DDS中。 ```java // 1. 配置 ttsMode /** * TTS播报模式<br> * 取值:internal or external<br> * internal:为sdk内部合成播报<br> * external:用户负责合成播报,需调用 <code>DDS.getInstance().getAgent().setTTSRequestListener()</code> 通过回调获取播报时机, * 播报完成后调用 <code>DDS.getInstance().getAgent().notifyTTSEnd()</code> 标记结束<br> * 默认值:internal */ ddsConfigBuilder.createTtsBuilder().setTtsMode("external") // 2. 注册回调 TTSRequestListener listener = new TTSRequestListener() { @Override public void onStart(String type, String content) { Log.d(TAG, "tts start," + type + ":" + content); int code = mExternelTts.startSpeaking(content, mTtsListener); if (code != ErrorCode.SUCCESS) { Log.d(TAG, "语音合成失败,错误码: " + code); } } @Override public void onStop() { Log.d(TAG, "tts stop"); mExternelTts.stopSpeaking(); } }; DDS.getInstance().getAgent().setTTSRequestListener(listener) // 3. 合成完毕后必须播报结束通知 !!! DDS.getInstance().getAgent().notifyTTSEnd(); ``` ## 3.10 场景模式 SDK提供了一些场景(如TTS播报/拾音距离)的切换,您可以在需要的时候调用下面的接口。 ***DDSMode*** ```java //TTS场景切换 DDS.getInstance().getAgent().setDDSMode(DDSMode.TTS_SILENCE); //在静音模式下,所有TTS播报将会被静音。 DDS.getInstance().getAgent().setDDSMode(DDSMode.TTS_NORMAL); //默认值。 //拾音场景切换。仅在"电视助手"解决方案中生效。 DDS.getInstance().getAgent().setDDSMode(DDSMode.PICKUP_NEAR); //在近场模式下,将加载产品配置页中的近场配置 DDS.getInstance().getAgent().setDDSMode(DDSMode.PICKUP_FAR); //在远场模式下,将加载产品配置页中的远场配置 ``` ## 3.11 触发指定意图 如果您期望主动触发某个意图的对话,可以在需要的时候调用下面的接口。 参数说明: 1)**skill** 技能名称,必填。 2)**task** 任务名称,必填。 3)**intent** 意图名称,必填。 4)**slots** 语义槽的key-value Json,可选。 ***triggerIntent*** ```java // 跳过识别和语义,直接进入指定的意图对话。即:DDS主动向用户发起一轮对话。 DDS.getInstance().getAgent().triggerIntent("我是技能名称", "我是任务名称", "我是意图名称", new JSONObject().put("语义槽名称一", "语义槽取值一").put("语义槽名称二", "语义槽取值二").toString()); ``` 根据skillid触发意图: ```java SkillIntent skillIntent = new SkillIntent("我是技能id", "我是任务名称", "我是意图名称", new JSONObject().put("语义槽名称一", "语义槽取值一").put("语义槽名称二", "语义槽取值二").toString()); DDS.getInstance().getAgent().triggerIntent(skillIntent); ``` ## 3.12 更新用户词库 如果您期望更新用户的词库,使其能在技能中使用,可以在需要的时候调用下面的接口。上传结果可以通过sys.upload.result消息来获取。 注意:更新的词库仅在该设备下有效,不会影响其他设备。 参数说明: **vocabIntent:** 词库intent。包含词库名name,操作action,词条列表contents。 注1:若词库名是sys.联系人,则会自动做分词处理。 注2:若需要上传带同义词的词条,格式如下:"${词条取值}:${同义词1},${同义词2}" 返回值:请求ID,用于追踪sys.upload.result。 ***updateVocabs*** ```java //向指定词库中添加词条 final String reqId = DDS.getInstance().getAgent().updateVocabs( new VocabIntent() .setName("应用列表") .setAction(VocabIntent.ACTION_INSERT) .setContents(Arrays.asList("淘宝", "支付宝:支护宝,付款码")) ); //清空之前上传到该词库的所有词条,然后添加词条 DDS.getInstance().getAgent().updateVocabs( new VocabIntent() .setName("设备词库") .setAction(VocabIntent.ACTION_CLEAR_AND_INSERT) .setContents(Arrays.asList("空调", "电视:电视机")) ); //删除该词库的词条 DDS.getInstance().getAgent().updateVocabs( new VocabIntent() .setName("歌手名") .setAction(VocabIntent.ACTION_REMOVE) .addContent("刘德华") ); //清空之前上传到该词库的所有词条 DDS.getInstance().getAgent().updateVocabs( new VocabIntent() .setName("歌曲") .setAction(VocabIntent.ACTION_CLEAR_ALL) ); //批量更新词库 DDS.getInstance().getAgent().updateVocabs( //向指定词库中添加词条 new VocabIntent() .setName("sys.联系人") .setAction(VocabIntent.ACTION_INSERT) .addContent("董芳芳") .addContent("王强"), //清空之前上传到该词库的所有词条,然后添加词条 new VocabIntent() .setName("设备词库") .setAction(VocabIntent.ACTION_CLEAR_AND_INSERT) .setContents(Arrays.asList("空调", "电视")), //删除该词库的词条 new VocabIntent() .setName("歌手名") .setAction(VocabIntent.ACTION_REMOVE) .addContent("刘德华"), //清空之前上传到该词库的所有词条 new VocabIntent() .setName("歌曲") .setAction(VocabIntent.ACTION_CLEAR_ALL) ); //关注更新词库的结果 DDS.getInstance().getAgent().subscribe("sys.upload.result", new MessageObserver() { @Override public void onMessage(String message, String data) { Log.e(TAG, message + ":" + data); try { JSONObject obj = new JSONObject(data); if (reqId.equals(obj.optString("reqId", ""))) { Log.d(TAG, "updateVocabs result:" + obj.optBoolean("result", false)); } } catch (JSONException e) { e.printStackTrace(); } } }); ``` ## 3.13 startListening 仅开启识别 如果您期望仅开启识别并获取识别结果,可以在需要的时候调用下面的接口。 2.0.0.1 及以上版本使用 startListening 在半双工和全双工下表现一致 ***ASREngine*** ```java //获取识别引擎 ASREngine asrEngine = DDS.getInstance().getAgent().getASREngine(); ASREngine.ListeningParams params = new ASREngine.ListeningParams(); params.setVadEnable(true); //开启识别 params 可不设置 asrEngine.startListening(params, new ASREngine.Callback() { @Override public void beginningOfSpeech() { AILog.i(TAG, "检测到用户开始说话"); } /** * @buffer 音频数据属性:单声道 16bit */ @Override public void bufferReceived(byte[] buffer) { AILog.i(TAG, "用户说话的音频数据"); } @Override public void endOfSpeech() { AILog.i(TAG, "检测到用户结束说话"); } /** * @results. eof 为 0 代表识别过程中的结果, 为 1 代表识别结束的最终结果 * {"var":"今","sessionId":"0b48c0d086efc42d55bbf1068137655d","recordId":"6b3ccb1ee745d42fa6ddb74330b1c059","eof":0} * {"var":"今天 天气","sessionId":"0b48c0d086efc42d55bbf1068137655d","recordId":"6b3ccb1ee745d42fa6ddb74330b1c059","eof":0} * {"sessionId":"0b48c0d086efc42d55bbf1068137655d","pinyin":"jin tian tian qi","recordId":"6b3ccb1ee745d42fa6ddb74330b1c059","eof":1,"text":"今天 天气"} */ @Override public void partialResults(String results) { AILog.i(TAG, "实时识别结果反馈"); } /** * @results. {"sessionId":"0b48c0d086efc42d55bbf1068137655d","recordId":"6b3ccb1ee745d42fa6ddb74330b1c059","eof":1,"text":"今天天气","pinyin":"jin tian tian qi"} */ @Override public void finalResults(String results) { AILog.i(TAG, "最终识别结果反馈"); } @Override public void error(String error) { AILog.i(TAG, "识别过程中发生的错误"); } @Override public void rmsChanged(float rmsdB) { AILog.i(TAG, "用户说话的音量分贝"); } }); //主动结束此次识别,使用 vad 的话不需要主动停止 asrEngine.stopListening(); //取消此次识别 asrEngine.cancel(); ``` ### 3.13.1 设置实时回传音量 ```java // 设置实时回传音量大小, 默认为true // 设置false之后, ASREngine.Callback.rmsChanged()不再回传音量变化值 DDS.getInstance().getAgent().getASREngine().enableVolume(boolean enable); ``` ### 3.13.2 feed OPUS 或者 SBC 音频 如需 feed OPUS 或者 SBC 音频,只需按下面配置即可,其它操作不变 ```java // feed opus DDS.getInstance().getAgent().setRecorderMode(Agent.RecorderMode.OPUS); DDS.getInstance().getAgent().feedOpus(buffer); // feed sbc DDS.getInstance().getAgent().setRecorderMode(Agent.RecorderMode.SBC); DDS.getInstance().getAgent().feedSbc(buffer); ``` ## 3.14 VAD相关设置 ### 3.14.1设置VAD后端停顿时间 如果您期望修改VAD后端检测的时间,可以在需要的时候调用下面的接口。 **参数说明** **millis** 后端检测时间,单位毫秒,默认500毫秒。即若VAD在用户说话时停顿超过一定的时间,则认为用户已经说完,发出sys.vad.end消息,结束录音。 ***setVadPauseTime*** ```java //设置VAD后端停顿时间 DDS.getInstance().getAgent().getASREngine().setVadPauseTime(1000);//获取VAD后端停顿时间 DDS.getInstance().getAgent().getASREngine().getVadPauseTime(); ``` ### 3.14.2 设置VAD前端静音检测超时时间 如果您期望修改VAD前端静音检测超时时间,可以在需要的时候调用下面的接口。设置成功后,vad再次启动生效。 参数说明: **millis** 前端静音检测超时时间,单位毫秒,默认8000毫秒。若VAD启动,一直未检测到用户说话,超过一定时间,发出sys.vad.timeout消息,结束录音。 ***setVadTimeout*** ```java //设置VAD前端静音检测超时时间 DDS.getInstance().getAgent().getASREngine().setVadTimeout(1000); //获取VAD前端静音检测超时时间 DDS.getInstance().getAgent().getASREngine().getVadTimeout(); ``` ## 3.15 对话输入文本 如果您期望跳过识别直接输入文本进行对话,可以在需要的时候调用下面的接口。 ***sendText*** ```java /** * 输入文本的接口 * <p> * 跳过对话中的识别过程,直接开始对话中的语义理解 * <p> * 若当前没有在对话中,则以文本作为首轮说法,新发起一轮对话请求 * 若当前正在对话中,则跳过识别,直接发送文本 * * @param text 输入的文本内容 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().sendText(String text) ``` ## 3.16 TTS相关设置 ### 3.16.1 TTS事件回调 如果您想在TTS播报的相关时间节点收到通知,如:开始播报,播报结束。可以使用如下的方式: ***TTSEngine.Callback*** ```java DDS.getInstance().getAgent().getTTSEngine().setListener(new TTSEngine.Callback() { /** * 开始合成时的回调 * @param ttsId 当前TTS的id, 对话过程中的播报ttsid默认为0,通过speak接口调用的播报,ttsid由speak接口指定。 */ @Override public void beginning(String ttsId) { Log.d(TAG, "TTS开始播报"); } /** * 合成的音频数据的回调,可能会返回多次,data长度为0表示音频结束 * @param data 音频数据属性:单声道 16bit, pcm */ @Override public void received(byte[] data) { Log.d(TAG, "收到音频,此方法会回调多次,直至data为0,音频结束"); } /** * TTS播报完成的回调 * @param status 播报结束的状态。 * 正常播报结束为0 * 播报中途被打断结束为1 */ @Override public void end(String ttsId, int status) { Log.d(TAG, "TTS播报结束"); } /** * 合成过程中出现错误的回调 * @param error 错误信息 */ @Override public void error(String error) { Log.d(TAG, "出现错误,"+error); } /** * 合成过程中的音素回掉,需要资源支持 */ @Override public void phoneReturnReceived(String phoneReturn) { } }); ``` ***TTSEngine.CallbackOptimize*** 带进度的回调 ```java DDS.getInstance().getAgent().getTTSEngine().setListenerByProcess(new TTSEngine.CallbackOptimize() { @Override public void beginning(String ttsId) { Log.d(TAG, "beginning() ttsId: " + ttsId); if (TextUtils.equals(ttsId, mTtsId)) { listenerSuccessTimes++; } } @Override public void end(String ttsId, int status) { Log.d(TAG, "end() ttsId: " + ttsId + " status: " + status); if (TextUtils.equals(ttsId, mTtsId) && status == 0) { listenerSuccessTimes++; } } @Override public void error(String error) { Log.d(TAG, "error() error: " + error); } @Override public void onSpeechProgress(String ttsId, int currentFrame, int totalFrame, boolean isDataReady) { super.onSpeechProgress(ttsId, currentFrame, totalFrame, isDataReady); Log.d(TAG, "onSpeechProgress() ttsId: " + ttsId + " currentFrame:" + currentFrame + " totalFrame:" + totalFrame + " isDataReady:" + isDataReady); if (listenerSuccessTimes == 1 && TextUtils.equals(ttsId, mTtsId) && isDataReady) { listenerSuccessTimes++; } } }); ``` ### 3.16.2 TTS参数设置 如果您想通过SDK定制不同的合成音类型,可以在需要的时候调用下面的接口。 ***setTTS*** ```java /** * 设置TTS播报类型的接口 * 调用此接口则云端配置的合成音类型失效,此后的合成音类型都将由此接口来托管 * @param speaker 取值如:zhilingf, gdgm等,若取为null,则表示随机改为一个不同的类型 */ DDS.getInstance().getAgent().getTTSEngine().setSpeaker("zhilingf"); /** * 设置TTS播报类型的接口 * 调用此接口则云端配置的合成音类型失效,此后的合成音类型都将由此接口来托管 * @param speaker 取值如:zhilingf, gdgm等 * @param path 取值如:"sdcard/tts/zhilingf.bin",自定义合成间类型的全路径 */ DDS.getInstance().getAgent().getTTSEngine().setSpeaker("zhilingf","sdcard/tts/zhilingf.bin"); /** * 设置TTS播报语速的接口 * 调用此接口则云端配置的合成音语速失效,此后的合成音语速都将由此接口来托管 * * @param speed 语速,取值0.5-2.0,0.5语速最快,2.0语速最慢,对应控制台上合成音倍率值 */ DDS.getInstance().getAgent().getTTSEngine().setSpeed(1.0f); /** * 设置TTS播报音量的接口 * 调用此接口则云端配置的合成音音量失效,此后的合成音音量都将由此接口来托管 * * @param volume 音量大小,取值1-100 */ DDS.getInstance().getAgent().getTTSEngine().setVolume(50); /** * 设置TTS引擎为本地或者云端 * * @param mode 取值 TTSEngine.LOCAL(本地合成),TTSEngine.CLOUD(云端合成) */ DDS.getInstance().getAgent().getTTSEngine().setMode(TTSEngine.LOCAL); /** * 设置TTS播报的通道 * @param streamType AudioManager.STREAM_*的取值 * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().getTTSEngine().setStreamType(int streamType); /** * 设置TTS播报的通道(安卓7.0版本以上支持,并且需要配置K_AUDIO_USAGE和K_CONTENT_TYPE) * @param usage 取值: AudioAttributes.USAGE_* * @param contentType 取值: AudioAttributes.CONTENT_TYPE_* * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().getTTSEngine().setUsage(int usage, int contentType); /** * 设置TTS播报自定义录音 * @param customAudioList 自定义播报音频列表 * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().getTTSEngine().setCustomAudio(List<CustomAudioBean> customAudioList); /** * 设备抢焦点,默认为抢焦点 * @param enable true/false true: 抢焦点 false: 不抢焦点 */ DDS.getInstance().getAgent().getTTSEngine().enableFocus(boolean enable); /** * 设置TTS人设 * @param style 风格,humor:幽默;calm:沉稳;common:普通;简短:short; */ DDS.getInstance().getAgent().getTTSEngine().setStyle(String style); /** * 清除TTS人设 */ DDS.getInstance().getAgent().getTTSEngine().removeStyle(); /** * 设置TTS结束后延迟时间,单位ms * @param afterTime * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().getTTSEngine().setPlayAfterTime(int afterTime); ``` ### 3.16.3 TTS参数获取 如果您想获取当前TTS配置的接口,可以调用如下接口: ***getTTS*** ```java //返回String 当前使用的合成音类型,如:"zhilingf",获取失败返回null DDS.getInstance().getAgent().getTTSEngine().getSpeaker(); //返回float 当前合成音语速,返回值0.5-2.0,0.5语速最快,2.0语速最慢,获取失败返回 0 DDS.getInstance().getAgent().getTTSEngine().getSpeed(); //返回int 当前合成音音量,返回值1-100,获取失败返回 0 DDS.getInstance().getAgent().getTTSEngine().getVolume(); //返回List<CustomAudioBean> 当前自定义播报音频的列表 DDS.getInstance().getAgent().getTTSEngine().getCustomAudio(); //获取当前TTS人设 DDS.getInstance().getAgent().getTTSEngine().getStyle(); ``` ### 3.16.4 音频焦点处理模式 播放 TTS 时的音频焦点默认是 sdk 内部处理,也可交由外部处理 1. 配置 AudioFocusMode ```java /** * 音频焦点处理模式<br> * 取值:internal or external<br> * internal:由SDK内部处理音频焦点的获取与释放<br> * external:由外部根据对话状态及TTS播报状态处理对话及TTS等的焦点问题,SDK不再获取或者释放焦点。 * 用户可以使用 <CODE>DDS.getInstance().getAgent().setAudioFocusCallback(audioFocusCallback)</CODE> 在回调里处理音频焦点<br> * 是否必需:否<br> * 默认值:internal */ ddsConfigBuilder.createTtsBuilder().setAudioFocusMode("external") ``` 2. 设置回调并在回调里处理音频焦点 ```java private AudioFocusCallback audioFocusCallback = new AudioFocusCallback() { @Override public void onRequest() { Log.d(TAG, "onRequest()"); } @Override public void onAbandon() { Log.d(TAG, "onAbandon()"); } }; DDS.getInstance().getAgent().setAudioFocusCallback(audioFocusCallback); ``` ## 3.17 唤醒相关设置 **唤醒词介绍:**当前除了主唤醒,唤醒词的类型还包括了 副唤醒词、命令唤醒词(快捷唤醒词)、打断唤醒词、快速唤醒词。 副唤醒词(Minor):如果您期望为设备增加一个别名,可以通过设置该类型唤醒词来完成。 命令唤醒词(快捷唤醒词/Command): 如果您期望在唤醒的时候执行一条指令,可以通过设置该类型唤醒词来完成。 打断唤醒词(ShortCut):如果您期望在唤醒的时候能同时打断语音播报,可以通过设置该类型唤醒词来完成。打断唤醒词唤醒后会把唤醒词作为识别结果发送给服务器,由服务器给出对话结果。 快速唤醒词(QuickStart):您想通过代码来控制快速唤醒,QuickStart词为类似“导航去”、“我想听”等,此类唤醒词只在oneshot模式下生效,作用为在未唤醒状态下语音输入“导航去天安门”,可直接进入对话流程。 ### 3.17.1 开关唤醒 默认SDK加载后不会启动语音唤醒,您可以通过以下接口对需对语音唤醒进行控制。 ***wakeup*** ```java // 开启语音唤醒 DDS.getInstance().getAgent().getWakeupEngine().enableWakeup(); // 关闭语音唤醒 DDS.getInstance().getAgent().getWakeupEngine().disableWakeup(); // 获取主唤醒词列表 DDS.getInstance().getAgent().getWakeupEngine().getWakeupWords(); ``` ### 3.17.2 更新副唤醒词 如果您期望为设备增加一个副唤醒词,可以在需要的时候调用下面的接口。 参数说明: 1)**word** 副唤醒词,若设置null,则清空当前的副唤醒词。 2)**pinyin** 副唤醒词的拼音,形如:ni hao xiao chi。 3)**threshold** 副唤醒词的阈值,形如:0.120(取值范围:0-1)。若设置null,则自动估算。 4)**greetings** 副唤醒词的欢迎语,若设置null,则与主唤醒词保持一致。 ***updateMinorWakeupWord*** ```java // 实时更新副唤醒词(1.1.2.1版本及以上支持) WakeupWord minorWord = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小迟") .setThreshold("0.15") .addGreeting("我在"); DDS.getInstance().getAgent().getWakeupEngine().updateMinorWakeupWord(minorWord); //获取当前的副唤醒词 DDS.getInstance().getAgent().getWakeupEngine().getMinorWakeupWord(); ``` ### 3.17.3 更新命令唤醒词(快捷唤醒词) 如果您期望在唤醒的时候执行一条指令,可以在需要的时候调用下面的接口。命令唤醒词和DUI控制台所设置的快捷唤醒词是相同的功能,且互不影响,可同时存在,根据需求来选择设置方式。 参数说明: 1)**actions** 命令唤醒词指令,为string数组,不为null。 2)**words** 命令唤醒词,为string数组,不为null。 3)**pinyins** 命令唤醒词的拼音,形如:ni hao xiao chi,为string数组, 不为null。 4)**thresholds** 命令唤醒词的阈值,形如:0.120(取值范围:0-1),为string数组,不为null。 5)**greetings** 命令唤醒词的欢迎语,为string二维数组,不为null,每维对应一个唤醒词的欢迎语。 ***updateCommandWakeupWord*** ```java // 实时更新命令唤醒词-更新一条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord() .setPinyin("xia yi shou") .setWord("下一首") .setThreshold("0.18") .addGreeting("我在") .setAction("sys.next"); DDS.getInstance().getAgent().getWakeupEngine().updateCommandWakeupWord(commandWord); // 实时更新命令唤醒词-更新多条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord() .setPinyin("xia yi shou") .setWord("下一首") .setThreshold("0.18") .addGreeting("我在") .setAction("sys.next"); WakeupWord commandWord1 = new WakeupWord() .setPinyin("shang yi shou") .setWord("上一首") .addGreeting("我在") .setThreshold("0.20") .setAction("sys.play"); List<WakeupWord> commandList = new ArrayList<>(); commandList.add(commandWord); commandList.add(commandWord1); DDS.getInstance().getAgent().getWakeupEngine().updateCommandWakeupWords(commandList); //清空当前设置的命令唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearCommandWakeupWord(); ``` ### 3.17.4 更新打断唤醒词 如果您期望在唤醒的时候能同时打断语音播报,可以在需要的时候调用下面的接口。打断唤醒词暂不支持在DUI控制台设置。(说明:打断需要设备开启回声消除) 参数说明: 1)**words** 打断唤醒词,为string数组,不为null。 2)**pinyins** 打断唤醒词的拼音,形如:ni hao xiao chi,为string数组,不为null。 3)**thresholds** 打断唤醒词的阈值,形如:0.120(取值范围:0-1)为string数组,不为null。 ***updateShortcutWakeupWord*** ```java //实时更新打断唤醒词-更新一条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); DDS.getInstance().getAgent().getWakeupEngine().updateShortcutWakeupWord(fixWord); //实时更新打断唤醒词-更新多条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); WakeupWord fixWord1 = new WakeupWord() .setPinyin("da duan ba") .setWord("打断吧") .setThreshold("0.16"); List<WakeupWord> fixList = new ArrayList<>(); fixList.add(fixWord); fixList.add(fixWord1); DDS.getInstance().getAgent().getWakeupEngine().updateShortcutWakeupWords(fixList); //清空当前设置的打断唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearShortCutWakeupWord(); ``` ### 3.17.5 添加/删除指定的命令唤醒词(快捷唤醒词) 如果您期望添加或者移除一些命令唤醒词而保留之前的命令唤醒词(**即控制台上的快捷唤醒词**),可以调用下面的接口。 ***add remove*** ```java // 实时添加命令唤醒词-添加一条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord() .setPinyin("xia yi shou") .setWord("下一首") .setThreshold("0.18") .addGreeting("我在") .setAction("sys.next"); DDS.getInstance().getAgent().getWakeupEngine().addCommandWakeupWord(commandWord); // 实时添加命令唤醒词-添加多条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord() .setPinyin("xia yi shou") .setWord("下一首") .setThreshold("0.18") .addGreeting("我在") .setAction("sys.next"); WakeupWord commandWord1 = new WakeupWord() .setPinyin("shang yi shou") .setWord("上一首") .addGreeting("我在") .setThreshold("0.20") .setAction("sys.play"); List<WakeupWord> commandList = new ArrayList<>(); commandList.add(commandWord); commandList.add(commandWord1); DDS.getInstance().getAgent().getWakeupEngine().addCommandWakeupWords(commandList); // 实时移除命令唤醒词-移除一条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord().setWord("下一首"); DDS.getInstance().getAgent().getWakeupEngine().removeCommandWakeupWord(commandWord); // 实时移除命令唤醒词-移除多条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord().setWord("下一首"); WakeupWord commandWord1 = new WakeupWord().setWord("上一首"); List<WakeupWord> commandList = new ArrayList<>(); commandList.add(commandWord); commandList.add(commandWord1); DDS.getInstance().getAgent().getWakeupEngine().removeCommandWakeupWords(commandList); /** * 设置快捷命令唤醒词可唤醒,默认开启 * (在已经调用enableWakeup的情况下临时使用, disableWakeup情况下无效) */ DDS.getInstance().getAgent().getWakeupEngine().enableCommandWakeupWord() /** * 设置快捷命令唤醒词不可唤醒 */ DDS.getInstance().getAgent().getWakeupEngine().disableCommandWakeupWord() ``` ### 3.17.6 添加/删除指定的打断唤醒词 如果您期望添加或者移除一些打断唤醒词而保留之前的打断唤醒词,可以在需要的时候调用下面的接口。 ***add remove*** ```java //实时添加打断唤醒词-添加一条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); DDS.getInstance().getAgent().getWakeupEngine().addShortcutWakeupWord(fixWord); //实时添加打断唤醒词-添加多条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); WakeupWord fixWord1 = new WakeupWord() .setPinyin("da duan ba") .setWord("打断吧") .setThreshold("0.16"); List<WakeupWord> fixList = new ArrayList<>(); fixList.add(fixWord); fixList.add(fixWord1); DDS.getInstance().getAgent().getWakeupEngine().addShortcutWakeupWords(fixList); //实时移除打断唤醒词-移除一条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord().setWord("打断"); DDS.getInstance().getAgent().getWakeupEngine().removeShortcutWakeupWord(fixWord); //实时移除打断唤醒词-移除多条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord().setWord("打断"); WakeupWord fixWord1 = new WakeupWord().setWord("打断吧"); List<WakeupWord> fixList = new ArrayList<>(); fixList.add(fixWord); fixList.add(fixWord1); DDS.getInstance().getAgent().getWakeupEngine().removeShortcutWakeupWords(fixList); ``` ### 3.17.7 添加/删除指定的主唤醒词 如果您想通过代码来控制主唤醒词,可以调用下面的接口。 注意:通过此接口添加的主唤醒词会屏蔽控制台添加的主唤醒词。 ***主唤醒词*** ```java //实时添加主唤醒词-添加一条主唤醒词(1.1.2.1版本及以上支持) WakeupWord mainWord = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小迟") .addGreeting("我在") .setThreshold("0.15"); DDS.getInstance().getAgent().getWakeupEngine().addMainWakeupWord(mainWord); //实时添加主唤醒词-添加多条主唤醒词(1.1.2.1版本及以上支持) WakeupWord mainWord = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小迟") .addGreeting("我在") .setThreshold("0.15"); WakeupWord mainWord2 = new WakeupWord() .setPinyin("ni hao xiao le") .setWord("你好小乐") .addGreeting("我在") .setThreshold("0.15"); List<WakeupWord> mainWordList = new ArrayList<>(); mainWordList.add(mainWord); mainWordList.add(mainWord2); DDS.getInstance().getAgent().getWakeupEngine().addMainWakeupWords(mainWordList); //实时移除主唤醒词-移除一条主唤醒词(1.1.2.1版本及以上支持) WakeupWord mainWord = new WakeupWord().setWord("你好小迟"); DDS.getInstance().getAgent().getWakeupEngine().removeMainWakeupWord(mainWord); //实时移除主唤醒词-移除多条主唤醒词(1.1.2.1版本及以上支持) WakeupWord mainWord = new WakeupWord().setWord("你好小迟"); WakeupWord mainWord2 = new WakeupWord().setWord("你好小乐"); List<WakeupWord> mainWordList = new ArrayList<>(); mainWordList.add(mainWord); mainWordList.add(mainWord2); DDS.getInstance().getAgent().getWakeupEngine().removeMainWakeupWords(mainWordList); //更新多条主唤醒词的接口,覆盖原有的主唤醒词 WakeupWord mainWord = new WakeupWord() .setPinyin("ni hao xiao le") .setWord("你好小乐") .setThreshold("0.15") .addGreeting("我在"); WakeupWord mainWord2 = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小迟") .setThreshold("0.15") .addGreeting("我在"); List<WakeupWord> mainWordList = new ArrayList<>(); mainWordList.add(mainWord); mainWordList.add(mainWord2); DDS.getInstance().getAgent().getWakeupEngine().updateMainWakeupWords(mainWordList); ``` ### 3.17.8 添加/删除指定的快速唤醒词 如果您想通过代码来控制快速唤醒,可以调用下面的接口。 QuickStart词为类似“导航去”、“我想听”等,此类唤醒词只在oneshot模式下生效,作用为在未唤醒状态下语音输入“导航去天安门”,可直接进入对话流程。 *quickStart* ```java //添加多条QuickStart词 WakeupWord quickStartWord = new WakeupWord() .setPinyin("dao hang qu") .setWord("导航去") .setThreshold("0.15"); List<WakeupWord> quickStartWordList = new ArrayList<>(); quickStartWordList.add(quickStartWord); DDS.getInstance().getAgent().getWakeupEngine().addQuickStartWords(quickStartWordList); //实时移除一条QuickStart词 WakeupWord quickStartWord = new WakeupWord().setWord("导航去"); DDS.getInstance().getAgent().getWakeupEngine().removeQuickStartWord(quickStartWord); //实时移除多条QuickStart词 WakeupWord quickStartWord = new WakeupWord().setWord("导航去"); List<WakeupWord> quickStartWordList = new ArrayList<>(); quickStartWordList.add(quickStartWord); DDS.getInstance().getAgent().getWakeupEngine().removeQuickStartWords(quickStartWordList); //更新多条QuickStart词的接口,覆盖原有的QuickStart词 WakeupWord quickStartWord = new WakeupWord() .setPinyin("dao hang qu") .setWord("导航去") .setThreshold("0.15"); List<WakeupWord> quickStartWordList = new ArrayList<>(); quickStartWordList.add(quickStartWord); DDS.getInstance().getAgent().getWakeupEngine().updateQuickStartWords(quickStartWordList); //清空QuickStart词 DDS.getInstance().getAgent().getWakeupEngine().clearQuickStartWords(); ``` ### 3.17.9 oneshot开关 如果您想动态更改唤醒模式,可以在需要的时候调用下面的接口。 ***oneshot*** ```java //开启oneshot DDS.getInstance().getAgent().getWakeupEngine().enableOneShot(); //关闭oneshot DDS.getInstance().getAgent().getWakeupEngine().disableOneShot(); /** * 获取当前oneshot开关状态,如果有本地接口调用开关,以本地接口调用优先。 */ DDS.getInstance().getAgent().getWakeupEngine().getOneshotState() ``` ### 3.17.10 设置环麦增强角度 // 如果您想动态指定环麦增强角度,可以在需要的时候调用下面的接口。 ```java DDS.getInstance().getAgent().getWakeupEngine().setWakeupDoa(int doa); ``` ### 3.17.11 设置车载双麦模式 // 如果您想动态指定车载双麦的驾驶模式,可以在需要的时候调用下面的接口。 ```java // 驾驶模式, 0:定位模式, 1:主驾模式, 2: 副驾模式 3:全车模式 DDS.getInstance().getAgent().getWakeupEngine().setWakeupMode(int mode); ``` ### 3.17.12 ~~获取当前唤醒内核的版本号~~ ~~如果您想获取当前唤醒内核的版本号,可以在需要的时候调用下面的接口。~~ ```java // 该接口废弃,获取到的版本号在 2.0.0.0 版本上无效 DDS.getInstance().getAgent().getWakeupEngine().getWakeupVersion(); ``` ### 3.17.13 设置是否允许识别过程中响应唤醒 如果您想设置是否允许识别过程中响应唤醒,可以在需要的时候调用下面的接口。 ```java //enable: true. 允许识别过程中响应唤醒, false. 不允许识别过程中响应唤醒 DDS.getInstance().getAgent().getWakeupEngine().enableWakeupWhenAsr(boolean enable); ``` ### 3.17.14 设置自定义欢迎语回调 如果您想设置自定义欢迎语,可以在需要的时候调用下面的接口, 该方法是阻塞方法,不建议在该方法内部做耗时操作。 ```java DDS.getInstance().getAgent().getWakeupEngine().setWakeupCallback(WakeupCallback wakeupCallback);DDS.getInstance().getAgent().getWakeupEngine().setWakeupCallback(new WakeupCallback() { /** * * @param wakeupResult 唤醒状态 * <p> * wakeupResult.optString("word");// 唤醒词 * <p> * wakeupResult.optString("type");// 唤醒状态: major: 正常唤醒 command: 命令唤醒 interrupt: 打断唤醒 * <p> * wakeupResult.optString("gender");// 性别: male / female * <p> * wakeupResult.optString("action");// 命令唤醒的动作 * <p> * @return */ @Override public JSONObject onWakeup(JSONObject wakeupResult) { JSONObject result = new JSONObject(); try { result.put("greeting", "你好"); } catch (JSONException e) { e.printStackTrace(); } return result; }); ``` ### 3.17.15 获取主唤醒词列表 获取主唤醒词列表,默认返回WakeupEngine所设置的主唤醒词,如果没有设置过则返回产品配置的主唤醒词。 ```java DDS.getInstance().getAgent().getWakeupEngine().getMainWakeupWords(); ``` ### 3.17.16 设置Beamforming监听器 设置Beamforming监听器, 实时回调Beamforming音频 ```java DDS.getInstance().getAgent().getWakeupEngine().setBfListener(BfListener listener); ``` ### 3.17.17 设置唤醒模块的模式/低功耗模式 * 设置唤醒模块的模式(信号处理模块不变,环麦中暂不支持) ```java DDS.getInstance().getAgent().getWakeupEngine().setWakeupSwitch(boolean mode); ``` * ~~开启低功耗模式后, 唤醒功能不可用, cpu会降低~~ (此方法已废弃) ```java DDS.getInstance().getAgent().getWakeupEngine().setLpSwitch(boolean lpSwitch); ``` ### 3.17.18 就近唤醒 如果你有使用就近唤醒的需求, 可以参照下面的配置与设置(目前支持线性双麦/线性四麦的就近唤醒, 1.2.12.1-1.2.15.1的版本暂不支持就近唤醒) ```java 1. 设置major参数为true (从1.2.8.1版本开始, 不再需要此配置) config.addConfig("MAJOR", "true"); 2. 设置K_USE_NEAR_WAKEUP为true, 表示使用就近唤醒 config.addConfig(DDSConfig.K_USE_NEAR_WAKEUP, "true"); 3. 在设置唤醒词的时候设置major字段 WakeupWord minorWord = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小驰") .setThreshold("0.25") .setMajor("1")(从1.2.8.1版本开始, 不再需要此配置) .addGreeting("小驰回来了"); DDS.getInstance().getAgent().getWakeupEngine().addMainWakeupWord(minorWord); 4. 就近唤醒运行过程中可以动态设置一些参数 /** * 设置就近唤醒参数与调度参数<p> * 仅在就近唤醒状态下生效(线双麦/线四麦) */ DDS.getInstance().getAgent().getWakeupEngine().setNearWakeupParams(JSONObject paramsObj) ``` ### 3.17.19 拦截唤醒结果 ```java /** * 拦截唤醒结果 wakeup.result, * * @param isCatch 是否拦截唤醒结果 true-拦截 ;false-取消拦截 * @return * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getWakeupEngine().catchWakeupResult(boolean isCatch) ``` ### 3.17.20 设置 VOIP 音频回调 1. 配置 VOIP 信息 ```java config.addConfig(DDSConfig.K_VOIP_TYPE, "line_2"); ``` 2. 设置 VOIP 回调 ```java /** * 设置voip监听器, 时时回调voip音频 * * @param listener */ public void setVoipListener(VoipListener listener) ``` 3. 启动 VOIP 功能 ```java // 调用 setEnableVoip(true) 时 voipNode 才会注册消息,接收音频数据 DDS.getInstance().getAgent().setEnableVoip(true); ``` 4. 录音后就会从回调里回调 voip 音频 5. 取消 VOIP 回调 ```java // 取消 VOIP 回调 DDS.getInstance().getAgent().getWakeupEngine().setVoipListener(null); // 停用 voip 功能 DDS.getInstance().getAgent().setEnableVoip(false); ``` ### 3.17.21 更新唤醒词 支持多个不同类型的唤醒词同时配置 ```java List<WakeupWordIntent> wakeupWordIntentList = new ArrayList<>(); // 添加主唤醒词 WakeupWordIntent wakeupWordIntent0 = new WakeupWordIntent(WakeupType.ADD_MAIN) .setPinyin("ni hao xiao le") .setWord("你好小乐") .setThreshold("0.16") .addGreeting("小乐来了"); WakeupWordIntent wakeupWordIntent1 = new WakeupWordIntent(WakeupType.ADD_COMMAND) .setPinyin("kai shi bo fang") .setWord("开始播放") .setThreshold("0.18") .addGreeting("好的,开始播放") .setAction("sys.play"); WakeupWordIntent wakeupWordIntent2 = new WakeupWordIntent(WakeupType.ADD_SHORTCUT) .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); wakeupWordIntentList.add(wakeupWordIntent0); wakeupWordIntentList.add(wakeupWordIntent1); wakeupWordIntentList.add(wakeupWordIntent2); DDS.getInstance().getAgent().getWakeupEngine().updateWakeupWords(wakeupWordIntentList); ``` ### 3.17.22 动态设置对话模式 ```java /** * 动态设置对话模式<br> * 唤醒后是否进入对话<br> * <p> * 取值:dialog or partner<br> * dialog:唤醒默认进入识别及对话<br> * partner:唤醒后不会主动进入识别及对话,同时唤醒后会收到sys.wakeup.result的message消息<br> * 是否必需:否<br> * 默认值:dialog<br> * * @param type 对话模式 partner */ DDS.getInstance().getAgent().getWakeupEngine().updateWkupRouter(String type) ``` ### 3.17.23 增强唤醒(可见即可说和全局免唤醒) 增强唤醒使用本地识别功能识别设置的唤醒词,当达到阈值(默认0.63)时即认为唤醒成功. ##### 功能特性 • 支持两个字的唤醒词 • 支持同一个唤醒词写入很多类型说法(目前最大可支持500个,延时200ms) • 支持中英文唤醒(打开wifi、打开音乐); 中英文功能从 WTK v1.9.39 开始支持 • 唤醒词设置为打开导航,可支持说“哈哈打开导航”不响应(极大减小误唤醒) ##### 使用场景 • 解决相似说法串到唤醒,比如定义“打开导航”,说“开导航”也能唤醒 • 解决语音起名字及手动设置唤醒词设置为“思必驰”“小小思”等容易误唤醒问题 • 解决开发者定义唤醒词每次都需要思必驰提供阈值,开发者只需要输入中文即可 • 解决在导航列表tts播报时说分词容易误选择问题,比如导航列表有“天安门”“北京天安门”“天安门地铁站“,以前用唤醒说”天安门地铁站“会响应”天安门“ ##### 使用方法 1. 初始化 - 打开增强唤醒功能 ```java config.addConfig(DDSConfig.K_SUPPORT_INCREMENT_WAKEUP, "true"); ``` - 开启可见即可说 ```java JSONObject ctrlObj = new JSONObject(); ctrlObj.put("type", "hotWord"); DDS.getInstance().getAgent().getWakeupEngine().enableIncrementWakeup(ctrlObj); ``` - 开启全局免唤醒 ```java JSONObject ctrlObj = new JSONObject(); ctrlObj.put("type", "globalWord"); DDS.getInstance().getAgent().getWakeupEngine().enableIncrementWakeup(ctrlObj); ``` - 开启可见即可说 + 全局免唤醒 DDS.getInstance().getAgent().getWakeupEngine().enableIncrementWakeup(); 2. 优先级设置 - 如果可见即可说和全局免唤醒词有相同的词,可见即可说优先 ```java DDS.getInstance().getAgent().getWakeupEngine().setIncrementPriority(WakeupPriorityType.HOTWORD_FIRST); ``` - 如果可见即可说和全局免唤醒词有相同的词,全局免唤醒优先 ```java DDS.getInstance().getAgent().getWakeupEngine().setIncrementPriority(WakeupPriorityType.GLOBALWORD_FIRST); ``` 3. 使用全局免唤醒词 ```java 可使用增强唤醒的唤醒词类型: * 命令唤醒词(Command) * 打断唤醒词(ShortCut) WakeupWord commandWord = new WakeupWord() .setWord("开始播放") .setThreshold("0.18") .setIncrementWord("true") .addGreeting("好的,开始播放") .setAction("sys.play"); WakeupWord commandWord1 = new WakeupWord() .setWord("下一首") .setThreshold("0.18") .setIncrementWord("true") .addGreeting("下一首nlg") .setAction("command.next"); List<WakeupWord> wakeupWordList = new ArrayList<>(); wakeupWordList.add(commandWord); wakeupWordList.add(commandWord1); DDS.getInstance().getAgent().getWakeupEngine().addCommandWakeupWords(wakeupWordList); WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setIncrementWord("true") // 使用增强唤醒 .setThreshold("0.18"); List<WakeupWord> wakeupWordList = new ArrayList<>(); wakeupWordList.add(fixWord); DDS.getInstance().getAgent().getWakeupEngine().updateShortcutWakeupWords(wakeupWordList); // 删除增强唤醒的打断唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearIncrementShortCutWakeupWord(); // 删除增强唤醒的命令唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearIncrementCommandWakeupWord(); // 删除增强唤醒的所有唤醒词,包括打断唤醒词和命令唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearIncrementWakeupWord(); ``` 4. 使用可见即可说 ```java HotWords.HotWord hotWord1 = new HotWords.HotWord() .setOrigin("抖音最全中文歌")//页面原始词汇 .addSegment("抖音最全")//分词内容 .addCustom("id", "123123")//上层自定义扩展字段 .addCustom("name", "music");//上层自定义扩展字段 HotWords.HotWord hotWord2 = new HotWords.HotWord() .setOrigin("开始播放") .addCustom("id", "123123"); HotWords.Slots slots = new HotWords.Slots() .setSlot("dynamic_slot") .addHotWord(hotWord1) .addHotWord(hotWord2); HotWords hotWords = new HotWords() .setScene("default_scene") .addSlots(slots); DDS.getInstance().getAgent().getWakeupEngine().updateHotWords(hotWords); ``` ## 3.18 数据上传接口 开发者调用此接口来上传一些设备的信息,这些信息可以在本地使用,也可以在对话中获取,作为对话决策的一部分。 ***数据点*** ```java /** * 更新设备状态,产品级的配置。比如:定位信息,设备硬件状态等 * 更新结果可以通过sys.upload.result消息来获取 * * @param intent 请求的ContextIntent对象,包括key和value * 如:ContextIntent intent = new ContextIntent("status","{\"platform\":\"Android\"}"); * updateProductContext(intent); * 技能里通过$context.system.settings.status.platform$即可获取到"Android" * @return 请求的ID,用于追踪sys.upload.result * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().updateProductContext(ContextIntent intent); /** * 更新技能配置,需要调用ContextIntent.setSkillId设置技能ID * 更新结果可以通过sys.upload.result消息来获取 * * @param intent 请求的ContextIntent对象,包括key、value、skillId。 * 如:ContextIntent intent = new ContextIntent("status","{\"platform\":\"Android\"}"); * intent.setSkillId("xxxxxxxxxxxxxxxxx"); * updateSkillContext(intent); * 该技能里通过$context.skill.settings.status.platform$即可获取到"Android",其他技能则无法使用。 * @return 请求的ID,用于追踪sys.upload.result * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().updateSkillContext(ContextIntent intent); /** * 获取产品的配置信息 * * @param inputKey 配置项名称 * @return 配置项值 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getProductContext(String inputKey); /** * 获取技能的配置信息 * * @param skillId 技能ID * @param inputKey 配置项名称 * @return 配置项值 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getSkillContext(String skillId, String inputKey); /** * 更新热词识别接口(请求级别) * 每次请求都会带上热词词库, 例如: [{"type": "vocab", "name": "sys.联系人", "data":["章大樵"]}] * 本接口为覆盖式接口,以最新设置为准,如需要清除请求空参数即可 * * @param phraseHints 需要更新的热词识别列表 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().updatePhraseHints(PhraseHintsIntent... phraseHints); ``` ## 3.19 获取性别/年龄信息 1. 在初始化DDS SDK的时候加入如下配置: ```java config.addConfig(DDSConfig.K_USE_GENDER, "true"); config.addConfig(DDSConfig.K_USE_AGE, "true"); ``` 2. 然后订阅 message: sys.dialog.start 收到消息内容类似:```{"reason":"wakeup.major","gender":"male","age":"adult"}``` gender就是性别字段,age就是年龄字段,性别年龄信息只有在主唤醒词唤醒后,才会发出。 ## 3.20 动态更新云端识别模型 ```java /** * 更新云端识别的模型名字, * 在调用完该接口后,下一次对话开始时生效,并一直用该模型,除非客户端再调用该接口设置为其他的模型 * * @param asrModel 云端识别的模型名字,有aihome, airobot等, * 默认为dui控制台配置的模型资源, * 如果填null,则表示清除之前本地配置的模型名,之后会使用dui控制台配置的模型资源 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getASREngine().updateAsrModel("aihome"); /** * 获取当前使用的云端识别的模型 * * @return asrModel 当前使用的云端识别的模型, 如果失败会返回null,或者抛出DDSNotInitCompleteException * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getASREngine().getAsrModel(); ``` ## 3.21 快捷指令 订阅 sys.dm.inspire 消息,用于接收快捷指令。只在 **半双工** 模式有该功能 ```java DDS.getInstance().getAgent().subscribe(new String[]{..., "sys.dm.inspire", ...}, new MessageObserver() { @Override public void onMessage(String message, String data) { if("sys.dm.inspire".equals(message)) { try { JSONObject obj = new JSONObject(data); JSONArray inspire = obj.getJSONArray("inspire"); ... } catch (JSONException e) { e.printStackTrace(); } catch (DDSNotInitCompleteException e) { e.printStackTrace(); } } } }); ``` inspire 即为快捷指令的集合数组,通过解析 inspire 来处理指令。inspire 数据结构如下: ```json inspire:[ { speak:{ "type":"text", "text":"123" }, widget:{ "type":"text", ... } }, { speak:{ "type":"text", "text":"抱歉由于设备离线,操作失败,请再试一次" }, widget:{ "type":"list", ... } }, ... ] ``` 上述结构中,speak 并不是必须的字段, widget 是必须的。 widget即为需要需要展示出来的控件样式,其中 type 字段代表控件样式的类型,有 text/list/web/image/media... 关于 widget 不同控件样式的不同数据结构,可参考: https://www.duiopen.com/docs/ct_UI 。举例:list 对应这 context.widget.list,以此类推 ## 3.22 全双工模式 ### 3.22.1 模式切换 ```java // 设置dds模式 DDS.getInstance().getAgent().setDuplexMode(Agent.DuplexMode.HALF_DUPLEX);// 半双工,一问一答模式 DDS.getInstance().getAgent().setDuplexMode(Agent.DuplexMode.FULL_DUPLEX);// 全双工模式 注意: 在切换全双工时如需要唤醒还请调用: DDS.getInstance().getAgent().getWakeupEngine().enableWakeup(); // 获取dds模式 DDS.getInstance().getAgent().getDuplexMode(); ``` ### 3.22.2 全双工不支持接口与功能 2. 由于全双工模式为全程识别状态, 所以开启识别和 startListening 不可同时使用 [3.13开启/终止识别](#3.13 仅开启识别) 3. 由于全双工模式为全程识别状态, 在模拟半双工下的打断唤醒由识别代替,主唤醒词正常开启下一轮对话 4. 由于全双工模式下暂时没有快捷指令的使用场景, 所以快捷指令在全双工模式下不可用 [3.21 快捷指令](#3.21 快捷指令) 5. 全双工模式下默认使用 vad 功能, 比如 oneshot 功能就需要打开 vad。配置 K_USE_VAD_IN_FULLDUPLEX 为 true(默认为true) ### 3.22.3 全双工下跳过Vad超时检测 ```java // 在全双工模式下, 如果你想跳过Vad的检测直接进入识别状态则可以调用下面的接口 DDS.getInstance().getAgent().getASREngine().killVadTimeoutInFullDuplex(); ``` ## 3.23 对话结果支持内容修改 此接口支持修改对话中的语音播报,修改文本展示等功能 ```java // 从1.2.8.1版本开始支持 DDS.getInstance().getAgent().setDMTaskCallback(new DMTaskCallback() { @Override public JSONObject onDMTaskResult(JSONObject dmTaskResult, Type type) { if (type == DMTaskCallback.Type.DM_OUTPUT) {// 处理dm的消息 // 显示 String display = dmTaskResult.optString("display"); // 播报 String nlg = dmTaskResult.optString("nlg"); Log.d(TAG, "display = " + display); Log.d(TAG, "nlg = " + nlg); try { dmTaskResult.put("display", "修改之后的显示"); dmTaskResult.put("nlg", "修改之后的播报"); dmTaskResult.put("speakUrl", null); // 不设置为null的话,播放的还是原来的音频 } catch (JSONException e) { e.printStackTrace(); } } else if (type == DMTaskCallback.Type.CDM_ERROR) {// // 处理对话异常的消息 // 播报 String nlg = dmTaskResult.optString("nlg"); Log.d(TAG, "nlg = " + nlg); try { dmTaskResult.put("nlg", "修改之后的播报"); dmTaskResult.put("speakUrl", null); // 不设置为null的话,播放的还是原来的音频 } catch (JSONException e) { e.printStackTrace(); } } return dmTaskResult; } }); ``` ## 3.24 声纹 声纹注册、验证的词也是主唤醒词,唤醒后会使用唤醒音频做声纹的注册、验证等操作 1. 打开声纹配置 ```java config.addConfig(DDSConfig.K_VPRINT_ENABLE, "true"); // 是否开启声纹功能 DDS.getInstance().getAgent().getWakeupEngine().useVprintInWakeUp(true) // 动态设置是否在唤醒中启用声纹 ``` 2. 使用声纹 ```java // 注册声纹监听器 VprintEngine.getInstance().setVprintListener(new VprintListener() { @Override public void onState(String state) { Log.e(TAG, "onState = " + state); } @Override public void onResults(String result) { Log.e(TAG, "onResults = " + result); } @Override public void onError(String error) { Log.e(TAG, "onError = " + error); } }); // 注册声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.REGISTER) .setTrainNum(3) .setOutChannelNum(2) .setSnrThresh(8.67f) .setUserId("userId") .setVprintWord("ni hao xiao chi") .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 更新声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.UPDATE) .setTrainNum(3) .setOutChannelNum(2) .setSnrThresh(8.67f) .setUserId("userId") .setVprintWord("ni hao xiao chi") .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 追加声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.APPEND) .setTrainNum(3) .setOutChannelNum(2) .setSnrThresh(8.67f) .setUserId("userId") .setVprintWord("ni hao xiao chi") .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 删除一条声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.UNREGISTER) .setTrainNum(3) .setOutChannelNum(2) .setUserId("userId") .setVprintWord("ni hao xiao chi") .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 删除所有声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.UNREGISTER_ALL) .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 使用声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.TEST) .setOutChannelNum(2) .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 获取声纹模型 String result = VprintEngine.getInstance().getMode(); // 停止使用声纹 VprintEngine.getInstance().stop(); ``` ## 3.25 配合 dca app 使用 通过语音控制家居设备时需要对话带上 dca app 里用户的信息。 ```java TokenListener tokenListener = new TokenListener() { @Override public void onSuccess(TokenResult tokenResult) { Log.d(TAG, "onSuccess() " + tokenResult); success = true; } @Override public void onError(int errorId, String error) { Log.d(TAG, "errorId:" + errorId + " error:" + error); } }; AuthInfo authInfo = new AuthInfo(); authInfo.setUserId("8458"); authInfo.setCodeVerifier("doeifkgdgurgg"); authInfo.setClientId("dfgoeigjergndfgdfg"); authInfo.setAuthCode("dfgerhrh6564hgrh4hr"); authInfo.setRedirectUri("http://dui.aadf.bb.cc"); DDS.getInstance().getAgent().setAuthCode(authInfo, tokenListener); ``` ## 3.26 自定义提示语 ```java JSONObject jsonObject = new JSONObject(); try { jsonObject.put("071304", "自定义vad超时"); } catch (JSONException e) { e.printStackTrace(); } /** * 此接口支持动态修改 custom tips * * @param obj <p> * 取值:JSON字符串,如:<br> * { * "71304":"这是识别结果为空的自定义播报", * "71305":"这是语义结果为空的自定义播报", * "71308":"这是进入闲聊技能的自定义播报", * "713**":"*****" * }<br> * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().setCustomTips(jsonObject); ``` ## 3.27 一句话多意图 背景: 一句话多意图: 一句话里包括多个意图或动作,比如:调节空调和车窗 使用场景:智能家居或车联网等场景 此功能需要云端配置,如需使用请和思必驰客户经理联系 ```java String[] mTopic = new String[]{ "sys.native.inspire" }; NativeCommandObserver nativeCommandObserver = new NativeCommandObserver() { @Override public JSONObject onNativeCommandCall(String command, String data) { try { if (TextUtils.equals(command, "sys.native.inspire")) { AILog.d(TAG, "command = " + command); AILog.d(TAG, "data = " + data); JSONObject dataObj = new JSONObject(data); JSONArray inspireArr = dataObj.optJSONArray("inspire"); JSONObject selectInspireItem = null;// 用户选择要dds执行的一个item for (int i = 0; i < inspireArr.length(); i++) { JSONObject itemObj = inspireArr.optJSONObject(i); // TODO 业务逻辑,判断哪一个item是要后续dds处理的 selectInspireItem = itemObj; JSONObject commandObj = itemObj.optJSONObject("command"); String commandApi = commandObj.optString("api"); // TODO 业务逻辑,解析command,处理command JSONObject speakObj = itemObj.optJSONObject("speak"); String text = speakObj.optString("text"); // 模拟播报与output DDS.getInstance().getAgent().getTTSEngine().speak(text, 0); DDS.getInstance().getAgent().getBusClient().publish("context.output.text", new JSONObject().put("text", text).toString()); SystemClock.sleep(2000); } // 返回最终要播报的文本 return JSONObjectUtil.create() .put("nlg", "已全部打开") .put("selectInspireItem", selectInspireItem)// 回传了选择的item,则修改nlg播报无效 .build(); } } catch (Exception e) { e.printStackTrace(); } return null; } }; DDS.getInstance().getAgent().subscribeSync(mTopic, nativeCommandObserver); ``` # 四、DDSConfig 配置项 DDSConfigBuilder 构造方法需要设置 **产品信息** 和 **duicore 资源** ,最简单的demo无需设置其它即可以单麦方式运行。 ```java DDSConfigBuilder ddsConfigBuilder = new DDSConfigBuilder( productId, aliasKey, apiKey, productKey, productSecret, duicoreZip ) ``` **ddsConfigBuilder 包含很多配置,现按功能分为以下子类:** 1. ddsConfigBuilder.createCommonBuilder() [4.1 设备信息、使用目录、VOIP功能 等通用配置](#4.1 设备信息、使用目录、VOIP功能 等通用配置) 2. ddsConfigBuilder.createNodeBuilder() [4.2 sdk 功能开关](#4.2 sdk 功能开关) 3. ddsConfigBuilder.createWakeupBuilder() [4.3 麦克风类型、唤醒、声纹、asrpp、Oneshot 配置](#4.3 麦克风类型、唤醒、声纹、asrpp、Oneshot 配置) 4. ddsConfigBuilder.createDmBuilder() [4.4 对话配置](#4.4 对话配置) 5. ddsConfigBuilder.createTtsBuilder() [4.5 TTS、播放 配置](#4.5 TTS、播放 配置) 6. ddsConfigBuilder.createAsrBuilder() [4.6 识别配置](#4.6 识别配置) 7. ddsConfigBuilder.createRecorderBuilder() [4.7 录音配置](#4.7 录音配置) * ddsConfigBuilder 还可通过以下方法添加额外配置 ```java ddsConfigBuilder.addConfig(String key, Object value) ``` 下面简单介绍一下配置项,详细说明请看 DDSConfig 配置请参见 [javadoc在线文档](https://www.duiopen.com/duilite-doc/dds) * 定制资源集成差异 v2.0.0.1 及以上版本不仅支持系统目录,也支持放入 assets 目录。v1.X.X.X 版本只支持 sdcard 系统目录配置 使用方式如下: 1. assets 目录配置方式: setWakeupBin("wakeup.bin") 2. 系统目录配置方式: setWakeupBin("/sdcard/sd/wakeup.bin") ## 4.1 设备信息、使用目录、VOIP功能 等通用配置 ```java ddsConfigBuilder.createCommonBuilder() .setCustomZip("product.zip") // 使用内置产品资源包, 从官网下载的产品资源包一般叫 product.zip .setDeviceName("XXXXXX") // 设备 DeviceName, 用来标识设备的标识符 .setDeviceId("XXXXXX") // 设备ID。用于授权认证的原始设备ID,请务必确保设备ID唯一 .setAccessToken("XXXXXX") // 保留字段,上传、升级时使用 .setCachePath("/sdcard/cache") // 缓存文件保存目录,默认 /sdcard/Android/data/包名/cache .setCacheSize(500000) // 缓存文件大小限制。自定义缓存目录时必需 .setCustomHome("/sdcard/aispeech") // 产品资源存放的目录,默认 /data/data/com.xxx/files/dds/custom/ .setDuicoreHome("/sdcard/aispeech") // duicore 资源存放的目录,默认 /data/data/包名/files/dds/duicore/ .setInitTimeout(60) // SDK初始化超时时间,单位秒。默认 60秒 .setCustomSha256("XXXXXX") // 自定义授权证书的SHA256, sdk 授权时会检查包名和签名的SHA256,第三方加固后可能改变 .setCustomBackupZipUserDefined("/sdcard/aispeech") // 产品资源包保存路径 .setDuicoreBackupZipUserDefined("/sdcard/aispeech") // duicore 资源包保存路径 .setUserId("XXXXXX") // 用户ID 保留字段 .setVoipType("line_4") // 使用 voip 功能时的类型,现在支持线性双麦(line_2)和线性4麦(line_4) .setVoipResBin("voip.bin") // voip 的资源的路径。assets目录下为压缩包名称,其他目录为绝对路径 .setOfflineProfileName(".profile") // assets目录下的离线授权文件名 .enableUpdateDuicore(false) // 是否更新 duicore 资源。 默认 false .enableDynamicUiAgentPort(false) // 是否使用 uiagent 动态端口号,默认 false .enableReplaceProfile(true) // 试用授权是否连网更新成在线授权 默认 true .setProductLicenseId("XXXXXX") // 产品licenseID .setProductVersion("2") // 连接指定产品版本的对话服务 .enableUpdateNotification(true); // 是否使用内置的资源更新通知栏 默认 true ``` ## 4.2 sdk 功能开关 sdk 需要使用到的功能开关配置。 包括推送、大数据、socket节点、唤醒、声纹、VAD节点、H5节点 等 ```java ddsConfigBuilder.createNodeBuilder() .enablePush(true) // 是否开启推送功能。默认 true .enableSocket(true) // 是否开启 socket 节点功能。默认 true .enableUpdate(true) // 是否开启数据更新功能。默认 true .enableUpload(true) // 是否开启大数据上传功能。默认 true .enableVad(true) // 是否开启 VAD 节点功能。默认 true .enableWakeup(true) // 是否开启唤醒节点功能。默认 true .enableIncrementWakeup(false) // 是否支持本地识别增量更新。默认 false .enableVprint(false) // 是否开启声纹功能。默认 false .enableH5(false); // 是否开启H5节点功能。默认 false ``` ## 4.3 麦克风类型、唤醒、声纹、asrpp、Oneshot 配置 ```java ddsConfigBuilder.createWakeupBuilder() .setMicType(0) // 硬件麦克风采集模组的类型。即 单麦、双麦、线性4麦、环形6麦等。 详情请看:4.3.1 MicType 说明 .setWakeupBin("wakeup.bin") // 设置唤醒资源 .setBeamformingBin("ULA_2.bin") // 麦克风阵列 beamforming 资源 .setAecBin("aec.bin") // 麦克风阵列AEC资源 .setIncrementWakeupBin("increment.bin") // 增强唤醒的 asr 资源,增强唤醒是使用 本地asr 实现的 .setVprintBin("vprint.bin") // 声纹资源 .enableVprintInWakeup(false) // 是否在唤醒中使用声纹判断,默认 false。诺设置为 true,需开启声纹功能 .enableSspe(false) // 多麦是否使用 sspe,默认 false .setSspeBin("sspe.bin") // 麦克风阵列 sspe 资源 .enableAge(false) // 是否使用年龄识别,默认 false .setAgeBin("age.bin") // 年龄识别资源 .enableGender(false) // 是否使用性别识别,默认 false .setGenderBin("gender.bin") // 性别识别资源 .enableNearWakeup(false) // 是否使用就近唤醒,默认 false .setNearWakeupDebounceWnd(450) // 就近唤醒防抖窗口期,单位毫秒 .setNearWakeupWkpWnd(300) // 就近唤醒决策窗口期,单位毫秒 .setNearWakeupMdsBin("mds.bin") // 就近唤醒的 mds 资源 .setOneshotMidTime(500) // 详情请看:4.3.2 OneShot配置 .setOneshotEndTime(0) // 详情请看:4.3.2 OneShot配置 .enableCustomGreeting(true) // 是否使用自定义欢迎语功能,默认 true .enableWakeupInAsr(true) // 识别过程中是否响应唤醒,默认 true .setAecMode("internal") // AEC(回声消除)模式,设置 external 后,单麦播放 tts 时唤醒节点不会停止,打断唤醒词仍可使用 .setWakeupRouter("dialog"); // 唤醒后的路由,唤醒后是否进入对话。取值:dialog(唤醒默认进入识别及对话) or partner(唤醒后不会主动进入识别及对话,同时唤醒后会收到sys.wakeup.result的message消息) ``` #### 4.3.1 MicType 说明 setMicType 配置说明 设置硬件麦克风采集模组的类型 默认使用 DUI 控制台上产品的对应配置项。若客户端通过SDK配置此项后,则以客户端配置为准 取值: * 0:单麦(1路音频)**默认** * 1:单麦回消(1路音频+1路参考音) * 2:线性四麦(4路音频) * 3:环形六麦(6路音频) * 4:车载双麦(废弃) * 5:家居双麦(2路音频) * 6:环麦4麦(4路音频) * 7:车载双麦(2路音频) * 8:线性六麦(6路音频) * 10:线性八麦(8路音频) #### 4.3.2 OneShot配置 setOneshotMidTime 和 setOneshotEndTime 配置说明 * 取值:毫秒 * 若MIDTIME为0 & ENDTIME为0,唤醒后进入识别;若VAD检测超时,则直接退出对话 * 若MIDTIME为0 & ENDTIME不为0,唤醒后进入识别;若ENDTIME超时,则直接退出对话 * 若MIDTIME不为0 & ENDTIME为0,唤醒后进入识别;若MIDTIME超时,则播放欢迎语,继续识别(默认) * 若MIDTIME不为0 & ENDTIME不为0,唤醒后进入识别;若MIDTIME超时,则播放欢迎语,继续识别,若ENDTIME超时,则直接退出对话 * 默认值:MIDTIME=500ms,ENDTIME=0ms ## 4.4 对话配置 对话相关配置 * 设置对话模式是半双工还是全双工配置已经删除,请用 ``` Agent.setDuplexMode(final DuplexMode duplexMode)``` 接口设置 ```java ddsConfigBuilder.createDmBuilder() .enableVadInFullduplex(true) // 全双工模式下是否使用 vad,默认 true .enableTipsInFullduplex(true) // 全双工中是否使用消息的提示音,默认 true .enableLocalDDS(false) // 是否使用本地对话,默认 false .setCustomTips(customTips) // 指定对话错误码的 TTS 播报内容 .setClickMode(0) // 点击模式,调用 Agent#avatarClick 后sdk的响应模式。 .setDmTimeout(10000) // 对话响应超时时间。单位 毫秒。默认 10000 ms .setNativeApiTimeout(10000); // nativeApi 超时时间。单位 毫秒。默认 10000 ms ``` ## 4.5 TTS、播放 配置 ```java ddsConfigBuilder.createTtsBuilder() .enableExoPlaySpeakUrl(false) // 是否使用 exoPlayer 播报 tts 的 url,默认 false .enableOptmization(false) // 本地 TTS 是否开启内存优化,默认 false .setAudioFocusMode("internal") // 音频焦点处理模式。internal sdk内部处理(默认),external 使用方式见:3.16.4 音频焦点处理模式 .setTtsMode("internal") // TTS播报模式,internal sdk内部合成播报, external 用户负责合成播报,使用方式见:3.9 外部TTS引擎 .setStreamType(AudioManager.STREAM_MUSIC) // 内置播放器的 STREAM 类型,默认 AudioManager.STREAM_ALARM .setAudioUsage(AudioAttributes.USAGE_ALARM) // 内置播放器的 USAGE 类型,默认 AudioAttributes.USAGE_ALARM .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION); // 内置播放器的 contentType 类型,默认 AudioAttributes.CONTENT_TYPE_SONIFICATION ``` ## 4.6 识别配置 包括云端服务的 asr 配置和本地 asr 配置 ```java ddsConfigBuilder.createAsrBuilder() .enableStreamUrl(true) // 对话接口里是否返回 tts 的 speakUrl,默认 true .setNotDropWakeupInAsr(false) // 识别时不要过滤唤醒词,默认 false .setCloudVadPauseTime(500) // 云端VAD在结束语音时静音时长,默认500ms .enableAudioDetection(false) // 噪声检测配置,开启后可过滤噪声识别结果,默认 false .enableCloudAsrpp(false) // 云端asrpp参数,设置为true后云端识别返回性别/年龄/情绪等字段,默认 false .enableNumberConvert(false) // 中文数字转阿拉伯数字,默认 false .enablePunctuation(false) // 识别结果是否带标点符号,默认 false .enableSignalVad(false) // 是否禁用VAD单字打断功能,默认 false .enableTone(false) // 识别结果的拼音是否带音调,默认 false .setCustomWakeupScore("12.5") // 自定义唤醒词阈值,该分数一般是通过测试集调整。默认值:无 .setAsrPermanentSkill("{\"skillId\":\"123123\"}") // 技能锁定接口,让用户说的话只落在锁定的技能里 .setCustomAsrConf("[{\"name\":\"宝宝睡觉模式\",\"confidence\":\"0.61\"}]")// 设置指定语料的识别置信度,防止某些语料识别置信度过低被抛弃 .enableWakeNetwork(false) // 弱网环境配置,如使用的是弱网环境,则配置为true,默认 false .setAsrRouter("dialog") // 区分识别后是否进入对话。默认 dialog 进入对话,partner 不进入对话,可通过ASREngine相关方法获取识别结果 .setCustomAsrBin("net.bin") // 预置本地识别 net.bin 资源 .setVadBin("vad.bin") // VAD资源 .enableOptGramBuild(false) // 优化离线gram编译逻辑,默认 false .enableTipsBeforeAsr(false) // 开启后识别前都会有叮咚声,默认 false .enableAsrTips(false); // 是否开启识别完成音效,默认 false ``` ## 4.7 录音配置 ```java ddsConfigBuilder.createRecorderBuilder() .setRecorderMode("internal") // 录音模式, internal SDK内部录音, external sdk外部录音,通过 Agent().feedPcm() 方法传入音频 .setAudioSource(MediaRecorder.AudioSource.DEFAULT) // 录音机参数: audioSource 录音机数据源类型 .setAudioSamplerate(16000) // 录音机参数: sampleRateInHz 录音时音频采样率 .setAudioChannelConf(AudioFormat.CHANNEL_IN_MONO) // 录音机参数:channelConfig 录音机频道源类型 .setAudioFormat(AudioFormat.ENCODING_PCM_16BIT) // 录音机参数:audioFormat 每个采样大小 .setAudioBufferSizeInByte(192000) // 录音机参数:bufferSizeInBytes 录音机的缓存大小 .setAudioChannelCount(1) // 音频通道数 单麦是1 .setAudioBufferSize(3200) // 录音机每次读取音频的 buffer 大小, 单麦每 100ms 是 3200B .setReversAudioChannel(false) // 是否翻转单麦 1mic+1ref 音频通道,默认 false .enableHighVolume(false); // 是否使用大音量检测,默认 false ``` # 五、错误码描述 | error id | 错误描述 | 原因及解决办法 | | -------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | 1 | 解压dds.bin失败 | 系统IO错误 | | 2 | 内核执行出错 | 请检查配置是否正确,或者联系DUI客服 | | 3 | 系统启动超时 | 请检查配置是否正确,或者联系DUI客服 | | 4 | 存储空间不足 | 请检查/data/目录剩余大小是否充足 | | 070302 | 解码失败 | 联系DUI客服 | | 070303 | 未发现新的版本 | 略 | | 070304 | 请先升级您的客户端(SDK) | 因为产品选择的dui内核和sdk不匹配,需要升级sdk | | 070305 | 网络异常, 无法连接至服务器 | 请检查网络是否正常或者配置是否正确 | | 070306 | 解码失败 | 联系DUI客服 | | 070307 | 补丁安装失败 | 存储空间不够 | | 070308 | 备份压缩文件不存在 | 用户手动删掉了app私有空间的文件 | | 070309 | 提取压缩文件失败 | 存储空间不够 | | 070310 | 未设置产品ID | 需要设置产品id | | 070311 | 无效客户端版本 | 请检查配置是否正确 | | 070312 | 无效用户ID | 需要设置用户id | | 070313 | 无效设备ID | 设备id不正确 | | 070314 | 资源地址无效 | 请检查配置是否正确 | | 070315 | 无效的产品分支 | 需要设置产品分支 | | 070316 | 无法找到资源 | 服务端资源生成有问题,请尝试重新发布一下,或者请提供产品id反馈给DUI客服,去后台查询根本原因 | | 070317 | 资源校验失败 | 下载资源过程中资源被纂改 | | 070319 | 当前产品中内核资源版本过低,请前往DUI控制台对应的产品中,选择最新的内核资源版本并重新发布产品 | 当前产品中内核资源版本过低,请前往DUI控制台对应的产品中,选择最新的内核资源版本并重新发布产品。 | | 070320 | 存储空间不足 | 系统存储空间不足,请在错误的详细信息中查看需要的存储空间和现有的存储空间 | | 070601 | 设备注册过程中无法连接到授权服务器 | 请检查网络是否畅通,或者productId与api key是否匹配 | | 070602 | 设备注册过程无法颁发有效的profile文件 | 请检查产品ID或与客服联系 | | 070603 | API Key无效 | 请到产品授权配置页面查看api key信息是否正确,请确认签名证书是否匹配,包名是否匹配 | | 070604 | 无效的产品ID | 无效的产品ID,请确认是否未设置产品ID | | 070606 | profile文件被禁用 | 请重新进行注册或者与客服联系 | | 070607 | profile文件过期 | 请重新进行注册或者与客服联系 | | 070609 | 设备注册无法保存有效的profile文件 | 检查文件系统是否异常或者重新进行设备注册 | | 070612 | dns resolve failed: no answers(DNS失败) | 请检查网络是否畅通 | | 070613 | dns resolve failed: exceed retry count(DNS失败) | 请检查网络是否畅通 | | 070614 | dns resolve failed: network is unreachable(DNS失败) | 请检查网络是否畅通 | | 070615 | dns resolve timeout(DNS超时) | 请检查网络是否畅通 | | 070616 | connect ip failed: reasons for uncertainty(建立连接失败) | 请检查网络是否畅通 | | 070617 | connect ip failed: network is unreachable(建立连接失败) | 请检查网络是否畅通 | | 070618 | connect ip timeout(建立连接超时) | 请检查网络是否畅通 | | 070619 | websocket handshake failed: reasons for uncertainty(websocket握手失败) | 请检查网络是否畅通 | | 070620 | recv timeout(接收超时) | 请检查网络是否畅通 | | 070621 | send timeout(发送超时) | 请检查网络是否畅通 | | 070622 | orderly shutdown(发完数据正常关闭) | 请检查网络是否畅通 | | 071301 | TTS合成超时 | 请检查网络是否正常 | | 071302 | 对话超时 | 请检查网络是否正常 | | 071303 | 对话连接断开 | 请检查网络是否正常 | | 071304 | 识别结果为空 | 未检测到用户说法 | | 071305 | 语义结果为空 | 用户的说法没有命中产品中的非闲聊技能 | | 071306 | WebAPI错误 | 调用WebAPI时发生错误 | | 071307 | NativeAPI错误 | 调用NativeAPI时发生错误 | | 071308 | 进入闲聊技能 | 用户的首轮说法没有命中产品中的非闲聊技能 | | 071309 | 异常重试达到最大次数 | 达到了产品错误处理的最大重试次数 | | 071310 | 命中退出词 | 用户说了产品的退出词 | | 071311 | NativeAPI未注册 | 请检测是否注册并实现NativeAPI | | 071312 | 本地识别结果置信度低 | 网络离线时本地识别结果置信度较低 | | 071313 | 自定义技能响应超时 | 联系技能开发者 | | 071314 | 自定义技能内部错误 | 联系技能开发者 | | 071315 | 自定义技能返回为空 | 联系技能开发者 | | 071316 | 当前场景下不支持这么说 | 该技能关闭了智能调度 | | 071317 | 进入兜底技能 | 用户说法进入了产品中的兜底技能 | | 071318 | 进入知识型技能 | 用户说法进入了产品中的知识型技能,包括新闲聊、新百科 | | 071319 | 未授权 | 请调用doAuth | | 071320 | 在使用oauth授权方式的情况下,access token过期或者不合法 | 请使用oauth sdk刷新access token,然后用dds sdk的接口传入access token | | 071321 | 自定义技能speaklist播报超时 | 请检查音频链接是否正常 | | 071322 | 鉴权失败的情况下请求对话 | 请检查授权错误信息 | | 071300 | 对话服务内部错误 | 联系DUI客服 | | 072101 | ASREngine VAD超时 | 超过8s未检测到人声 | | 072102 | ASREngine 识别为空 | 未检测到用户说法 | | 072103 | ASREngine 识别服务超时 | 请检查网络是否正常 | | 072104 | ASREngine 识别过程发生错误 | 联系DUI客服 | | 072201 | 唤醒词个数超过限制 | 请清除之前的唤醒词后重试 | # 六、常见问题 ## 6.1 SDK的软硬件配置要求 1) 需要Android 4.1及其以上的系统; 2) /data分区剩余空间至少200MB。 ## 6.2 授权失败 关于070603授权错误的问题,请做以下检查: 1) 平台上生成的apikey和本地使用的apikey是否一致; 2) 平台上release debug的sha256值是否和本地的对应; 3) gradle文件配置的signingConfigs,只针对generate生成的apk; 4) 如果是点run运行的程序,debug的sha256要填的.android目录下的debug.keystore(最常见) ## 6.3 客户端没收到Command/NativeAPI回调 常见原因如下,可以自上而下确认: 1) 技能修改后,未发布; 2) 技能发布后,产品没有选择该技能的最新版本; 3) 产品未发布; 4) 客户端配置的K_ALIAS_KEY和产品发布的分支不一致; 5) 客户端没有注册对应的Command/NativeAPI; 6) 客户端注册Command/NativeAPI的逻辑没走到; 7) 客户端注册的Command/NativeAPI没填对,把?后面的参数也填进去了; 8) 客户端注册Command/NativeAPI后,又重新初始化了DDS(release+init) ## 6.4 智能家居refreshToken 断网2年后导致SDK授权失败且无法恢复的问题 1)通过订阅token_refresh.fail消息,如果token刷新失败,SDK会抛出这个消息,应用收到该消息后,重新走以下流程:requestAuthCode() -- setAuthCode() 2)setAuthCode授权成功后,将智能家居绑定流程再重新走一遍;确保DCA绑定成功。

一. 集成准备

1.1 获取产品ID

集成SDK之前,您首先需要到DUI控制台创建产品,获取如下的产品ID。
Minion

1.2 下载和导入SDK

SDK 下载页

sdk 不同版本 duicore.zip、product.zip 获取方式差异:

  • V2.X.X.X 版本 duicore.zip 在 sdk 里一同发布,product.zip 仍在产品发布页下载。!!! 不要使用错误的 duicore.zip
  • v1.X.X.X 版本通过产品发布页下载 duicore.zip 和 product.zip

1.3 集成

SDK要求Android 4.1及其以上的系统且 /data分区剩余空间至少200MB

将SDK包中的aar文件( 如 lib-dds-2.0.1.1-release.aar )导入到现有的工程中,放入到libs目录下,并添加配置:

build.gradle

repositories{ flatDir { dirs 'libs' } }

在build.gradle中配置引用sync工程即可。

build.gradle

dependencies { implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) //在线授权必须引入 implementation 'com.squareup.okhttp3:okhttp:3.9.0' }

1.4 代码混淆

如果您的应用使用了混淆, proguard-rules.pro 内请添加:

  • 2.0.0.1 及以上版本:
-keep class com.aispeech.**{*;}
-keep class com.aispeech.**$**{*;}
-keep class ai.dui.sdk.**{*;}
-keep class ai.dui.sdk.**$**{*;}
  • 1.X.X.X 版本:
-keep com.aispeech.**
-keep public class com.aispeech.lasa{*;}
-keep public class com.aispeech.dui.dds.luaBug{ *; }
-keep class ai.dui.sdk.**{*;}
-keep class ai.dui.sdk.**$**{*;}

1.5 权限说明

SDK需要的权限如下表,请在AndroidManifest.xml 文件中添加。

权限 用途
android.permission.INTERNET 允许接入互联网,以便访问我们的云服务
android.permission.RECORD_AUDIO 允许采集音频
android.permission.ACCESS_WIFI_STATE 获取WIFI状态
android.permission.ACCESS_NETWORK_STATE 获取网络状态
android.permission.WRITE_EXTERNAL_STORAGE (非必须)写入外部存储数据,用于存储一些缓存文件
android.permission.READ_EXTERNAL_STORAGE (非必须)读取外部存储数据

1.6 精简 sdk

sdk 包含所有的功能及资源,客户可根据自己的需要裁剪不需要的功能,可减少 app 或者下载的资源包的大小。

sdk 包里有 mini 文件夹,内含:

  1. mini.py // 精简脚本
  2. mini.config // 精简脚本的配置文件

脚本使用方法:

  1. 修改 mini/mini.config 配置文件,0表示不使用, 1表示使用
  2. 在mini目录下执行python脚本: python3 mini.py
  3. 在mini目录下生成精减的 aar 与 duicore

!!!强烈建议客户使用匹配的 duicore 和 aar, 支持客户自己根据需要裁剪,一般可以减少一半大小

二. 初始化 SDK

初始化包含

  1. 授权
  2. 资源、产品的初始化

2.1 构造 DDSConfig

初始化时需要 DDSConfig 参数配置。以下说明核心的初始化参数,更多参数说明请见 javadoc在线文档

  • DDSConfigBuilder 方式构造 DDSConfig
// DDSConfig 新的方式构造 2.0.1.1 及以上版本支持 DDSConfigBuilder ddsConfigBuilder = new DDSConfigBuilder( "productId", // 产品ID "aliasKey", // 产品分支 prod/test/(自定义分支) "apiKey", // 产品的 apiKey,一个API Key只被指定的客户端使用,来源:DUI控制台-产品接入-授权管理 "productKey", // 产品的 productKey,用于设备注册请求签名,来源:DUI控制台-产品接入-授权管理 "productSecret", // 产品的 productSecret,用于设备注册请求签名,来源:DUI控制台-产品接入-授权管理 "duicoreZip" // 资源包,用法详见下方"duicore 配置规则" ); ddsConfigBuilder.createWakeupBuilder() .setMicType(0); ddsConfigBuilder.createCommonBuilder() .setCustomZip("product.zip") .setDeviceName("XXX") .setDeviceId("XXX") // 设备ID,授权时认证的原始设备ID,请务必确保设备ID唯一。若不填,则SDK自己收集设备ID。可以通过DDS.getInstance().getDeviceName()获取授权认证后的唯一ID .setUserId("123456"); // 用户ID DDSConfig ddsConfig = ddsConfigBuilder.build(); // 之后 2.2 init 初始化
  • DDSConfig 旧的方式构造
// DDSConfig 旧的方式构造 DDSConfig ddsConfig = new DDSConfig() .addConfig(DDSConfig.K_DEVICE_ID, deviceId) // 设备ID字符串,授权时认证的原始设备ID,请务必确保设备ID唯一。若不填,则SDK自己收集设备ID。可以通过DDS.getInstance().getDeviceName()获取授权认证后的唯一ID .addConfig(DDSConfig.K_PRODUCT_ID, productId) // 产品ID字符串 .addConfig(DDSConfig.K_USER_ID, userId) // 用户ID字符串 .addConfig(DDSConfig.K_ALIAS_KEY, aliasKey) // 产品分支 prod/test/(自定义分支) .addConfig(DDSConfig.K_API_KEY, apiKey) // 产品的 apiKey字符串,一个API Key只被指定的客户端使用,来源:DUI控制台-产品接入-授权管理 .addConfig(DDSConfig.K_PRODUCT_KEY, productKey) // 产品的 productKey字符串,用于设备注册请求签名,来源:DUI控制台-产品接入-授权管理 .addConfig(DDSConfig.K_PRODUCT_SECRET, productSecret) // 产品的 productSecret字符串,用于设备注册请求签名,来源:DUI控制台-产品接入-授权管理 .addConfig(DDSConfig.K_DUICORE_ZIP, "duicore.zip") // 资源包,用法详见下方"duicore 配置规则" // 之后 2.2 init 初始化

duicore 配置规则:

  1. 如果duicore.zip的放置位置在assets目录下,那么就可以使用如代码中的写法,直接使用文件名:duicore.zip

  2. 如果duicore.zip没有放置在assets目录下,就必须使用duicore.zip文件的绝对路径:/sdcard/dds/duicore.zip

  3. 在 duicore.zip 所在的文件夹中,必须包含与 duicore.zip 文件相应的 duicore.zip.md5sum 文件(2.16.0.1以上版本非必须),文件内容是 duicore.zip 文件的 md5 值

在收到 onInitComplete 回调之后,就可以使用DDS来完成对应的操作。
isFull参数如果为false,则表示不完全初始化,缺失资源包,您需要等待资源包的更新下载完成或者内置一份资源包; 如果为true,则表示完全初始化,所有功能就绪。

2.2 授权和初始化

官网产品授权操作说明

  • 2.0.0.1 及以上版本会先回调收授权状态,之后回调 init 状态。即 onInitComplete 回调 true 即表示初始化和授权都成功

init 时会进行授权操作,必须授权成功后才能正常使用sdk

v2.X.X.X 版本授权流程:

  1. 授权操作需要连接到互联网, 在 SDK 初始化时设置 DDSAuthListener 来监听授权的情况
  2. SDK 初始化完毕后,您可以使用 DDS.getInstance().isAuthSuccess() 来判断当前是否授权成功。
  3. 授权成功会回调 DDSAuthListener.onAuthSuccess(),授权失败会收到回调DDSAuthListener.onAuthFailed(),需要重新调用DDS.getInstance().doAuth()
  4. 授权错误码从 "070601“ – “070622”,详见 五、错误码描述。
  • v1.X.X.X 版本的授权和 init 回调没有顺序关系
  1. 在SDK初始化时,可以设置 DDSAuthListener 来监听授权的情况。

  2. SDK初始化完毕后,您可以使用 DDS.getInstance().isAuthSuccess() 来判断当前是否授权成功。

  3. 授权成功会回调 DDSAuthListener.onAuthSuccess,授权失败会收到回调DDSAuthListener.onAuthFailed,需要重新调用DDS.getInstance().doAuth()。

  4. Profile文件的错误码范围从 "070601“ – “070622”,详见 五、错误码描述。

DDS.getInstance().init( getApplicationContext(), ddsConfig, new DDSInitListener() { // 资源、产品的初始化回调 @Override public void onInitComplete(boolean isFull) { AILog.d(TAG, "onInitComplete: " + isFull); } @Override public void onError(int what, String msg) { AILog.d(TAG, "onError: " + what + ", error: " + msg); } }, new DDSAuthListener() { // 授权回调 @Override public void onAuthSuccess() { AILog.d(TAG, "onAuthSuccess"); } @Override public void onAuthFailed(String errId, String error) { AILog.d(TAG, "onAuthFailed: " + errId + ", error:" + error); } });

2.3 初始化状态检查

除了通过监听 onInitComplete 回调外,还可以直接通过判断 DDS.getInstance().getInitStatus()的返回值来检查初始化是否完毕。

当未初始化完成时,尝试调用一些方法时,会抛出 DDSNotInitCompleteException 异常

/** * 获取当前初始化的状态 * * @return DDS.INIT_COMPLETE_NONE 还未初始化完成,表示DDS正在初始化 * DDS.INIT_COMPLETE_NOT_FULL 部分初始化完成,表示DDS已经初始化完成,但还没有完成更新 * DDS.INIT_COMPLETE_FULL 完全初始化完成,已经完成更新 */ DDS.getInstance().getInitStatus();

2.4 release SDK

init 回调 INIT_COMPLETE_FULL之后即可使用sdk的功能。

如果不需要 sdk ,可以通过以下方法释放 sdk 资源

DDS.getInstance().release();

注意事项

• DDS.getInstance().init()和DDS.getInstance().release() 方法不会阻塞应用程序的主线程,也不会影响应用程序性能
• 当调用DDS.getInstance().release()之后,之前注册的 observers 会失效,再次 DDS.getInstance().init() 后,您需要对之前的 observers 重新注册

2.5 更新产品

2.0.0.1 及以上版本不支持更新 duicore 资源,但可以更新产品配置

如果您想对发布的产品持续进行免安装的更新,您需要做以下几个操作:

  • 请求更新

您可以选择在合适的时候发起更新请求。当sdk在检查到有更新后,会自动下载资源并进行热更新,结果会以回调的方式通知。

DDS.getInstance().getUpdater().update(new DDSUpdateListener() { @Override public void onUpdateFound(String detail) { AILog.d(TAG, "onUpdateFound"); } @Override public void onUpdateFinish() { AILog.d(TAG, "onUpdateFinish"); } @Override public void onDownloadProgress(float progress) { AILog.d(TAG, "onDownloadProgress :" + progress); } @Override public void onError(int what, String error) { AILog.d(TAG, "onError :" + what + ", error: " + error); } @Override public void onUpgrade(String version) { AILog.d(TAG, "onUpgrade :" + version); } };
  • 处理更新推送

在您成功发布产品之后,若您已经订阅了消息 sys.resource.updated,则会收到资源更新的推送,此时您可以选择直接请求更新,以保持产品一直处于最新状态(推荐)。

DDS.getInstance().getAgent().subscribe("sys.resource.updated", new MessageObserver() { @Override public void onMessage(String message, String data) { try { DDS.getInstance().getUpdater().update(ddsUpdateListener); } catch (DDSNotInitCompleteException e) { e.printStackTrace(); } } });

SDK中内置一套资源更新时的通知的UI实现,您可以在DDSConfig中设置 DDSConfig.K_USE_UPDATE_NOTIFICATION 来开启或关闭。

2.6 Debug

2.6.1 日志

2.0.0.1 及以上版本默认日志级别是 4 (同 Log.INFO)。修改日志级别:

DDS.getInstance().setDebugMode(2); // 日志默认保存路径: /sdcard/Android/data/包名/cache/alog/

日志压缩成xlog形式,默认保存2天的日志,以下是日志需要的大概大小

  • 单麦D级别日志24小时保存日志 150M
  • 多麦D级别日志24小时保存日志 400M

2.6.2 音频调试

初始化后可动态设置保存 debug 音频

DDS.getInstance().setAudioDebug(true); // 音频默认保存路径: /sdcard/Android/data/包名/cache/

动态音频调试接口可替代静态调试配置: K_VAD_DEBUG、K_TTS_DEBUG、K_WAKEUP_DEBUG、K_ASR_DEBUG、K_ASRPP_DEBUG

2.6.3 动态Debug

DDS.getInstance().startDebug(); // 开启调试模式 日志级别调为V 保存调试音频 DDS.getInstance().stopDebug(); // 关闭调试模式 日志级别修改为E 停止保存调试音频

2.6.4 缓存配置

缓存目录的配置需要在初始化时设置,如不设置,默认 /sdcard/Android/data/包名/cache

ddsConfigBuilder.createCommonBuilder() .setCachePath("/sdcard/cache") // 缓存文件保存目录,默认 /sdcard/Android/data/包名/cache .setCacheSize(500000) // 缓存文件大小限制。自定义缓存目录时必需

三. 功能列表

3.1 响应command

  1. 客户端动作的执行

详细的command编写说明,请参见客户端动作(command)编写说明

Minion

  1. 快捷唤醒中的命令响应

Minion

当您在DUI平台上定制了command之后,可以使用如下的方式在您的工程中做实现。
类似于Android的广播接收器,您可以在需要的地方注册和注销CommandObserver,同一个CommandObserver可以处理多个commands。

// 注册 DDS.getInstance().getAgent().subscribe(new String[]{"open_window", "increase.volume"}, commandObserver); // 注销 DDS.getInstance().getAgent().unSubscribe(commandObserver); private CommandObserver commandObserver = new CommandObserver() { @Override public void onCall(final String command, final String data) { if (command.equals("open_window")){ JSONObject jsonData = new JSONObject(data); String intentName = jsonData.optString("intentName"); String w = jsonData.optString("w"); // TODO 依据w的值,执行打开窗户操作 }else if (command.equals("increase.volume")) { // TODO 处理调大音量的快捷唤醒指令 } } };

3.2 响应native api

Minion

当您在DUI平台上定制了native api之后,可以使用如下的方式在您的工程中做实现。
类似于Android的广播接收器,您可以在需要的地方注册和注销NativeApiObserver,同一个NativeApiObserver可以处理多个native api。

// 注册 DDS.getInstance().getAgent().subscribe(new String[]{"query_battery"}, nativeApiObserver); // 注销 DDS.getInstance().getAgent().unSubscribe(nativeApiObserver); private NativeApiObserver nativeApiObserver = new NativeApiObserver() { @Override public void onQuery(final String nativeApi, final String data) { if (nativeApi.equals("query_battery")){ JSONObject jsonData = new JSONObject(data); String intentName = jsonData.optString("intentName"); // 执行查询电量操作 String battery = DeviceUtil.getBattery(); DDS.getInstance().getAgent().feedbackNativeApiResult(nativeApi, new TextWidget().setText(battery)); } } };

nativeCommand支持异步写法

String mCommand; public void doNativeCommand() { String[] mTopic = new String[]{ "sys.car.crl" }; NativeCommandObserver nativeCommandObserver = new NativeCommandAsyncObserver() { @Override public void onNativeCommandAsync(String command, String data) { mCommand = command; AILog.d(TAG, "command = " + command); AILog.d(TAG, "data = " + data); // 异步执行操作,这里不能再有耗时操作,应用层保证这里切异步 } }; DDS.getInstance().getAgent().subscribeSync(mTopic, nativeCommandObserver); }

注:

• 每个onQuery方法执行时,需要调用 feedbackNativeApiResult() 来向DUI平台返回执行结果,表示一个native api执行结束。

• native api的执行超时时间为10s

更多详细使用请查看: https://www.duiopen.com/docs/skill_FAQ

3.3 响应内置消息

内置消息列表:

message data 说明
sys.kernel.ready SDK加载完成,所有功能就绪
sys.dialog.start { reason:“wakeup.major”,//唤醒词触发对话 } 对话开始,及开始原因:
1.wakeup.major,唤醒词唤醒
2.wakeup.command,快捷命令唤醒
3.api.startDialog,调用startDialog接口
4.api.sendText,调用sendText接口(对话Idle时)
5.api.triggerIntent,调用triggerIntent接口(对话Idle时)
6.api.avatarClick,调用avatarClick接口(对话Idle时)
7.api.avatarPress,调用avatarPress接口(对话Idle时)
sys.dialog.error { errId:071304, errMsg:“asr null” recordId:“70dcef50b8b31a6c02dbac1acee31de7” } 对话中发生的错误
sys.dialog.end { reason:“normal”,//对话正常结束 skillId:“100001246” } { reason:“error”,//对话发生错误; errId:071304, errMsg:“asr null”, skillId:“100001246” } { reason:“interrupt”,//对话被打断。强制结束对话时出现。 skillId:“100001246” } 对话结束,及结束原因: 1.normal,对话正常结束 2.error,对话发生错误退出 3.interrupt,对话被打断退出
sys.dialog.continue 对话恢复
sys.resource.updated 收到线上资源变更的推送
sys.wakeup.result { “type”: “major”, “word”: “你好小驰”, “greeting”:“主人你好” } 语音唤醒
sys.vad.begin VAD触发
sys.vad.end VAD结束
sys.upload.result { “result”:false, “reqId”:“xxxx”, “errId”:71801, “errMsg”:“Network Invalid” } 上传词库、设备信息的结果

类似于Android的广播接收器,您可以在需要的地方注册和注销MessageObserver,同一个MessageObserver可以处理多个message。

您可以通过注册消息接收器来接收需要UI展示的数据,具体UI消息数据见:
UI事件及数据定义

// 注册 DDS.getInstance().getAgent().subscribe(new String[]{"sys.dialog.start","sys.dialog.end","context.input.text"}, messageObserver); // 注销 DDS.getInstance().getAgent().unSubscribe(messageObserver); private MessageObserver messageObserver = new MessageObserver() { @Override public void onMessage(final String message, final String data) { if (message.equals("sys.dialog.start")){ runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, "对话开始了", Toast.LENGTH_LONG).show(); } }); }else if(message.equals("context.input.text")){ final JSONObject dataJson = new JSONObject(data); runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, dataJson.optString("text"), Toast.LENGTH_LONG).show(); } }); } } };

3.4 开启/终止对话

您可以在任何时刻主动开启/终止对话,通过以下接口实现。
dialog

// 开启对话。如果当前已经在对话中,则重新开启新一轮对话。 DDS.getInstance().getAgent().startDialog(); // 关闭对话 DDS.getInstance().getAgent().stopDialog(); // 支持在开启对话时传入一个播报文本, 先播报文本,后开启对话 Agent.startDialog(JSONObject jsonObject) // 支持在关闭对话时传入一个播报文本, 文本播报结束后自动结束对话 Agent.stopDialog(JSONObject jsonObject) 使用示例: JSONObject jsonObject = new JSONObject(); jsonObject.put("speakText", "播报文本"); DDS.getInstance().getAgent().startDialog(jsonObject); // 多模态事件同步,该接口用于客户端给对话中控发送一个事件 DDS.getInstance().getAgent().updateDispatchEvent(JSONObject obj) 使用示例: JSONObject obj = new JSONObject(); // obj.put("endSkill", "true"); // 不支持 endSkill DDS.getInstance().getAgent().updateDispatchEvent(obj);

3.4.1 提示音

半双工 使用 startDialog 后默认会有一个提示音,提示音可以自由打开或者关闭

DDS.getInstance().getAgent().openTip(); // 打开tip提示音 DDS.getInstance().getAgent().closeTip(); // 关闭tip提示音

3.5 按键事件

如果您有相关的软硬件按键,您需要关注以下接口,将用户的行为反馈给SDK。

// 点击唤醒/停止识别/打断播报 操作接口 DDS.getInstance().getAgent().avatarClick(); // 点击唤醒/停止识别/打断播报 操作接口 , 并附带一则欢迎语,当此次是唤醒时,播报这则欢迎语 DDS.getInstance().getAgent().avatarClick("有什么可以帮你"); // 按下按键接口(调用此接口需关闭VAD,VAD在创建产品时可配置) DDS.getInstance().getAgent().avatarPress(); // 释放按键接口(调用此接口需关闭VAD,VAD在创建产品时可配置) DDS.getInstance().getAgent().avatarRelease();

3.6 语音播报

使用 DDS.getInstance().getAgent().getTTSEngine().speak() 接口可以主动播放 TTS 。

参数说明:
1)text 播报文本,支持SSML
2)priority 优先级

提供4个优先级播报。
①优先级0:保留,与DDS语音交互同级,仅限内部使用;
②优先级1:正常,默认选项,同级按序播放;
③优先级2:重要,可以插话<优先级1>,同级按序播放,播报完毕后继续播报刚才被插话的<优先级1>;
④优先级3:紧急,可以打断当前正在播放的<优先级1|优先级2>,同级按序播放,播报完毕后不再继续播报刚才被打断的<优先级1|优先级2>。
3)ttsId 用于追踪该次播报的id,建议使用UUID。
4)audioFocus 该次播报的音频焦点,默认值:
①优先级0:android.media.AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
②优先级非0:android.media.AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK

  • Text 播报
// 请求语音播报 DDS.getInstance().getAgent().getTTSEngine().speak("这是一段测试文本", 1, "100", AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
  • SSML 播报
String test1 = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<speak xml:lang=\"cn\">\n" + " 测试SSML播报\n" + "</speak>"; DDS.getInstance().getAgent().getTTSEngine().speak(test1, 1, "10004", AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, TTSEngine.SSML);

停止播报接口如下:
1)ttsId与speak接口的ttsId一致,则停止或者移除该播报;
2)ttsId为空, 停止所有播报;
3)ttsId为"0",停止当前播报。

// 停止播报 DDS.getInstance().getAgent().getTTSEngine().shutup("100");

3.7 内置H5

2.0.0.1 及以上版本基于性能考虑默认关闭H5通信节点, 如有需要, 可以通过配置打开:

// 新的配置方式 ddsConfigBuilder.createNodeBuilder() .enableH5(true); // 旧的配置方式 config.addConfig(DDSConfig.K_H5_NODE_ENABLE, "true");

您如果使用和上传了h5资源,可以通过以下方法来获取h5资源包的 index.html 文件的路径用来在webview中加载

// 获取资源包中h5的index.html文件的绝对路径 DDS.getInstance().getAgent().getValidH5Path();

3.8 外部录音机拾音

您如果有自己的录音机,可以通过以下方法将录音数据传给DDS。

// 注意:需要将DDSConfig.K_RECORDER_MODE设置为"external",用来关闭DDS内置录音机,该接口才会生效 // 音频长度:无限制。内置录音机是3200(单麦) // 调用间隔:无限制。内置录音机是100ms // 调用时机:无限制。DDS会根据对话状态将接受的音频送入识别或者丢弃。 DDS.getInstance().getAgent().feedPcm(byte[] pcm);

3.9 外部TTS引擎

您如果使用第三方的TTS引擎,可以通过以下方法来将第三方TTS引擎注册到DDS中。

// 1. 配置 ttsMode /** * TTS播报模式<br> * 取值:internal or external<br> * internal:为sdk内部合成播报<br> * external:用户负责合成播报,需调用 <code>DDS.getInstance().getAgent().setTTSRequestListener()</code> 通过回调获取播报时机, * 播报完成后调用 <code>DDS.getInstance().getAgent().notifyTTSEnd()</code> 标记结束<br> * 默认值:internal */ ddsConfigBuilder.createTtsBuilder().setTtsMode("external") // 2. 注册回调 TTSRequestListener listener = new TTSRequestListener() { @Override public void onStart(String type, String content) { Log.d(TAG, "tts start," + type + ":" + content); int code = mExternelTts.startSpeaking(content, mTtsListener); if (code != ErrorCode.SUCCESS) { Log.d(TAG, "语音合成失败,错误码: " + code); } } @Override public void onStop() { Log.d(TAG, "tts stop"); mExternelTts.stopSpeaking(); } }; DDS.getInstance().getAgent().setTTSRequestListener(listener) // 3. 合成完毕后必须播报结束通知 !!! DDS.getInstance().getAgent().notifyTTSEnd();

3.10 场景模式

SDK提供了一些场景(如TTS播报/拾音距离)的切换,您可以在需要的时候调用下面的接口。

DDSMode

//TTS场景切换 DDS.getInstance().getAgent().setDDSMode(DDSMode.TTS_SILENCE); //在静音模式下,所有TTS播报将会被静音。 DDS.getInstance().getAgent().setDDSMode(DDSMode.TTS_NORMAL); //默认值。 //拾音场景切换。仅在"电视助手"解决方案中生效。 DDS.getInstance().getAgent().setDDSMode(DDSMode.PICKUP_NEAR); //在近场模式下,将加载产品配置页中的近场配置 DDS.getInstance().getAgent().setDDSMode(DDSMode.PICKUP_FAR); //在远场模式下,将加载产品配置页中的远场配置

3.11 触发指定意图

如果您期望主动触发某个意图的对话,可以在需要的时候调用下面的接口。

参数说明:

1)skill 技能名称,必填。
2)task 任务名称,必填。
3)intent 意图名称,必填。
4)slots 语义槽的key-value Json,可选。

triggerIntent

// 跳过识别和语义,直接进入指定的意图对话。即:DDS主动向用户发起一轮对话。 DDS.getInstance().getAgent().triggerIntent("我是技能名称", "我是任务名称", "我是意图名称", new JSONObject().put("语义槽名称一", "语义槽取值一").put("语义槽名称二", "语义槽取值二").toString());

根据skillid触发意图:

SkillIntent skillIntent = new SkillIntent("我是技能id", "我是任务名称", "我是意图名称", new JSONObject().put("语义槽名称一", "语义槽取值一").put("语义槽名称二", "语义槽取值二").toString()); DDS.getInstance().getAgent().triggerIntent(skillIntent);

3.12 更新用户词库

如果您期望更新用户的词库,使其能在技能中使用,可以在需要的时候调用下面的接口。上传结果可以通过sys.upload.result消息来获取。

注意:更新的词库仅在该设备下有效,不会影响其他设备。
参数说明:
vocabIntent: 词库intent。包含词库名name,操作action,词条列表contents。
注1:若词库名是sys.联系人,则会自动做分词处理。
注2:若需要上传带同义词的词条,格式如下:"{词条取值}:{同义词1},${同义词2}"
返回值:请求ID,用于追踪sys.upload.result。

updateVocabs

//向指定词库中添加词条 final String reqId = DDS.getInstance().getAgent().updateVocabs( new VocabIntent() .setName("应用列表") .setAction(VocabIntent.ACTION_INSERT) .setContents(Arrays.asList("淘宝", "支付宝:支护宝,付款码")) ); //清空之前上传到该词库的所有词条,然后添加词条 DDS.getInstance().getAgent().updateVocabs( new VocabIntent() .setName("设备词库") .setAction(VocabIntent.ACTION_CLEAR_AND_INSERT) .setContents(Arrays.asList("空调", "电视:电视机")) ); //删除该词库的词条 DDS.getInstance().getAgent().updateVocabs( new VocabIntent() .setName("歌手名") .setAction(VocabIntent.ACTION_REMOVE) .addContent("刘德华") ); //清空之前上传到该词库的所有词条 DDS.getInstance().getAgent().updateVocabs( new VocabIntent() .setName("歌曲") .setAction(VocabIntent.ACTION_CLEAR_ALL) ); //批量更新词库 DDS.getInstance().getAgent().updateVocabs( //向指定词库中添加词条 new VocabIntent() .setName("sys.联系人") .setAction(VocabIntent.ACTION_INSERT) .addContent("董芳芳") .addContent("王强"), //清空之前上传到该词库的所有词条,然后添加词条 new VocabIntent() .setName("设备词库") .setAction(VocabIntent.ACTION_CLEAR_AND_INSERT) .setContents(Arrays.asList("空调", "电视")), //删除该词库的词条 new VocabIntent() .setName("歌手名") .setAction(VocabIntent.ACTION_REMOVE) .addContent("刘德华"), //清空之前上传到该词库的所有词条 new VocabIntent() .setName("歌曲") .setAction(VocabIntent.ACTION_CLEAR_ALL) ); //关注更新词库的结果 DDS.getInstance().getAgent().subscribe("sys.upload.result", new MessageObserver() { @Override public void onMessage(String message, String data) { Log.e(TAG, message + ":" + data); try { JSONObject obj = new JSONObject(data); if (reqId.equals(obj.optString("reqId", ""))) { Log.d(TAG, "updateVocabs result:" + obj.optBoolean("result", false)); } } catch (JSONException e) { e.printStackTrace(); } } });

3.13 startListening 仅开启识别

如果您期望仅开启识别并获取识别结果,可以在需要的时候调用下面的接口。

2.0.0.1 及以上版本使用 startListening 在半双工和全双工下表现一致

ASREngine

//获取识别引擎 ASREngine asrEngine = DDS.getInstance().getAgent().getASREngine(); ASREngine.ListeningParams params = new ASREngine.ListeningParams(); params.setVadEnable(true); //开启识别 params 可不设置 asrEngine.startListening(params, new ASREngine.Callback() { @Override public void beginningOfSpeech() { AILog.i(TAG, "检测到用户开始说话"); } /** * @buffer 音频数据属性:单声道 16bit */ @Override public void bufferReceived(byte[] buffer) { AILog.i(TAG, "用户说话的音频数据"); } @Override public void endOfSpeech() { AILog.i(TAG, "检测到用户结束说话"); } /** * @results. eof 为 0 代表识别过程中的结果, 为 1 代表识别结束的最终结果 * {"var":"今","sessionId":"0b48c0d086efc42d55bbf1068137655d","recordId":"6b3ccb1ee745d42fa6ddb74330b1c059","eof":0} * {"var":"今天 天气","sessionId":"0b48c0d086efc42d55bbf1068137655d","recordId":"6b3ccb1ee745d42fa6ddb74330b1c059","eof":0} * {"sessionId":"0b48c0d086efc42d55bbf1068137655d","pinyin":"jin tian tian qi","recordId":"6b3ccb1ee745d42fa6ddb74330b1c059","eof":1,"text":"今天 天气"} */ @Override public void partialResults(String results) { AILog.i(TAG, "实时识别结果反馈"); } /** * @results. {"sessionId":"0b48c0d086efc42d55bbf1068137655d","recordId":"6b3ccb1ee745d42fa6ddb74330b1c059","eof":1,"text":"今天天气","pinyin":"jin tian tian qi"} */ @Override public void finalResults(String results) { AILog.i(TAG, "最终识别结果反馈"); } @Override public void error(String error) { AILog.i(TAG, "识别过程中发生的错误"); } @Override public void rmsChanged(float rmsdB) { AILog.i(TAG, "用户说话的音量分贝"); } }); //主动结束此次识别,使用 vad 的话不需要主动停止 asrEngine.stopListening(); //取消此次识别 asrEngine.cancel();

3.13.1 设置实时回传音量

// 设置实时回传音量大小, 默认为true // 设置false之后, ASREngine.Callback.rmsChanged()不再回传音量变化值 DDS.getInstance().getAgent().getASREngine().enableVolume(boolean enable);

3.13.2 feed OPUS 或者 SBC 音频

如需 feed OPUS 或者 SBC 音频,只需按下面配置即可,其它操作不变

// feed opus DDS.getInstance().getAgent().setRecorderMode(Agent.RecorderMode.OPUS); DDS.getInstance().getAgent().feedOpus(buffer); // feed sbc DDS.getInstance().getAgent().setRecorderMode(Agent.RecorderMode.SBC); DDS.getInstance().getAgent().feedSbc(buffer);

3.14 VAD相关设置

3.14.1设置VAD后端停顿时间

如果您期望修改VAD后端检测的时间,可以在需要的时候调用下面的接口。

参数说明
millis 后端检测时间,单位毫秒,默认500毫秒。即若VAD在用户说话时停顿超过一定的时间,则认为用户已经说完,发出sys.vad.end消息,结束录音。

setVadPauseTime

//设置VAD后端停顿时间 DDS.getInstance().getAgent().getASREngine().setVadPauseTime(1000);//获取VAD后端停顿时间 DDS.getInstance().getAgent().getASREngine().getVadPauseTime();

3.14.2 设置VAD前端静音检测超时时间

如果您期望修改VAD前端静音检测超时时间,可以在需要的时候调用下面的接口。设置成功后,vad再次启动生效。
参数说明:
millis 前端静音检测超时时间,单位毫秒,默认8000毫秒。若VAD启动,一直未检测到用户说话,超过一定时间,发出sys.vad.timeout消息,结束录音。

setVadTimeout

//设置VAD前端静音检测超时时间 DDS.getInstance().getAgent().getASREngine().setVadTimeout(1000); //获取VAD前端静音检测超时时间 DDS.getInstance().getAgent().getASREngine().getVadTimeout();

3.15 对话输入文本

如果您期望跳过识别直接输入文本进行对话,可以在需要的时候调用下面的接口。

sendText

/** * 输入文本的接口 * <p> * 跳过对话中的识别过程,直接开始对话中的语义理解 * <p> * 若当前没有在对话中,则以文本作为首轮说法,新发起一轮对话请求 * 若当前正在对话中,则跳过识别,直接发送文本 * * @param text 输入的文本内容 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().sendText(String text)

3.16 TTS相关设置

3.16.1 TTS事件回调

如果您想在TTS播报的相关时间节点收到通知,如:开始播报,播报结束。可以使用如下的方式:

TTSEngine.Callback

DDS.getInstance().getAgent().getTTSEngine().setListener(new TTSEngine.Callback() { /** * 开始合成时的回调 * @param ttsId 当前TTS的id, 对话过程中的播报ttsid默认为0,通过speak接口调用的播报,ttsid由speak接口指定。 */ @Override public void beginning(String ttsId) { Log.d(TAG, "TTS开始播报"); } /** * 合成的音频数据的回调,可能会返回多次,data长度为0表示音频结束 * @param data 音频数据属性:单声道 16bit, pcm */ @Override public void received(byte[] data) { Log.d(TAG, "收到音频,此方法会回调多次,直至data为0,音频结束"); } /** * TTS播报完成的回调 * @param status 播报结束的状态。 * 正常播报结束为0 * 播报中途被打断结束为1 */ @Override public void end(String ttsId, int status) { Log.d(TAG, "TTS播报结束"); } /** * 合成过程中出现错误的回调 * @param error 错误信息 */ @Override public void error(String error) { Log.d(TAG, "出现错误,"+error); } /** * 合成过程中的音素回掉,需要资源支持 */ @Override public void phoneReturnReceived(String phoneReturn) { } });

TTSEngine.CallbackOptimize 带进度的回调

DDS.getInstance().getAgent().getTTSEngine().setListenerByProcess(new TTSEngine.CallbackOptimize() { @Override public void beginning(String ttsId) { Log.d(TAG, "beginning() ttsId: " + ttsId); if (TextUtils.equals(ttsId, mTtsId)) { listenerSuccessTimes++; } } @Override public void end(String ttsId, int status) { Log.d(TAG, "end() ttsId: " + ttsId + " status: " + status); if (TextUtils.equals(ttsId, mTtsId) && status == 0) { listenerSuccessTimes++; } } @Override public void error(String error) { Log.d(TAG, "error() error: " + error); } @Override public void onSpeechProgress(String ttsId, int currentFrame, int totalFrame, boolean isDataReady) { super.onSpeechProgress(ttsId, currentFrame, totalFrame, isDataReady); Log.d(TAG, "onSpeechProgress() ttsId: " + ttsId + " currentFrame:" + currentFrame + " totalFrame:" + totalFrame + " isDataReady:" + isDataReady); if (listenerSuccessTimes == 1 && TextUtils.equals(ttsId, mTtsId) && isDataReady) { listenerSuccessTimes++; } } });

3.16.2 TTS参数设置

如果您想通过SDK定制不同的合成音类型,可以在需要的时候调用下面的接口。

setTTS

/** * 设置TTS播报类型的接口 * 调用此接口则云端配置的合成音类型失效,此后的合成音类型都将由此接口来托管 * @param speaker 取值如:zhilingf, gdgm等,若取为null,则表示随机改为一个不同的类型 */ DDS.getInstance().getAgent().getTTSEngine().setSpeaker("zhilingf"); /** * 设置TTS播报类型的接口 * 调用此接口则云端配置的合成音类型失效,此后的合成音类型都将由此接口来托管 * @param speaker 取值如:zhilingf, gdgm等 * @param path 取值如:"sdcard/tts/zhilingf.bin",自定义合成间类型的全路径 */ DDS.getInstance().getAgent().getTTSEngine().setSpeaker("zhilingf","sdcard/tts/zhilingf.bin"); /** * 设置TTS播报语速的接口 * 调用此接口则云端配置的合成音语速失效,此后的合成音语速都将由此接口来托管 * * @param speed 语速,取值0.5-2.0,0.5语速最快,2.0语速最慢,对应控制台上合成音倍率值 */ DDS.getInstance().getAgent().getTTSEngine().setSpeed(1.0f); /** * 设置TTS播报音量的接口 * 调用此接口则云端配置的合成音音量失效,此后的合成音音量都将由此接口来托管 * * @param volume 音量大小,取值1-100 */ DDS.getInstance().getAgent().getTTSEngine().setVolume(50); /** * 设置TTS引擎为本地或者云端 * * @param mode 取值 TTSEngine.LOCAL(本地合成),TTSEngine.CLOUD(云端合成) */ DDS.getInstance().getAgent().getTTSEngine().setMode(TTSEngine.LOCAL); /** * 设置TTS播报的通道 * @param streamType AudioManager.STREAM_*的取值 * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().getTTSEngine().setStreamType(int streamType); /** * 设置TTS播报的通道(安卓7.0版本以上支持,并且需要配置K_AUDIO_USAGE和K_CONTENT_TYPE) * @param usage 取值: AudioAttributes.USAGE_* * @param contentType 取值: AudioAttributes.CONTENT_TYPE_* * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().getTTSEngine().setUsage(int usage, int contentType); /** * 设置TTS播报自定义录音 * @param customAudioList 自定义播报音频列表 * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().getTTSEngine().setCustomAudio(List<CustomAudioBean> customAudioList); /** * 设备抢焦点,默认为抢焦点 * @param enable true/false true: 抢焦点 false: 不抢焦点 */ DDS.getInstance().getAgent().getTTSEngine().enableFocus(boolean enable); /** * 设置TTS人设 * @param style 风格,humor:幽默;calm:沉稳;common:普通;简短:short; */ DDS.getInstance().getAgent().getTTSEngine().setStyle(String style); /** * 清除TTS人设 */ DDS.getInstance().getAgent().getTTSEngine().removeStyle(); /** * 设置TTS结束后延迟时间,单位ms * @param afterTime * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().getTTSEngine().setPlayAfterTime(int afterTime);

3.16.3 TTS参数获取

如果您想获取当前TTS配置的接口,可以调用如下接口:
getTTS

//返回String 当前使用的合成音类型,如:"zhilingf",获取失败返回null DDS.getInstance().getAgent().getTTSEngine().getSpeaker(); //返回float 当前合成音语速,返回值0.5-2.0,0.5语速最快,2.0语速最慢,获取失败返回 0 DDS.getInstance().getAgent().getTTSEngine().getSpeed(); //返回int 当前合成音音量,返回值1-100,获取失败返回 0 DDS.getInstance().getAgent().getTTSEngine().getVolume(); //返回List<CustomAudioBean> 当前自定义播报音频的列表 DDS.getInstance().getAgent().getTTSEngine().getCustomAudio(); //获取当前TTS人设 DDS.getInstance().getAgent().getTTSEngine().getStyle();

3.16.4 音频焦点处理模式

播放 TTS 时的音频焦点默认是 sdk 内部处理,也可交由外部处理

  1. 配置 AudioFocusMode
/** * 音频焦点处理模式<br> * 取值:internal or external<br> * internal:由SDK内部处理音频焦点的获取与释放<br> * external:由外部根据对话状态及TTS播报状态处理对话及TTS等的焦点问题,SDK不再获取或者释放焦点。 * 用户可以使用 <CODE>DDS.getInstance().getAgent().setAudioFocusCallback(audioFocusCallback)</CODE> 在回调里处理音频焦点<br> * 是否必需:否<br> * 默认值:internal */ ddsConfigBuilder.createTtsBuilder().setAudioFocusMode("external")
  1. 设置回调并在回调里处理音频焦点
private AudioFocusCallback audioFocusCallback = new AudioFocusCallback() { @Override public void onRequest() { Log.d(TAG, "onRequest()"); } @Override public void onAbandon() { Log.d(TAG, "onAbandon()"); } }; DDS.getInstance().getAgent().setAudioFocusCallback(audioFocusCallback);

3.17 唤醒相关设置

**唤醒词介绍:**当前除了主唤醒,唤醒词的类型还包括了 副唤醒词、命令唤醒词(快捷唤醒词)、打断唤醒词、快速唤醒词。

副唤醒词(Minor):如果您期望为设备增加一个别名,可以通过设置该类型唤醒词来完成。

命令唤醒词(快捷唤醒词/Command): 如果您期望在唤醒的时候执行一条指令,可以通过设置该类型唤醒词来完成。

打断唤醒词(ShortCut):如果您期望在唤醒的时候能同时打断语音播报,可以通过设置该类型唤醒词来完成。打断唤醒词唤醒后会把唤醒词作为识别结果发送给服务器,由服务器给出对话结果。

快速唤醒词(QuickStart):您想通过代码来控制快速唤醒,QuickStart词为类似“导航去”、“我想听”等,此类唤醒词只在oneshot模式下生效,作用为在未唤醒状态下语音输入“导航去天安门”,可直接进入对话流程。

3.17.1 开关唤醒

默认SDK加载后不会启动语音唤醒,您可以通过以下接口对需对语音唤醒进行控制。
wakeup

// 开启语音唤醒 DDS.getInstance().getAgent().getWakeupEngine().enableWakeup(); // 关闭语音唤醒 DDS.getInstance().getAgent().getWakeupEngine().disableWakeup(); // 获取主唤醒词列表 DDS.getInstance().getAgent().getWakeupEngine().getWakeupWords();

3.17.2 更新副唤醒词

如果您期望为设备增加一个副唤醒词,可以在需要的时候调用下面的接口。

参数说明:

1)word 副唤醒词,若设置null,则清空当前的副唤醒词。
2)pinyin 副唤醒词的拼音,形如:ni hao xiao chi。
3)threshold 副唤醒词的阈值,形如:0.120(取值范围:0-1)。若设置null,则自动估算。
4)greetings 副唤醒词的欢迎语,若设置null,则与主唤醒词保持一致。

updateMinorWakeupWord

// 实时更新副唤醒词(1.1.2.1版本及以上支持) WakeupWord minorWord = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小迟") .setThreshold("0.15") .addGreeting("我在"); DDS.getInstance().getAgent().getWakeupEngine().updateMinorWakeupWord(minorWord); //获取当前的副唤醒词 DDS.getInstance().getAgent().getWakeupEngine().getMinorWakeupWord();

3.17.3 更新命令唤醒词(快捷唤醒词)

如果您期望在唤醒的时候执行一条指令,可以在需要的时候调用下面的接口。命令唤醒词和DUI控制台所设置的快捷唤醒词是相同的功能,且互不影响,可同时存在,根据需求来选择设置方式。
参数说明:
1)actions 命令唤醒词指令,为string数组,不为null。
2)words 命令唤醒词,为string数组,不为null。
3)pinyins 命令唤醒词的拼音,形如:ni hao xiao chi,为string数组, 不为null。
4)thresholds 命令唤醒词的阈值,形如:0.120(取值范围:0-1),为string数组,不为null。
5)greetings 命令唤醒词的欢迎语,为string二维数组,不为null,每维对应一个唤醒词的欢迎语。

updateCommandWakeupWord

// 实时更新命令唤醒词-更新一条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord() .setPinyin("xia yi shou") .setWord("下一首") .setThreshold("0.18") .addGreeting("我在") .setAction("sys.next"); DDS.getInstance().getAgent().getWakeupEngine().updateCommandWakeupWord(commandWord); // 实时更新命令唤醒词-更新多条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord() .setPinyin("xia yi shou") .setWord("下一首") .setThreshold("0.18") .addGreeting("我在") .setAction("sys.next"); WakeupWord commandWord1 = new WakeupWord() .setPinyin("shang yi shou") .setWord("上一首") .addGreeting("我在") .setThreshold("0.20") .setAction("sys.play"); List<WakeupWord> commandList = new ArrayList<>(); commandList.add(commandWord); commandList.add(commandWord1); DDS.getInstance().getAgent().getWakeupEngine().updateCommandWakeupWords(commandList); //清空当前设置的命令唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearCommandWakeupWord();

3.17.4 更新打断唤醒词

如果您期望在唤醒的时候能同时打断语音播报,可以在需要的时候调用下面的接口。打断唤醒词暂不支持在DUI控制台设置。(说明:打断需要设备开启回声消除)
参数说明:
1)words 打断唤醒词,为string数组,不为null。
2)pinyins 打断唤醒词的拼音,形如:ni hao xiao chi,为string数组,不为null。
3)thresholds 打断唤醒词的阈值,形如:0.120(取值范围:0-1)为string数组,不为null。

updateShortcutWakeupWord

//实时更新打断唤醒词-更新一条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); DDS.getInstance().getAgent().getWakeupEngine().updateShortcutWakeupWord(fixWord); //实时更新打断唤醒词-更新多条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); WakeupWord fixWord1 = new WakeupWord() .setPinyin("da duan ba") .setWord("打断吧") .setThreshold("0.16"); List<WakeupWord> fixList = new ArrayList<>(); fixList.add(fixWord); fixList.add(fixWord1); DDS.getInstance().getAgent().getWakeupEngine().updateShortcutWakeupWords(fixList); //清空当前设置的打断唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearShortCutWakeupWord();

3.17.5 添加/删除指定的命令唤醒词(快捷唤醒词)

如果您期望添加或者移除一些命令唤醒词而保留之前的命令唤醒词(即控制台上的快捷唤醒词),可以调用下面的接口。
add remove

// 实时添加命令唤醒词-添加一条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord() .setPinyin("xia yi shou") .setWord("下一首") .setThreshold("0.18") .addGreeting("我在") .setAction("sys.next"); DDS.getInstance().getAgent().getWakeupEngine().addCommandWakeupWord(commandWord); // 实时添加命令唤醒词-添加多条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord() .setPinyin("xia yi shou") .setWord("下一首") .setThreshold("0.18") .addGreeting("我在") .setAction("sys.next"); WakeupWord commandWord1 = new WakeupWord() .setPinyin("shang yi shou") .setWord("上一首") .addGreeting("我在") .setThreshold("0.20") .setAction("sys.play"); List<WakeupWord> commandList = new ArrayList<>(); commandList.add(commandWord); commandList.add(commandWord1); DDS.getInstance().getAgent().getWakeupEngine().addCommandWakeupWords(commandList); // 实时移除命令唤醒词-移除一条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord().setWord("下一首"); DDS.getInstance().getAgent().getWakeupEngine().removeCommandWakeupWord(commandWord); // 实时移除命令唤醒词-移除多条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord().setWord("下一首"); WakeupWord commandWord1 = new WakeupWord().setWord("上一首"); List<WakeupWord> commandList = new ArrayList<>(); commandList.add(commandWord); commandList.add(commandWord1); DDS.getInstance().getAgent().getWakeupEngine().removeCommandWakeupWords(commandList); /** * 设置快捷命令唤醒词可唤醒,默认开启 * (在已经调用enableWakeup的情况下临时使用, disableWakeup情况下无效) */ DDS.getInstance().getAgent().getWakeupEngine().enableCommandWakeupWord() /** * 设置快捷命令唤醒词不可唤醒 */ DDS.getInstance().getAgent().getWakeupEngine().disableCommandWakeupWord()

3.17.6 添加/删除指定的打断唤醒词

如果您期望添加或者移除一些打断唤醒词而保留之前的打断唤醒词,可以在需要的时候调用下面的接口。

add remove

//实时添加打断唤醒词-添加一条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); DDS.getInstance().getAgent().getWakeupEngine().addShortcutWakeupWord(fixWord); //实时添加打断唤醒词-添加多条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); WakeupWord fixWord1 = new WakeupWord() .setPinyin("da duan ba") .setWord("打断吧") .setThreshold("0.16"); List<WakeupWord> fixList = new ArrayList<>(); fixList.add(fixWord); fixList.add(fixWord1); DDS.getInstance().getAgent().getWakeupEngine().addShortcutWakeupWords(fixList); //实时移除打断唤醒词-移除一条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord().setWord("打断"); DDS.getInstance().getAgent().getWakeupEngine().removeShortcutWakeupWord(fixWord); //实时移除打断唤醒词-移除多条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord().setWord("打断"); WakeupWord fixWord1 = new WakeupWord().setWord("打断吧"); List<WakeupWord> fixList = new ArrayList<>(); fixList.add(fixWord); fixList.add(fixWord1); DDS.getInstance().getAgent().getWakeupEngine().removeShortcutWakeupWords(fixList);

3.17.7 添加/删除指定的主唤醒词

如果您想通过代码来控制主唤醒词,可以调用下面的接口。
注意:通过此接口添加的主唤醒词会屏蔽控制台添加的主唤醒词。

主唤醒词

//实时添加主唤醒词-添加一条主唤醒词(1.1.2.1版本及以上支持) WakeupWord mainWord = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小迟") .addGreeting("我在") .setThreshold("0.15"); DDS.getInstance().getAgent().getWakeupEngine().addMainWakeupWord(mainWord); //实时添加主唤醒词-添加多条主唤醒词(1.1.2.1版本及以上支持) WakeupWord mainWord = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小迟") .addGreeting("我在") .setThreshold("0.15"); WakeupWord mainWord2 = new WakeupWord() .setPinyin("ni hao xiao le") .setWord("你好小乐") .addGreeting("我在") .setThreshold("0.15"); List<WakeupWord> mainWordList = new ArrayList<>(); mainWordList.add(mainWord); mainWordList.add(mainWord2); DDS.getInstance().getAgent().getWakeupEngine().addMainWakeupWords(mainWordList); //实时移除主唤醒词-移除一条主唤醒词(1.1.2.1版本及以上支持) WakeupWord mainWord = new WakeupWord().setWord("你好小迟"); DDS.getInstance().getAgent().getWakeupEngine().removeMainWakeupWord(mainWord); //实时移除主唤醒词-移除多条主唤醒词(1.1.2.1版本及以上支持) WakeupWord mainWord = new WakeupWord().setWord("你好小迟"); WakeupWord mainWord2 = new WakeupWord().setWord("你好小乐"); List<WakeupWord> mainWordList = new ArrayList<>(); mainWordList.add(mainWord); mainWordList.add(mainWord2); DDS.getInstance().getAgent().getWakeupEngine().removeMainWakeupWords(mainWordList); //更新多条主唤醒词的接口,覆盖原有的主唤醒词 WakeupWord mainWord = new WakeupWord() .setPinyin("ni hao xiao le") .setWord("你好小乐") .setThreshold("0.15") .addGreeting("我在"); WakeupWord mainWord2 = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小迟") .setThreshold("0.15") .addGreeting("我在"); List<WakeupWord> mainWordList = new ArrayList<>(); mainWordList.add(mainWord); mainWordList.add(mainWord2); DDS.getInstance().getAgent().getWakeupEngine().updateMainWakeupWords(mainWordList);

3.17.8 添加/删除指定的快速唤醒词

如果您想通过代码来控制快速唤醒,可以调用下面的接口。

QuickStart词为类似“导航去”、“我想听”等,此类唤醒词只在oneshot模式下生效,作用为在未唤醒状态下语音输入“导航去天安门”,可直接进入对话流程。

quickStart

//添加多条QuickStart词 WakeupWord quickStartWord = new WakeupWord() .setPinyin("dao hang qu") .setWord("导航去") .setThreshold("0.15"); List<WakeupWord> quickStartWordList = new ArrayList<>(); quickStartWordList.add(quickStartWord); DDS.getInstance().getAgent().getWakeupEngine().addQuickStartWords(quickStartWordList); //实时移除一条QuickStart词 WakeupWord quickStartWord = new WakeupWord().setWord("导航去"); DDS.getInstance().getAgent().getWakeupEngine().removeQuickStartWord(quickStartWord); //实时移除多条QuickStart词 WakeupWord quickStartWord = new WakeupWord().setWord("导航去"); List<WakeupWord> quickStartWordList = new ArrayList<>(); quickStartWordList.add(quickStartWord); DDS.getInstance().getAgent().getWakeupEngine().removeQuickStartWords(quickStartWordList); //更新多条QuickStart词的接口,覆盖原有的QuickStart词 WakeupWord quickStartWord = new WakeupWord() .setPinyin("dao hang qu") .setWord("导航去") .setThreshold("0.15"); List<WakeupWord> quickStartWordList = new ArrayList<>(); quickStartWordList.add(quickStartWord); DDS.getInstance().getAgent().getWakeupEngine().updateQuickStartWords(quickStartWordList); //清空QuickStart词 DDS.getInstance().getAgent().getWakeupEngine().clearQuickStartWords();

3.17.9 oneshot开关

如果您想动态更改唤醒模式,可以在需要的时候调用下面的接口。

oneshot

//开启oneshot DDS.getInstance().getAgent().getWakeupEngine().enableOneShot(); //关闭oneshot DDS.getInstance().getAgent().getWakeupEngine().disableOneShot(); /** * 获取当前oneshot开关状态,如果有本地接口调用开关,以本地接口调用优先。 */ DDS.getInstance().getAgent().getWakeupEngine().getOneshotState()

3.17.10 设置环麦增强角度

// 如果您想动态指定环麦增强角度,可以在需要的时候调用下面的接口。

DDS.getInstance().getAgent().getWakeupEngine().setWakeupDoa(int doa);

3.17.11 设置车载双麦模式

// 如果您想动态指定车载双麦的驾驶模式,可以在需要的时候调用下面的接口。

// 驾驶模式, 0:定位模式, 1:主驾模式, 2: 副驾模式 3:全车模式 DDS.getInstance().getAgent().getWakeupEngine().setWakeupMode(int mode);

3.17.12 获取当前唤醒内核的版本号

如果您想获取当前唤醒内核的版本号,可以在需要的时候调用下面的接口。

// 该接口废弃,获取到的版本号在 2.0.0.0 版本上无效 DDS.getInstance().getAgent().getWakeupEngine().getWakeupVersion();

3.17.13 设置是否允许识别过程中响应唤醒

如果您想设置是否允许识别过程中响应唤醒,可以在需要的时候调用下面的接口。

//enable: true. 允许识别过程中响应唤醒, false. 不允许识别过程中响应唤醒 DDS.getInstance().getAgent().getWakeupEngine().enableWakeupWhenAsr(boolean enable);

3.17.14 设置自定义欢迎语回调

如果您想设置自定义欢迎语,可以在需要的时候调用下面的接口, 该方法是阻塞方法,不建议在该方法内部做耗时操作。

DDS.getInstance().getAgent().getWakeupEngine().setWakeupCallback(WakeupCallback wakeupCallback);DDS.getInstance().getAgent().getWakeupEngine().setWakeupCallback(new WakeupCallback() { /** * * @param wakeupResult 唤醒状态 * <p> * wakeupResult.optString("word");// 唤醒词 * <p> * wakeupResult.optString("type");// 唤醒状态: major: 正常唤醒 command: 命令唤醒 interrupt: 打断唤醒 * <p> * wakeupResult.optString("gender");// 性别: male / female * <p> * wakeupResult.optString("action");// 命令唤醒的动作 * <p> * @return */ @Override public JSONObject onWakeup(JSONObject wakeupResult) { JSONObject result = new JSONObject(); try { result.put("greeting", "你好"); } catch (JSONException e) { e.printStackTrace(); } return result; });

3.17.15 获取主唤醒词列表

获取主唤醒词列表,默认返回WakeupEngine所设置的主唤醒词,如果没有设置过则返回产品配置的主唤醒词。

DDS.getInstance().getAgent().getWakeupEngine().getMainWakeupWords();

3.17.16 设置Beamforming监听器

设置Beamforming监听器, 实时回调Beamforming音频

DDS.getInstance().getAgent().getWakeupEngine().setBfListener(BfListener listener);

3.17.17 设置唤醒模块的模式/低功耗模式

  • 设置唤醒模块的模式(信号处理模块不变,环麦中暂不支持)
DDS.getInstance().getAgent().getWakeupEngine().setWakeupSwitch(boolean mode);
  • 开启低功耗模式后, 唤醒功能不可用, cpu会降低 (此方法已废弃)
DDS.getInstance().getAgent().getWakeupEngine().setLpSwitch(boolean lpSwitch);

3.17.18 就近唤醒

如果你有使用就近唤醒的需求, 可以参照下面的配置与设置(目前支持线性双麦/线性四麦的就近唤醒, 1.2.12.1-1.2.15.1的版本暂不支持就近唤醒)

1. 设置major参数为true (从1.2.8.1版本开始, 不再需要此配置) config.addConfig("MAJOR", "true"); 2. 设置K_USE_NEAR_WAKEUP为true, 表示使用就近唤醒 config.addConfig(DDSConfig.K_USE_NEAR_WAKEUP, "true"); 3. 在设置唤醒词的时候设置major字段 WakeupWord minorWord = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小驰") .setThreshold("0.25") .setMajor("1")(从1.2.8.1版本开始, 不再需要此配置) .addGreeting("小驰回来了"); DDS.getInstance().getAgent().getWakeupEngine().addMainWakeupWord(minorWord); 4. 就近唤醒运行过程中可以动态设置一些参数 /** * 设置就近唤醒参数与调度参数<p> * 仅在就近唤醒状态下生效(线双麦/线四麦) */ DDS.getInstance().getAgent().getWakeupEngine().setNearWakeupParams(JSONObject paramsObj)

3.17.19 拦截唤醒结果

/** * 拦截唤醒结果 wakeup.result, * * @param isCatch 是否拦截唤醒结果 true-拦截 ;false-取消拦截 * @return * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getWakeupEngine().catchWakeupResult(boolean isCatch)

3.17.20 设置 VOIP 音频回调

  1. 配置 VOIP 信息
config.addConfig(DDSConfig.K_VOIP_TYPE, "line_2");
  1. 设置 VOIP 回调
/** * 设置voip监听器, 时时回调voip音频 * * @param listener */ public void setVoipListener(VoipListener listener)
  1. 启动 VOIP 功能
// 调用 setEnableVoip(true) 时 voipNode 才会注册消息,接收音频数据 DDS.getInstance().getAgent().setEnableVoip(true);
  1. 录音后就会从回调里回调 voip 音频

  2. 取消 VOIP 回调

// 取消 VOIP 回调 DDS.getInstance().getAgent().getWakeupEngine().setVoipListener(null); // 停用 voip 功能 DDS.getInstance().getAgent().setEnableVoip(false);

3.17.21 更新唤醒词

支持多个不同类型的唤醒词同时配置

List<WakeupWordIntent> wakeupWordIntentList = new ArrayList<>(); // 添加主唤醒词 WakeupWordIntent wakeupWordIntent0 = new WakeupWordIntent(WakeupType.ADD_MAIN) .setPinyin("ni hao xiao le") .setWord("你好小乐") .setThreshold("0.16") .addGreeting("小乐来了"); WakeupWordIntent wakeupWordIntent1 = new WakeupWordIntent(WakeupType.ADD_COMMAND) .setPinyin("kai shi bo fang") .setWord("开始播放") .setThreshold("0.18") .addGreeting("好的,开始播放") .setAction("sys.play"); WakeupWordIntent wakeupWordIntent2 = new WakeupWordIntent(WakeupType.ADD_SHORTCUT) .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); wakeupWordIntentList.add(wakeupWordIntent0); wakeupWordIntentList.add(wakeupWordIntent1); wakeupWordIntentList.add(wakeupWordIntent2); DDS.getInstance().getAgent().getWakeupEngine().updateWakeupWords(wakeupWordIntentList);

3.17.22 动态设置对话模式

/** * 动态设置对话模式<br> * 唤醒后是否进入对话<br> * <p> * 取值:dialog or partner<br> * dialog:唤醒默认进入识别及对话<br> * partner:唤醒后不会主动进入识别及对话,同时唤醒后会收到sys.wakeup.result的message消息<br> * 是否必需:否<br> * 默认值:dialog<br> * * @param type 对话模式 partner */ DDS.getInstance().getAgent().getWakeupEngine().updateWkupRouter(String type)

3.17.23 增强唤醒(可见即可说和全局免唤醒)

增强唤醒使用本地识别功能识别设置的唤醒词,当达到阈值(默认0.63)时即认为唤醒成功.

功能特性

• 支持两个字的唤醒词

• 支持同一个唤醒词写入很多类型说法(目前最大可支持500个,延时200ms)

• 支持中英文唤醒(打开wifi、打开音乐); 中英文功能从 WTK v1.9.39 开始支持

• 唤醒词设置为打开导航,可支持说“哈哈打开导航”不响应(极大减小误唤醒)

使用场景

• 解决相似说法串到唤醒,比如定义“打开导航”,说“开导航”也能唤醒

• 解决语音起名字及手动设置唤醒词设置为“思必驰”“小小思”等容易误唤醒问题

• 解决开发者定义唤醒词每次都需要思必驰提供阈值,开发者只需要输入中文即可

• 解决在导航列表tts播报时说分词容易误选择问题,比如导航列表有“天安门”“北京天安门”“天安门地铁站“,以前用唤醒说”天安门地铁站“会响应”天安门“

使用方法
  1. 初始化
  • 打开增强唤醒功能
config.addConfig(DDSConfig.K_SUPPORT_INCREMENT_WAKEUP, "true");
  • 开启可见即可说
JSONObject ctrlObj = new JSONObject(); ctrlObj.put("type", "hotWord"); DDS.getInstance().getAgent().getWakeupEngine().enableIncrementWakeup(ctrlObj);
  • 开启全局免唤醒
JSONObject ctrlObj = new JSONObject(); ctrlObj.put("type", "globalWord"); DDS.getInstance().getAgent().getWakeupEngine().enableIncrementWakeup(ctrlObj);
  • 开启可见即可说 + 全局免唤醒
    DDS.getInstance().getAgent().getWakeupEngine().enableIncrementWakeup();
  1. 优先级设置
  • 如果可见即可说和全局免唤醒词有相同的词,可见即可说优先
DDS.getInstance().getAgent().getWakeupEngine().setIncrementPriority(WakeupPriorityType.HOTWORD_FIRST);
  • 如果可见即可说和全局免唤醒词有相同的词,全局免唤醒优先
DDS.getInstance().getAgent().getWakeupEngine().setIncrementPriority(WakeupPriorityType.GLOBALWORD_FIRST);
  1. 使用全局免唤醒词
可使用增强唤醒的唤醒词类型: * 命令唤醒词(Command) * 打断唤醒词(ShortCut) WakeupWord commandWord = new WakeupWord() .setWord("开始播放") .setThreshold("0.18") .setIncrementWord("true") .addGreeting("好的,开始播放") .setAction("sys.play"); WakeupWord commandWord1 = new WakeupWord() .setWord("下一首") .setThreshold("0.18") .setIncrementWord("true") .addGreeting("下一首nlg") .setAction("command.next"); List<WakeupWord> wakeupWordList = new ArrayList<>(); wakeupWordList.add(commandWord); wakeupWordList.add(commandWord1); DDS.getInstance().getAgent().getWakeupEngine().addCommandWakeupWords(wakeupWordList); WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setIncrementWord("true") // 使用增强唤醒 .setThreshold("0.18"); List<WakeupWord> wakeupWordList = new ArrayList<>(); wakeupWordList.add(fixWord); DDS.getInstance().getAgent().getWakeupEngine().updateShortcutWakeupWords(wakeupWordList); // 删除增强唤醒的打断唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearIncrementShortCutWakeupWord(); // 删除增强唤醒的命令唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearIncrementCommandWakeupWord(); // 删除增强唤醒的所有唤醒词,包括打断唤醒词和命令唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearIncrementWakeupWord();
  1. 使用可见即可说
HotWords.HotWord hotWord1 = new HotWords.HotWord() .setOrigin("抖音最全中文歌")//页面原始词汇 .addSegment("抖音最全")//分词内容 .addCustom("id", "123123")//上层自定义扩展字段 .addCustom("name", "music");//上层自定义扩展字段 HotWords.HotWord hotWord2 = new HotWords.HotWord() .setOrigin("开始播放") .addCustom("id", "123123"); HotWords.Slots slots = new HotWords.Slots() .setSlot("dynamic_slot") .addHotWord(hotWord1) .addHotWord(hotWord2); HotWords hotWords = new HotWords() .setScene("default_scene") .addSlots(slots); DDS.getInstance().getAgent().getWakeupEngine().updateHotWords(hotWords);

3.18 数据上传接口

开发者调用此接口来上传一些设备的信息,这些信息可以在本地使用,也可以在对话中获取,作为对话决策的一部分。
数据点

/** * 更新设备状态,产品级的配置。比如:定位信息,设备硬件状态等 * 更新结果可以通过sys.upload.result消息来获取 * * @param intent 请求的ContextIntent对象,包括key和value * 如:ContextIntent intent = new ContextIntent("status","{\"platform\":\"Android\"}"); * updateProductContext(intent); * 技能里通过$context.system.settings.status.platform$即可获取到"Android" * @return 请求的ID,用于追踪sys.upload.result * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().updateProductContext(ContextIntent intent); /** * 更新技能配置,需要调用ContextIntent.setSkillId设置技能ID * 更新结果可以通过sys.upload.result消息来获取 * * @param intent 请求的ContextIntent对象,包括key、value、skillId。 * 如:ContextIntent intent = new ContextIntent("status","{\"platform\":\"Android\"}"); * intent.setSkillId("xxxxxxxxxxxxxxxxx"); * updateSkillContext(intent); * 该技能里通过$context.skill.settings.status.platform$即可获取到"Android",其他技能则无法使用。 * @return 请求的ID,用于追踪sys.upload.result * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().updateSkillContext(ContextIntent intent); /** * 获取产品的配置信息 * * @param inputKey 配置项名称 * @return 配置项值 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getProductContext(String inputKey); /** * 获取技能的配置信息 * * @param skillId 技能ID * @param inputKey 配置项名称 * @return 配置项值 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getSkillContext(String skillId, String inputKey); /** * 更新热词识别接口(请求级别) * 每次请求都会带上热词词库, 例如: [{"type": "vocab", "name": "sys.联系人", "data":["章大樵"]}] * 本接口为覆盖式接口,以最新设置为准,如需要清除请求空参数即可 * * @param phraseHints 需要更新的热词识别列表 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().updatePhraseHints(PhraseHintsIntent... phraseHints);

3.19 获取性别/年龄信息

  1. 在初始化DDS SDK的时候加入如下配置:
config.addConfig(DDSConfig.K_USE_GENDER, "true"); config.addConfig(DDSConfig.K_USE_AGE, "true");
  1. 然后订阅 message: sys.dialog.start
    收到消息内容类似:{"reason":"wakeup.major","gender":"male","age":"adult"}

gender就是性别字段,age就是年龄字段,性别年龄信息只有在主唤醒词唤醒后,才会发出。

3.20 动态更新云端识别模型

/** * 更新云端识别的模型名字, * 在调用完该接口后,下一次对话开始时生效,并一直用该模型,除非客户端再调用该接口设置为其他的模型 * * @param asrModel 云端识别的模型名字,有aihome, airobot等, * 默认为dui控制台配置的模型资源, * 如果填null,则表示清除之前本地配置的模型名,之后会使用dui控制台配置的模型资源 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getASREngine().updateAsrModel("aihome"); /** * 获取当前使用的云端识别的模型 * * @return asrModel 当前使用的云端识别的模型, 如果失败会返回null,或者抛出DDSNotInitCompleteException * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getASREngine().getAsrModel();

3.21 快捷指令

订阅 sys.dm.inspire 消息,用于接收快捷指令。只在 半双工 模式有该功能

DDS.getInstance().getAgent().subscribe(new String[]{..., "sys.dm.inspire", ...}, new MessageObserver() { @Override public void onMessage(String message, String data) { if("sys.dm.inspire".equals(message)) { try { JSONObject obj = new JSONObject(data); JSONArray inspire = obj.getJSONArray("inspire"); ... } catch (JSONException e) { e.printStackTrace(); } catch (DDSNotInitCompleteException e) { e.printStackTrace(); } } } });

inspire 即为快捷指令的集合数组,通过解析 inspire 来处理指令。inspire 数据结构如下:

inspire:[ { speak:{ "type":"text", "text":"123" }, widget:{ "type":"text", ... } }, { speak:{ "type":"text", "text":"抱歉由于设备离线,操作失败,请再试一次" }, widget:{ "type":"list", ... } }, ... ]

上述结构中,speak 并不是必须的字段, widget 是必须的。

widget即为需要需要展示出来的控件样式,其中 type 字段代表控件样式的类型,有 text/list/web/image/media…

关于 widget 不同控件样式的不同数据结构,可参考: https://www.duiopen.com/docs/ct_UI 。举例:list 对应这 context.widget.list,以此类推

3.22 全双工模式

3.22.1 模式切换

// 设置dds模式 DDS.getInstance().getAgent().setDuplexMode(Agent.DuplexMode.HALF_DUPLEX);// 半双工,一问一答模式 DDS.getInstance().getAgent().setDuplexMode(Agent.DuplexMode.FULL_DUPLEX);// 全双工模式 注意: 在切换全双工时如需要唤醒还请调用: DDS.getInstance().getAgent().getWakeupEngine().enableWakeup(); // 获取dds模式 DDS.getInstance().getAgent().getDuplexMode();

3.22.2 全双工不支持接口与功能

  1. 由于全双工模式为全程识别状态, 所以开启识别和 startListening 不可同时使用 [3.13开启/终止识别](#3.13 仅开启识别)

  2. 由于全双工模式为全程识别状态, 在模拟半双工下的打断唤醒由识别代替,主唤醒词正常开启下一轮对话

  3. 由于全双工模式下暂时没有快捷指令的使用场景, 所以快捷指令在全双工模式下不可用 [3.21 快捷指令](#3.21 快捷指令)

  4. 全双工模式下默认使用 vad 功能, 比如 oneshot 功能就需要打开 vad。配置 K_USE_VAD_IN_FULLDUPLEX 为 true(默认为true)

3.22.3 全双工下跳过Vad超时检测

// 在全双工模式下, 如果你想跳过Vad的检测直接进入识别状态则可以调用下面的接口 DDS.getInstance().getAgent().getASREngine().killVadTimeoutInFullDuplex();

3.23 对话结果支持内容修改

此接口支持修改对话中的语音播报,修改文本展示等功能

// 从1.2.8.1版本开始支持 DDS.getInstance().getAgent().setDMTaskCallback(new DMTaskCallback() { @Override public JSONObject onDMTaskResult(JSONObject dmTaskResult, Type type) { if (type == DMTaskCallback.Type.DM_OUTPUT) {// 处理dm的消息 // 显示 String display = dmTaskResult.optString("display"); // 播报 String nlg = dmTaskResult.optString("nlg"); Log.d(TAG, "display = " + display); Log.d(TAG, "nlg = " + nlg); try { dmTaskResult.put("display", "修改之后的显示"); dmTaskResult.put("nlg", "修改之后的播报"); dmTaskResult.put("speakUrl", null); // 不设置为null的话,播放的还是原来的音频 } catch (JSONException e) { e.printStackTrace(); } } else if (type == DMTaskCallback.Type.CDM_ERROR) {// // 处理对话异常的消息 // 播报 String nlg = dmTaskResult.optString("nlg"); Log.d(TAG, "nlg = " + nlg); try { dmTaskResult.put("nlg", "修改之后的播报"); dmTaskResult.put("speakUrl", null); // 不设置为null的话,播放的还是原来的音频 } catch (JSONException e) { e.printStackTrace(); } } return dmTaskResult; } });

3.24 声纹

声纹注册、验证的词也是主唤醒词,唤醒后会使用唤醒音频做声纹的注册、验证等操作

  1. 打开声纹配置
config.addConfig(DDSConfig.K_VPRINT_ENABLE, "true"); // 是否开启声纹功能 DDS.getInstance().getAgent().getWakeupEngine().useVprintInWakeUp(true) // 动态设置是否在唤醒中启用声纹
  1. 使用声纹
// 注册声纹监听器 VprintEngine.getInstance().setVprintListener(new VprintListener() { @Override public void onState(String state) { Log.e(TAG, "onState = " + state); } @Override public void onResults(String result) { Log.e(TAG, "onResults = " + result); } @Override public void onError(String error) { Log.e(TAG, "onError = " + error); } }); // 注册声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.REGISTER) .setTrainNum(3) .setOutChannelNum(2) .setSnrThresh(8.67f) .setUserId("userId") .setVprintWord("ni hao xiao chi") .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 更新声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.UPDATE) .setTrainNum(3) .setOutChannelNum(2) .setSnrThresh(8.67f) .setUserId("userId") .setVprintWord("ni hao xiao chi") .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 追加声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.APPEND) .setTrainNum(3) .setOutChannelNum(2) .setSnrThresh(8.67f) .setUserId("userId") .setVprintWord("ni hao xiao chi") .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 删除一条声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.UNREGISTER) .setTrainNum(3) .setOutChannelNum(2) .setUserId("userId") .setVprintWord("ni hao xiao chi") .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 删除所有声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.UNREGISTER_ALL) .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 使用声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.TEST) .setOutChannelNum(2) .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 获取声纹模型 String result = VprintEngine.getInstance().getMode(); // 停止使用声纹 VprintEngine.getInstance().stop();

3.25 配合 dca app 使用

通过语音控制家居设备时需要对话带上 dca app 里用户的信息。

TokenListener tokenListener = new TokenListener() { @Override public void onSuccess(TokenResult tokenResult) { Log.d(TAG, "onSuccess() " + tokenResult); success = true; } @Override public void onError(int errorId, String error) { Log.d(TAG, "errorId:" + errorId + " error:" + error); } }; AuthInfo authInfo = new AuthInfo(); authInfo.setUserId("8458"); authInfo.setCodeVerifier("doeifkgdgurgg"); authInfo.setClientId("dfgoeigjergndfgdfg"); authInfo.setAuthCode("dfgerhrh6564hgrh4hr"); authInfo.setRedirectUri("http://dui.aadf.bb.cc"); DDS.getInstance().getAgent().setAuthCode(authInfo, tokenListener);

3.26 自定义提示语

JSONObject jsonObject = new JSONObject(); try { jsonObject.put("071304", "自定义vad超时"); } catch (JSONException e) { e.printStackTrace(); } /** * 此接口支持动态修改 custom tips * * @param obj <p> * 取值:JSON字符串,如:<br> * { * "71304":"这是识别结果为空的自定义播报", * "71305":"这是语义结果为空的自定义播报", * "71308":"这是进入闲聊技能的自定义播报", * "713**":"*****" * }<br> * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().setCustomTips(jsonObject);

3.27 一句话多意图

背景:
一句话多意图: 一句话里包括多个意图或动作,比如:调节空调和车窗
使用场景:智能家居或车联网等场景
此功能需要云端配置,如需使用请和思必驰客户经理联系

String[] mTopic = new String[]{ "sys.native.inspire" }; NativeCommandObserver nativeCommandObserver = new NativeCommandObserver() { @Override public JSONObject onNativeCommandCall(String command, String data) { try { if (TextUtils.equals(command, "sys.native.inspire")) { AILog.d(TAG, "command = " + command); AILog.d(TAG, "data = " + data); JSONObject dataObj = new JSONObject(data); JSONArray inspireArr = dataObj.optJSONArray("inspire"); JSONObject selectInspireItem = null;// 用户选择要dds执行的一个item for (int i = 0; i < inspireArr.length(); i++) { JSONObject itemObj = inspireArr.optJSONObject(i); // TODO 业务逻辑,判断哪一个item是要后续dds处理的 selectInspireItem = itemObj; JSONObject commandObj = itemObj.optJSONObject("command"); String commandApi = commandObj.optString("api"); // TODO 业务逻辑,解析command,处理command JSONObject speakObj = itemObj.optJSONObject("speak"); String text = speakObj.optString("text"); // 模拟播报与output DDS.getInstance().getAgent().getTTSEngine().speak(text, 0); DDS.getInstance().getAgent().getBusClient().publish("context.output.text", new JSONObject().put("text", text).toString()); SystemClock.sleep(2000); } // 返回最终要播报的文本 return JSONObjectUtil.create() .put("nlg", "已全部打开") .put("selectInspireItem", selectInspireItem)// 回传了选择的item,则修改nlg播报无效 .build(); } } catch (Exception e) { e.printStackTrace(); } return null; } }; DDS.getInstance().getAgent().subscribeSync(mTopic, nativeCommandObserver);

四、DDSConfig 配置项

DDSConfigBuilder 构造方法需要设置 产品信息duicore 资源 ,最简单的demo无需设置其它即可以单麦方式运行。

DDSConfigBuilder ddsConfigBuilder = new DDSConfigBuilder( productId, aliasKey, apiKey, productKey, productSecret, duicoreZip )

ddsConfigBuilder 包含很多配置,现按功能分为以下子类:

  1. ddsConfigBuilder.createCommonBuilder() [4.1 设备信息、使用目录、VOIP功能 等通用配置](#4.1 设备信息、使用目录、VOIP功能 等通用配置)
  2. ddsConfigBuilder.createNodeBuilder() [4.2 sdk 功能开关](#4.2 sdk 功能开关)
  3. ddsConfigBuilder.createWakeupBuilder() [4.3 麦克风类型、唤醒、声纹、asrpp、Oneshot 配置](#4.3 麦克风类型、唤醒、声纹、asrpp、Oneshot 配置)
  4. ddsConfigBuilder.createDmBuilder() [4.4 对话配置](#4.4 对话配置)
  5. ddsConfigBuilder.createTtsBuilder() [4.5 TTS、播放 配置](#4.5 TTS、播放 配置)
  6. ddsConfigBuilder.createAsrBuilder() [4.6 识别配置](#4.6 识别配置)
  7. ddsConfigBuilder.createRecorderBuilder() [4.7 录音配置](#4.7 录音配置)
  • ddsConfigBuilder 还可通过以下方法添加额外配置
ddsConfigBuilder.addConfig(String key, Object value)

下面简单介绍一下配置项,详细说明请看 DDSConfig 配置请参见 javadoc在线文档

  • 定制资源集成差异
    v2.0.0.1 及以上版本不仅支持系统目录,也支持放入 assets 目录。v1.X.X.X 版本只支持 sdcard 系统目录配置

使用方式如下:

  1. assets 目录配置方式: setWakeupBin(“wakeup.bin”)
  2. 系统目录配置方式: setWakeupBin("/sdcard/sd/wakeup.bin")

4.1 设备信息、使用目录、VOIP功能 等通用配置

ddsConfigBuilder.createCommonBuilder() .setCustomZip("product.zip") // 使用内置产品资源包, 从官网下载的产品资源包一般叫 product.zip .setDeviceName("XXXXXX") // 设备 DeviceName, 用来标识设备的标识符 .setDeviceId("XXXXXX") // 设备ID。用于授权认证的原始设备ID,请务必确保设备ID唯一 .setAccessToken("XXXXXX") // 保留字段,上传、升级时使用 .setCachePath("/sdcard/cache") // 缓存文件保存目录,默认 /sdcard/Android/data/包名/cache .setCacheSize(500000) // 缓存文件大小限制。自定义缓存目录时必需 .setCustomHome("/sdcard/aispeech") // 产品资源存放的目录,默认 /data/data/com.xxx/files/dds/custom/ .setDuicoreHome("/sdcard/aispeech") // duicore 资源存放的目录,默认 /data/data/包名/files/dds/duicore/ .setInitTimeout(60) // SDK初始化超时时间,单位秒。默认 60秒 .setCustomSha256("XXXXXX") // 自定义授权证书的SHA256, sdk 授权时会检查包名和签名的SHA256,第三方加固后可能改变 .setCustomBackupZipUserDefined("/sdcard/aispeech") // 产品资源包保存路径 .setDuicoreBackupZipUserDefined("/sdcard/aispeech") // duicore 资源包保存路径 .setUserId("XXXXXX") // 用户ID 保留字段 .setVoipType("line_4") // 使用 voip 功能时的类型,现在支持线性双麦(line_2)和线性4麦(line_4) .setVoipResBin("voip.bin") // voip 的资源的路径。assets目录下为压缩包名称,其他目录为绝对路径 .setOfflineProfileName(".profile") // assets目录下的离线授权文件名 .enableUpdateDuicore(false) // 是否更新 duicore 资源。 默认 false .enableDynamicUiAgentPort(false) // 是否使用 uiagent 动态端口号,默认 false .enableReplaceProfile(true) // 试用授权是否连网更新成在线授权 默认 true .setProductLicenseId("XXXXXX") // 产品licenseID .setProductVersion("2") // 连接指定产品版本的对话服务 .enableUpdateNotification(true); // 是否使用内置的资源更新通知栏 默认 true

4.2 sdk 功能开关

sdk 需要使用到的功能开关配置。

包括推送、大数据、socket节点、唤醒、声纹、VAD节点、H5节点 等

ddsConfigBuilder.createNodeBuilder() .enablePush(true) // 是否开启推送功能。默认 true .enableSocket(true) // 是否开启 socket 节点功能。默认 true .enableUpdate(true) // 是否开启数据更新功能。默认 true .enableUpload(true) // 是否开启大数据上传功能。默认 true .enableVad(true) // 是否开启 VAD 节点功能。默认 true .enableWakeup(true) // 是否开启唤醒节点功能。默认 true .enableIncrementWakeup(false) // 是否支持本地识别增量更新。默认 false .enableVprint(false) // 是否开启声纹功能。默认 false .enableH5(false); // 是否开启H5节点功能。默认 false

4.3 麦克风类型、唤醒、声纹、asrpp、Oneshot 配置

ddsConfigBuilder.createWakeupBuilder() .setMicType(0) // 硬件麦克风采集模组的类型。即 单麦、双麦、线性4麦、环形6麦等。 详情请看:4.3.1 MicType 说明 .setWakeupBin("wakeup.bin") // 设置唤醒资源 .setBeamformingBin("ULA_2.bin") // 麦克风阵列 beamforming 资源 .setAecBin("aec.bin") // 麦克风阵列AEC资源 .setIncrementWakeupBin("increment.bin") // 增强唤醒的 asr 资源,增强唤醒是使用 本地asr 实现的 .setVprintBin("vprint.bin") // 声纹资源 .enableVprintInWakeup(false) // 是否在唤醒中使用声纹判断,默认 false。诺设置为 true,需开启声纹功能 .enableSspe(false) // 多麦是否使用 sspe,默认 false .setSspeBin("sspe.bin") // 麦克风阵列 sspe 资源 .enableAge(false) // 是否使用年龄识别,默认 false .setAgeBin("age.bin") // 年龄识别资源 .enableGender(false) // 是否使用性别识别,默认 false .setGenderBin("gender.bin") // 性别识别资源 .enableNearWakeup(false) // 是否使用就近唤醒,默认 false .setNearWakeupDebounceWnd(450) // 就近唤醒防抖窗口期,单位毫秒 .setNearWakeupWkpWnd(300) // 就近唤醒决策窗口期,单位毫秒 .setNearWakeupMdsBin("mds.bin") // 就近唤醒的 mds 资源 .setOneshotMidTime(500) // 详情请看:4.3.2 OneShot配置 .setOneshotEndTime(0) // 详情请看:4.3.2 OneShot配置 .enableCustomGreeting(true) // 是否使用自定义欢迎语功能,默认 true .enableWakeupInAsr(true) // 识别过程中是否响应唤醒,默认 true .setAecMode("internal") // AEC(回声消除)模式,设置 external 后,单麦播放 tts 时唤醒节点不会停止,打断唤醒词仍可使用 .setWakeupRouter("dialog"); // 唤醒后的路由,唤醒后是否进入对话。取值:dialog(唤醒默认进入识别及对话) or partner(唤醒后不会主动进入识别及对话,同时唤醒后会收到sys.wakeup.result的message消息)

4.3.1 MicType 说明

setMicType 配置说明

设置硬件麦克风采集模组的类型

默认使用 DUI 控制台上产品的对应配置项。若客户端通过SDK配置此项后,则以客户端配置为准

取值:

  • 0:单麦(1路音频)默认
  • 1:单麦回消(1路音频+1路参考音)
  • 2:线性四麦(4路音频)
  • 3:环形六麦(6路音频)
  • 4:车载双麦(废弃)
  • 5:家居双麦(2路音频)
  • 6:环麦4麦(4路音频)
  • 7:车载双麦(2路音频)
  • 8:线性六麦(6路音频)
  • 10:线性八麦(8路音频)

4.3.2 OneShot配置

setOneshotMidTime 和 setOneshotEndTime 配置说明

  • 取值:毫秒
  • 若MIDTIME为0 & ENDTIME为0,唤醒后进入识别;若VAD检测超时,则直接退出对话
  • 若MIDTIME为0 & ENDTIME不为0,唤醒后进入识别;若ENDTIME超时,则直接退出对话
  • 若MIDTIME不为0 & ENDTIME为0,唤醒后进入识别;若MIDTIME超时,则播放欢迎语,继续识别(默认)
  • 若MIDTIME不为0 & ENDTIME不为0,唤醒后进入识别;若MIDTIME超时,则播放欢迎语,继续识别,若ENDTIME超时,则直接退出对话
  • 默认值:MIDTIME=500ms,ENDTIME=0ms

4.4 对话配置

对话相关配置

  • 设置对话模式是半双工还是全双工配置已经删除,请用 Agent.setDuplexMode(final DuplexMode duplexMode) 接口设置
ddsConfigBuilder.createDmBuilder() .enableVadInFullduplex(true) // 全双工模式下是否使用 vad,默认 true .enableTipsInFullduplex(true) // 全双工中是否使用消息的提示音,默认 true .enableLocalDDS(false) // 是否使用本地对话,默认 false .setCustomTips(customTips) // 指定对话错误码的 TTS 播报内容 .setClickMode(0) // 点击模式,调用 Agent#avatarClick 后sdk的响应模式。 .setDmTimeout(10000) // 对话响应超时时间。单位 毫秒。默认 10000 ms .setNativeApiTimeout(10000); // nativeApi 超时时间。单位 毫秒。默认 10000 ms

4.5 TTS、播放 配置

ddsConfigBuilder.createTtsBuilder() .enableExoPlaySpeakUrl(false) // 是否使用 exoPlayer 播报 tts 的 url,默认 false .enableOptmization(false) // 本地 TTS 是否开启内存优化,默认 false .setAudioFocusMode("internal") // 音频焦点处理模式。internal sdk内部处理(默认),external 使用方式见:3.16.4 音频焦点处理模式 .setTtsMode("internal") // TTS播报模式,internal sdk内部合成播报, external 用户负责合成播报,使用方式见:3.9 外部TTS引擎 .setStreamType(AudioManager.STREAM_MUSIC) // 内置播放器的 STREAM 类型,默认 AudioManager.STREAM_ALARM .setAudioUsage(AudioAttributes.USAGE_ALARM) // 内置播放器的 USAGE 类型,默认 AudioAttributes.USAGE_ALARM .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION); // 内置播放器的 contentType 类型,默认 AudioAttributes.CONTENT_TYPE_SONIFICATION

4.6 识别配置

包括云端服务的 asr 配置和本地 asr 配置

ddsConfigBuilder.createAsrBuilder() .enableStreamUrl(true) // 对话接口里是否返回 tts 的 speakUrl,默认 true .setNotDropWakeupInAsr(false) // 识别时不要过滤唤醒词,默认 false .setCloudVadPauseTime(500) // 云端VAD在结束语音时静音时长,默认500ms .enableAudioDetection(false) // 噪声检测配置,开启后可过滤噪声识别结果,默认 false .enableCloudAsrpp(false) // 云端asrpp参数,设置为true后云端识别返回性别/年龄/情绪等字段,默认 false .enableNumberConvert(false) // 中文数字转阿拉伯数字,默认 false .enablePunctuation(false) // 识别结果是否带标点符号,默认 false .enableSignalVad(false) // 是否禁用VAD单字打断功能,默认 false .enableTone(false) // 识别结果的拼音是否带音调,默认 false .setCustomWakeupScore("12.5") // 自定义唤醒词阈值,该分数一般是通过测试集调整。默认值:无 .setAsrPermanentSkill("{\"skillId\":\"123123\"}") // 技能锁定接口,让用户说的话只落在锁定的技能里 .setCustomAsrConf("[{\"name\":\"宝宝睡觉模式\",\"confidence\":\"0.61\"}]")// 设置指定语料的识别置信度,防止某些语料识别置信度过低被抛弃 .enableWakeNetwork(false) // 弱网环境配置,如使用的是弱网环境,则配置为true,默认 false .setAsrRouter("dialog") // 区分识别后是否进入对话。默认 dialog 进入对话,partner 不进入对话,可通过ASREngine相关方法获取识别结果 .setCustomAsrBin("net.bin") // 预置本地识别 net.bin 资源 .setVadBin("vad.bin") // VAD资源 .enableOptGramBuild(false) // 优化离线gram编译逻辑,默认 false .enableTipsBeforeAsr(false) // 开启后识别前都会有叮咚声,默认 false .enableAsrTips(false); // 是否开启识别完成音效,默认 false

4.7 录音配置

ddsConfigBuilder.createRecorderBuilder() .setRecorderMode("internal") // 录音模式, internal SDK内部录音, external sdk外部录音,通过 Agent().feedPcm() 方法传入音频 .setAudioSource(MediaRecorder.AudioSource.DEFAULT) // 录音机参数: audioSource 录音机数据源类型 .setAudioSamplerate(16000) // 录音机参数: sampleRateInHz 录音时音频采样率 .setAudioChannelConf(AudioFormat.CHANNEL_IN_MONO) // 录音机参数:channelConfig 录音机频道源类型 .setAudioFormat(AudioFormat.ENCODING_PCM_16BIT) // 录音机参数:audioFormat 每个采样大小 .setAudioBufferSizeInByte(192000) // 录音机参数:bufferSizeInBytes 录音机的缓存大小 .setAudioChannelCount(1) // 音频通道数 单麦是1 .setAudioBufferSize(3200) // 录音机每次读取音频的 buffer 大小, 单麦每 100ms 是 3200B .setReversAudioChannel(false) // 是否翻转单麦 1mic+1ref 音频通道,默认 false .enableHighVolume(false); // 是否使用大音量检测,默认 false

五、错误码描述

error id 错误描述 原因及解决办法
1 解压dds.bin失败 系统IO错误
2 内核执行出错 请检查配置是否正确,或者联系DUI客服
3 系统启动超时 请检查配置是否正确,或者联系DUI客服
4 存储空间不足 请检查/data/目录剩余大小是否充足
070302 解码失败 联系DUI客服
070303 未发现新的版本
070304 请先升级您的客户端(SDK) 因为产品选择的dui内核和sdk不匹配,需要升级sdk
070305 网络异常, 无法连接至服务器 请检查网络是否正常或者配置是否正确
070306 解码失败 联系DUI客服
070307 补丁安装失败 存储空间不够
070308 备份压缩文件不存在 用户手动删掉了app私有空间的文件
070309 提取压缩文件失败 存储空间不够
070310 未设置产品ID 需要设置产品id
070311 无效客户端版本 请检查配置是否正确
070312 无效用户ID 需要设置用户id
070313 无效设备ID 设备id不正确
070314 资源地址无效 请检查配置是否正确
070315 无效的产品分支 需要设置产品分支
070316 无法找到资源 服务端资源生成有问题,请尝试重新发布一下,或者请提供产品id反馈给DUI客服,去后台查询根本原因
070317 资源校验失败 下载资源过程中资源被纂改
070319 当前产品中内核资源版本过低,请前往DUI控制台对应的产品中,选择最新的内核资源版本并重新发布产品 当前产品中内核资源版本过低,请前往DUI控制台对应的产品中,选择最新的内核资源版本并重新发布产品。
070320 存储空间不足 系统存储空间不足,请在错误的详细信息中查看需要的存储空间和现有的存储空间
070601 设备注册过程中无法连接到授权服务器 请检查网络是否畅通,或者productId与api key是否匹配
070602 设备注册过程无法颁发有效的profile文件 请检查产品ID或与客服联系
070603 API Key无效 请到产品授权配置页面查看api key信息是否正确,请确认签名证书是否匹配,包名是否匹配
070604 无效的产品ID 无效的产品ID,请确认是否未设置产品ID
070606 profile文件被禁用 请重新进行注册或者与客服联系
070607 profile文件过期 请重新进行注册或者与客服联系
070609 设备注册无法保存有效的profile文件 检查文件系统是否异常或者重新进行设备注册
070612 dns resolve failed: no answers(DNS失败) 请检查网络是否畅通
070613 dns resolve failed: exceed retry count(DNS失败) 请检查网络是否畅通
070614 dns resolve failed: network is unreachable(DNS失败) 请检查网络是否畅通
070615 dns resolve timeout(DNS超时) 请检查网络是否畅通
070616 connect ip failed: reasons for uncertainty(建立连接失败) 请检查网络是否畅通
070617 connect ip failed: network is unreachable(建立连接失败) 请检查网络是否畅通
070618 connect ip timeout(建立连接超时) 请检查网络是否畅通
070619 websocket handshake failed: reasons for uncertainty(websocket握手失败) 请检查网络是否畅通
070620 recv timeout(接收超时) 请检查网络是否畅通
070621 send timeout(发送超时) 请检查网络是否畅通
070622 orderly shutdown(发完数据正常关闭) 请检查网络是否畅通
071301 TTS合成超时 请检查网络是否正常
071302 对话超时 请检查网络是否正常
071303 对话连接断开 请检查网络是否正常
071304 识别结果为空 未检测到用户说法
071305 语义结果为空 用户的说法没有命中产品中的非闲聊技能
071306 WebAPI错误 调用WebAPI时发生错误
071307 NativeAPI错误 调用NativeAPI时发生错误
071308 进入闲聊技能 用户的首轮说法没有命中产品中的非闲聊技能
071309 异常重试达到最大次数 达到了产品错误处理的最大重试次数
071310 命中退出词 用户说了产品的退出词
071311 NativeAPI未注册 请检测是否注册并实现NativeAPI
071312 本地识别结果置信度低 网络离线时本地识别结果置信度较低
071313 自定义技能响应超时 联系技能开发者
071314 自定义技能内部错误 联系技能开发者
071315 自定义技能返回为空 联系技能开发者
071316 当前场景下不支持这么说 该技能关闭了智能调度
071317 进入兜底技能 用户说法进入了产品中的兜底技能
071318 进入知识型技能 用户说法进入了产品中的知识型技能,包括新闲聊、新百科
071319 未授权 请调用doAuth
071320 在使用oauth授权方式的情况下,access token过期或者不合法 请使用oauth sdk刷新access token,然后用dds sdk的接口传入access token
071321 自定义技能speaklist播报超时 请检查音频链接是否正常
071322 鉴权失败的情况下请求对话 请检查授权错误信息
071300 对话服务内部错误 联系DUI客服
072101 ASREngine VAD超时 超过8s未检测到人声
072102 ASREngine 识别为空 未检测到用户说法
072103 ASREngine 识别服务超时 请检查网络是否正常
072104 ASREngine 识别过程发生错误 联系DUI客服
072201 唤醒词个数超过限制 请清除之前的唤醒词后重试

六、常见问题

6.1 SDK的软硬件配置要求

  1. 需要Android 4.1及其以上的系统;
  2. /data分区剩余空间至少200MB。

6.2 授权失败

关于070603授权错误的问题,请做以下检查:

  1. 平台上生成的apikey和本地使用的apikey是否一致;
  2. 平台上release debug的sha256值是否和本地的对应;
  3. gradle文件配置的signingConfigs,只针对generate生成的apk;
  4. 如果是点run运行的程序,debug的sha256要填的.android目录下的debug.keystore(最常见)

6.3 客户端没收到Command/NativeAPI回调

常见原因如下,可以自上而下确认:

  1. 技能修改后,未发布;
  2. 技能发布后,产品没有选择该技能的最新版本;
  3. 产品未发布;
  4. 客户端配置的K_ALIAS_KEY和产品发布的分支不一致;
  5. 客户端没有注册对应的Command/NativeAPI;
  6. 客户端注册Command/NativeAPI的逻辑没走到;
  7. 客户端注册的Command/NativeAPI没填对,把?后面的参数也填进去了;
  8. 客户端注册Command/NativeAPI后,又重新初始化了DDS(release+init)

6.4 智能家居refreshToken 断网2年后导致SDK授权失败且无法恢复的问题

1)通过订阅token_refresh.fail消息,如果token刷新失败,SDK会抛出这个消息,应用收到该消息后,重新走以下流程:requestAuthCode() – setAuthCode()
2)setAuthCode授权成功后,将智能家居绑定流程再重新走一遍;确保DCA绑定成功。

导航目录

一. 集成准备

1.1 获取产品ID

集成SDK之前,您首先需要到DUI控制台创建产品,获取如下的产品ID。
Minion

1.2 下载和导入SDK

SDK 下载页

sdk 不同版本 duicore.zip、product.zip 获取方式差异:

  • V2.X.X.X 版本 duicore.zip 在 sdk 里一同发布,product.zip 仍在产品发布页下载。!!! 不要使用错误的 duicore.zip
  • v1.X.X.X 版本通过产品发布页下载 duicore.zip 和 product.zip

1.3 集成

SDK要求Android 4.1及其以上的系统且 /data分区剩余空间至少200MB

将SDK包中的aar文件( 如 lib-dds-2.0.1.1-release.aar )导入到现有的工程中,放入到libs目录下,并添加配置:

build.gradle

repositories{ flatDir { dirs 'libs' } }

在build.gradle中配置引用sync工程即可。

build.gradle

dependencies { implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) //在线授权必须引入 implementation 'com.squareup.okhttp3:okhttp:3.9.0' }

1.4 代码混淆

如果您的应用使用了混淆, proguard-rules.pro 内请添加:

  • 2.0.0.1 及以上版本:
-keep class com.aispeech.**{*;}
-keep class com.aispeech.**$**{*;}
-keep class ai.dui.sdk.**{*;}
-keep class ai.dui.sdk.**$**{*;}
  • 1.X.X.X 版本:
-keep com.aispeech.**
-keep public class com.aispeech.lasa{*;}
-keep public class com.aispeech.dui.dds.luaBug{ *; }
-keep class ai.dui.sdk.**{*;}
-keep class ai.dui.sdk.**$**{*;}

1.5 权限说明

SDK需要的权限如下表,请在AndroidManifest.xml 文件中添加。

权限 用途
android.permission.INTERNET 允许接入互联网,以便访问我们的云服务
android.permission.RECORD_AUDIO 允许采集音频
android.permission.ACCESS_WIFI_STATE 获取WIFI状态
android.permission.ACCESS_NETWORK_STATE 获取网络状态
android.permission.WRITE_EXTERNAL_STORAGE (非必须)写入外部存储数据,用于存储一些缓存文件
android.permission.READ_EXTERNAL_STORAGE (非必须)读取外部存储数据

1.6 精简 sdk

sdk 包含所有的功能及资源,客户可根据自己的需要裁剪不需要的功能,可减少 app 或者下载的资源包的大小。

sdk 包里有 mini 文件夹,内含:

  1. mini.py // 精简脚本
  2. mini.config // 精简脚本的配置文件

脚本使用方法:

  1. 修改 mini/mini.config 配置文件,0表示不使用, 1表示使用
  2. 在mini目录下执行python脚本: python3 mini.py
  3. 在mini目录下生成精减的 aar 与 duicore

!!!强烈建议客户使用匹配的 duicore 和 aar, 支持客户自己根据需要裁剪,一般可以减少一半大小

二. 初始化 SDK

初始化包含

  1. 授权
  2. 资源、产品的初始化

2.1 构造 DDSConfig

初始化时需要 DDSConfig 参数配置。以下说明核心的初始化参数,更多参数说明请见 javadoc在线文档

  • DDSConfigBuilder 方式构造 DDSConfig
// DDSConfig 新的方式构造 2.0.1.1 及以上版本支持 DDSConfigBuilder ddsConfigBuilder = new DDSConfigBuilder( "productId", // 产品ID "aliasKey", // 产品分支 prod/test/(自定义分支) "apiKey", // 产品的 apiKey,一个API Key只被指定的客户端使用,来源:DUI控制台-产品接入-授权管理 "productKey", // 产品的 productKey,用于设备注册请求签名,来源:DUI控制台-产品接入-授权管理 "productSecret", // 产品的 productSecret,用于设备注册请求签名,来源:DUI控制台-产品接入-授权管理 "duicoreZip" // 资源包,用法详见下方"duicore 配置规则" ); ddsConfigBuilder.createWakeupBuilder() .setMicType(0); ddsConfigBuilder.createCommonBuilder() .setCustomZip("product.zip") .setDeviceName("XXX") .setDeviceId("XXX") // 设备ID,授权时认证的原始设备ID,请务必确保设备ID唯一。若不填,则SDK自己收集设备ID。可以通过DDS.getInstance().getDeviceName()获取授权认证后的唯一ID .setUserId("123456"); // 用户ID DDSConfig ddsConfig = ddsConfigBuilder.build(); // 之后 2.2 init 初始化
  • DDSConfig 旧的方式构造
// DDSConfig 旧的方式构造 DDSConfig ddsConfig = new DDSConfig() .addConfig(DDSConfig.K_DEVICE_ID, deviceId) // 设备ID字符串,授权时认证的原始设备ID,请务必确保设备ID唯一。若不填,则SDK自己收集设备ID。可以通过DDS.getInstance().getDeviceName()获取授权认证后的唯一ID .addConfig(DDSConfig.K_PRODUCT_ID, productId) // 产品ID字符串 .addConfig(DDSConfig.K_USER_ID, userId) // 用户ID字符串 .addConfig(DDSConfig.K_ALIAS_KEY, aliasKey) // 产品分支 prod/test/(自定义分支) .addConfig(DDSConfig.K_API_KEY, apiKey) // 产品的 apiKey字符串,一个API Key只被指定的客户端使用,来源:DUI控制台-产品接入-授权管理 .addConfig(DDSConfig.K_PRODUCT_KEY, productKey) // 产品的 productKey字符串,用于设备注册请求签名,来源:DUI控制台-产品接入-授权管理 .addConfig(DDSConfig.K_PRODUCT_SECRET, productSecret) // 产品的 productSecret字符串,用于设备注册请求签名,来源:DUI控制台-产品接入-授权管理 .addConfig(DDSConfig.K_DUICORE_ZIP, "duicore.zip") // 资源包,用法详见下方"duicore 配置规则" // 之后 2.2 init 初始化

duicore 配置规则:

  1. 如果duicore.zip的放置位置在assets目录下,那么就可以使用如代码中的写法,直接使用文件名:duicore.zip

  2. 如果duicore.zip没有放置在assets目录下,就必须使用duicore.zip文件的绝对路径:/sdcard/dds/duicore.zip

  3. 在 duicore.zip 所在的文件夹中,必须包含与 duicore.zip 文件相应的 duicore.zip.md5sum 文件(2.16.0.1以上版本非必须),文件内容是 duicore.zip 文件的 md5 值

在收到 onInitComplete 回调之后,就可以使用DDS来完成对应的操作。
isFull参数如果为false,则表示不完全初始化,缺失资源包,您需要等待资源包的更新下载完成或者内置一份资源包; 如果为true,则表示完全初始化,所有功能就绪。

2.2 授权和初始化

官网产品授权操作说明

  • 2.0.0.1 及以上版本会先回调收授权状态,之后回调 init 状态。即 onInitComplete 回调 true 即表示初始化和授权都成功

init 时会进行授权操作,必须授权成功后才能正常使用sdk

v2.X.X.X 版本授权流程:

  1. 授权操作需要连接到互联网, 在 SDK 初始化时设置 DDSAuthListener 来监听授权的情况
  2. SDK 初始化完毕后,您可以使用 DDS.getInstance().isAuthSuccess() 来判断当前是否授权成功。
  3. 授权成功会回调 DDSAuthListener.onAuthSuccess(),授权失败会收到回调DDSAuthListener.onAuthFailed(),需要重新调用DDS.getInstance().doAuth()
  4. 授权错误码从 "070601“ – “070622”,详见 五、错误码描述。
  • v1.X.X.X 版本的授权和 init 回调没有顺序关系
  1. 在SDK初始化时,可以设置 DDSAuthListener 来监听授权的情况。

  2. SDK初始化完毕后,您可以使用 DDS.getInstance().isAuthSuccess() 来判断当前是否授权成功。

  3. 授权成功会回调 DDSAuthListener.onAuthSuccess,授权失败会收到回调DDSAuthListener.onAuthFailed,需要重新调用DDS.getInstance().doAuth()。

  4. Profile文件的错误码范围从 "070601“ – “070622”,详见 五、错误码描述。

DDS.getInstance().init( getApplicationContext(), ddsConfig, new DDSInitListener() { // 资源、产品的初始化回调 @Override public void onInitComplete(boolean isFull) { AILog.d(TAG, "onInitComplete: " + isFull); } @Override public void onError(int what, String msg) { AILog.d(TAG, "onError: " + what + ", error: " + msg); } }, new DDSAuthListener() { // 授权回调 @Override public void onAuthSuccess() { AILog.d(TAG, "onAuthSuccess"); } @Override public void onAuthFailed(String errId, String error) { AILog.d(TAG, "onAuthFailed: " + errId + ", error:" + error); } });

2.3 初始化状态检查

除了通过监听 onInitComplete 回调外,还可以直接通过判断 DDS.getInstance().getInitStatus()的返回值来检查初始化是否完毕。

当未初始化完成时,尝试调用一些方法时,会抛出 DDSNotInitCompleteException 异常

/** * 获取当前初始化的状态 * * @return DDS.INIT_COMPLETE_NONE 还未初始化完成,表示DDS正在初始化 * DDS.INIT_COMPLETE_NOT_FULL 部分初始化完成,表示DDS已经初始化完成,但还没有完成更新 * DDS.INIT_COMPLETE_FULL 完全初始化完成,已经完成更新 */ DDS.getInstance().getInitStatus();

2.4 release SDK

init 回调 INIT_COMPLETE_FULL之后即可使用sdk的功能。

如果不需要 sdk ,可以通过以下方法释放 sdk 资源

DDS.getInstance().release();

注意事项

• DDS.getInstance().init()和DDS.getInstance().release() 方法不会阻塞应用程序的主线程,也不会影响应用程序性能
• 当调用DDS.getInstance().release()之后,之前注册的 observers 会失效,再次 DDS.getInstance().init() 后,您需要对之前的 observers 重新注册

2.5 更新产品

2.0.0.1 及以上版本不支持更新 duicore 资源,但可以更新产品配置

如果您想对发布的产品持续进行免安装的更新,您需要做以下几个操作:

  • 请求更新

您可以选择在合适的时候发起更新请求。当sdk在检查到有更新后,会自动下载资源并进行热更新,结果会以回调的方式通知。

DDS.getInstance().getUpdater().update(new DDSUpdateListener() { @Override public void onUpdateFound(String detail) { AILog.d(TAG, "onUpdateFound"); } @Override public void onUpdateFinish() { AILog.d(TAG, "onUpdateFinish"); } @Override public void onDownloadProgress(float progress) { AILog.d(TAG, "onDownloadProgress :" + progress); } @Override public void onError(int what, String error) { AILog.d(TAG, "onError :" + what + ", error: " + error); } @Override public void onUpgrade(String version) { AILog.d(TAG, "onUpgrade :" + version); } };
  • 处理更新推送

在您成功发布产品之后,若您已经订阅了消息 sys.resource.updated,则会收到资源更新的推送,此时您可以选择直接请求更新,以保持产品一直处于最新状态(推荐)。

DDS.getInstance().getAgent().subscribe("sys.resource.updated", new MessageObserver() { @Override public void onMessage(String message, String data) { try { DDS.getInstance().getUpdater().update(ddsUpdateListener); } catch (DDSNotInitCompleteException e) { e.printStackTrace(); } } });

SDK中内置一套资源更新时的通知的UI实现,您可以在DDSConfig中设置 DDSConfig.K_USE_UPDATE_NOTIFICATION 来开启或关闭。

2.6 Debug

2.6.1 日志

2.0.0.1 及以上版本默认日志级别是 4 (同 Log.INFO)。修改日志级别:

DDS.getInstance().setDebugMode(2); // 日志默认保存路径: /sdcard/Android/data/包名/cache/alog/

日志压缩成xlog形式,默认保存2天的日志,以下是日志需要的大概大小

  • 单麦D级别日志24小时保存日志 150M
  • 多麦D级别日志24小时保存日志 400M

2.6.2 音频调试

初始化后可动态设置保存 debug 音频

DDS.getInstance().setAudioDebug(true); // 音频默认保存路径: /sdcard/Android/data/包名/cache/

动态音频调试接口可替代静态调试配置: K_VAD_DEBUG、K_TTS_DEBUG、K_WAKEUP_DEBUG、K_ASR_DEBUG、K_ASRPP_DEBUG

2.6.3 动态Debug

DDS.getInstance().startDebug(); // 开启调试模式 日志级别调为V 保存调试音频 DDS.getInstance().stopDebug(); // 关闭调试模式 日志级别修改为E 停止保存调试音频

2.6.4 缓存配置

缓存目录的配置需要在初始化时设置,如不设置,默认 /sdcard/Android/data/包名/cache

ddsConfigBuilder.createCommonBuilder() .setCachePath("/sdcard/cache") // 缓存文件保存目录,默认 /sdcard/Android/data/包名/cache .setCacheSize(500000) // 缓存文件大小限制。自定义缓存目录时必需

三. 功能列表

3.1 响应command

  1. 客户端动作的执行

详细的command编写说明,请参见客户端动作(command)编写说明

Minion

  1. 快捷唤醒中的命令响应

Minion

当您在DUI平台上定制了command之后,可以使用如下的方式在您的工程中做实现。
类似于Android的广播接收器,您可以在需要的地方注册和注销CommandObserver,同一个CommandObserver可以处理多个commands。

// 注册 DDS.getInstance().getAgent().subscribe(new String[]{"open_window", "increase.volume"}, commandObserver); // 注销 DDS.getInstance().getAgent().unSubscribe(commandObserver); private CommandObserver commandObserver = new CommandObserver() { @Override public void onCall(final String command, final String data) { if (command.equals("open_window")){ JSONObject jsonData = new JSONObject(data); String intentName = jsonData.optString("intentName"); String w = jsonData.optString("w"); // TODO 依据w的值,执行打开窗户操作 }else if (command.equals("increase.volume")) { // TODO 处理调大音量的快捷唤醒指令 } } };

3.2 响应native api

Minion

当您在DUI平台上定制了native api之后,可以使用如下的方式在您的工程中做实现。
类似于Android的广播接收器,您可以在需要的地方注册和注销NativeApiObserver,同一个NativeApiObserver可以处理多个native api。

// 注册 DDS.getInstance().getAgent().subscribe(new String[]{"query_battery"}, nativeApiObserver); // 注销 DDS.getInstance().getAgent().unSubscribe(nativeApiObserver); private NativeApiObserver nativeApiObserver = new NativeApiObserver() { @Override public void onQuery(final String nativeApi, final String data) { if (nativeApi.equals("query_battery")){ JSONObject jsonData = new JSONObject(data); String intentName = jsonData.optString("intentName"); // 执行查询电量操作 String battery = DeviceUtil.getBattery(); DDS.getInstance().getAgent().feedbackNativeApiResult(nativeApi, new TextWidget().setText(battery)); } } };

nativeCommand支持异步写法

String mCommand; public void doNativeCommand() { String[] mTopic = new String[]{ "sys.car.crl" }; NativeCommandObserver nativeCommandObserver = new NativeCommandAsyncObserver() { @Override public void onNativeCommandAsync(String command, String data) { mCommand = command; AILog.d(TAG, "command = " + command); AILog.d(TAG, "data = " + data); // 异步执行操作,这里不能再有耗时操作,应用层保证这里切异步 } }; DDS.getInstance().getAgent().subscribeSync(mTopic, nativeCommandObserver); }

注:

• 每个onQuery方法执行时,需要调用 feedbackNativeApiResult() 来向DUI平台返回执行结果,表示一个native api执行结束。

• native api的执行超时时间为10s

更多详细使用请查看: https://www.duiopen.com/docs/skill_FAQ

3.3 响应内置消息

内置消息列表:

message data 说明
sys.kernel.ready SDK加载完成,所有功能就绪
sys.dialog.start { reason:“wakeup.major”,//唤醒词触发对话 } 对话开始,及开始原因:
1.wakeup.major,唤醒词唤醒
2.wakeup.command,快捷命令唤醒
3.api.startDialog,调用startDialog接口
4.api.sendText,调用sendText接口(对话Idle时)
5.api.triggerIntent,调用triggerIntent接口(对话Idle时)
6.api.avatarClick,调用avatarClick接口(对话Idle时)
7.api.avatarPress,调用avatarPress接口(对话Idle时)
sys.dialog.error { errId:071304, errMsg:“asr null” recordId:“70dcef50b8b31a6c02dbac1acee31de7” } 对话中发生的错误
sys.dialog.end { reason:“normal”,//对话正常结束 skillId:“100001246” } { reason:“error”,//对话发生错误; errId:071304, errMsg:“asr null”, skillId:“100001246” } { reason:“interrupt”,//对话被打断。强制结束对话时出现。 skillId:“100001246” } 对话结束,及结束原因: 1.normal,对话正常结束 2.error,对话发生错误退出 3.interrupt,对话被打断退出
sys.dialog.continue 对话恢复
sys.resource.updated 收到线上资源变更的推送
sys.wakeup.result { “type”: “major”, “word”: “你好小驰”, “greeting”:“主人你好” } 语音唤醒
sys.vad.begin VAD触发
sys.vad.end VAD结束
sys.upload.result { “result”:false, “reqId”:“xxxx”, “errId”:71801, “errMsg”:“Network Invalid” } 上传词库、设备信息的结果

类似于Android的广播接收器,您可以在需要的地方注册和注销MessageObserver,同一个MessageObserver可以处理多个message。

您可以通过注册消息接收器来接收需要UI展示的数据,具体UI消息数据见:
UI事件及数据定义

// 注册 DDS.getInstance().getAgent().subscribe(new String[]{"sys.dialog.start","sys.dialog.end","context.input.text"}, messageObserver); // 注销 DDS.getInstance().getAgent().unSubscribe(messageObserver); private MessageObserver messageObserver = new MessageObserver() { @Override public void onMessage(final String message, final String data) { if (message.equals("sys.dialog.start")){ runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, "对话开始了", Toast.LENGTH_LONG).show(); } }); }else if(message.equals("context.input.text")){ final JSONObject dataJson = new JSONObject(data); runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, dataJson.optString("text"), Toast.LENGTH_LONG).show(); } }); } } };

3.4 开启/终止对话

您可以在任何时刻主动开启/终止对话,通过以下接口实现。
dialog

// 开启对话。如果当前已经在对话中,则重新开启新一轮对话。 DDS.getInstance().getAgent().startDialog(); // 关闭对话 DDS.getInstance().getAgent().stopDialog(); // 支持在开启对话时传入一个播报文本, 先播报文本,后开启对话 Agent.startDialog(JSONObject jsonObject) // 支持在关闭对话时传入一个播报文本, 文本播报结束后自动结束对话 Agent.stopDialog(JSONObject jsonObject) 使用示例: JSONObject jsonObject = new JSONObject(); jsonObject.put("speakText", "播报文本"); DDS.getInstance().getAgent().startDialog(jsonObject); // 多模态事件同步,该接口用于客户端给对话中控发送一个事件 DDS.getInstance().getAgent().updateDispatchEvent(JSONObject obj) 使用示例: JSONObject obj = new JSONObject(); // obj.put("endSkill", "true"); // 不支持 endSkill DDS.getInstance().getAgent().updateDispatchEvent(obj);

3.4.1 提示音

半双工 使用 startDialog 后默认会有一个提示音,提示音可以自由打开或者关闭

DDS.getInstance().getAgent().openTip(); // 打开tip提示音 DDS.getInstance().getAgent().closeTip(); // 关闭tip提示音

3.5 按键事件

如果您有相关的软硬件按键,您需要关注以下接口,将用户的行为反馈给SDK。

// 点击唤醒/停止识别/打断播报 操作接口 DDS.getInstance().getAgent().avatarClick(); // 点击唤醒/停止识别/打断播报 操作接口 , 并附带一则欢迎语,当此次是唤醒时,播报这则欢迎语 DDS.getInstance().getAgent().avatarClick("有什么可以帮你"); // 按下按键接口(调用此接口需关闭VAD,VAD在创建产品时可配置) DDS.getInstance().getAgent().avatarPress(); // 释放按键接口(调用此接口需关闭VAD,VAD在创建产品时可配置) DDS.getInstance().getAgent().avatarRelease();

3.6 语音播报

使用 DDS.getInstance().getAgent().getTTSEngine().speak() 接口可以主动播放 TTS 。

参数说明:
1)text 播报文本,支持SSML
2)priority 优先级

提供4个优先级播报。
①优先级0:保留,与DDS语音交互同级,仅限内部使用;
②优先级1:正常,默认选项,同级按序播放;
③优先级2:重要,可以插话<优先级1>,同级按序播放,播报完毕后继续播报刚才被插话的<优先级1>;
④优先级3:紧急,可以打断当前正在播放的<优先级1|优先级2>,同级按序播放,播报完毕后不再继续播报刚才被打断的<优先级1|优先级2>。
3)ttsId 用于追踪该次播报的id,建议使用UUID。
4)audioFocus 该次播报的音频焦点,默认值:
①优先级0:android.media.AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
②优先级非0:android.media.AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK

  • Text 播报
// 请求语音播报 DDS.getInstance().getAgent().getTTSEngine().speak("这是一段测试文本", 1, "100", AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
  • SSML 播报
String test1 = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<speak xml:lang=\"cn\">\n" + " 测试SSML播报\n" + "</speak>"; DDS.getInstance().getAgent().getTTSEngine().speak(test1, 1, "10004", AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, TTSEngine.SSML);

停止播报接口如下:
1)ttsId与speak接口的ttsId一致,则停止或者移除该播报;
2)ttsId为空, 停止所有播报;
3)ttsId为"0",停止当前播报。

// 停止播报 DDS.getInstance().getAgent().getTTSEngine().shutup("100");

3.7 内置H5

2.0.0.1 及以上版本基于性能考虑默认关闭H5通信节点, 如有需要, 可以通过配置打开:

// 新的配置方式 ddsConfigBuilder.createNodeBuilder() .enableH5(true); // 旧的配置方式 config.addConfig(DDSConfig.K_H5_NODE_ENABLE, "true");

您如果使用和上传了h5资源,可以通过以下方法来获取h5资源包的 index.html 文件的路径用来在webview中加载

// 获取资源包中h5的index.html文件的绝对路径 DDS.getInstance().getAgent().getValidH5Path();

3.8 外部录音机拾音

您如果有自己的录音机,可以通过以下方法将录音数据传给DDS。

// 注意:需要将DDSConfig.K_RECORDER_MODE设置为"external",用来关闭DDS内置录音机,该接口才会生效 // 音频长度:无限制。内置录音机是3200(单麦) // 调用间隔:无限制。内置录音机是100ms // 调用时机:无限制。DDS会根据对话状态将接受的音频送入识别或者丢弃。 DDS.getInstance().getAgent().feedPcm(byte[] pcm);

3.9 外部TTS引擎

您如果使用第三方的TTS引擎,可以通过以下方法来将第三方TTS引擎注册到DDS中。

// 1. 配置 ttsMode /** * TTS播报模式<br> * 取值:internal or external<br> * internal:为sdk内部合成播报<br> * external:用户负责合成播报,需调用 <code>DDS.getInstance().getAgent().setTTSRequestListener()</code> 通过回调获取播报时机, * 播报完成后调用 <code>DDS.getInstance().getAgent().notifyTTSEnd()</code> 标记结束<br> * 默认值:internal */ ddsConfigBuilder.createTtsBuilder().setTtsMode("external") // 2. 注册回调 TTSRequestListener listener = new TTSRequestListener() { @Override public void onStart(String type, String content) { Log.d(TAG, "tts start," + type + ":" + content); int code = mExternelTts.startSpeaking(content, mTtsListener); if (code != ErrorCode.SUCCESS) { Log.d(TAG, "语音合成失败,错误码: " + code); } } @Override public void onStop() { Log.d(TAG, "tts stop"); mExternelTts.stopSpeaking(); } }; DDS.getInstance().getAgent().setTTSRequestListener(listener) // 3. 合成完毕后必须播报结束通知 !!! DDS.getInstance().getAgent().notifyTTSEnd();

3.10 场景模式

SDK提供了一些场景(如TTS播报/拾音距离)的切换,您可以在需要的时候调用下面的接口。

DDSMode

//TTS场景切换 DDS.getInstance().getAgent().setDDSMode(DDSMode.TTS_SILENCE); //在静音模式下,所有TTS播报将会被静音。 DDS.getInstance().getAgent().setDDSMode(DDSMode.TTS_NORMAL); //默认值。 //拾音场景切换。仅在"电视助手"解决方案中生效。 DDS.getInstance().getAgent().setDDSMode(DDSMode.PICKUP_NEAR); //在近场模式下,将加载产品配置页中的近场配置 DDS.getInstance().getAgent().setDDSMode(DDSMode.PICKUP_FAR); //在远场模式下,将加载产品配置页中的远场配置

3.11 触发指定意图

如果您期望主动触发某个意图的对话,可以在需要的时候调用下面的接口。

参数说明:

1)skill 技能名称,必填。
2)task 任务名称,必填。
3)intent 意图名称,必填。
4)slots 语义槽的key-value Json,可选。

triggerIntent

// 跳过识别和语义,直接进入指定的意图对话。即:DDS主动向用户发起一轮对话。 DDS.getInstance().getAgent().triggerIntent("我是技能名称", "我是任务名称", "我是意图名称", new JSONObject().put("语义槽名称一", "语义槽取值一").put("语义槽名称二", "语义槽取值二").toString());

根据skillid触发意图:

SkillIntent skillIntent = new SkillIntent("我是技能id", "我是任务名称", "我是意图名称", new JSONObject().put("语义槽名称一", "语义槽取值一").put("语义槽名称二", "语义槽取值二").toString()); DDS.getInstance().getAgent().triggerIntent(skillIntent);

3.12 更新用户词库

如果您期望更新用户的词库,使其能在技能中使用,可以在需要的时候调用下面的接口。上传结果可以通过sys.upload.result消息来获取。

注意:更新的词库仅在该设备下有效,不会影响其他设备。
参数说明:
vocabIntent: 词库intent。包含词库名name,操作action,词条列表contents。
注1:若词库名是sys.联系人,则会自动做分词处理。
注2:若需要上传带同义词的词条,格式如下:"{词条取值}:{同义词1},${同义词2}"
返回值:请求ID,用于追踪sys.upload.result。

updateVocabs

//向指定词库中添加词条 final String reqId = DDS.getInstance().getAgent().updateVocabs( new VocabIntent() .setName("应用列表") .setAction(VocabIntent.ACTION_INSERT) .setContents(Arrays.asList("淘宝", "支付宝:支护宝,付款码")) ); //清空之前上传到该词库的所有词条,然后添加词条 DDS.getInstance().getAgent().updateVocabs( new VocabIntent() .setName("设备词库") .setAction(VocabIntent.ACTION_CLEAR_AND_INSERT) .setContents(Arrays.asList("空调", "电视:电视机")) ); //删除该词库的词条 DDS.getInstance().getAgent().updateVocabs( new VocabIntent() .setName("歌手名") .setAction(VocabIntent.ACTION_REMOVE) .addContent("刘德华") ); //清空之前上传到该词库的所有词条 DDS.getInstance().getAgent().updateVocabs( new VocabIntent() .setName("歌曲") .setAction(VocabIntent.ACTION_CLEAR_ALL) ); //批量更新词库 DDS.getInstance().getAgent().updateVocabs( //向指定词库中添加词条 new VocabIntent() .setName("sys.联系人") .setAction(VocabIntent.ACTION_INSERT) .addContent("董芳芳") .addContent("王强"), //清空之前上传到该词库的所有词条,然后添加词条 new VocabIntent() .setName("设备词库") .setAction(VocabIntent.ACTION_CLEAR_AND_INSERT) .setContents(Arrays.asList("空调", "电视")), //删除该词库的词条 new VocabIntent() .setName("歌手名") .setAction(VocabIntent.ACTION_REMOVE) .addContent("刘德华"), //清空之前上传到该词库的所有词条 new VocabIntent() .setName("歌曲") .setAction(VocabIntent.ACTION_CLEAR_ALL) ); //关注更新词库的结果 DDS.getInstance().getAgent().subscribe("sys.upload.result", new MessageObserver() { @Override public void onMessage(String message, String data) { Log.e(TAG, message + ":" + data); try { JSONObject obj = new JSONObject(data); if (reqId.equals(obj.optString("reqId", ""))) { Log.d(TAG, "updateVocabs result:" + obj.optBoolean("result", false)); } } catch (JSONException e) { e.printStackTrace(); } } });

3.13 startListening 仅开启识别

如果您期望仅开启识别并获取识别结果,可以在需要的时候调用下面的接口。

2.0.0.1 及以上版本使用 startListening 在半双工和全双工下表现一致

ASREngine

//获取识别引擎 ASREngine asrEngine = DDS.getInstance().getAgent().getASREngine(); ASREngine.ListeningParams params = new ASREngine.ListeningParams(); params.setVadEnable(true); //开启识别 params 可不设置 asrEngine.startListening(params, new ASREngine.Callback() { @Override public void beginningOfSpeech() { AILog.i(TAG, "检测到用户开始说话"); } /** * @buffer 音频数据属性:单声道 16bit */ @Override public void bufferReceived(byte[] buffer) { AILog.i(TAG, "用户说话的音频数据"); } @Override public void endOfSpeech() { AILog.i(TAG, "检测到用户结束说话"); } /** * @results. eof 为 0 代表识别过程中的结果, 为 1 代表识别结束的最终结果 * {"var":"今","sessionId":"0b48c0d086efc42d55bbf1068137655d","recordId":"6b3ccb1ee745d42fa6ddb74330b1c059","eof":0} * {"var":"今天 天气","sessionId":"0b48c0d086efc42d55bbf1068137655d","recordId":"6b3ccb1ee745d42fa6ddb74330b1c059","eof":0} * {"sessionId":"0b48c0d086efc42d55bbf1068137655d","pinyin":"jin tian tian qi","recordId":"6b3ccb1ee745d42fa6ddb74330b1c059","eof":1,"text":"今天 天气"} */ @Override public void partialResults(String results) { AILog.i(TAG, "实时识别结果反馈"); } /** * @results. {"sessionId":"0b48c0d086efc42d55bbf1068137655d","recordId":"6b3ccb1ee745d42fa6ddb74330b1c059","eof":1,"text":"今天天气","pinyin":"jin tian tian qi"} */ @Override public void finalResults(String results) { AILog.i(TAG, "最终识别结果反馈"); } @Override public void error(String error) { AILog.i(TAG, "识别过程中发生的错误"); } @Override public void rmsChanged(float rmsdB) { AILog.i(TAG, "用户说话的音量分贝"); } }); //主动结束此次识别,使用 vad 的话不需要主动停止 asrEngine.stopListening(); //取消此次识别 asrEngine.cancel();

3.13.1 设置实时回传音量

// 设置实时回传音量大小, 默认为true // 设置false之后, ASREngine.Callback.rmsChanged()不再回传音量变化值 DDS.getInstance().getAgent().getASREngine().enableVolume(boolean enable);

3.13.2 feed OPUS 或者 SBC 音频

如需 feed OPUS 或者 SBC 音频,只需按下面配置即可,其它操作不变

// feed opus DDS.getInstance().getAgent().setRecorderMode(Agent.RecorderMode.OPUS); DDS.getInstance().getAgent().feedOpus(buffer); // feed sbc DDS.getInstance().getAgent().setRecorderMode(Agent.RecorderMode.SBC); DDS.getInstance().getAgent().feedSbc(buffer);

3.14 VAD相关设置

3.14.1设置VAD后端停顿时间

如果您期望修改VAD后端检测的时间,可以在需要的时候调用下面的接口。

参数说明
millis 后端检测时间,单位毫秒,默认500毫秒。即若VAD在用户说话时停顿超过一定的时间,则认为用户已经说完,发出sys.vad.end消息,结束录音。

setVadPauseTime

//设置VAD后端停顿时间 DDS.getInstance().getAgent().getASREngine().setVadPauseTime(1000);//获取VAD后端停顿时间 DDS.getInstance().getAgent().getASREngine().getVadPauseTime();

3.14.2 设置VAD前端静音检测超时时间

如果您期望修改VAD前端静音检测超时时间,可以在需要的时候调用下面的接口。设置成功后,vad再次启动生效。
参数说明:
millis 前端静音检测超时时间,单位毫秒,默认8000毫秒。若VAD启动,一直未检测到用户说话,超过一定时间,发出sys.vad.timeout消息,结束录音。

setVadTimeout

//设置VAD前端静音检测超时时间 DDS.getInstance().getAgent().getASREngine().setVadTimeout(1000); //获取VAD前端静音检测超时时间 DDS.getInstance().getAgent().getASREngine().getVadTimeout();

3.15 对话输入文本

如果您期望跳过识别直接输入文本进行对话,可以在需要的时候调用下面的接口。

sendText

/** * 输入文本的接口 * <p> * 跳过对话中的识别过程,直接开始对话中的语义理解 * <p> * 若当前没有在对话中,则以文本作为首轮说法,新发起一轮对话请求 * 若当前正在对话中,则跳过识别,直接发送文本 * * @param text 输入的文本内容 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().sendText(String text)

3.16 TTS相关设置

3.16.1 TTS事件回调

如果您想在TTS播报的相关时间节点收到通知,如:开始播报,播报结束。可以使用如下的方式:

TTSEngine.Callback

DDS.getInstance().getAgent().getTTSEngine().setListener(new TTSEngine.Callback() { /** * 开始合成时的回调 * @param ttsId 当前TTS的id, 对话过程中的播报ttsid默认为0,通过speak接口调用的播报,ttsid由speak接口指定。 */ @Override public void beginning(String ttsId) { Log.d(TAG, "TTS开始播报"); } /** * 合成的音频数据的回调,可能会返回多次,data长度为0表示音频结束 * @param data 音频数据属性:单声道 16bit, pcm */ @Override public void received(byte[] data) { Log.d(TAG, "收到音频,此方法会回调多次,直至data为0,音频结束"); } /** * TTS播报完成的回调 * @param status 播报结束的状态。 * 正常播报结束为0 * 播报中途被打断结束为1 */ @Override public void end(String ttsId, int status) { Log.d(TAG, "TTS播报结束"); } /** * 合成过程中出现错误的回调 * @param error 错误信息 */ @Override public void error(String error) { Log.d(TAG, "出现错误,"+error); } /** * 合成过程中的音素回掉,需要资源支持 */ @Override public void phoneReturnReceived(String phoneReturn) { } });

TTSEngine.CallbackOptimize 带进度的回调

DDS.getInstance().getAgent().getTTSEngine().setListenerByProcess(new TTSEngine.CallbackOptimize() { @Override public void beginning(String ttsId) { Log.d(TAG, "beginning() ttsId: " + ttsId); if (TextUtils.equals(ttsId, mTtsId)) { listenerSuccessTimes++; } } @Override public void end(String ttsId, int status) { Log.d(TAG, "end() ttsId: " + ttsId + " status: " + status); if (TextUtils.equals(ttsId, mTtsId) && status == 0) { listenerSuccessTimes++; } } @Override public void error(String error) { Log.d(TAG, "error() error: " + error); } @Override public void onSpeechProgress(String ttsId, int currentFrame, int totalFrame, boolean isDataReady) { super.onSpeechProgress(ttsId, currentFrame, totalFrame, isDataReady); Log.d(TAG, "onSpeechProgress() ttsId: " + ttsId + " currentFrame:" + currentFrame + " totalFrame:" + totalFrame + " isDataReady:" + isDataReady); if (listenerSuccessTimes == 1 && TextUtils.equals(ttsId, mTtsId) && isDataReady) { listenerSuccessTimes++; } } });

3.16.2 TTS参数设置

如果您想通过SDK定制不同的合成音类型,可以在需要的时候调用下面的接口。

setTTS

/** * 设置TTS播报类型的接口 * 调用此接口则云端配置的合成音类型失效,此后的合成音类型都将由此接口来托管 * @param speaker 取值如:zhilingf, gdgm等,若取为null,则表示随机改为一个不同的类型 */ DDS.getInstance().getAgent().getTTSEngine().setSpeaker("zhilingf"); /** * 设置TTS播报类型的接口 * 调用此接口则云端配置的合成音类型失效,此后的合成音类型都将由此接口来托管 * @param speaker 取值如:zhilingf, gdgm等 * @param path 取值如:"sdcard/tts/zhilingf.bin",自定义合成间类型的全路径 */ DDS.getInstance().getAgent().getTTSEngine().setSpeaker("zhilingf","sdcard/tts/zhilingf.bin"); /** * 设置TTS播报语速的接口 * 调用此接口则云端配置的合成音语速失效,此后的合成音语速都将由此接口来托管 * * @param speed 语速,取值0.5-2.0,0.5语速最快,2.0语速最慢,对应控制台上合成音倍率值 */ DDS.getInstance().getAgent().getTTSEngine().setSpeed(1.0f); /** * 设置TTS播报音量的接口 * 调用此接口则云端配置的合成音音量失效,此后的合成音音量都将由此接口来托管 * * @param volume 音量大小,取值1-100 */ DDS.getInstance().getAgent().getTTSEngine().setVolume(50); /** * 设置TTS引擎为本地或者云端 * * @param mode 取值 TTSEngine.LOCAL(本地合成),TTSEngine.CLOUD(云端合成) */ DDS.getInstance().getAgent().getTTSEngine().setMode(TTSEngine.LOCAL); /** * 设置TTS播报的通道 * @param streamType AudioManager.STREAM_*的取值 * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().getTTSEngine().setStreamType(int streamType); /** * 设置TTS播报的通道(安卓7.0版本以上支持,并且需要配置K_AUDIO_USAGE和K_CONTENT_TYPE) * @param usage 取值: AudioAttributes.USAGE_* * @param contentType 取值: AudioAttributes.CONTENT_TYPE_* * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().getTTSEngine().setUsage(int usage, int contentType); /** * 设置TTS播报自定义录音 * @param customAudioList 自定义播报音频列表 * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().getTTSEngine().setCustomAudio(List<CustomAudioBean> customAudioList); /** * 设备抢焦点,默认为抢焦点 * @param enable true/false true: 抢焦点 false: 不抢焦点 */ DDS.getInstance().getAgent().getTTSEngine().enableFocus(boolean enable); /** * 设置TTS人设 * @param style 风格,humor:幽默;calm:沉稳;common:普通;简短:short; */ DDS.getInstance().getAgent().getTTSEngine().setStyle(String style); /** * 清除TTS人设 */ DDS.getInstance().getAgent().getTTSEngine().removeStyle(); /** * 设置TTS结束后延迟时间,单位ms * @param afterTime * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().getTTSEngine().setPlayAfterTime(int afterTime);

3.16.3 TTS参数获取

如果您想获取当前TTS配置的接口,可以调用如下接口:
getTTS

//返回String 当前使用的合成音类型,如:"zhilingf",获取失败返回null DDS.getInstance().getAgent().getTTSEngine().getSpeaker(); //返回float 当前合成音语速,返回值0.5-2.0,0.5语速最快,2.0语速最慢,获取失败返回 0 DDS.getInstance().getAgent().getTTSEngine().getSpeed(); //返回int 当前合成音音量,返回值1-100,获取失败返回 0 DDS.getInstance().getAgent().getTTSEngine().getVolume(); //返回List<CustomAudioBean> 当前自定义播报音频的列表 DDS.getInstance().getAgent().getTTSEngine().getCustomAudio(); //获取当前TTS人设 DDS.getInstance().getAgent().getTTSEngine().getStyle();

3.16.4 音频焦点处理模式

播放 TTS 时的音频焦点默认是 sdk 内部处理,也可交由外部处理

  1. 配置 AudioFocusMode
/** * 音频焦点处理模式<br> * 取值:internal or external<br> * internal:由SDK内部处理音频焦点的获取与释放<br> * external:由外部根据对话状态及TTS播报状态处理对话及TTS等的焦点问题,SDK不再获取或者释放焦点。 * 用户可以使用 <CODE>DDS.getInstance().getAgent().setAudioFocusCallback(audioFocusCallback)</CODE> 在回调里处理音频焦点<br> * 是否必需:否<br> * 默认值:internal */ ddsConfigBuilder.createTtsBuilder().setAudioFocusMode("external")
  1. 设置回调并在回调里处理音频焦点
private AudioFocusCallback audioFocusCallback = new AudioFocusCallback() { @Override public void onRequest() { Log.d(TAG, "onRequest()"); } @Override public void onAbandon() { Log.d(TAG, "onAbandon()"); } }; DDS.getInstance().getAgent().setAudioFocusCallback(audioFocusCallback);

3.17 唤醒相关设置

**唤醒词介绍:**当前除了主唤醒,唤醒词的类型还包括了 副唤醒词、命令唤醒词(快捷唤醒词)、打断唤醒词、快速唤醒词。

副唤醒词(Minor):如果您期望为设备增加一个别名,可以通过设置该类型唤醒词来完成。

命令唤醒词(快捷唤醒词/Command): 如果您期望在唤醒的时候执行一条指令,可以通过设置该类型唤醒词来完成。

打断唤醒词(ShortCut):如果您期望在唤醒的时候能同时打断语音播报,可以通过设置该类型唤醒词来完成。打断唤醒词唤醒后会把唤醒词作为识别结果发送给服务器,由服务器给出对话结果。

快速唤醒词(QuickStart):您想通过代码来控制快速唤醒,QuickStart词为类似“导航去”、“我想听”等,此类唤醒词只在oneshot模式下生效,作用为在未唤醒状态下语音输入“导航去天安门”,可直接进入对话流程。

3.17.1 开关唤醒

默认SDK加载后不会启动语音唤醒,您可以通过以下接口对需对语音唤醒进行控制。
wakeup

// 开启语音唤醒 DDS.getInstance().getAgent().getWakeupEngine().enableWakeup(); // 关闭语音唤醒 DDS.getInstance().getAgent().getWakeupEngine().disableWakeup(); // 获取主唤醒词列表 DDS.getInstance().getAgent().getWakeupEngine().getWakeupWords();

3.17.2 更新副唤醒词

如果您期望为设备增加一个副唤醒词,可以在需要的时候调用下面的接口。

参数说明:

1)word 副唤醒词,若设置null,则清空当前的副唤醒词。
2)pinyin 副唤醒词的拼音,形如:ni hao xiao chi。
3)threshold 副唤醒词的阈值,形如:0.120(取值范围:0-1)。若设置null,则自动估算。
4)greetings 副唤醒词的欢迎语,若设置null,则与主唤醒词保持一致。

updateMinorWakeupWord

// 实时更新副唤醒词(1.1.2.1版本及以上支持) WakeupWord minorWord = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小迟") .setThreshold("0.15") .addGreeting("我在"); DDS.getInstance().getAgent().getWakeupEngine().updateMinorWakeupWord(minorWord); //获取当前的副唤醒词 DDS.getInstance().getAgent().getWakeupEngine().getMinorWakeupWord();

3.17.3 更新命令唤醒词(快捷唤醒词)

如果您期望在唤醒的时候执行一条指令,可以在需要的时候调用下面的接口。命令唤醒词和DUI控制台所设置的快捷唤醒词是相同的功能,且互不影响,可同时存在,根据需求来选择设置方式。
参数说明:
1)actions 命令唤醒词指令,为string数组,不为null。
2)words 命令唤醒词,为string数组,不为null。
3)pinyins 命令唤醒词的拼音,形如:ni hao xiao chi,为string数组, 不为null。
4)thresholds 命令唤醒词的阈值,形如:0.120(取值范围:0-1),为string数组,不为null。
5)greetings 命令唤醒词的欢迎语,为string二维数组,不为null,每维对应一个唤醒词的欢迎语。

updateCommandWakeupWord

// 实时更新命令唤醒词-更新一条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord() .setPinyin("xia yi shou") .setWord("下一首") .setThreshold("0.18") .addGreeting("我在") .setAction("sys.next"); DDS.getInstance().getAgent().getWakeupEngine().updateCommandWakeupWord(commandWord); // 实时更新命令唤醒词-更新多条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord() .setPinyin("xia yi shou") .setWord("下一首") .setThreshold("0.18") .addGreeting("我在") .setAction("sys.next"); WakeupWord commandWord1 = new WakeupWord() .setPinyin("shang yi shou") .setWord("上一首") .addGreeting("我在") .setThreshold("0.20") .setAction("sys.play"); List<WakeupWord> commandList = new ArrayList<>(); commandList.add(commandWord); commandList.add(commandWord1); DDS.getInstance().getAgent().getWakeupEngine().updateCommandWakeupWords(commandList); //清空当前设置的命令唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearCommandWakeupWord();

3.17.4 更新打断唤醒词

如果您期望在唤醒的时候能同时打断语音播报,可以在需要的时候调用下面的接口。打断唤醒词暂不支持在DUI控制台设置。(说明:打断需要设备开启回声消除)
参数说明:
1)words 打断唤醒词,为string数组,不为null。
2)pinyins 打断唤醒词的拼音,形如:ni hao xiao chi,为string数组,不为null。
3)thresholds 打断唤醒词的阈值,形如:0.120(取值范围:0-1)为string数组,不为null。

updateShortcutWakeupWord

//实时更新打断唤醒词-更新一条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); DDS.getInstance().getAgent().getWakeupEngine().updateShortcutWakeupWord(fixWord); //实时更新打断唤醒词-更新多条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); WakeupWord fixWord1 = new WakeupWord() .setPinyin("da duan ba") .setWord("打断吧") .setThreshold("0.16"); List<WakeupWord> fixList = new ArrayList<>(); fixList.add(fixWord); fixList.add(fixWord1); DDS.getInstance().getAgent().getWakeupEngine().updateShortcutWakeupWords(fixList); //清空当前设置的打断唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearShortCutWakeupWord();

3.17.5 添加/删除指定的命令唤醒词(快捷唤醒词)

如果您期望添加或者移除一些命令唤醒词而保留之前的命令唤醒词(即控制台上的快捷唤醒词),可以调用下面的接口。
add remove

// 实时添加命令唤醒词-添加一条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord() .setPinyin("xia yi shou") .setWord("下一首") .setThreshold("0.18") .addGreeting("我在") .setAction("sys.next"); DDS.getInstance().getAgent().getWakeupEngine().addCommandWakeupWord(commandWord); // 实时添加命令唤醒词-添加多条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord() .setPinyin("xia yi shou") .setWord("下一首") .setThreshold("0.18") .addGreeting("我在") .setAction("sys.next"); WakeupWord commandWord1 = new WakeupWord() .setPinyin("shang yi shou") .setWord("上一首") .addGreeting("我在") .setThreshold("0.20") .setAction("sys.play"); List<WakeupWord> commandList = new ArrayList<>(); commandList.add(commandWord); commandList.add(commandWord1); DDS.getInstance().getAgent().getWakeupEngine().addCommandWakeupWords(commandList); // 实时移除命令唤醒词-移除一条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord().setWord("下一首"); DDS.getInstance().getAgent().getWakeupEngine().removeCommandWakeupWord(commandWord); // 实时移除命令唤醒词-移除多条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord().setWord("下一首"); WakeupWord commandWord1 = new WakeupWord().setWord("上一首"); List<WakeupWord> commandList = new ArrayList<>(); commandList.add(commandWord); commandList.add(commandWord1); DDS.getInstance().getAgent().getWakeupEngine().removeCommandWakeupWords(commandList); /** * 设置快捷命令唤醒词可唤醒,默认开启 * (在已经调用enableWakeup的情况下临时使用, disableWakeup情况下无效) */ DDS.getInstance().getAgent().getWakeupEngine().enableCommandWakeupWord() /** * 设置快捷命令唤醒词不可唤醒 */ DDS.getInstance().getAgent().getWakeupEngine().disableCommandWakeupWord()

3.17.6 添加/删除指定的打断唤醒词

如果您期望添加或者移除一些打断唤醒词而保留之前的打断唤醒词,可以在需要的时候调用下面的接口。

add remove

//实时添加打断唤醒词-添加一条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); DDS.getInstance().getAgent().getWakeupEngine().addShortcutWakeupWord(fixWord); //实时添加打断唤醒词-添加多条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); WakeupWord fixWord1 = new WakeupWord() .setPinyin("da duan ba") .setWord("打断吧") .setThreshold("0.16"); List<WakeupWord> fixList = new ArrayList<>(); fixList.add(fixWord); fixList.add(fixWord1); DDS.getInstance().getAgent().getWakeupEngine().addShortcutWakeupWords(fixList); //实时移除打断唤醒词-移除一条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord().setWord("打断"); DDS.getInstance().getAgent().getWakeupEngine().removeShortcutWakeupWord(fixWord); //实时移除打断唤醒词-移除多条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord().setWord("打断"); WakeupWord fixWord1 = new WakeupWord().setWord("打断吧"); List<WakeupWord> fixList = new ArrayList<>(); fixList.add(fixWord); fixList.add(fixWord1); DDS.getInstance().getAgent().getWakeupEngine().removeShortcutWakeupWords(fixList);

3.17.7 添加/删除指定的主唤醒词

如果您想通过代码来控制主唤醒词,可以调用下面的接口。
注意:通过此接口添加的主唤醒词会屏蔽控制台添加的主唤醒词。

主唤醒词

//实时添加主唤醒词-添加一条主唤醒词(1.1.2.1版本及以上支持) WakeupWord mainWord = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小迟") .addGreeting("我在") .setThreshold("0.15"); DDS.getInstance().getAgent().getWakeupEngine().addMainWakeupWord(mainWord); //实时添加主唤醒词-添加多条主唤醒词(1.1.2.1版本及以上支持) WakeupWord mainWord = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小迟") .addGreeting("我在") .setThreshold("0.15"); WakeupWord mainWord2 = new WakeupWord() .setPinyin("ni hao xiao le") .setWord("你好小乐") .addGreeting("我在") .setThreshold("0.15"); List<WakeupWord> mainWordList = new ArrayList<>(); mainWordList.add(mainWord); mainWordList.add(mainWord2); DDS.getInstance().getAgent().getWakeupEngine().addMainWakeupWords(mainWordList); //实时移除主唤醒词-移除一条主唤醒词(1.1.2.1版本及以上支持) WakeupWord mainWord = new WakeupWord().setWord("你好小迟"); DDS.getInstance().getAgent().getWakeupEngine().removeMainWakeupWord(mainWord); //实时移除主唤醒词-移除多条主唤醒词(1.1.2.1版本及以上支持) WakeupWord mainWord = new WakeupWord().setWord("你好小迟"); WakeupWord mainWord2 = new WakeupWord().setWord("你好小乐"); List<WakeupWord> mainWordList = new ArrayList<>(); mainWordList.add(mainWord); mainWordList.add(mainWord2); DDS.getInstance().getAgent().getWakeupEngine().removeMainWakeupWords(mainWordList); //更新多条主唤醒词的接口,覆盖原有的主唤醒词 WakeupWord mainWord = new WakeupWord() .setPinyin("ni hao xiao le") .setWord("你好小乐") .setThreshold("0.15") .addGreeting("我在"); WakeupWord mainWord2 = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小迟") .setThreshold("0.15") .addGreeting("我在"); List<WakeupWord> mainWordList = new ArrayList<>(); mainWordList.add(mainWord); mainWordList.add(mainWord2); DDS.getInstance().getAgent().getWakeupEngine().updateMainWakeupWords(mainWordList);

3.17.8 添加/删除指定的快速唤醒词

如果您想通过代码来控制快速唤醒,可以调用下面的接口。

QuickStart词为类似“导航去”、“我想听”等,此类唤醒词只在oneshot模式下生效,作用为在未唤醒状态下语音输入“导航去天安门”,可直接进入对话流程。

quickStart

//添加多条QuickStart词 WakeupWord quickStartWord = new WakeupWord() .setPinyin("dao hang qu") .setWord("导航去") .setThreshold("0.15"); List<WakeupWord> quickStartWordList = new ArrayList<>(); quickStartWordList.add(quickStartWord); DDS.getInstance().getAgent().getWakeupEngine().addQuickStartWords(quickStartWordList); //实时移除一条QuickStart词 WakeupWord quickStartWord = new WakeupWord().setWord("导航去"); DDS.getInstance().getAgent().getWakeupEngine().removeQuickStartWord(quickStartWord); //实时移除多条QuickStart词 WakeupWord quickStartWord = new WakeupWord().setWord("导航去"); List<WakeupWord> quickStartWordList = new ArrayList<>(); quickStartWordList.add(quickStartWord); DDS.getInstance().getAgent().getWakeupEngine().removeQuickStartWords(quickStartWordList); //更新多条QuickStart词的接口,覆盖原有的QuickStart词 WakeupWord quickStartWord = new WakeupWord() .setPinyin("dao hang qu") .setWord("导航去") .setThreshold("0.15"); List<WakeupWord> quickStartWordList = new ArrayList<>(); quickStartWordList.add(quickStartWord); DDS.getInstance().getAgent().getWakeupEngine().updateQuickStartWords(quickStartWordList); //清空QuickStart词 DDS.getInstance().getAgent().getWakeupEngine().clearQuickStartWords();

3.17.9 oneshot开关

如果您想动态更改唤醒模式,可以在需要的时候调用下面的接口。

oneshot

//开启oneshot DDS.getInstance().getAgent().getWakeupEngine().enableOneShot(); //关闭oneshot DDS.getInstance().getAgent().getWakeupEngine().disableOneShot(); /** * 获取当前oneshot开关状态,如果有本地接口调用开关,以本地接口调用优先。 */ DDS.getInstance().getAgent().getWakeupEngine().getOneshotState()

3.17.10 设置环麦增强角度

// 如果您想动态指定环麦增强角度,可以在需要的时候调用下面的接口。

DDS.getInstance().getAgent().getWakeupEngine().setWakeupDoa(int doa);

3.17.11 设置车载双麦模式

// 如果您想动态指定车载双麦的驾驶模式,可以在需要的时候调用下面的接口。

// 驾驶模式, 0:定位模式, 1:主驾模式, 2: 副驾模式 3:全车模式 DDS.getInstance().getAgent().getWakeupEngine().setWakeupMode(int mode);

3.17.12 获取当前唤醒内核的版本号

如果您想获取当前唤醒内核的版本号,可以在需要的时候调用下面的接口。

// 该接口废弃,获取到的版本号在 2.0.0.0 版本上无效 DDS.getInstance().getAgent().getWakeupEngine().getWakeupVersion();

3.17.13 设置是否允许识别过程中响应唤醒

如果您想设置是否允许识别过程中响应唤醒,可以在需要的时候调用下面的接口。

//enable: true. 允许识别过程中响应唤醒, false. 不允许识别过程中响应唤醒 DDS.getInstance().getAgent().getWakeupEngine().enableWakeupWhenAsr(boolean enable);

3.17.14 设置自定义欢迎语回调

如果您想设置自定义欢迎语,可以在需要的时候调用下面的接口, 该方法是阻塞方法,不建议在该方法内部做耗时操作。

DDS.getInstance().getAgent().getWakeupEngine().setWakeupCallback(WakeupCallback wakeupCallback);DDS.getInstance().getAgent().getWakeupEngine().setWakeupCallback(new WakeupCallback() { /** * * @param wakeupResult 唤醒状态 * <p> * wakeupResult.optString("word");// 唤醒词 * <p> * wakeupResult.optString("type");// 唤醒状态: major: 正常唤醒 command: 命令唤醒 interrupt: 打断唤醒 * <p> * wakeupResult.optString("gender");// 性别: male / female * <p> * wakeupResult.optString("action");// 命令唤醒的动作 * <p> * @return */ @Override public JSONObject onWakeup(JSONObject wakeupResult) { JSONObject result = new JSONObject(); try { result.put("greeting", "你好"); } catch (JSONException e) { e.printStackTrace(); } return result; });

3.17.15 获取主唤醒词列表

获取主唤醒词列表,默认返回WakeupEngine所设置的主唤醒词,如果没有设置过则返回产品配置的主唤醒词。

DDS.getInstance().getAgent().getWakeupEngine().getMainWakeupWords();

3.17.16 设置Beamforming监听器

设置Beamforming监听器, 实时回调Beamforming音频

DDS.getInstance().getAgent().getWakeupEngine().setBfListener(BfListener listener);

3.17.17 设置唤醒模块的模式/低功耗模式

  • 设置唤醒模块的模式(信号处理模块不变,环麦中暂不支持)
DDS.getInstance().getAgent().getWakeupEngine().setWakeupSwitch(boolean mode);
  • 开启低功耗模式后, 唤醒功能不可用, cpu会降低 (此方法已废弃)
DDS.getInstance().getAgent().getWakeupEngine().setLpSwitch(boolean lpSwitch);

3.17.18 就近唤醒

如果你有使用就近唤醒的需求, 可以参照下面的配置与设置(目前支持线性双麦/线性四麦的就近唤醒, 1.2.12.1-1.2.15.1的版本暂不支持就近唤醒)

1. 设置major参数为true (从1.2.8.1版本开始, 不再需要此配置) config.addConfig("MAJOR", "true"); 2. 设置K_USE_NEAR_WAKEUP为true, 表示使用就近唤醒 config.addConfig(DDSConfig.K_USE_NEAR_WAKEUP, "true"); 3. 在设置唤醒词的时候设置major字段 WakeupWord minorWord = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小驰") .setThreshold("0.25") .setMajor("1")(从1.2.8.1版本开始, 不再需要此配置) .addGreeting("小驰回来了"); DDS.getInstance().getAgent().getWakeupEngine().addMainWakeupWord(minorWord); 4. 就近唤醒运行过程中可以动态设置一些参数 /** * 设置就近唤醒参数与调度参数<p> * 仅在就近唤醒状态下生效(线双麦/线四麦) */ DDS.getInstance().getAgent().getWakeupEngine().setNearWakeupParams(JSONObject paramsObj)

3.17.19 拦截唤醒结果

/** * 拦截唤醒结果 wakeup.result, * * @param isCatch 是否拦截唤醒结果 true-拦截 ;false-取消拦截 * @return * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getWakeupEngine().catchWakeupResult(boolean isCatch)

3.17.20 设置 VOIP 音频回调

  1. 配置 VOIP 信息
config.addConfig(DDSConfig.K_VOIP_TYPE, "line_2");
  1. 设置 VOIP 回调
/** * 设置voip监听器, 时时回调voip音频 * * @param listener */ public void setVoipListener(VoipListener listener)
  1. 启动 VOIP 功能
// 调用 setEnableVoip(true) 时 voipNode 才会注册消息,接收音频数据 DDS.getInstance().getAgent().setEnableVoip(true);
  1. 录音后就会从回调里回调 voip 音频

  2. 取消 VOIP 回调

// 取消 VOIP 回调 DDS.getInstance().getAgent().getWakeupEngine().setVoipListener(null); // 停用 voip 功能 DDS.getInstance().getAgent().setEnableVoip(false);

3.17.21 更新唤醒词

支持多个不同类型的唤醒词同时配置

List<WakeupWordIntent> wakeupWordIntentList = new ArrayList<>(); // 添加主唤醒词 WakeupWordIntent wakeupWordIntent0 = new WakeupWordIntent(WakeupType.ADD_MAIN) .setPinyin("ni hao xiao le") .setWord("你好小乐") .setThreshold("0.16") .addGreeting("小乐来了"); WakeupWordIntent wakeupWordIntent1 = new WakeupWordIntent(WakeupType.ADD_COMMAND) .setPinyin("kai shi bo fang") .setWord("开始播放") .setThreshold("0.18") .addGreeting("好的,开始播放") .setAction("sys.play"); WakeupWordIntent wakeupWordIntent2 = new WakeupWordIntent(WakeupType.ADD_SHORTCUT) .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); wakeupWordIntentList.add(wakeupWordIntent0); wakeupWordIntentList.add(wakeupWordIntent1); wakeupWordIntentList.add(wakeupWordIntent2); DDS.getInstance().getAgent().getWakeupEngine().updateWakeupWords(wakeupWordIntentList);

3.17.22 动态设置对话模式

/** * 动态设置对话模式<br> * 唤醒后是否进入对话<br> * <p> * 取值:dialog or partner<br> * dialog:唤醒默认进入识别及对话<br> * partner:唤醒后不会主动进入识别及对话,同时唤醒后会收到sys.wakeup.result的message消息<br> * 是否必需:否<br> * 默认值:dialog<br> * * @param type 对话模式 partner */ DDS.getInstance().getAgent().getWakeupEngine().updateWkupRouter(String type)

3.17.23 增强唤醒(可见即可说和全局免唤醒)

增强唤醒使用本地识别功能识别设置的唤醒词,当达到阈值(默认0.63)时即认为唤醒成功.

功能特性

• 支持两个字的唤醒词

• 支持同一个唤醒词写入很多类型说法(目前最大可支持500个,延时200ms)

• 支持中英文唤醒(打开wifi、打开音乐); 中英文功能从 WTK v1.9.39 开始支持

• 唤醒词设置为打开导航,可支持说“哈哈打开导航”不响应(极大减小误唤醒)

使用场景

• 解决相似说法串到唤醒,比如定义“打开导航”,说“开导航”也能唤醒

• 解决语音起名字及手动设置唤醒词设置为“思必驰”“小小思”等容易误唤醒问题

• 解决开发者定义唤醒词每次都需要思必驰提供阈值,开发者只需要输入中文即可

• 解决在导航列表tts播报时说分词容易误选择问题,比如导航列表有“天安门”“北京天安门”“天安门地铁站“,以前用唤醒说”天安门地铁站“会响应”天安门“

使用方法
  1. 初始化
  • 打开增强唤醒功能
config.addConfig(DDSConfig.K_SUPPORT_INCREMENT_WAKEUP, "true");
  • 开启可见即可说
JSONObject ctrlObj = new JSONObject(); ctrlObj.put("type", "hotWord"); DDS.getInstance().getAgent().getWakeupEngine().enableIncrementWakeup(ctrlObj);
  • 开启全局免唤醒
JSONObject ctrlObj = new JSONObject(); ctrlObj.put("type", "globalWord"); DDS.getInstance().getAgent().getWakeupEngine().enableIncrementWakeup(ctrlObj);
  • 开启可见即可说 + 全局免唤醒
    DDS.getInstance().getAgent().getWakeupEngine().enableIncrementWakeup();
  1. 优先级设置
  • 如果可见即可说和全局免唤醒词有相同的词,可见即可说优先
DDS.getInstance().getAgent().getWakeupEngine().setIncrementPriority(WakeupPriorityType.HOTWORD_FIRST);
  • 如果可见即可说和全局免唤醒词有相同的词,全局免唤醒优先
DDS.getInstance().getAgent().getWakeupEngine().setIncrementPriority(WakeupPriorityType.GLOBALWORD_FIRST);
  1. 使用全局免唤醒词
可使用增强唤醒的唤醒词类型: * 命令唤醒词(Command) * 打断唤醒词(ShortCut) WakeupWord commandWord = new WakeupWord() .setWord("开始播放") .setThreshold("0.18") .setIncrementWord("true") .addGreeting("好的,开始播放") .setAction("sys.play"); WakeupWord commandWord1 = new WakeupWord() .setWord("下一首") .setThreshold("0.18") .setIncrementWord("true") .addGreeting("下一首nlg") .setAction("command.next"); List<WakeupWord> wakeupWordList = new ArrayList<>(); wakeupWordList.add(commandWord); wakeupWordList.add(commandWord1); DDS.getInstance().getAgent().getWakeupEngine().addCommandWakeupWords(wakeupWordList); WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setIncrementWord("true") // 使用增强唤醒 .setThreshold("0.18"); List<WakeupWord> wakeupWordList = new ArrayList<>(); wakeupWordList.add(fixWord); DDS.getInstance().getAgent().getWakeupEngine().updateShortcutWakeupWords(wakeupWordList); // 删除增强唤醒的打断唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearIncrementShortCutWakeupWord(); // 删除增强唤醒的命令唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearIncrementCommandWakeupWord(); // 删除增强唤醒的所有唤醒词,包括打断唤醒词和命令唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearIncrementWakeupWord();
  1. 使用可见即可说
HotWords.HotWord hotWord1 = new HotWords.HotWord() .setOrigin("抖音最全中文歌")//页面原始词汇 .addSegment("抖音最全")//分词内容 .addCustom("id", "123123")//上层自定义扩展字段 .addCustom("name", "music");//上层自定义扩展字段 HotWords.HotWord hotWord2 = new HotWords.HotWord() .setOrigin("开始播放") .addCustom("id", "123123"); HotWords.Slots slots = new HotWords.Slots() .setSlot("dynamic_slot") .addHotWord(hotWord1) .addHotWord(hotWord2); HotWords hotWords = new HotWords() .setScene("default_scene") .addSlots(slots); DDS.getInstance().getAgent().getWakeupEngine().updateHotWords(hotWords);

3.18 数据上传接口

开发者调用此接口来上传一些设备的信息,这些信息可以在本地使用,也可以在对话中获取,作为对话决策的一部分。
数据点

/** * 更新设备状态,产品级的配置。比如:定位信息,设备硬件状态等 * 更新结果可以通过sys.upload.result消息来获取 * * @param intent 请求的ContextIntent对象,包括key和value * 如:ContextIntent intent = new ContextIntent("status","{\"platform\":\"Android\"}"); * updateProductContext(intent); * 技能里通过$context.system.settings.status.platform$即可获取到"Android" * @return 请求的ID,用于追踪sys.upload.result * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().updateProductContext(ContextIntent intent); /** * 更新技能配置,需要调用ContextIntent.setSkillId设置技能ID * 更新结果可以通过sys.upload.result消息来获取 * * @param intent 请求的ContextIntent对象,包括key、value、skillId。 * 如:ContextIntent intent = new ContextIntent("status","{\"platform\":\"Android\"}"); * intent.setSkillId("xxxxxxxxxxxxxxxxx"); * updateSkillContext(intent); * 该技能里通过$context.skill.settings.status.platform$即可获取到"Android",其他技能则无法使用。 * @return 请求的ID,用于追踪sys.upload.result * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().updateSkillContext(ContextIntent intent); /** * 获取产品的配置信息 * * @param inputKey 配置项名称 * @return 配置项值 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getProductContext(String inputKey); /** * 获取技能的配置信息 * * @param skillId 技能ID * @param inputKey 配置项名称 * @return 配置项值 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getSkillContext(String skillId, String inputKey); /** * 更新热词识别接口(请求级别) * 每次请求都会带上热词词库, 例如: [{"type": "vocab", "name": "sys.联系人", "data":["章大樵"]}] * 本接口为覆盖式接口,以最新设置为准,如需要清除请求空参数即可 * * @param phraseHints 需要更新的热词识别列表 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().updatePhraseHints(PhraseHintsIntent... phraseHints);

3.19 获取性别/年龄信息

  1. 在初始化DDS SDK的时候加入如下配置:
config.addConfig(DDSConfig.K_USE_GENDER, "true"); config.addConfig(DDSConfig.K_USE_AGE, "true");
  1. 然后订阅 message: sys.dialog.start
    收到消息内容类似:{"reason":"wakeup.major","gender":"male","age":"adult"}

gender就是性别字段,age就是年龄字段,性别年龄信息只有在主唤醒词唤醒后,才会发出。

3.20 动态更新云端识别模型

/** * 更新云端识别的模型名字, * 在调用完该接口后,下一次对话开始时生效,并一直用该模型,除非客户端再调用该接口设置为其他的模型 * * @param asrModel 云端识别的模型名字,有aihome, airobot等, * 默认为dui控制台配置的模型资源, * 如果填null,则表示清除之前本地配置的模型名,之后会使用dui控制台配置的模型资源 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getASREngine().updateAsrModel("aihome"); /** * 获取当前使用的云端识别的模型 * * @return asrModel 当前使用的云端识别的模型, 如果失败会返回null,或者抛出DDSNotInitCompleteException * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getASREngine().getAsrModel();

3.21 快捷指令

订阅 sys.dm.inspire 消息,用于接收快捷指令。只在 半双工 模式有该功能

DDS.getInstance().getAgent().subscribe(new String[]{..., "sys.dm.inspire", ...}, new MessageObserver() { @Override public void onMessage(String message, String data) { if("sys.dm.inspire".equals(message)) { try { JSONObject obj = new JSONObject(data); JSONArray inspire = obj.getJSONArray("inspire"); ... } catch (JSONException e) { e.printStackTrace(); } catch (DDSNotInitCompleteException e) { e.printStackTrace(); } } } });

inspire 即为快捷指令的集合数组,通过解析 inspire 来处理指令。inspire 数据结构如下:

inspire:[ { speak:{ "type":"text", "text":"123" }, widget:{ "type":"text", ... } }, { speak:{ "type":"text", "text":"抱歉由于设备离线,操作失败,请再试一次" }, widget:{ "type":"list", ... } }, ... ]

上述结构中,speak 并不是必须的字段, widget 是必须的。

widget即为需要需要展示出来的控件样式,其中 type 字段代表控件样式的类型,有 text/list/web/image/media…

关于 widget 不同控件样式的不同数据结构,可参考: https://www.duiopen.com/docs/ct_UI 。举例:list 对应这 context.widget.list,以此类推

3.22 全双工模式

3.22.1 模式切换

// 设置dds模式 DDS.getInstance().getAgent().setDuplexMode(Agent.DuplexMode.HALF_DUPLEX);// 半双工,一问一答模式 DDS.getInstance().getAgent().setDuplexMode(Agent.DuplexMode.FULL_DUPLEX);// 全双工模式 注意: 在切换全双工时如需要唤醒还请调用: DDS.getInstance().getAgent().getWakeupEngine().enableWakeup(); // 获取dds模式 DDS.getInstance().getAgent().getDuplexMode();

3.22.2 全双工不支持接口与功能

  1. 由于全双工模式为全程识别状态, 所以开启识别和 startListening 不可同时使用 [3.13开启/终止识别](#3.13 仅开启识别)

  2. 由于全双工模式为全程识别状态, 在模拟半双工下的打断唤醒由识别代替,主唤醒词正常开启下一轮对话

  3. 由于全双工模式下暂时没有快捷指令的使用场景, 所以快捷指令在全双工模式下不可用 [3.21 快捷指令](#3.21 快捷指令)

  4. 全双工模式下默认使用 vad 功能, 比如 oneshot 功能就需要打开 vad。配置 K_USE_VAD_IN_FULLDUPLEX 为 true(默认为true)

3.22.3 全双工下跳过Vad超时检测

// 在全双工模式下, 如果你想跳过Vad的检测直接进入识别状态则可以调用下面的接口 DDS.getInstance().getAgent().getASREngine().killVadTimeoutInFullDuplex();

3.23 对话结果支持内容修改

此接口支持修改对话中的语音播报,修改文本展示等功能

// 从1.2.8.1版本开始支持 DDS.getInstance().getAgent().setDMTaskCallback(new DMTaskCallback() { @Override public JSONObject onDMTaskResult(JSONObject dmTaskResult, Type type) { if (type == DMTaskCallback.Type.DM_OUTPUT) {// 处理dm的消息 // 显示 String display = dmTaskResult.optString("display"); // 播报 String nlg = dmTaskResult.optString("nlg"); Log.d(TAG, "display = " + display); Log.d(TAG, "nlg = " + nlg); try { dmTaskResult.put("display", "修改之后的显示"); dmTaskResult.put("nlg", "修改之后的播报"); dmTaskResult.put("speakUrl", null); // 不设置为null的话,播放的还是原来的音频 } catch (JSONException e) { e.printStackTrace(); } } else if (type == DMTaskCallback.Type.CDM_ERROR) {// // 处理对话异常的消息 // 播报 String nlg = dmTaskResult.optString("nlg"); Log.d(TAG, "nlg = " + nlg); try { dmTaskResult.put("nlg", "修改之后的播报"); dmTaskResult.put("speakUrl", null); // 不设置为null的话,播放的还是原来的音频 } catch (JSONException e) { e.printStackTrace(); } } return dmTaskResult; } });

3.24 声纹

声纹注册、验证的词也是主唤醒词,唤醒后会使用唤醒音频做声纹的注册、验证等操作

  1. 打开声纹配置
config.addConfig(DDSConfig.K_VPRINT_ENABLE, "true"); // 是否开启声纹功能 DDS.getInstance().getAgent().getWakeupEngine().useVprintInWakeUp(true) // 动态设置是否在唤醒中启用声纹
  1. 使用声纹
// 注册声纹监听器 VprintEngine.getInstance().setVprintListener(new VprintListener() { @Override public void onState(String state) { Log.e(TAG, "onState = " + state); } @Override public void onResults(String result) { Log.e(TAG, "onResults = " + result); } @Override public void onError(String error) { Log.e(TAG, "onError = " + error); } }); // 注册声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.REGISTER) .setTrainNum(3) .setOutChannelNum(2) .setSnrThresh(8.67f) .setUserId("userId") .setVprintWord("ni hao xiao chi") .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 更新声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.UPDATE) .setTrainNum(3) .setOutChannelNum(2) .setSnrThresh(8.67f) .setUserId("userId") .setVprintWord("ni hao xiao chi") .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 追加声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.APPEND) .setTrainNum(3) .setOutChannelNum(2) .setSnrThresh(8.67f) .setUserId("userId") .setVprintWord("ni hao xiao chi") .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 删除一条声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.UNREGISTER) .setTrainNum(3) .setOutChannelNum(2) .setUserId("userId") .setVprintWord("ni hao xiao chi") .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 删除所有声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.UNREGISTER_ALL) .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 使用声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.TEST) .setOutChannelNum(2) .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 获取声纹模型 String result = VprintEngine.getInstance().getMode(); // 停止使用声纹 VprintEngine.getInstance().stop();

3.25 配合 dca app 使用

通过语音控制家居设备时需要对话带上 dca app 里用户的信息。

TokenListener tokenListener = new TokenListener() { @Override public void onSuccess(TokenResult tokenResult) { Log.d(TAG, "onSuccess() " + tokenResult); success = true; } @Override public void onError(int errorId, String error) { Log.d(TAG, "errorId:" + errorId + " error:" + error); } }; AuthInfo authInfo = new AuthInfo(); authInfo.setUserId("8458"); authInfo.setCodeVerifier("doeifkgdgurgg"); authInfo.setClientId("dfgoeigjergndfgdfg"); authInfo.setAuthCode("dfgerhrh6564hgrh4hr"); authInfo.setRedirectUri("http://dui.aadf.bb.cc"); DDS.getInstance().getAgent().setAuthCode(authInfo, tokenListener);

3.26 自定义提示语

JSONObject jsonObject = new JSONObject(); try { jsonObject.put("071304", "自定义vad超时"); } catch (JSONException e) { e.printStackTrace(); } /** * 此接口支持动态修改 custom tips * * @param obj <p> * 取值:JSON字符串,如:<br> * { * "71304":"这是识别结果为空的自定义播报", * "71305":"这是语义结果为空的自定义播报", * "71308":"这是进入闲聊技能的自定义播报", * "713**":"*****" * }<br> * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().setCustomTips(jsonObject);

3.27 一句话多意图

背景:
一句话多意图: 一句话里包括多个意图或动作,比如:调节空调和车窗
使用场景:智能家居或车联网等场景
此功能需要云端配置,如需使用请和思必驰客户经理联系

String[] mTopic = new String[]{ "sys.native.inspire" }; NativeCommandObserver nativeCommandObserver = new NativeCommandObserver() { @Override public JSONObject onNativeCommandCall(String command, String data) { try { if (TextUtils.equals(command, "sys.native.inspire")) { AILog.d(TAG, "command = " + command); AILog.d(TAG, "data = " + data); JSONObject dataObj = new JSONObject(data); JSONArray inspireArr = dataObj.optJSONArray("inspire"); JSONObject selectInspireItem = null;// 用户选择要dds执行的一个item for (int i = 0; i < inspireArr.length(); i++) { JSONObject itemObj = inspireArr.optJSONObject(i); // TODO 业务逻辑,判断哪一个item是要后续dds处理的 selectInspireItem = itemObj; JSONObject commandObj = itemObj.optJSONObject("command"); String commandApi = commandObj.optString("api"); // TODO 业务逻辑,解析command,处理command JSONObject speakObj = itemObj.optJSONObject("speak"); String text = speakObj.optString("text"); // 模拟播报与output DDS.getInstance().getAgent().getTTSEngine().speak(text, 0); DDS.getInstance().getAgent().getBusClient().publish("context.output.text", new JSONObject().put("text", text).toString()); SystemClock.sleep(2000); } // 返回最终要播报的文本 return JSONObjectUtil.create() .put("nlg", "已全部打开") .put("selectInspireItem", selectInspireItem)// 回传了选择的item,则修改nlg播报无效 .build(); } } catch (Exception e) { e.printStackTrace(); } return null; } }; DDS.getInstance().getAgent().subscribeSync(mTopic, nativeCommandObserver);

四、DDSConfig 配置项

DDSConfigBuilder 构造方法需要设置 产品信息duicore 资源 ,最简单的demo无需设置其它即可以单麦方式运行。

DDSConfigBuilder ddsConfigBuilder = new DDSConfigBuilder( productId, aliasKey, apiKey, productKey, productSecret, duicoreZip )

ddsConfigBuilder 包含很多配置,现按功能分为以下子类:

  1. ddsConfigBuilder.createCommonBuilder() [4.1 设备信息、使用目录、VOIP功能 等通用配置](#4.1 设备信息、使用目录、VOIP功能 等通用配置)
  2. ddsConfigBuilder.createNodeBuilder() [4.2 sdk 功能开关](#4.2 sdk 功能开关)
  3. ddsConfigBuilder.createWakeupBuilder() [4.3 麦克风类型、唤醒、声纹、asrpp、Oneshot 配置](#4.3 麦克风类型、唤醒、声纹、asrpp、Oneshot 配置)
  4. ddsConfigBuilder.createDmBuilder() [4.4 对话配置](#4.4 对话配置)
  5. ddsConfigBuilder.createTtsBuilder() [4.5 TTS、播放 配置](#4.5 TTS、播放 配置)
  6. ddsConfigBuilder.createAsrBuilder() [4.6 识别配置](#4.6 识别配置)
  7. ddsConfigBuilder.createRecorderBuilder() [4.7 录音配置](#4.7 录音配置)
  • ddsConfigBuilder 还可通过以下方法添加额外配置
ddsConfigBuilder.addConfig(String key, Object value)

下面简单介绍一下配置项,详细说明请看 DDSConfig 配置请参见 javadoc在线文档

  • 定制资源集成差异
    v2.0.0.1 及以上版本不仅支持系统目录,也支持放入 assets 目录。v1.X.X.X 版本只支持 sdcard 系统目录配置

使用方式如下:

  1. assets 目录配置方式: setWakeupBin(“wakeup.bin”)
  2. 系统目录配置方式: setWakeupBin("/sdcard/sd/wakeup.bin")

4.1 设备信息、使用目录、VOIP功能 等通用配置

ddsConfigBuilder.createCommonBuilder() .setCustomZip("product.zip") // 使用内置产品资源包, 从官网下载的产品资源包一般叫 product.zip .setDeviceName("XXXXXX") // 设备 DeviceName, 用来标识设备的标识符 .setDeviceId("XXXXXX") // 设备ID。用于授权认证的原始设备ID,请务必确保设备ID唯一 .setAccessToken("XXXXXX") // 保留字段,上传、升级时使用 .setCachePath("/sdcard/cache") // 缓存文件保存目录,默认 /sdcard/Android/data/包名/cache .setCacheSize(500000) // 缓存文件大小限制。自定义缓存目录时必需 .setCustomHome("/sdcard/aispeech") // 产品资源存放的目录,默认 /data/data/com.xxx/files/dds/custom/ .setDuicoreHome("/sdcard/aispeech") // duicore 资源存放的目录,默认 /data/data/包名/files/dds/duicore/ .setInitTimeout(60) // SDK初始化超时时间,单位秒。默认 60秒 .setCustomSha256("XXXXXX") // 自定义授权证书的SHA256, sdk 授权时会检查包名和签名的SHA256,第三方加固后可能改变 .setCustomBackupZipUserDefined("/sdcard/aispeech") // 产品资源包保存路径 .setDuicoreBackupZipUserDefined("/sdcard/aispeech") // duicore 资源包保存路径 .setUserId("XXXXXX") // 用户ID 保留字段 .setVoipType("line_4") // 使用 voip 功能时的类型,现在支持线性双麦(line_2)和线性4麦(line_4) .setVoipResBin("voip.bin") // voip 的资源的路径。assets目录下为压缩包名称,其他目录为绝对路径 .setOfflineProfileName(".profile") // assets目录下的离线授权文件名 .enableUpdateDuicore(false) // 是否更新 duicore 资源。 默认 false .enableDynamicUiAgentPort(false) // 是否使用 uiagent 动态端口号,默认 false .enableReplaceProfile(true) // 试用授权是否连网更新成在线授权 默认 true .setProductLicenseId("XXXXXX") // 产品licenseID .setProductVersion("2") // 连接指定产品版本的对话服务 .enableUpdateNotification(true); // 是否使用内置的资源更新通知栏 默认 true

4.2 sdk 功能开关

sdk 需要使用到的功能开关配置。

包括推送、大数据、socket节点、唤醒、声纹、VAD节点、H5节点 等

ddsConfigBuilder.createNodeBuilder() .enablePush(true) // 是否开启推送功能。默认 true .enableSocket(true) // 是否开启 socket 节点功能。默认 true .enableUpdate(true) // 是否开启数据更新功能。默认 true .enableUpload(true) // 是否开启大数据上传功能。默认 true .enableVad(true) // 是否开启 VAD 节点功能。默认 true .enableWakeup(true) // 是否开启唤醒节点功能。默认 true .enableIncrementWakeup(false) // 是否支持本地识别增量更新。默认 false .enableVprint(false) // 是否开启声纹功能。默认 false .enableH5(false); // 是否开启H5节点功能。默认 false

4.3 麦克风类型、唤醒、声纹、asrpp、Oneshot 配置

ddsConfigBuilder.createWakeupBuilder() .setMicType(0) // 硬件麦克风采集模组的类型。即 单麦、双麦、线性4麦、环形6麦等。 详情请看:4.3.1 MicType 说明 .setWakeupBin("wakeup.bin") // 设置唤醒资源 .setBeamformingBin("ULA_2.bin") // 麦克风阵列 beamforming 资源 .setAecBin("aec.bin") // 麦克风阵列AEC资源 .setIncrementWakeupBin("increment.bin") // 增强唤醒的 asr 资源,增强唤醒是使用 本地asr 实现的 .setVprintBin("vprint.bin") // 声纹资源 .enableVprintInWakeup(false) // 是否在唤醒中使用声纹判断,默认 false。诺设置为 true,需开启声纹功能 .enableSspe(false) // 多麦是否使用 sspe,默认 false .setSspeBin("sspe.bin") // 麦克风阵列 sspe 资源 .enableAge(false) // 是否使用年龄识别,默认 false .setAgeBin("age.bin") // 年龄识别资源 .enableGender(false) // 是否使用性别识别,默认 false .setGenderBin("gender.bin") // 性别识别资源 .enableNearWakeup(false) // 是否使用就近唤醒,默认 false .setNearWakeupDebounceWnd(450) // 就近唤醒防抖窗口期,单位毫秒 .setNearWakeupWkpWnd(300) // 就近唤醒决策窗口期,单位毫秒 .setNearWakeupMdsBin("mds.bin") // 就近唤醒的 mds 资源 .setOneshotMidTime(500) // 详情请看:4.3.2 OneShot配置 .setOneshotEndTime(0) // 详情请看:4.3.2 OneShot配置 .enableCustomGreeting(true) // 是否使用自定义欢迎语功能,默认 true .enableWakeupInAsr(true) // 识别过程中是否响应唤醒,默认 true .setAecMode("internal") // AEC(回声消除)模式,设置 external 后,单麦播放 tts 时唤醒节点不会停止,打断唤醒词仍可使用 .setWakeupRouter("dialog"); // 唤醒后的路由,唤醒后是否进入对话。取值:dialog(唤醒默认进入识别及对话) or partner(唤醒后不会主动进入识别及对话,同时唤醒后会收到sys.wakeup.result的message消息)

4.3.1 MicType 说明

setMicType 配置说明

设置硬件麦克风采集模组的类型

默认使用 DUI 控制台上产品的对应配置项。若客户端通过SDK配置此项后,则以客户端配置为准

取值:

  • 0:单麦(1路音频)默认
  • 1:单麦回消(1路音频+1路参考音)
  • 2:线性四麦(4路音频)
  • 3:环形六麦(6路音频)
  • 4:车载双麦(废弃)
  • 5:家居双麦(2路音频)
  • 6:环麦4麦(4路音频)
  • 7:车载双麦(2路音频)
  • 8:线性六麦(6路音频)
  • 10:线性八麦(8路音频)

4.3.2 OneShot配置

setOneshotMidTime 和 setOneshotEndTime 配置说明

  • 取值:毫秒
  • 若MIDTIME为0 & ENDTIME为0,唤醒后进入识别;若VAD检测超时,则直接退出对话
  • 若MIDTIME为0 & ENDTIME不为0,唤醒后进入识别;若ENDTIME超时,则直接退出对话
  • 若MIDTIME不为0 & ENDTIME为0,唤醒后进入识别;若MIDTIME超时,则播放欢迎语,继续识别(默认)
  • 若MIDTIME不为0 & ENDTIME不为0,唤醒后进入识别;若MIDTIME超时,则播放欢迎语,继续识别,若ENDTIME超时,则直接退出对话
  • 默认值:MIDTIME=500ms,ENDTIME=0ms

4.4 对话配置

对话相关配置

  • 设置对话模式是半双工还是全双工配置已经删除,请用 Agent.setDuplexMode(final DuplexMode duplexMode) 接口设置
ddsConfigBuilder.createDmBuilder() .enableVadInFullduplex(true) // 全双工模式下是否使用 vad,默认 true .enableTipsInFullduplex(true) // 全双工中是否使用消息的提示音,默认 true .enableLocalDDS(false) // 是否使用本地对话,默认 false .setCustomTips(customTips) // 指定对话错误码的 TTS 播报内容 .setClickMode(0) // 点击模式,调用 Agent#avatarClick 后sdk的响应模式。 .setDmTimeout(10000) // 对话响应超时时间。单位 毫秒。默认 10000 ms .setNativeApiTimeout(10000); // nativeApi 超时时间。单位 毫秒。默认 10000 ms

4.5 TTS、播放 配置

ddsConfigBuilder.createTtsBuilder() .enableExoPlaySpeakUrl(false) // 是否使用 exoPlayer 播报 tts 的 url,默认 false .enableOptmization(false) // 本地 TTS 是否开启内存优化,默认 false .setAudioFocusMode("internal") // 音频焦点处理模式。internal sdk内部处理(默认),external 使用方式见:3.16.4 音频焦点处理模式 .setTtsMode("internal") // TTS播报模式,internal sdk内部合成播报, external 用户负责合成播报,使用方式见:3.9 外部TTS引擎 .setStreamType(AudioManager.STREAM_MUSIC) // 内置播放器的 STREAM 类型,默认 AudioManager.STREAM_ALARM .setAudioUsage(AudioAttributes.USAGE_ALARM) // 内置播放器的 USAGE 类型,默认 AudioAttributes.USAGE_ALARM .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION); // 内置播放器的 contentType 类型,默认 AudioAttributes.CONTENT_TYPE_SONIFICATION

4.6 识别配置

包括云端服务的 asr 配置和本地 asr 配置

ddsConfigBuilder.createAsrBuilder() .enableStreamUrl(true) // 对话接口里是否返回 tts 的 speakUrl,默认 true .setNotDropWakeupInAsr(false) // 识别时不要过滤唤醒词,默认 false .setCloudVadPauseTime(500) // 云端VAD在结束语音时静音时长,默认500ms .enableAudioDetection(false) // 噪声检测配置,开启后可过滤噪声识别结果,默认 false .enableCloudAsrpp(false) // 云端asrpp参数,设置为true后云端识别返回性别/年龄/情绪等字段,默认 false .enableNumberConvert(false) // 中文数字转阿拉伯数字,默认 false .enablePunctuation(false) // 识别结果是否带标点符号,默认 false .enableSignalVad(false) // 是否禁用VAD单字打断功能,默认 false .enableTone(false) // 识别结果的拼音是否带音调,默认 false .setCustomWakeupScore("12.5") // 自定义唤醒词阈值,该分数一般是通过测试集调整。默认值:无 .setAsrPermanentSkill("{\"skillId\":\"123123\"}") // 技能锁定接口,让用户说的话只落在锁定的技能里 .setCustomAsrConf("[{\"name\":\"宝宝睡觉模式\",\"confidence\":\"0.61\"}]")// 设置指定语料的识别置信度,防止某些语料识别置信度过低被抛弃 .enableWakeNetwork(false) // 弱网环境配置,如使用的是弱网环境,则配置为true,默认 false .setAsrRouter("dialog") // 区分识别后是否进入对话。默认 dialog 进入对话,partner 不进入对话,可通过ASREngine相关方法获取识别结果 .setCustomAsrBin("net.bin") // 预置本地识别 net.bin 资源 .setVadBin("vad.bin") // VAD资源 .enableOptGramBuild(false) // 优化离线gram编译逻辑,默认 false .enableTipsBeforeAsr(false) // 开启后识别前都会有叮咚声,默认 false .enableAsrTips(false); // 是否开启识别完成音效,默认 false

4.7 录音配置

ddsConfigBuilder.createRecorderBuilder() .setRecorderMode("internal") // 录音模式, internal SDK内部录音, external sdk外部录音,通过 Agent().feedPcm() 方法传入音频 .setAudioSource(MediaRecorder.AudioSource.DEFAULT) // 录音机参数: audioSource 录音机数据源类型 .setAudioSamplerate(16000) // 录音机参数: sampleRateInHz 录音时音频采样率 .setAudioChannelConf(AudioFormat.CHANNEL_IN_MONO) // 录音机参数:channelConfig 录音机频道源类型 .setAudioFormat(AudioFormat.ENCODING_PCM_16BIT) // 录音机参数:audioFormat 每个采样大小 .setAudioBufferSizeInByte(192000) // 录音机参数:bufferSizeInBytes 录音机的缓存大小 .setAudioChannelCount(1) // 音频通道数 单麦是1 .setAudioBufferSize(3200) // 录音机每次读取音频的 buffer 大小, 单麦每 100ms 是 3200B .setReversAudioChannel(false) // 是否翻转单麦 1mic+1ref 音频通道,默认 false .enableHighVolume(false); // 是否使用大音量检测,默认 false

五、错误码描述

error id 错误描述 原因及解决办法
1 解压dds.bin失败 系统IO错误
2 内核执行出错 请检查配置是否正确,或者联系DUI客服
3 系统启动超时 请检查配置是否正确,或者联系DUI客服
4 存储空间不足 请检查/data/目录剩余大小是否充足
070302 解码失败 联系DUI客服
070303 未发现新的版本
070304 请先升级您的客户端(SDK) 因为产品选择的dui内核和sdk不匹配,需要升级sdk
070305 网络异常, 无法连接至服务器 请检查网络是否正常或者配置是否正确
070306 解码失败 联系DUI客服
070307 补丁安装失败 存储空间不够
070308 备份压缩文件不存在 用户手动删掉了app私有空间的文件
070309 提取压缩文件失败 存储空间不够
070310 未设置产品ID 需要设置产品id
070311 无效客户端版本 请检查配置是否正确
070312 无效用户ID 需要设置用户id
070313 无效设备ID 设备id不正确
070314 资源地址无效 请检查配置是否正确
070315 无效的产品分支 需要设置产品分支
070316 无法找到资源 服务端资源生成有问题,请尝试重新发布一下,或者请提供产品id反馈给DUI客服,去后台查询根本原因
070317 资源校验失败 下载资源过程中资源被纂改
070319 当前产品中内核资源版本过低,请前往DUI控制台对应的产品中,选择最新的内核资源版本并重新发布产品 当前产品中内核资源版本过低,请前往DUI控制台对应的产品中,选择最新的内核资源版本并重新发布产品。
070320 存储空间不足 系统存储空间不足,请在错误的详细信息中查看需要的存储空间和现有的存储空间
070601 设备注册过程中无法连接到授权服务器 请检查网络是否畅通,或者productId与api key是否匹配
070602 设备注册过程无法颁发有效的profile文件 请检查产品ID或与客服联系
070603 API Key无效 请到产品授权配置页面查看api key信息是否正确,请确认签名证书是否匹配,包名是否匹配
070604 无效的产品ID 无效的产品ID,请确认是否未设置产品ID
070606 profile文件被禁用 请重新进行注册或者与客服联系
070607 profile文件过期 请重新进行注册或者与客服联系
070609 设备注册无法保存有效的profile文件 检查文件系统是否异常或者重新进行设备注册
070612 dns resolve failed: no answers(DNS失败) 请检查网络是否畅通
070613 dns resolve failed: exceed retry count(DNS失败) 请检查网络是否畅通
070614 dns resolve failed: network is unreachable(DNS失败) 请检查网络是否畅通
070615 dns resolve timeout(DNS超时) 请检查网络是否畅通
070616 connect ip failed: reasons for uncertainty(建立连接失败) 请检查网络是否畅通
070617 connect ip failed: network is unreachable(建立连接失败) 请检查网络是否畅通
070618 connect ip timeout(建立连接超时) 请检查网络是否畅通
070619 websocket handshake failed: reasons for uncertainty(websocket握手失败) 请检查网络是否畅通
070620 recv timeout(接收超时) 请检查网络是否畅通
070621 send timeout(发送超时) 请检查网络是否畅通
070622 orderly shutdown(发完数据正常关闭) 请检查网络是否畅通
071301 TTS合成超时 请检查网络是否正常
071302 对话超时 请检查网络是否正常
071303 对话连接断开 请检查网络是否正常
071304 识别结果为空 未检测到用户说法
071305 语义结果为空 用户的说法没有命中产品中的非闲聊技能
071306 WebAPI错误 调用WebAPI时发生错误
071307 NativeAPI错误 调用NativeAPI时发生错误
071308 进入闲聊技能 用户的首轮说法没有命中产品中的非闲聊技能
071309 异常重试达到最大次数 达到了产品错误处理的最大重试次数
071310 命中退出词 用户说了产品的退出词
071311 NativeAPI未注册 请检测是否注册并实现NativeAPI
071312 本地识别结果置信度低 网络离线时本地识别结果置信度较低
071313 自定义技能响应超时 联系技能开发者
071314 自定义技能内部错误 联系技能开发者
071315 自定义技能返回为空 联系技能开发者
071316 当前场景下不支持这么说 该技能关闭了智能调度
071317 进入兜底技能 用户说法进入了产品中的兜底技能
071318 进入知识型技能 用户说法进入了产品中的知识型技能,包括新闲聊、新百科
071319 未授权 请调用doAuth
071320 在使用oauth授权方式的情况下,access token过期或者不合法 请使用oauth sdk刷新access token,然后用dds sdk的接口传入access token
071321 自定义技能speaklist播报超时 请检查音频链接是否正常
071322 鉴权失败的情况下请求对话 请检查授权错误信息
071300 对话服务内部错误 联系DUI客服
072101 ASREngine VAD超时 超过8s未检测到人声
072102 ASREngine 识别为空 未检测到用户说法
072103 ASREngine 识别服务超时 请检查网络是否正常
072104 ASREngine 识别过程发生错误 联系DUI客服
072201 唤醒词个数超过限制 请清除之前的唤醒词后重试

六、常见问题

6.1 SDK的软硬件配置要求

  1. 需要Android 4.1及其以上的系统;
  2. /data分区剩余空间至少200MB。

6.2 授权失败

关于070603授权错误的问题,请做以下检查:

  1. 平台上生成的apikey和本地使用的apikey是否一致;
  2. 平台上release debug的sha256值是否和本地的对应;
  3. gradle文件配置的signingConfigs,只针对generate生成的apk;
  4. 如果是点run运行的程序,debug的sha256要填的.android目录下的debug.keystore(最常见)

6.3 客户端没收到Command/NativeAPI回调

常见原因如下,可以自上而下确认:

  1. 技能修改后,未发布;
  2. 技能发布后,产品没有选择该技能的最新版本;
  3. 产品未发布;
  4. 客户端配置的K_ALIAS_KEY和产品发布的分支不一致;
  5. 客户端没有注册对应的Command/NativeAPI;
  6. 客户端注册Command/NativeAPI的逻辑没走到;
  7. 客户端注册的Command/NativeAPI没填对,把?后面的参数也填进去了;
  8. 客户端注册Command/NativeAPI后,又重新初始化了DDS(release+init)

6.4 智能家居refreshToken 断网2年后导致SDK授权失败且无法恢复的问题

1)通过订阅token_refresh.fail消息,如果token刷新失败,SDK会抛出这个消息,应用收到该消息后,重新走以下流程:requestAuthCode() – setAuthCode()
2)setAuthCode授权成功后,将智能家居绑定流程再重新走一遍;确保DCA绑定成功。

一. 集成准备

1.1 获取产品ID

集成SDK之前,您首先需要到DUI控制台创建产品,获取如下的产品ID。
Minion

1.2 下载和导入SDK

SDK 下载页

sdk 不同版本 duicore.zip、product.zip 获取方式差异:

  • V2.X.X.X 版本 duicore.zip 在 sdk 里一同发布,product.zip 仍在产品发布页下载。!!! 不要使用错误的 duicore.zip
  • v1.X.X.X 版本通过产品发布页下载 duicore.zip 和 product.zip

1.3 集成

SDK要求Android 4.1及其以上的系统且 /data分区剩余空间至少200MB

将SDK包中的aar文件( 如 lib-dds-2.0.1.1-release.aar )导入到现有的工程中,放入到libs目录下,并添加配置:

build.gradle

repositories{ flatDir { dirs 'libs' } }

在build.gradle中配置引用sync工程即可。

build.gradle

dependencies { implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) //在线授权必须引入 implementation 'com.squareup.okhttp3:okhttp:3.9.0' }

1.4 代码混淆

如果您的应用使用了混淆, proguard-rules.pro 内请添加:

  • 2.0.0.1 及以上版本:
-keep class com.aispeech.**{*;}
-keep class com.aispeech.**$**{*;}
-keep class ai.dui.sdk.**{*;}
-keep class ai.dui.sdk.**$**{*;}
  • 1.X.X.X 版本:
-keep com.aispeech.**
-keep public class com.aispeech.lasa{*;}
-keep public class com.aispeech.dui.dds.luaBug{ *; }
-keep class ai.dui.sdk.**{*;}
-keep class ai.dui.sdk.**$**{*;}

1.5 权限说明

SDK需要的权限如下表,请在AndroidManifest.xml 文件中添加。

权限 用途
android.permission.INTERNET 允许接入互联网,以便访问我们的云服务
android.permission.RECORD_AUDIO 允许采集音频
android.permission.ACCESS_WIFI_STATE 获取WIFI状态
android.permission.ACCESS_NETWORK_STATE 获取网络状态
android.permission.WRITE_EXTERNAL_STORAGE (非必须)写入外部存储数据,用于存储一些缓存文件
android.permission.READ_EXTERNAL_STORAGE (非必须)读取外部存储数据

1.6 精简 sdk

sdk 包含所有的功能及资源,客户可根据自己的需要裁剪不需要的功能,可减少 app 或者下载的资源包的大小。

sdk 包里有 mini 文件夹,内含:

  1. mini.py // 精简脚本
  2. mini.config // 精简脚本的配置文件

脚本使用方法:

  1. 修改 mini/mini.config 配置文件,0表示不使用, 1表示使用
  2. 在mini目录下执行python脚本: python3 mini.py
  3. 在mini目录下生成精减的 aar 与 duicore

!!!强烈建议客户使用匹配的 duicore 和 aar, 支持客户自己根据需要裁剪,一般可以减少一半大小

二. 初始化 SDK

初始化包含

  1. 授权
  2. 资源、产品的初始化

2.1 构造 DDSConfig

初始化时需要 DDSConfig 参数配置。以下说明核心的初始化参数,更多参数说明请见 javadoc在线文档

  • DDSConfigBuilder 方式构造 DDSConfig
// DDSConfig 新的方式构造 2.0.1.1 及以上版本支持 DDSConfigBuilder ddsConfigBuilder = new DDSConfigBuilder( "productId", // 产品ID "aliasKey", // 产品分支 prod/test/(自定义分支) "apiKey", // 产品的 apiKey,一个API Key只被指定的客户端使用,来源:DUI控制台-产品接入-授权管理 "productKey", // 产品的 productKey,用于设备注册请求签名,来源:DUI控制台-产品接入-授权管理 "productSecret", // 产品的 productSecret,用于设备注册请求签名,来源:DUI控制台-产品接入-授权管理 "duicoreZip" // 资源包,用法详见下方"duicore 配置规则" ); ddsConfigBuilder.createWakeupBuilder() .setMicType(0); ddsConfigBuilder.createCommonBuilder() .setCustomZip("product.zip") .setDeviceName("XXX") .setDeviceId("XXX") // 设备ID,授权时认证的原始设备ID,请务必确保设备ID唯一。若不填,则SDK自己收集设备ID。可以通过DDS.getInstance().getDeviceName()获取授权认证后的唯一ID .setUserId("123456"); // 用户ID DDSConfig ddsConfig = ddsConfigBuilder.build(); // 之后 2.2 init 初始化
  • DDSConfig 旧的方式构造
// DDSConfig 旧的方式构造 DDSConfig ddsConfig = new DDSConfig() .addConfig(DDSConfig.K_DEVICE_ID, deviceId) // 设备ID字符串,授权时认证的原始设备ID,请务必确保设备ID唯一。若不填,则SDK自己收集设备ID。可以通过DDS.getInstance().getDeviceName()获取授权认证后的唯一ID .addConfig(DDSConfig.K_PRODUCT_ID, productId) // 产品ID字符串 .addConfig(DDSConfig.K_USER_ID, userId) // 用户ID字符串 .addConfig(DDSConfig.K_ALIAS_KEY, aliasKey) // 产品分支 prod/test/(自定义分支) .addConfig(DDSConfig.K_API_KEY, apiKey) // 产品的 apiKey字符串,一个API Key只被指定的客户端使用,来源:DUI控制台-产品接入-授权管理 .addConfig(DDSConfig.K_PRODUCT_KEY, productKey) // 产品的 productKey字符串,用于设备注册请求签名,来源:DUI控制台-产品接入-授权管理 .addConfig(DDSConfig.K_PRODUCT_SECRET, productSecret) // 产品的 productSecret字符串,用于设备注册请求签名,来源:DUI控制台-产品接入-授权管理 .addConfig(DDSConfig.K_DUICORE_ZIP, "duicore.zip") // 资源包,用法详见下方"duicore 配置规则" // 之后 2.2 init 初始化

duicore 配置规则:

  1. 如果duicore.zip的放置位置在assets目录下,那么就可以使用如代码中的写法,直接使用文件名:duicore.zip

  2. 如果duicore.zip没有放置在assets目录下,就必须使用duicore.zip文件的绝对路径:/sdcard/dds/duicore.zip

  3. 在 duicore.zip 所在的文件夹中,必须包含与 duicore.zip 文件相应的 duicore.zip.md5sum 文件(2.16.0.1以上版本非必须),文件内容是 duicore.zip 文件的 md5 值

在收到 onInitComplete 回调之后,就可以使用DDS来完成对应的操作。
isFull参数如果为false,则表示不完全初始化,缺失资源包,您需要等待资源包的更新下载完成或者内置一份资源包; 如果为true,则表示完全初始化,所有功能就绪。

2.2 授权和初始化

官网产品授权操作说明

  • 2.0.0.1 及以上版本会先回调收授权状态,之后回调 init 状态。即 onInitComplete 回调 true 即表示初始化和授权都成功

init 时会进行授权操作,必须授权成功后才能正常使用sdk

v2.X.X.X 版本授权流程:

  1. 授权操作需要连接到互联网, 在 SDK 初始化时设置 DDSAuthListener 来监听授权的情况
  2. SDK 初始化完毕后,您可以使用 DDS.getInstance().isAuthSuccess() 来判断当前是否授权成功。
  3. 授权成功会回调 DDSAuthListener.onAuthSuccess(),授权失败会收到回调DDSAuthListener.onAuthFailed(),需要重新调用DDS.getInstance().doAuth()
  4. 授权错误码从 "070601“ – “070622”,详见 五、错误码描述。
  • v1.X.X.X 版本的授权和 init 回调没有顺序关系
  1. 在SDK初始化时,可以设置 DDSAuthListener 来监听授权的情况。

  2. SDK初始化完毕后,您可以使用 DDS.getInstance().isAuthSuccess() 来判断当前是否授权成功。

  3. 授权成功会回调 DDSAuthListener.onAuthSuccess,授权失败会收到回调DDSAuthListener.onAuthFailed,需要重新调用DDS.getInstance().doAuth()。

  4. Profile文件的错误码范围从 "070601“ – “070622”,详见 五、错误码描述。

DDS.getInstance().init( getApplicationContext(), ddsConfig, new DDSInitListener() { // 资源、产品的初始化回调 @Override public void onInitComplete(boolean isFull) { AILog.d(TAG, "onInitComplete: " + isFull); } @Override public void onError(int what, String msg) { AILog.d(TAG, "onError: " + what + ", error: " + msg); } }, new DDSAuthListener() { // 授权回调 @Override public void onAuthSuccess() { AILog.d(TAG, "onAuthSuccess"); } @Override public void onAuthFailed(String errId, String error) { AILog.d(TAG, "onAuthFailed: " + errId + ", error:" + error); } });

2.3 初始化状态检查

除了通过监听 onInitComplete 回调外,还可以直接通过判断 DDS.getInstance().getInitStatus()的返回值来检查初始化是否完毕。

当未初始化完成时,尝试调用一些方法时,会抛出 DDSNotInitCompleteException 异常

/** * 获取当前初始化的状态 * * @return DDS.INIT_COMPLETE_NONE 还未初始化完成,表示DDS正在初始化 * DDS.INIT_COMPLETE_NOT_FULL 部分初始化完成,表示DDS已经初始化完成,但还没有完成更新 * DDS.INIT_COMPLETE_FULL 完全初始化完成,已经完成更新 */ DDS.getInstance().getInitStatus();

2.4 release SDK

init 回调 INIT_COMPLETE_FULL之后即可使用sdk的功能。

如果不需要 sdk ,可以通过以下方法释放 sdk 资源

DDS.getInstance().release();

注意事项

• DDS.getInstance().init()和DDS.getInstance().release() 方法不会阻塞应用程序的主线程,也不会影响应用程序性能
• 当调用DDS.getInstance().release()之后,之前注册的 observers 会失效,再次 DDS.getInstance().init() 后,您需要对之前的 observers 重新注册

2.5 更新产品

2.0.0.1 及以上版本不支持更新 duicore 资源,但可以更新产品配置

如果您想对发布的产品持续进行免安装的更新,您需要做以下几个操作:

  • 请求更新

您可以选择在合适的时候发起更新请求。当sdk在检查到有更新后,会自动下载资源并进行热更新,结果会以回调的方式通知。

DDS.getInstance().getUpdater().update(new DDSUpdateListener() { @Override public void onUpdateFound(String detail) { AILog.d(TAG, "onUpdateFound"); } @Override public void onUpdateFinish() { AILog.d(TAG, "onUpdateFinish"); } @Override public void onDownloadProgress(float progress) { AILog.d(TAG, "onDownloadProgress :" + progress); } @Override public void onError(int what, String error) { AILog.d(TAG, "onError :" + what + ", error: " + error); } @Override public void onUpgrade(String version) { AILog.d(TAG, "onUpgrade :" + version); } };
  • 处理更新推送

在您成功发布产品之后,若您已经订阅了消息 sys.resource.updated,则会收到资源更新的推送,此时您可以选择直接请求更新,以保持产品一直处于最新状态(推荐)。

DDS.getInstance().getAgent().subscribe("sys.resource.updated", new MessageObserver() { @Override public void onMessage(String message, String data) { try { DDS.getInstance().getUpdater().update(ddsUpdateListener); } catch (DDSNotInitCompleteException e) { e.printStackTrace(); } } });

SDK中内置一套资源更新时的通知的UI实现,您可以在DDSConfig中设置 DDSConfig.K_USE_UPDATE_NOTIFICATION 来开启或关闭。

2.6 Debug

2.6.1 日志

2.0.0.1 及以上版本默认日志级别是 4 (同 Log.INFO)。修改日志级别:

DDS.getInstance().setDebugMode(2); // 日志默认保存路径: /sdcard/Android/data/包名/cache/alog/

日志压缩成xlog形式,默认保存2天的日志,以下是日志需要的大概大小

  • 单麦D级别日志24小时保存日志 150M
  • 多麦D级别日志24小时保存日志 400M

2.6.2 音频调试

初始化后可动态设置保存 debug 音频

DDS.getInstance().setAudioDebug(true); // 音频默认保存路径: /sdcard/Android/data/包名/cache/

动态音频调试接口可替代静态调试配置: K_VAD_DEBUG、K_TTS_DEBUG、K_WAKEUP_DEBUG、K_ASR_DEBUG、K_ASRPP_DEBUG

2.6.3 动态Debug

DDS.getInstance().startDebug(); // 开启调试模式 日志级别调为V 保存调试音频 DDS.getInstance().stopDebug(); // 关闭调试模式 日志级别修改为E 停止保存调试音频

2.6.4 缓存配置

缓存目录的配置需要在初始化时设置,如不设置,默认 /sdcard/Android/data/包名/cache

ddsConfigBuilder.createCommonBuilder() .setCachePath("/sdcard/cache") // 缓存文件保存目录,默认 /sdcard/Android/data/包名/cache .setCacheSize(500000) // 缓存文件大小限制。自定义缓存目录时必需

三. 功能列表

3.1 响应command

  1. 客户端动作的执行

详细的command编写说明,请参见客户端动作(command)编写说明

Minion

  1. 快捷唤醒中的命令响应

Minion

当您在DUI平台上定制了command之后,可以使用如下的方式在您的工程中做实现。
类似于Android的广播接收器,您可以在需要的地方注册和注销CommandObserver,同一个CommandObserver可以处理多个commands。

// 注册 DDS.getInstance().getAgent().subscribe(new String[]{"open_window", "increase.volume"}, commandObserver); // 注销 DDS.getInstance().getAgent().unSubscribe(commandObserver); private CommandObserver commandObserver = new CommandObserver() { @Override public void onCall(final String command, final String data) { if (command.equals("open_window")){ JSONObject jsonData = new JSONObject(data); String intentName = jsonData.optString("intentName"); String w = jsonData.optString("w"); // TODO 依据w的值,执行打开窗户操作 }else if (command.equals("increase.volume")) { // TODO 处理调大音量的快捷唤醒指令 } } };

3.2 响应native api

Minion

当您在DUI平台上定制了native api之后,可以使用如下的方式在您的工程中做实现。
类似于Android的广播接收器,您可以在需要的地方注册和注销NativeApiObserver,同一个NativeApiObserver可以处理多个native api。

// 注册 DDS.getInstance().getAgent().subscribe(new String[]{"query_battery"}, nativeApiObserver); // 注销 DDS.getInstance().getAgent().unSubscribe(nativeApiObserver); private NativeApiObserver nativeApiObserver = new NativeApiObserver() { @Override public void onQuery(final String nativeApi, final String data) { if (nativeApi.equals("query_battery")){ JSONObject jsonData = new JSONObject(data); String intentName = jsonData.optString("intentName"); // 执行查询电量操作 String battery = DeviceUtil.getBattery(); DDS.getInstance().getAgent().feedbackNativeApiResult(nativeApi, new TextWidget().setText(battery)); } } };

nativeCommand支持异步写法

String mCommand; public void doNativeCommand() { String[] mTopic = new String[]{ "sys.car.crl" }; NativeCommandObserver nativeCommandObserver = new NativeCommandAsyncObserver() { @Override public void onNativeCommandAsync(String command, String data) { mCommand = command; AILog.d(TAG, "command = " + command); AILog.d(TAG, "data = " + data); // 异步执行操作,这里不能再有耗时操作,应用层保证这里切异步 } }; DDS.getInstance().getAgent().subscribeSync(mTopic, nativeCommandObserver); }

注:

• 每个onQuery方法执行时,需要调用 feedbackNativeApiResult() 来向DUI平台返回执行结果,表示一个native api执行结束。

• native api的执行超时时间为10s

更多详细使用请查看: https://www.duiopen.com/docs/skill_FAQ

3.3 响应内置消息

内置消息列表:

message data 说明
sys.kernel.ready SDK加载完成,所有功能就绪
sys.dialog.start { reason:“wakeup.major”,//唤醒词触发对话 } 对话开始,及开始原因:
1.wakeup.major,唤醒词唤醒
2.wakeup.command,快捷命令唤醒
3.api.startDialog,调用startDialog接口
4.api.sendText,调用sendText接口(对话Idle时)
5.api.triggerIntent,调用triggerIntent接口(对话Idle时)
6.api.avatarClick,调用avatarClick接口(对话Idle时)
7.api.avatarPress,调用avatarPress接口(对话Idle时)
sys.dialog.error { errId:071304, errMsg:“asr null” recordId:“70dcef50b8b31a6c02dbac1acee31de7” } 对话中发生的错误
sys.dialog.end { reason:“normal”,//对话正常结束 skillId:“100001246” } { reason:“error”,//对话发生错误; errId:071304, errMsg:“asr null”, skillId:“100001246” } { reason:“interrupt”,//对话被打断。强制结束对话时出现。 skillId:“100001246” } 对话结束,及结束原因: 1.normal,对话正常结束 2.error,对话发生错误退出 3.interrupt,对话被打断退出
sys.dialog.continue 对话恢复
sys.resource.updated 收到线上资源变更的推送
sys.wakeup.result { “type”: “major”, “word”: “你好小驰”, “greeting”:“主人你好” } 语音唤醒
sys.vad.begin VAD触发
sys.vad.end VAD结束
sys.upload.result { “result”:false, “reqId”:“xxxx”, “errId”:71801, “errMsg”:“Network Invalid” } 上传词库、设备信息的结果

类似于Android的广播接收器,您可以在需要的地方注册和注销MessageObserver,同一个MessageObserver可以处理多个message。

您可以通过注册消息接收器来接收需要UI展示的数据,具体UI消息数据见:
UI事件及数据定义

// 注册 DDS.getInstance().getAgent().subscribe(new String[]{"sys.dialog.start","sys.dialog.end","context.input.text"}, messageObserver); // 注销 DDS.getInstance().getAgent().unSubscribe(messageObserver); private MessageObserver messageObserver = new MessageObserver() { @Override public void onMessage(final String message, final String data) { if (message.equals("sys.dialog.start")){ runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, "对话开始了", Toast.LENGTH_LONG).show(); } }); }else if(message.equals("context.input.text")){ final JSONObject dataJson = new JSONObject(data); runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, dataJson.optString("text"), Toast.LENGTH_LONG).show(); } }); } } };

3.4 开启/终止对话

您可以在任何时刻主动开启/终止对话,通过以下接口实现。
dialog

// 开启对话。如果当前已经在对话中,则重新开启新一轮对话。 DDS.getInstance().getAgent().startDialog(); // 关闭对话 DDS.getInstance().getAgent().stopDialog(); // 支持在开启对话时传入一个播报文本, 先播报文本,后开启对话 Agent.startDialog(JSONObject jsonObject) // 支持在关闭对话时传入一个播报文本, 文本播报结束后自动结束对话 Agent.stopDialog(JSONObject jsonObject) 使用示例: JSONObject jsonObject = new JSONObject(); jsonObject.put("speakText", "播报文本"); DDS.getInstance().getAgent().startDialog(jsonObject); // 多模态事件同步,该接口用于客户端给对话中控发送一个事件 DDS.getInstance().getAgent().updateDispatchEvent(JSONObject obj) 使用示例: JSONObject obj = new JSONObject(); // obj.put("endSkill", "true"); // 不支持 endSkill DDS.getInstance().getAgent().updateDispatchEvent(obj);

3.4.1 提示音

半双工 使用 startDialog 后默认会有一个提示音,提示音可以自由打开或者关闭

DDS.getInstance().getAgent().openTip(); // 打开tip提示音 DDS.getInstance().getAgent().closeTip(); // 关闭tip提示音

3.5 按键事件

如果您有相关的软硬件按键,您需要关注以下接口,将用户的行为反馈给SDK。

// 点击唤醒/停止识别/打断播报 操作接口 DDS.getInstance().getAgent().avatarClick(); // 点击唤醒/停止识别/打断播报 操作接口 , 并附带一则欢迎语,当此次是唤醒时,播报这则欢迎语 DDS.getInstance().getAgent().avatarClick("有什么可以帮你"); // 按下按键接口(调用此接口需关闭VAD,VAD在创建产品时可配置) DDS.getInstance().getAgent().avatarPress(); // 释放按键接口(调用此接口需关闭VAD,VAD在创建产品时可配置) DDS.getInstance().getAgent().avatarRelease();

3.6 语音播报

使用 DDS.getInstance().getAgent().getTTSEngine().speak() 接口可以主动播放 TTS 。

参数说明:
1)text 播报文本,支持SSML
2)priority 优先级

提供4个优先级播报。
①优先级0:保留,与DDS语音交互同级,仅限内部使用;
②优先级1:正常,默认选项,同级按序播放;
③优先级2:重要,可以插话<优先级1>,同级按序播放,播报完毕后继续播报刚才被插话的<优先级1>;
④优先级3:紧急,可以打断当前正在播放的<优先级1|优先级2>,同级按序播放,播报完毕后不再继续播报刚才被打断的<优先级1|优先级2>。
3)ttsId 用于追踪该次播报的id,建议使用UUID。
4)audioFocus 该次播报的音频焦点,默认值:
①优先级0:android.media.AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
②优先级非0:android.media.AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK

  • Text 播报
// 请求语音播报 DDS.getInstance().getAgent().getTTSEngine().speak("这是一段测试文本", 1, "100", AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
  • SSML 播报
String test1 = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<speak xml:lang=\"cn\">\n" + " 测试SSML播报\n" + "</speak>"; DDS.getInstance().getAgent().getTTSEngine().speak(test1, 1, "10004", AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, TTSEngine.SSML);

停止播报接口如下:
1)ttsId与speak接口的ttsId一致,则停止或者移除该播报;
2)ttsId为空, 停止所有播报;
3)ttsId为"0",停止当前播报。

// 停止播报 DDS.getInstance().getAgent().getTTSEngine().shutup("100");

3.7 内置H5

2.0.0.1 及以上版本基于性能考虑默认关闭H5通信节点, 如有需要, 可以通过配置打开:

// 新的配置方式 ddsConfigBuilder.createNodeBuilder() .enableH5(true); // 旧的配置方式 config.addConfig(DDSConfig.K_H5_NODE_ENABLE, "true");

您如果使用和上传了h5资源,可以通过以下方法来获取h5资源包的 index.html 文件的路径用来在webview中加载

// 获取资源包中h5的index.html文件的绝对路径 DDS.getInstance().getAgent().getValidH5Path();

3.8 外部录音机拾音

您如果有自己的录音机,可以通过以下方法将录音数据传给DDS。

// 注意:需要将DDSConfig.K_RECORDER_MODE设置为"external",用来关闭DDS内置录音机,该接口才会生效 // 音频长度:无限制。内置录音机是3200(单麦) // 调用间隔:无限制。内置录音机是100ms // 调用时机:无限制。DDS会根据对话状态将接受的音频送入识别或者丢弃。 DDS.getInstance().getAgent().feedPcm(byte[] pcm);

3.9 外部TTS引擎

您如果使用第三方的TTS引擎,可以通过以下方法来将第三方TTS引擎注册到DDS中。

// 1. 配置 ttsMode /** * TTS播报模式<br> * 取值:internal or external<br> * internal:为sdk内部合成播报<br> * external:用户负责合成播报,需调用 <code>DDS.getInstance().getAgent().setTTSRequestListener()</code> 通过回调获取播报时机, * 播报完成后调用 <code>DDS.getInstance().getAgent().notifyTTSEnd()</code> 标记结束<br> * 默认值:internal */ ddsConfigBuilder.createTtsBuilder().setTtsMode("external") // 2. 注册回调 TTSRequestListener listener = new TTSRequestListener() { @Override public void onStart(String type, String content) { Log.d(TAG, "tts start," + type + ":" + content); int code = mExternelTts.startSpeaking(content, mTtsListener); if (code != ErrorCode.SUCCESS) { Log.d(TAG, "语音合成失败,错误码: " + code); } } @Override public void onStop() { Log.d(TAG, "tts stop"); mExternelTts.stopSpeaking(); } }; DDS.getInstance().getAgent().setTTSRequestListener(listener) // 3. 合成完毕后必须播报结束通知 !!! DDS.getInstance().getAgent().notifyTTSEnd();

3.10 场景模式

SDK提供了一些场景(如TTS播报/拾音距离)的切换,您可以在需要的时候调用下面的接口。

DDSMode

//TTS场景切换 DDS.getInstance().getAgent().setDDSMode(DDSMode.TTS_SILENCE); //在静音模式下,所有TTS播报将会被静音。 DDS.getInstance().getAgent().setDDSMode(DDSMode.TTS_NORMAL); //默认值。 //拾音场景切换。仅在"电视助手"解决方案中生效。 DDS.getInstance().getAgent().setDDSMode(DDSMode.PICKUP_NEAR); //在近场模式下,将加载产品配置页中的近场配置 DDS.getInstance().getAgent().setDDSMode(DDSMode.PICKUP_FAR); //在远场模式下,将加载产品配置页中的远场配置

3.11 触发指定意图

如果您期望主动触发某个意图的对话,可以在需要的时候调用下面的接口。

参数说明:

1)skill 技能名称,必填。
2)task 任务名称,必填。
3)intent 意图名称,必填。
4)slots 语义槽的key-value Json,可选。

triggerIntent

// 跳过识别和语义,直接进入指定的意图对话。即:DDS主动向用户发起一轮对话。 DDS.getInstance().getAgent().triggerIntent("我是技能名称", "我是任务名称", "我是意图名称", new JSONObject().put("语义槽名称一", "语义槽取值一").put("语义槽名称二", "语义槽取值二").toString());

根据skillid触发意图:

SkillIntent skillIntent = new SkillIntent("我是技能id", "我是任务名称", "我是意图名称", new JSONObject().put("语义槽名称一", "语义槽取值一").put("语义槽名称二", "语义槽取值二").toString()); DDS.getInstance().getAgent().triggerIntent(skillIntent);

3.12 更新用户词库

如果您期望更新用户的词库,使其能在技能中使用,可以在需要的时候调用下面的接口。上传结果可以通过sys.upload.result消息来获取。

注意:更新的词库仅在该设备下有效,不会影响其他设备。
参数说明:
vocabIntent: 词库intent。包含词库名name,操作action,词条列表contents。
注1:若词库名是sys.联系人,则会自动做分词处理。
注2:若需要上传带同义词的词条,格式如下:"{词条取值}:{同义词1},${同义词2}"
返回值:请求ID,用于追踪sys.upload.result。

updateVocabs

//向指定词库中添加词条 final String reqId = DDS.getInstance().getAgent().updateVocabs( new VocabIntent() .setName("应用列表") .setAction(VocabIntent.ACTION_INSERT) .setContents(Arrays.asList("淘宝", "支付宝:支护宝,付款码")) ); //清空之前上传到该词库的所有词条,然后添加词条 DDS.getInstance().getAgent().updateVocabs( new VocabIntent() .setName("设备词库") .setAction(VocabIntent.ACTION_CLEAR_AND_INSERT) .setContents(Arrays.asList("空调", "电视:电视机")) ); //删除该词库的词条 DDS.getInstance().getAgent().updateVocabs( new VocabIntent() .setName("歌手名") .setAction(VocabIntent.ACTION_REMOVE) .addContent("刘德华") ); //清空之前上传到该词库的所有词条 DDS.getInstance().getAgent().updateVocabs( new VocabIntent() .setName("歌曲") .setAction(VocabIntent.ACTION_CLEAR_ALL) ); //批量更新词库 DDS.getInstance().getAgent().updateVocabs( //向指定词库中添加词条 new VocabIntent() .setName("sys.联系人") .setAction(VocabIntent.ACTION_INSERT) .addContent("董芳芳") .addContent("王强"), //清空之前上传到该词库的所有词条,然后添加词条 new VocabIntent() .setName("设备词库") .setAction(VocabIntent.ACTION_CLEAR_AND_INSERT) .setContents(Arrays.asList("空调", "电视")), //删除该词库的词条 new VocabIntent() .setName("歌手名") .setAction(VocabIntent.ACTION_REMOVE) .addContent("刘德华"), //清空之前上传到该词库的所有词条 new VocabIntent() .setName("歌曲") .setAction(VocabIntent.ACTION_CLEAR_ALL) ); //关注更新词库的结果 DDS.getInstance().getAgent().subscribe("sys.upload.result", new MessageObserver() { @Override public void onMessage(String message, String data) { Log.e(TAG, message + ":" + data); try { JSONObject obj = new JSONObject(data); if (reqId.equals(obj.optString("reqId", ""))) { Log.d(TAG, "updateVocabs result:" + obj.optBoolean("result", false)); } } catch (JSONException e) { e.printStackTrace(); } } });

3.13 startListening 仅开启识别

如果您期望仅开启识别并获取识别结果,可以在需要的时候调用下面的接口。

2.0.0.1 及以上版本使用 startListening 在半双工和全双工下表现一致

ASREngine

//获取识别引擎 ASREngine asrEngine = DDS.getInstance().getAgent().getASREngine(); ASREngine.ListeningParams params = new ASREngine.ListeningParams(); params.setVadEnable(true); //开启识别 params 可不设置 asrEngine.startListening(params, new ASREngine.Callback() { @Override public void beginningOfSpeech() { AILog.i(TAG, "检测到用户开始说话"); } /** * @buffer 音频数据属性:单声道 16bit */ @Override public void bufferReceived(byte[] buffer) { AILog.i(TAG, "用户说话的音频数据"); } @Override public void endOfSpeech() { AILog.i(TAG, "检测到用户结束说话"); } /** * @results. eof 为 0 代表识别过程中的结果, 为 1 代表识别结束的最终结果 * {"var":"今","sessionId":"0b48c0d086efc42d55bbf1068137655d","recordId":"6b3ccb1ee745d42fa6ddb74330b1c059","eof":0} * {"var":"今天 天气","sessionId":"0b48c0d086efc42d55bbf1068137655d","recordId":"6b3ccb1ee745d42fa6ddb74330b1c059","eof":0} * {"sessionId":"0b48c0d086efc42d55bbf1068137655d","pinyin":"jin tian tian qi","recordId":"6b3ccb1ee745d42fa6ddb74330b1c059","eof":1,"text":"今天 天气"} */ @Override public void partialResults(String results) { AILog.i(TAG, "实时识别结果反馈"); } /** * @results. {"sessionId":"0b48c0d086efc42d55bbf1068137655d","recordId":"6b3ccb1ee745d42fa6ddb74330b1c059","eof":1,"text":"今天天气","pinyin":"jin tian tian qi"} */ @Override public void finalResults(String results) { AILog.i(TAG, "最终识别结果反馈"); } @Override public void error(String error) { AILog.i(TAG, "识别过程中发生的错误"); } @Override public void rmsChanged(float rmsdB) { AILog.i(TAG, "用户说话的音量分贝"); } }); //主动结束此次识别,使用 vad 的话不需要主动停止 asrEngine.stopListening(); //取消此次识别 asrEngine.cancel();

3.13.1 设置实时回传音量

// 设置实时回传音量大小, 默认为true // 设置false之后, ASREngine.Callback.rmsChanged()不再回传音量变化值 DDS.getInstance().getAgent().getASREngine().enableVolume(boolean enable);

3.13.2 feed OPUS 或者 SBC 音频

如需 feed OPUS 或者 SBC 音频,只需按下面配置即可,其它操作不变

// feed opus DDS.getInstance().getAgent().setRecorderMode(Agent.RecorderMode.OPUS); DDS.getInstance().getAgent().feedOpus(buffer); // feed sbc DDS.getInstance().getAgent().setRecorderMode(Agent.RecorderMode.SBC); DDS.getInstance().getAgent().feedSbc(buffer);

3.14 VAD相关设置

3.14.1设置VAD后端停顿时间

如果您期望修改VAD后端检测的时间,可以在需要的时候调用下面的接口。

参数说明
millis 后端检测时间,单位毫秒,默认500毫秒。即若VAD在用户说话时停顿超过一定的时间,则认为用户已经说完,发出sys.vad.end消息,结束录音。

setVadPauseTime

//设置VAD后端停顿时间 DDS.getInstance().getAgent().getASREngine().setVadPauseTime(1000);//获取VAD后端停顿时间 DDS.getInstance().getAgent().getASREngine().getVadPauseTime();

3.14.2 设置VAD前端静音检测超时时间

如果您期望修改VAD前端静音检测超时时间,可以在需要的时候调用下面的接口。设置成功后,vad再次启动生效。
参数说明:
millis 前端静音检测超时时间,单位毫秒,默认8000毫秒。若VAD启动,一直未检测到用户说话,超过一定时间,发出sys.vad.timeout消息,结束录音。

setVadTimeout

//设置VAD前端静音检测超时时间 DDS.getInstance().getAgent().getASREngine().setVadTimeout(1000); //获取VAD前端静音检测超时时间 DDS.getInstance().getAgent().getASREngine().getVadTimeout();

3.15 对话输入文本

如果您期望跳过识别直接输入文本进行对话,可以在需要的时候调用下面的接口。

sendText

/** * 输入文本的接口 * <p> * 跳过对话中的识别过程,直接开始对话中的语义理解 * <p> * 若当前没有在对话中,则以文本作为首轮说法,新发起一轮对话请求 * 若当前正在对话中,则跳过识别,直接发送文本 * * @param text 输入的文本内容 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().sendText(String text)

3.16 TTS相关设置

3.16.1 TTS事件回调

如果您想在TTS播报的相关时间节点收到通知,如:开始播报,播报结束。可以使用如下的方式:

TTSEngine.Callback

DDS.getInstance().getAgent().getTTSEngine().setListener(new TTSEngine.Callback() { /** * 开始合成时的回调 * @param ttsId 当前TTS的id, 对话过程中的播报ttsid默认为0,通过speak接口调用的播报,ttsid由speak接口指定。 */ @Override public void beginning(String ttsId) { Log.d(TAG, "TTS开始播报"); } /** * 合成的音频数据的回调,可能会返回多次,data长度为0表示音频结束 * @param data 音频数据属性:单声道 16bit, pcm */ @Override public void received(byte[] data) { Log.d(TAG, "收到音频,此方法会回调多次,直至data为0,音频结束"); } /** * TTS播报完成的回调 * @param status 播报结束的状态。 * 正常播报结束为0 * 播报中途被打断结束为1 */ @Override public void end(String ttsId, int status) { Log.d(TAG, "TTS播报结束"); } /** * 合成过程中出现错误的回调 * @param error 错误信息 */ @Override public void error(String error) { Log.d(TAG, "出现错误,"+error); } /** * 合成过程中的音素回掉,需要资源支持 */ @Override public void phoneReturnReceived(String phoneReturn) { } });

TTSEngine.CallbackOptimize 带进度的回调

DDS.getInstance().getAgent().getTTSEngine().setListenerByProcess(new TTSEngine.CallbackOptimize() { @Override public void beginning(String ttsId) { Log.d(TAG, "beginning() ttsId: " + ttsId); if (TextUtils.equals(ttsId, mTtsId)) { listenerSuccessTimes++; } } @Override public void end(String ttsId, int status) { Log.d(TAG, "end() ttsId: " + ttsId + " status: " + status); if (TextUtils.equals(ttsId, mTtsId) && status == 0) { listenerSuccessTimes++; } } @Override public void error(String error) { Log.d(TAG, "error() error: " + error); } @Override public void onSpeechProgress(String ttsId, int currentFrame, int totalFrame, boolean isDataReady) { super.onSpeechProgress(ttsId, currentFrame, totalFrame, isDataReady); Log.d(TAG, "onSpeechProgress() ttsId: " + ttsId + " currentFrame:" + currentFrame + " totalFrame:" + totalFrame + " isDataReady:" + isDataReady); if (listenerSuccessTimes == 1 && TextUtils.equals(ttsId, mTtsId) && isDataReady) { listenerSuccessTimes++; } } });

3.16.2 TTS参数设置

如果您想通过SDK定制不同的合成音类型,可以在需要的时候调用下面的接口。

setTTS

/** * 设置TTS播报类型的接口 * 调用此接口则云端配置的合成音类型失效,此后的合成音类型都将由此接口来托管 * @param speaker 取值如:zhilingf, gdgm等,若取为null,则表示随机改为一个不同的类型 */ DDS.getInstance().getAgent().getTTSEngine().setSpeaker("zhilingf"); /** * 设置TTS播报类型的接口 * 调用此接口则云端配置的合成音类型失效,此后的合成音类型都将由此接口来托管 * @param speaker 取值如:zhilingf, gdgm等 * @param path 取值如:"sdcard/tts/zhilingf.bin",自定义合成间类型的全路径 */ DDS.getInstance().getAgent().getTTSEngine().setSpeaker("zhilingf","sdcard/tts/zhilingf.bin"); /** * 设置TTS播报语速的接口 * 调用此接口则云端配置的合成音语速失效,此后的合成音语速都将由此接口来托管 * * @param speed 语速,取值0.5-2.0,0.5语速最快,2.0语速最慢,对应控制台上合成音倍率值 */ DDS.getInstance().getAgent().getTTSEngine().setSpeed(1.0f); /** * 设置TTS播报音量的接口 * 调用此接口则云端配置的合成音音量失效,此后的合成音音量都将由此接口来托管 * * @param volume 音量大小,取值1-100 */ DDS.getInstance().getAgent().getTTSEngine().setVolume(50); /** * 设置TTS引擎为本地或者云端 * * @param mode 取值 TTSEngine.LOCAL(本地合成),TTSEngine.CLOUD(云端合成) */ DDS.getInstance().getAgent().getTTSEngine().setMode(TTSEngine.LOCAL); /** * 设置TTS播报的通道 * @param streamType AudioManager.STREAM_*的取值 * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().getTTSEngine().setStreamType(int streamType); /** * 设置TTS播报的通道(安卓7.0版本以上支持,并且需要配置K_AUDIO_USAGE和K_CONTENT_TYPE) * @param usage 取值: AudioAttributes.USAGE_* * @param contentType 取值: AudioAttributes.CONTENT_TYPE_* * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().getTTSEngine().setUsage(int usage, int contentType); /** * 设置TTS播报自定义录音 * @param customAudioList 自定义播报音频列表 * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().getTTSEngine().setCustomAudio(List<CustomAudioBean> customAudioList); /** * 设备抢焦点,默认为抢焦点 * @param enable true/false true: 抢焦点 false: 不抢焦点 */ DDS.getInstance().getAgent().getTTSEngine().enableFocus(boolean enable); /** * 设置TTS人设 * @param style 风格,humor:幽默;calm:沉稳;common:普通;简短:short; */ DDS.getInstance().getAgent().getTTSEngine().setStyle(String style); /** * 清除TTS人设 */ DDS.getInstance().getAgent().getTTSEngine().removeStyle(); /** * 设置TTS结束后延迟时间,单位ms * @param afterTime * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().getTTSEngine().setPlayAfterTime(int afterTime);

3.16.3 TTS参数获取

如果您想获取当前TTS配置的接口,可以调用如下接口:
getTTS

//返回String 当前使用的合成音类型,如:"zhilingf",获取失败返回null DDS.getInstance().getAgent().getTTSEngine().getSpeaker(); //返回float 当前合成音语速,返回值0.5-2.0,0.5语速最快,2.0语速最慢,获取失败返回 0 DDS.getInstance().getAgent().getTTSEngine().getSpeed(); //返回int 当前合成音音量,返回值1-100,获取失败返回 0 DDS.getInstance().getAgent().getTTSEngine().getVolume(); //返回List<CustomAudioBean> 当前自定义播报音频的列表 DDS.getInstance().getAgent().getTTSEngine().getCustomAudio(); //获取当前TTS人设 DDS.getInstance().getAgent().getTTSEngine().getStyle();

3.16.4 音频焦点处理模式

播放 TTS 时的音频焦点默认是 sdk 内部处理,也可交由外部处理

  1. 配置 AudioFocusMode
/** * 音频焦点处理模式<br> * 取值:internal or external<br> * internal:由SDK内部处理音频焦点的获取与释放<br> * external:由外部根据对话状态及TTS播报状态处理对话及TTS等的焦点问题,SDK不再获取或者释放焦点。 * 用户可以使用 <CODE>DDS.getInstance().getAgent().setAudioFocusCallback(audioFocusCallback)</CODE> 在回调里处理音频焦点<br> * 是否必需:否<br> * 默认值:internal */ ddsConfigBuilder.createTtsBuilder().setAudioFocusMode("external")
  1. 设置回调并在回调里处理音频焦点
private AudioFocusCallback audioFocusCallback = new AudioFocusCallback() { @Override public void onRequest() { Log.d(TAG, "onRequest()"); } @Override public void onAbandon() { Log.d(TAG, "onAbandon()"); } }; DDS.getInstance().getAgent().setAudioFocusCallback(audioFocusCallback);

3.17 唤醒相关设置

**唤醒词介绍:**当前除了主唤醒,唤醒词的类型还包括了 副唤醒词、命令唤醒词(快捷唤醒词)、打断唤醒词、快速唤醒词。

副唤醒词(Minor):如果您期望为设备增加一个别名,可以通过设置该类型唤醒词来完成。

命令唤醒词(快捷唤醒词/Command): 如果您期望在唤醒的时候执行一条指令,可以通过设置该类型唤醒词来完成。

打断唤醒词(ShortCut):如果您期望在唤醒的时候能同时打断语音播报,可以通过设置该类型唤醒词来完成。打断唤醒词唤醒后会把唤醒词作为识别结果发送给服务器,由服务器给出对话结果。

快速唤醒词(QuickStart):您想通过代码来控制快速唤醒,QuickStart词为类似“导航去”、“我想听”等,此类唤醒词只在oneshot模式下生效,作用为在未唤醒状态下语音输入“导航去天安门”,可直接进入对话流程。

3.17.1 开关唤醒

默认SDK加载后不会启动语音唤醒,您可以通过以下接口对需对语音唤醒进行控制。
wakeup

// 开启语音唤醒 DDS.getInstance().getAgent().getWakeupEngine().enableWakeup(); // 关闭语音唤醒 DDS.getInstance().getAgent().getWakeupEngine().disableWakeup(); // 获取主唤醒词列表 DDS.getInstance().getAgent().getWakeupEngine().getWakeupWords();

3.17.2 更新副唤醒词

如果您期望为设备增加一个副唤醒词,可以在需要的时候调用下面的接口。

参数说明:

1)word 副唤醒词,若设置null,则清空当前的副唤醒词。
2)pinyin 副唤醒词的拼音,形如:ni hao xiao chi。
3)threshold 副唤醒词的阈值,形如:0.120(取值范围:0-1)。若设置null,则自动估算。
4)greetings 副唤醒词的欢迎语,若设置null,则与主唤醒词保持一致。

updateMinorWakeupWord

// 实时更新副唤醒词(1.1.2.1版本及以上支持) WakeupWord minorWord = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小迟") .setThreshold("0.15") .addGreeting("我在"); DDS.getInstance().getAgent().getWakeupEngine().updateMinorWakeupWord(minorWord); //获取当前的副唤醒词 DDS.getInstance().getAgent().getWakeupEngine().getMinorWakeupWord();

3.17.3 更新命令唤醒词(快捷唤醒词)

如果您期望在唤醒的时候执行一条指令,可以在需要的时候调用下面的接口。命令唤醒词和DUI控制台所设置的快捷唤醒词是相同的功能,且互不影响,可同时存在,根据需求来选择设置方式。
参数说明:
1)actions 命令唤醒词指令,为string数组,不为null。
2)words 命令唤醒词,为string数组,不为null。
3)pinyins 命令唤醒词的拼音,形如:ni hao xiao chi,为string数组, 不为null。
4)thresholds 命令唤醒词的阈值,形如:0.120(取值范围:0-1),为string数组,不为null。
5)greetings 命令唤醒词的欢迎语,为string二维数组,不为null,每维对应一个唤醒词的欢迎语。

updateCommandWakeupWord

// 实时更新命令唤醒词-更新一条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord() .setPinyin("xia yi shou") .setWord("下一首") .setThreshold("0.18") .addGreeting("我在") .setAction("sys.next"); DDS.getInstance().getAgent().getWakeupEngine().updateCommandWakeupWord(commandWord); // 实时更新命令唤醒词-更新多条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord() .setPinyin("xia yi shou") .setWord("下一首") .setThreshold("0.18") .addGreeting("我在") .setAction("sys.next"); WakeupWord commandWord1 = new WakeupWord() .setPinyin("shang yi shou") .setWord("上一首") .addGreeting("我在") .setThreshold("0.20") .setAction("sys.play"); List<WakeupWord> commandList = new ArrayList<>(); commandList.add(commandWord); commandList.add(commandWord1); DDS.getInstance().getAgent().getWakeupEngine().updateCommandWakeupWords(commandList); //清空当前设置的命令唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearCommandWakeupWord();

3.17.4 更新打断唤醒词

如果您期望在唤醒的时候能同时打断语音播报,可以在需要的时候调用下面的接口。打断唤醒词暂不支持在DUI控制台设置。(说明:打断需要设备开启回声消除)
参数说明:
1)words 打断唤醒词,为string数组,不为null。
2)pinyins 打断唤醒词的拼音,形如:ni hao xiao chi,为string数组,不为null。
3)thresholds 打断唤醒词的阈值,形如:0.120(取值范围:0-1)为string数组,不为null。

updateShortcutWakeupWord

//实时更新打断唤醒词-更新一条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); DDS.getInstance().getAgent().getWakeupEngine().updateShortcutWakeupWord(fixWord); //实时更新打断唤醒词-更新多条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); WakeupWord fixWord1 = new WakeupWord() .setPinyin("da duan ba") .setWord("打断吧") .setThreshold("0.16"); List<WakeupWord> fixList = new ArrayList<>(); fixList.add(fixWord); fixList.add(fixWord1); DDS.getInstance().getAgent().getWakeupEngine().updateShortcutWakeupWords(fixList); //清空当前设置的打断唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearShortCutWakeupWord();

3.17.5 添加/删除指定的命令唤醒词(快捷唤醒词)

如果您期望添加或者移除一些命令唤醒词而保留之前的命令唤醒词(即控制台上的快捷唤醒词),可以调用下面的接口。
add remove

// 实时添加命令唤醒词-添加一条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord() .setPinyin("xia yi shou") .setWord("下一首") .setThreshold("0.18") .addGreeting("我在") .setAction("sys.next"); DDS.getInstance().getAgent().getWakeupEngine().addCommandWakeupWord(commandWord); // 实时添加命令唤醒词-添加多条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord() .setPinyin("xia yi shou") .setWord("下一首") .setThreshold("0.18") .addGreeting("我在") .setAction("sys.next"); WakeupWord commandWord1 = new WakeupWord() .setPinyin("shang yi shou") .setWord("上一首") .addGreeting("我在") .setThreshold("0.20") .setAction("sys.play"); List<WakeupWord> commandList = new ArrayList<>(); commandList.add(commandWord); commandList.add(commandWord1); DDS.getInstance().getAgent().getWakeupEngine().addCommandWakeupWords(commandList); // 实时移除命令唤醒词-移除一条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord().setWord("下一首"); DDS.getInstance().getAgent().getWakeupEngine().removeCommandWakeupWord(commandWord); // 实时移除命令唤醒词-移除多条命令唤醒词(1.1.2.1版本及以上支持) WakeupWord commandWord = new WakeupWord().setWord("下一首"); WakeupWord commandWord1 = new WakeupWord().setWord("上一首"); List<WakeupWord> commandList = new ArrayList<>(); commandList.add(commandWord); commandList.add(commandWord1); DDS.getInstance().getAgent().getWakeupEngine().removeCommandWakeupWords(commandList); /** * 设置快捷命令唤醒词可唤醒,默认开启 * (在已经调用enableWakeup的情况下临时使用, disableWakeup情况下无效) */ DDS.getInstance().getAgent().getWakeupEngine().enableCommandWakeupWord() /** * 设置快捷命令唤醒词不可唤醒 */ DDS.getInstance().getAgent().getWakeupEngine().disableCommandWakeupWord()

3.17.6 添加/删除指定的打断唤醒词

如果您期望添加或者移除一些打断唤醒词而保留之前的打断唤醒词,可以在需要的时候调用下面的接口。

add remove

//实时添加打断唤醒词-添加一条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); DDS.getInstance().getAgent().getWakeupEngine().addShortcutWakeupWord(fixWord); //实时添加打断唤醒词-添加多条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); WakeupWord fixWord1 = new WakeupWord() .setPinyin("da duan ba") .setWord("打断吧") .setThreshold("0.16"); List<WakeupWord> fixList = new ArrayList<>(); fixList.add(fixWord); fixList.add(fixWord1); DDS.getInstance().getAgent().getWakeupEngine().addShortcutWakeupWords(fixList); //实时移除打断唤醒词-移除一条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord().setWord("打断"); DDS.getInstance().getAgent().getWakeupEngine().removeShortcutWakeupWord(fixWord); //实时移除打断唤醒词-移除多条打断唤醒词(1.1.2.1版本及以上支持) WakeupWord fixWord = new WakeupWord().setWord("打断"); WakeupWord fixWord1 = new WakeupWord().setWord("打断吧"); List<WakeupWord> fixList = new ArrayList<>(); fixList.add(fixWord); fixList.add(fixWord1); DDS.getInstance().getAgent().getWakeupEngine().removeShortcutWakeupWords(fixList);

3.17.7 添加/删除指定的主唤醒词

如果您想通过代码来控制主唤醒词,可以调用下面的接口。
注意:通过此接口添加的主唤醒词会屏蔽控制台添加的主唤醒词。

主唤醒词

//实时添加主唤醒词-添加一条主唤醒词(1.1.2.1版本及以上支持) WakeupWord mainWord = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小迟") .addGreeting("我在") .setThreshold("0.15"); DDS.getInstance().getAgent().getWakeupEngine().addMainWakeupWord(mainWord); //实时添加主唤醒词-添加多条主唤醒词(1.1.2.1版本及以上支持) WakeupWord mainWord = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小迟") .addGreeting("我在") .setThreshold("0.15"); WakeupWord mainWord2 = new WakeupWord() .setPinyin("ni hao xiao le") .setWord("你好小乐") .addGreeting("我在") .setThreshold("0.15"); List<WakeupWord> mainWordList = new ArrayList<>(); mainWordList.add(mainWord); mainWordList.add(mainWord2); DDS.getInstance().getAgent().getWakeupEngine().addMainWakeupWords(mainWordList); //实时移除主唤醒词-移除一条主唤醒词(1.1.2.1版本及以上支持) WakeupWord mainWord = new WakeupWord().setWord("你好小迟"); DDS.getInstance().getAgent().getWakeupEngine().removeMainWakeupWord(mainWord); //实时移除主唤醒词-移除多条主唤醒词(1.1.2.1版本及以上支持) WakeupWord mainWord = new WakeupWord().setWord("你好小迟"); WakeupWord mainWord2 = new WakeupWord().setWord("你好小乐"); List<WakeupWord> mainWordList = new ArrayList<>(); mainWordList.add(mainWord); mainWordList.add(mainWord2); DDS.getInstance().getAgent().getWakeupEngine().removeMainWakeupWords(mainWordList); //更新多条主唤醒词的接口,覆盖原有的主唤醒词 WakeupWord mainWord = new WakeupWord() .setPinyin("ni hao xiao le") .setWord("你好小乐") .setThreshold("0.15") .addGreeting("我在"); WakeupWord mainWord2 = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小迟") .setThreshold("0.15") .addGreeting("我在"); List<WakeupWord> mainWordList = new ArrayList<>(); mainWordList.add(mainWord); mainWordList.add(mainWord2); DDS.getInstance().getAgent().getWakeupEngine().updateMainWakeupWords(mainWordList);

3.17.8 添加/删除指定的快速唤醒词

如果您想通过代码来控制快速唤醒,可以调用下面的接口。

QuickStart词为类似“导航去”、“我想听”等,此类唤醒词只在oneshot模式下生效,作用为在未唤醒状态下语音输入“导航去天安门”,可直接进入对话流程。

quickStart

//添加多条QuickStart词 WakeupWord quickStartWord = new WakeupWord() .setPinyin("dao hang qu") .setWord("导航去") .setThreshold("0.15"); List<WakeupWord> quickStartWordList = new ArrayList<>(); quickStartWordList.add(quickStartWord); DDS.getInstance().getAgent().getWakeupEngine().addQuickStartWords(quickStartWordList); //实时移除一条QuickStart词 WakeupWord quickStartWord = new WakeupWord().setWord("导航去"); DDS.getInstance().getAgent().getWakeupEngine().removeQuickStartWord(quickStartWord); //实时移除多条QuickStart词 WakeupWord quickStartWord = new WakeupWord().setWord("导航去"); List<WakeupWord> quickStartWordList = new ArrayList<>(); quickStartWordList.add(quickStartWord); DDS.getInstance().getAgent().getWakeupEngine().removeQuickStartWords(quickStartWordList); //更新多条QuickStart词的接口,覆盖原有的QuickStart词 WakeupWord quickStartWord = new WakeupWord() .setPinyin("dao hang qu") .setWord("导航去") .setThreshold("0.15"); List<WakeupWord> quickStartWordList = new ArrayList<>(); quickStartWordList.add(quickStartWord); DDS.getInstance().getAgent().getWakeupEngine().updateQuickStartWords(quickStartWordList); //清空QuickStart词 DDS.getInstance().getAgent().getWakeupEngine().clearQuickStartWords();

3.17.9 oneshot开关

如果您想动态更改唤醒模式,可以在需要的时候调用下面的接口。

oneshot

//开启oneshot DDS.getInstance().getAgent().getWakeupEngine().enableOneShot(); //关闭oneshot DDS.getInstance().getAgent().getWakeupEngine().disableOneShot(); /** * 获取当前oneshot开关状态,如果有本地接口调用开关,以本地接口调用优先。 */ DDS.getInstance().getAgent().getWakeupEngine().getOneshotState()

3.17.10 设置环麦增强角度

// 如果您想动态指定环麦增强角度,可以在需要的时候调用下面的接口。

DDS.getInstance().getAgent().getWakeupEngine().setWakeupDoa(int doa);

3.17.11 设置车载双麦模式

// 如果您想动态指定车载双麦的驾驶模式,可以在需要的时候调用下面的接口。

// 驾驶模式, 0:定位模式, 1:主驾模式, 2: 副驾模式 3:全车模式 DDS.getInstance().getAgent().getWakeupEngine().setWakeupMode(int mode);

3.17.12 获取当前唤醒内核的版本号

如果您想获取当前唤醒内核的版本号,可以在需要的时候调用下面的接口。

// 该接口废弃,获取到的版本号在 2.0.0.0 版本上无效 DDS.getInstance().getAgent().getWakeupEngine().getWakeupVersion();

3.17.13 设置是否允许识别过程中响应唤醒

如果您想设置是否允许识别过程中响应唤醒,可以在需要的时候调用下面的接口。

//enable: true. 允许识别过程中响应唤醒, false. 不允许识别过程中响应唤醒 DDS.getInstance().getAgent().getWakeupEngine().enableWakeupWhenAsr(boolean enable);

3.17.14 设置自定义欢迎语回调

如果您想设置自定义欢迎语,可以在需要的时候调用下面的接口, 该方法是阻塞方法,不建议在该方法内部做耗时操作。

DDS.getInstance().getAgent().getWakeupEngine().setWakeupCallback(WakeupCallback wakeupCallback);DDS.getInstance().getAgent().getWakeupEngine().setWakeupCallback(new WakeupCallback() { /** * * @param wakeupResult 唤醒状态 * <p> * wakeupResult.optString("word");// 唤醒词 * <p> * wakeupResult.optString("type");// 唤醒状态: major: 正常唤醒 command: 命令唤醒 interrupt: 打断唤醒 * <p> * wakeupResult.optString("gender");// 性别: male / female * <p> * wakeupResult.optString("action");// 命令唤醒的动作 * <p> * @return */ @Override public JSONObject onWakeup(JSONObject wakeupResult) { JSONObject result = new JSONObject(); try { result.put("greeting", "你好"); } catch (JSONException e) { e.printStackTrace(); } return result; });

3.17.15 获取主唤醒词列表

获取主唤醒词列表,默认返回WakeupEngine所设置的主唤醒词,如果没有设置过则返回产品配置的主唤醒词。

DDS.getInstance().getAgent().getWakeupEngine().getMainWakeupWords();

3.17.16 设置Beamforming监听器

设置Beamforming监听器, 实时回调Beamforming音频

DDS.getInstance().getAgent().getWakeupEngine().setBfListener(BfListener listener);

3.17.17 设置唤醒模块的模式/低功耗模式

  • 设置唤醒模块的模式(信号处理模块不变,环麦中暂不支持)
DDS.getInstance().getAgent().getWakeupEngine().setWakeupSwitch(boolean mode);
  • 开启低功耗模式后, 唤醒功能不可用, cpu会降低 (此方法已废弃)
DDS.getInstance().getAgent().getWakeupEngine().setLpSwitch(boolean lpSwitch);

3.17.18 就近唤醒

如果你有使用就近唤醒的需求, 可以参照下面的配置与设置(目前支持线性双麦/线性四麦的就近唤醒, 1.2.12.1-1.2.15.1的版本暂不支持就近唤醒)

1. 设置major参数为true (从1.2.8.1版本开始, 不再需要此配置) config.addConfig("MAJOR", "true"); 2. 设置K_USE_NEAR_WAKEUP为true, 表示使用就近唤醒 config.addConfig(DDSConfig.K_USE_NEAR_WAKEUP, "true"); 3. 在设置唤醒词的时候设置major字段 WakeupWord minorWord = new WakeupWord() .setPinyin("ni hao xiao chi") .setWord("你好小驰") .setThreshold("0.25") .setMajor("1")(从1.2.8.1版本开始, 不再需要此配置) .addGreeting("小驰回来了"); DDS.getInstance().getAgent().getWakeupEngine().addMainWakeupWord(minorWord); 4. 就近唤醒运行过程中可以动态设置一些参数 /** * 设置就近唤醒参数与调度参数<p> * 仅在就近唤醒状态下生效(线双麦/线四麦) */ DDS.getInstance().getAgent().getWakeupEngine().setNearWakeupParams(JSONObject paramsObj)

3.17.19 拦截唤醒结果

/** * 拦截唤醒结果 wakeup.result, * * @param isCatch 是否拦截唤醒结果 true-拦截 ;false-取消拦截 * @return * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getWakeupEngine().catchWakeupResult(boolean isCatch)

3.17.20 设置 VOIP 音频回调

  1. 配置 VOIP 信息
config.addConfig(DDSConfig.K_VOIP_TYPE, "line_2");
  1. 设置 VOIP 回调
/** * 设置voip监听器, 时时回调voip音频 * * @param listener */ public void setVoipListener(VoipListener listener)
  1. 启动 VOIP 功能
// 调用 setEnableVoip(true) 时 voipNode 才会注册消息,接收音频数据 DDS.getInstance().getAgent().setEnableVoip(true);
  1. 录音后就会从回调里回调 voip 音频

  2. 取消 VOIP 回调

// 取消 VOIP 回调 DDS.getInstance().getAgent().getWakeupEngine().setVoipListener(null); // 停用 voip 功能 DDS.getInstance().getAgent().setEnableVoip(false);

3.17.21 更新唤醒词

支持多个不同类型的唤醒词同时配置

List<WakeupWordIntent> wakeupWordIntentList = new ArrayList<>(); // 添加主唤醒词 WakeupWordIntent wakeupWordIntent0 = new WakeupWordIntent(WakeupType.ADD_MAIN) .setPinyin("ni hao xiao le") .setWord("你好小乐") .setThreshold("0.16") .addGreeting("小乐来了"); WakeupWordIntent wakeupWordIntent1 = new WakeupWordIntent(WakeupType.ADD_COMMAND) .setPinyin("kai shi bo fang") .setWord("开始播放") .setThreshold("0.18") .addGreeting("好的,开始播放") .setAction("sys.play"); WakeupWordIntent wakeupWordIntent2 = new WakeupWordIntent(WakeupType.ADD_SHORTCUT) .setPinyin("da duan") .setWord("打断") .setThreshold("0.18"); wakeupWordIntentList.add(wakeupWordIntent0); wakeupWordIntentList.add(wakeupWordIntent1); wakeupWordIntentList.add(wakeupWordIntent2); DDS.getInstance().getAgent().getWakeupEngine().updateWakeupWords(wakeupWordIntentList);

3.17.22 动态设置对话模式

/** * 动态设置对话模式<br> * 唤醒后是否进入对话<br> * <p> * 取值:dialog or partner<br> * dialog:唤醒默认进入识别及对话<br> * partner:唤醒后不会主动进入识别及对话,同时唤醒后会收到sys.wakeup.result的message消息<br> * 是否必需:否<br> * 默认值:dialog<br> * * @param type 对话模式 partner */ DDS.getInstance().getAgent().getWakeupEngine().updateWkupRouter(String type)

3.17.23 增强唤醒(可见即可说和全局免唤醒)

增强唤醒使用本地识别功能识别设置的唤醒词,当达到阈值(默认0.63)时即认为唤醒成功.

功能特性

• 支持两个字的唤醒词

• 支持同一个唤醒词写入很多类型说法(目前最大可支持500个,延时200ms)

• 支持中英文唤醒(打开wifi、打开音乐); 中英文功能从 WTK v1.9.39 开始支持

• 唤醒词设置为打开导航,可支持说“哈哈打开导航”不响应(极大减小误唤醒)

使用场景

• 解决相似说法串到唤醒,比如定义“打开导航”,说“开导航”也能唤醒

• 解决语音起名字及手动设置唤醒词设置为“思必驰”“小小思”等容易误唤醒问题

• 解决开发者定义唤醒词每次都需要思必驰提供阈值,开发者只需要输入中文即可

• 解决在导航列表tts播报时说分词容易误选择问题,比如导航列表有“天安门”“北京天安门”“天安门地铁站“,以前用唤醒说”天安门地铁站“会响应”天安门“

使用方法
  1. 初始化
  • 打开增强唤醒功能
config.addConfig(DDSConfig.K_SUPPORT_INCREMENT_WAKEUP, "true");
  • 开启可见即可说
JSONObject ctrlObj = new JSONObject(); ctrlObj.put("type", "hotWord"); DDS.getInstance().getAgent().getWakeupEngine().enableIncrementWakeup(ctrlObj);
  • 开启全局免唤醒
JSONObject ctrlObj = new JSONObject(); ctrlObj.put("type", "globalWord"); DDS.getInstance().getAgent().getWakeupEngine().enableIncrementWakeup(ctrlObj);
  • 开启可见即可说 + 全局免唤醒
    DDS.getInstance().getAgent().getWakeupEngine().enableIncrementWakeup();
  1. 优先级设置
  • 如果可见即可说和全局免唤醒词有相同的词,可见即可说优先
DDS.getInstance().getAgent().getWakeupEngine().setIncrementPriority(WakeupPriorityType.HOTWORD_FIRST);
  • 如果可见即可说和全局免唤醒词有相同的词,全局免唤醒优先
DDS.getInstance().getAgent().getWakeupEngine().setIncrementPriority(WakeupPriorityType.GLOBALWORD_FIRST);
  1. 使用全局免唤醒词
可使用增强唤醒的唤醒词类型: * 命令唤醒词(Command) * 打断唤醒词(ShortCut) WakeupWord commandWord = new WakeupWord() .setWord("开始播放") .setThreshold("0.18") .setIncrementWord("true") .addGreeting("好的,开始播放") .setAction("sys.play"); WakeupWord commandWord1 = new WakeupWord() .setWord("下一首") .setThreshold("0.18") .setIncrementWord("true") .addGreeting("下一首nlg") .setAction("command.next"); List<WakeupWord> wakeupWordList = new ArrayList<>(); wakeupWordList.add(commandWord); wakeupWordList.add(commandWord1); DDS.getInstance().getAgent().getWakeupEngine().addCommandWakeupWords(wakeupWordList); WakeupWord fixWord = new WakeupWord() .setPinyin("da duan") .setWord("打断") .setIncrementWord("true") // 使用增强唤醒 .setThreshold("0.18"); List<WakeupWord> wakeupWordList = new ArrayList<>(); wakeupWordList.add(fixWord); DDS.getInstance().getAgent().getWakeupEngine().updateShortcutWakeupWords(wakeupWordList); // 删除增强唤醒的打断唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearIncrementShortCutWakeupWord(); // 删除增强唤醒的命令唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearIncrementCommandWakeupWord(); // 删除增强唤醒的所有唤醒词,包括打断唤醒词和命令唤醒词 DDS.getInstance().getAgent().getWakeupEngine().clearIncrementWakeupWord();
  1. 使用可见即可说
HotWords.HotWord hotWord1 = new HotWords.HotWord() .setOrigin("抖音最全中文歌")//页面原始词汇 .addSegment("抖音最全")//分词内容 .addCustom("id", "123123")//上层自定义扩展字段 .addCustom("name", "music");//上层自定义扩展字段 HotWords.HotWord hotWord2 = new HotWords.HotWord() .setOrigin("开始播放") .addCustom("id", "123123"); HotWords.Slots slots = new HotWords.Slots() .setSlot("dynamic_slot") .addHotWord(hotWord1) .addHotWord(hotWord2); HotWords hotWords = new HotWords() .setScene("default_scene") .addSlots(slots); DDS.getInstance().getAgent().getWakeupEngine().updateHotWords(hotWords);

3.18 数据上传接口

开发者调用此接口来上传一些设备的信息,这些信息可以在本地使用,也可以在对话中获取,作为对话决策的一部分。
数据点

/** * 更新设备状态,产品级的配置。比如:定位信息,设备硬件状态等 * 更新结果可以通过sys.upload.result消息来获取 * * @param intent 请求的ContextIntent对象,包括key和value * 如:ContextIntent intent = new ContextIntent("status","{\"platform\":\"Android\"}"); * updateProductContext(intent); * 技能里通过$context.system.settings.status.platform$即可获取到"Android" * @return 请求的ID,用于追踪sys.upload.result * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().updateProductContext(ContextIntent intent); /** * 更新技能配置,需要调用ContextIntent.setSkillId设置技能ID * 更新结果可以通过sys.upload.result消息来获取 * * @param intent 请求的ContextIntent对象,包括key、value、skillId。 * 如:ContextIntent intent = new ContextIntent("status","{\"platform\":\"Android\"}"); * intent.setSkillId("xxxxxxxxxxxxxxxxx"); * updateSkillContext(intent); * 该技能里通过$context.skill.settings.status.platform$即可获取到"Android",其他技能则无法使用。 * @return 请求的ID,用于追踪sys.upload.result * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().updateSkillContext(ContextIntent intent); /** * 获取产品的配置信息 * * @param inputKey 配置项名称 * @return 配置项值 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getProductContext(String inputKey); /** * 获取技能的配置信息 * * @param skillId 技能ID * @param inputKey 配置项名称 * @return 配置项值 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getSkillContext(String skillId, String inputKey); /** * 更新热词识别接口(请求级别) * 每次请求都会带上热词词库, 例如: [{"type": "vocab", "name": "sys.联系人", "data":["章大樵"]}] * 本接口为覆盖式接口,以最新设置为准,如需要清除请求空参数即可 * * @param phraseHints 需要更新的热词识别列表 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().updatePhraseHints(PhraseHintsIntent... phraseHints);

3.19 获取性别/年龄信息

  1. 在初始化DDS SDK的时候加入如下配置:
config.addConfig(DDSConfig.K_USE_GENDER, "true"); config.addConfig(DDSConfig.K_USE_AGE, "true");
  1. 然后订阅 message: sys.dialog.start
    收到消息内容类似:{"reason":"wakeup.major","gender":"male","age":"adult"}

gender就是性别字段,age就是年龄字段,性别年龄信息只有在主唤醒词唤醒后,才会发出。

3.20 动态更新云端识别模型

/** * 更新云端识别的模型名字, * 在调用完该接口后,下一次对话开始时生效,并一直用该模型,除非客户端再调用该接口设置为其他的模型 * * @param asrModel 云端识别的模型名字,有aihome, airobot等, * 默认为dui控制台配置的模型资源, * 如果填null,则表示清除之前本地配置的模型名,之后会使用dui控制台配置的模型资源 * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getASREngine().updateAsrModel("aihome"); /** * 获取当前使用的云端识别的模型 * * @return asrModel 当前使用的云端识别的模型, 如果失败会返回null,或者抛出DDSNotInitCompleteException * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception */ DDS.getInstance().getAgent().getASREngine().getAsrModel();

3.21 快捷指令

订阅 sys.dm.inspire 消息,用于接收快捷指令。只在 半双工 模式有该功能

DDS.getInstance().getAgent().subscribe(new String[]{..., "sys.dm.inspire", ...}, new MessageObserver() { @Override public void onMessage(String message, String data) { if("sys.dm.inspire".equals(message)) { try { JSONObject obj = new JSONObject(data); JSONArray inspire = obj.getJSONArray("inspire"); ... } catch (JSONException e) { e.printStackTrace(); } catch (DDSNotInitCompleteException e) { e.printStackTrace(); } } } });

inspire 即为快捷指令的集合数组,通过解析 inspire 来处理指令。inspire 数据结构如下:

inspire:[ { speak:{ "type":"text", "text":"123" }, widget:{ "type":"text", ... } }, { speak:{ "type":"text", "text":"抱歉由于设备离线,操作失败,请再试一次" }, widget:{ "type":"list", ... } }, ... ]

上述结构中,speak 并不是必须的字段, widget 是必须的。

widget即为需要需要展示出来的控件样式,其中 type 字段代表控件样式的类型,有 text/list/web/image/media…

关于 widget 不同控件样式的不同数据结构,可参考: https://www.duiopen.com/docs/ct_UI 。举例:list 对应这 context.widget.list,以此类推

3.22 全双工模式

3.22.1 模式切换

// 设置dds模式 DDS.getInstance().getAgent().setDuplexMode(Agent.DuplexMode.HALF_DUPLEX);// 半双工,一问一答模式 DDS.getInstance().getAgent().setDuplexMode(Agent.DuplexMode.FULL_DUPLEX);// 全双工模式 注意: 在切换全双工时如需要唤醒还请调用: DDS.getInstance().getAgent().getWakeupEngine().enableWakeup(); // 获取dds模式 DDS.getInstance().getAgent().getDuplexMode();

3.22.2 全双工不支持接口与功能

  1. 由于全双工模式为全程识别状态, 所以开启识别和 startListening 不可同时使用 [3.13开启/终止识别](#3.13 仅开启识别)

  2. 由于全双工模式为全程识别状态, 在模拟半双工下的打断唤醒由识别代替,主唤醒词正常开启下一轮对话

  3. 由于全双工模式下暂时没有快捷指令的使用场景, 所以快捷指令在全双工模式下不可用 [3.21 快捷指令](#3.21 快捷指令)

  4. 全双工模式下默认使用 vad 功能, 比如 oneshot 功能就需要打开 vad。配置 K_USE_VAD_IN_FULLDUPLEX 为 true(默认为true)

3.22.3 全双工下跳过Vad超时检测

// 在全双工模式下, 如果你想跳过Vad的检测直接进入识别状态则可以调用下面的接口 DDS.getInstance().getAgent().getASREngine().killVadTimeoutInFullDuplex();

3.23 对话结果支持内容修改

此接口支持修改对话中的语音播报,修改文本展示等功能

// 从1.2.8.1版本开始支持 DDS.getInstance().getAgent().setDMTaskCallback(new DMTaskCallback() { @Override public JSONObject onDMTaskResult(JSONObject dmTaskResult, Type type) { if (type == DMTaskCallback.Type.DM_OUTPUT) {// 处理dm的消息 // 显示 String display = dmTaskResult.optString("display"); // 播报 String nlg = dmTaskResult.optString("nlg"); Log.d(TAG, "display = " + display); Log.d(TAG, "nlg = " + nlg); try { dmTaskResult.put("display", "修改之后的显示"); dmTaskResult.put("nlg", "修改之后的播报"); dmTaskResult.put("speakUrl", null); // 不设置为null的话,播放的还是原来的音频 } catch (JSONException e) { e.printStackTrace(); } } else if (type == DMTaskCallback.Type.CDM_ERROR) {// // 处理对话异常的消息 // 播报 String nlg = dmTaskResult.optString("nlg"); Log.d(TAG, "nlg = " + nlg); try { dmTaskResult.put("nlg", "修改之后的播报"); dmTaskResult.put("speakUrl", null); // 不设置为null的话,播放的还是原来的音频 } catch (JSONException e) { e.printStackTrace(); } } return dmTaskResult; } });

3.24 声纹

声纹注册、验证的词也是主唤醒词,唤醒后会使用唤醒音频做声纹的注册、验证等操作

  1. 打开声纹配置
config.addConfig(DDSConfig.K_VPRINT_ENABLE, "true"); // 是否开启声纹功能 DDS.getInstance().getAgent().getWakeupEngine().useVprintInWakeUp(true) // 动态设置是否在唤醒中启用声纹
  1. 使用声纹
// 注册声纹监听器 VprintEngine.getInstance().setVprintListener(new VprintListener() { @Override public void onState(String state) { Log.e(TAG, "onState = " + state); } @Override public void onResults(String result) { Log.e(TAG, "onResults = " + result); } @Override public void onError(String error) { Log.e(TAG, "onError = " + error); } }); // 注册声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.REGISTER) .setTrainNum(3) .setOutChannelNum(2) .setSnrThresh(8.67f) .setUserId("userId") .setVprintWord("ni hao xiao chi") .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 更新声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.UPDATE) .setTrainNum(3) .setOutChannelNum(2) .setSnrThresh(8.67f) .setUserId("userId") .setVprintWord("ni hao xiao chi") .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 追加声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.APPEND) .setTrainNum(3) .setOutChannelNum(2) .setSnrThresh(8.67f) .setUserId("userId") .setVprintWord("ni hao xiao chi") .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 删除一条声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.UNREGISTER) .setTrainNum(3) .setOutChannelNum(2) .setUserId("userId") .setVprintWord("ni hao xiao chi") .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 删除所有声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.UNREGISTER_ALL) .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 使用声纹 VprintIntent intent = new VprintIntent.Builder() .setAction(VprintIntent.Action.TEST) .setOutChannelNum(2) .create(); DDS.getInstance().getAgent().getVprintEngine().start(intent); // 获取声纹模型 String result = VprintEngine.getInstance().getMode(); // 停止使用声纹 VprintEngine.getInstance().stop();

3.25 配合 dca app 使用

通过语音控制家居设备时需要对话带上 dca app 里用户的信息。

TokenListener tokenListener = new TokenListener() { @Override public void onSuccess(TokenResult tokenResult) { Log.d(TAG, "onSuccess() " + tokenResult); success = true; } @Override public void onError(int errorId, String error) { Log.d(TAG, "errorId:" + errorId + " error:" + error); } }; AuthInfo authInfo = new AuthInfo(); authInfo.setUserId("8458"); authInfo.setCodeVerifier("doeifkgdgurgg"); authInfo.setClientId("dfgoeigjergndfgdfg"); authInfo.setAuthCode("dfgerhrh6564hgrh4hr"); authInfo.setRedirectUri("http://dui.aadf.bb.cc"); DDS.getInstance().getAgent().setAuthCode(authInfo, tokenListener);

3.26 自定义提示语

JSONObject jsonObject = new JSONObject(); try { jsonObject.put("071304", "自定义vad超时"); } catch (JSONException e) { e.printStackTrace(); } /** * 此接口支持动态修改 custom tips * * @param obj <p> * 取值:JSON字符串,如:<br> * { * "71304":"这是识别结果为空的自定义播报", * "71305":"这是语义结果为空的自定义播报", * "71308":"这是进入闲聊技能的自定义播报", * "713**":"*****" * }<br> * @throws DDSNotInitCompleteException */ DDS.getInstance().getAgent().setCustomTips(jsonObject);

3.27 一句话多意图

背景:
一句话多意图: 一句话里包括多个意图或动作,比如:调节空调和车窗
使用场景:智能家居或车联网等场景
此功能需要云端配置,如需使用请和思必驰客户经理联系

String[] mTopic = new String[]{ "sys.native.inspire" }; NativeCommandObserver nativeCommandObserver = new NativeCommandObserver() { @Override public JSONObject onNativeCommandCall(String command, String data) { try { if (TextUtils.equals(command, "sys.native.inspire")) { AILog.d(TAG, "command = " + command); AILog.d(TAG, "data = " + data); JSONObject dataObj = new JSONObject(data); JSONArray inspireArr = dataObj.optJSONArray("inspire"); JSONObject selectInspireItem = null;// 用户选择要dds执行的一个item for (int i = 0; i < inspireArr.length(); i++) { JSONObject itemObj = inspireArr.optJSONObject(i); // TODO 业务逻辑,判断哪一个item是要后续dds处理的 selectInspireItem = itemObj; JSONObject commandObj = itemObj.optJSONObject("command"); String commandApi = commandObj.optString("api"); // TODO 业务逻辑,解析command,处理command JSONObject speakObj = itemObj.optJSONObject("speak"); String text = speakObj.optString("text"); // 模拟播报与output DDS.getInstance().getAgent().getTTSEngine().speak(text, 0); DDS.getInstance().getAgent().getBusClient().publish("context.output.text", new JSONObject().put("text", text).toString()); SystemClock.sleep(2000); } // 返回最终要播报的文本 return JSONObjectUtil.create() .put("nlg", "已全部打开") .put("selectInspireItem", selectInspireItem)// 回传了选择的item,则修改nlg播报无效 .build(); } } catch (Exception e) { e.printStackTrace(); } return null; } }; DDS.getInstance().getAgent().subscribeSync(mTopic, nativeCommandObserver);

四、DDSConfig 配置项

DDSConfigBuilder 构造方法需要设置 产品信息duicore 资源 ,最简单的demo无需设置其它即可以单麦方式运行。

DDSConfigBuilder ddsConfigBuilder = new DDSConfigBuilder( productId, aliasKey, apiKey, productKey, productSecret, duicoreZip )

ddsConfigBuilder 包含很多配置,现按功能分为以下子类:

  1. ddsConfigBuilder.createCommonBuilder() [4.1 设备信息、使用目录、VOIP功能 等通用配置](#4.1 设备信息、使用目录、VOIP功能 等通用配置)
  2. ddsConfigBuilder.createNodeBuilder() [4.2 sdk 功能开关](#4.2 sdk 功能开关)
  3. ddsConfigBuilder.createWakeupBuilder() [4.3 麦克风类型、唤醒、声纹、asrpp、Oneshot 配置](#4.3 麦克风类型、唤醒、声纹、asrpp、Oneshot 配置)
  4. ddsConfigBuilder.createDmBuilder() [4.4 对话配置](#4.4 对话配置)
  5. ddsConfigBuilder.createTtsBuilder() [4.5 TTS、播放 配置](#4.5 TTS、播放 配置)
  6. ddsConfigBuilder.createAsrBuilder() [4.6 识别配置](#4.6 识别配置)
  7. ddsConfigBuilder.createRecorderBuilder() [4.7 录音配置](#4.7 录音配置)
  • ddsConfigBuilder 还可通过以下方法添加额外配置
ddsConfigBuilder.addConfig(String key, Object value)

下面简单介绍一下配置项,详细说明请看 DDSConfig 配置请参见 javadoc在线文档

  • 定制资源集成差异
    v2.0.0.1 及以上版本不仅支持系统目录,也支持放入 assets 目录。v1.X.X.X 版本只支持 sdcard 系统目录配置

使用方式如下:

  1. assets 目录配置方式: setWakeupBin(“wakeup.bin”)
  2. 系统目录配置方式: setWakeupBin("/sdcard/sd/wakeup.bin")

4.1 设备信息、使用目录、VOIP功能 等通用配置

ddsConfigBuilder.createCommonBuilder() .setCustomZip("product.zip") // 使用内置产品资源包, 从官网下载的产品资源包一般叫 product.zip .setDeviceName("XXXXXX") // 设备 DeviceName, 用来标识设备的标识符 .setDeviceId("XXXXXX") // 设备ID。用于授权认证的原始设备ID,请务必确保设备ID唯一 .setAccessToken("XXXXXX") // 保留字段,上传、升级时使用 .setCachePath("/sdcard/cache") // 缓存文件保存目录,默认 /sdcard/Android/data/包名/cache .setCacheSize(500000) // 缓存文件大小限制。自定义缓存目录时必需 .setCustomHome("/sdcard/aispeech") // 产品资源存放的目录,默认 /data/data/com.xxx/files/dds/custom/ .setDuicoreHome("/sdcard/aispeech") // duicore 资源存放的目录,默认 /data/data/包名/files/dds/duicore/ .setInitTimeout(60) // SDK初始化超时时间,单位秒。默认 60秒 .setCustomSha256("XXXXXX") // 自定义授权证书的SHA256, sdk 授权时会检查包名和签名的SHA256,第三方加固后可能改变 .setCustomBackupZipUserDefined("/sdcard/aispeech") // 产品资源包保存路径 .setDuicoreBackupZipUserDefined("/sdcard/aispeech") // duicore 资源包保存路径 .setUserId("XXXXXX") // 用户ID 保留字段 .setVoipType("line_4") // 使用 voip 功能时的类型,现在支持线性双麦(line_2)和线性4麦(line_4) .setVoipResBin("voip.bin") // voip 的资源的路径。assets目录下为压缩包名称,其他目录为绝对路径 .setOfflineProfileName(".profile") // assets目录下的离线授权文件名 .enableUpdateDuicore(false) // 是否更新 duicore 资源。 默认 false .enableDynamicUiAgentPort(false) // 是否使用 uiagent 动态端口号,默认 false .enableReplaceProfile(true) // 试用授权是否连网更新成在线授权 默认 true .setProductLicenseId("XXXXXX") // 产品licenseID .setProductVersion("2") // 连接指定产品版本的对话服务 .enableUpdateNotification(true); // 是否使用内置的资源更新通知栏 默认 true

4.2 sdk 功能开关

sdk 需要使用到的功能开关配置。

包括推送、大数据、socket节点、唤醒、声纹、VAD节点、H5节点 等

ddsConfigBuilder.createNodeBuilder() .enablePush(true) // 是否开启推送功能。默认 true .enableSocket(true) // 是否开启 socket 节点功能。默认 true .enableUpdate(true) // 是否开启数据更新功能。默认 true .enableUpload(true) // 是否开启大数据上传功能。默认 true .enableVad(true) // 是否开启 VAD 节点功能。默认 true .enableWakeup(true) // 是否开启唤醒节点功能。默认 true .enableIncrementWakeup(false) // 是否支持本地识别增量更新。默认 false .enableVprint(false) // 是否开启声纹功能。默认 false .enableH5(false); // 是否开启H5节点功能。默认 false

4.3 麦克风类型、唤醒、声纹、asrpp、Oneshot 配置

ddsConfigBuilder.createWakeupBuilder() .setMicType(0) // 硬件麦克风采集模组的类型。即 单麦、双麦、线性4麦、环形6麦等。 详情请看:4.3.1 MicType 说明 .setWakeupBin("wakeup.bin") // 设置唤醒资源 .setBeamformingBin("ULA_2.bin") // 麦克风阵列 beamforming 资源 .setAecBin("aec.bin") // 麦克风阵列AEC资源 .setIncrementWakeupBin("increment.bin") // 增强唤醒的 asr 资源,增强唤醒是使用 本地asr 实现的 .setVprintBin("vprint.bin") // 声纹资源 .enableVprintInWakeup(false) // 是否在唤醒中使用声纹判断,默认 false。诺设置为 true,需开启声纹功能 .enableSspe(false) // 多麦是否使用 sspe,默认 false .setSspeBin("sspe.bin") // 麦克风阵列 sspe 资源 .enableAge(false) // 是否使用年龄识别,默认 false .setAgeBin("age.bin") // 年龄识别资源 .enableGender(false) // 是否使用性别识别,默认 false .setGenderBin("gender.bin") // 性别识别资源 .enableNearWakeup(false) // 是否使用就近唤醒,默认 false .setNearWakeupDebounceWnd(450) // 就近唤醒防抖窗口期,单位毫秒 .setNearWakeupWkpWnd(300) // 就近唤醒决策窗口期,单位毫秒 .setNearWakeupMdsBin("mds.bin") // 就近唤醒的 mds 资源 .setOneshotMidTime(500) // 详情请看:4.3.2 OneShot配置 .setOneshotEndTime(0) // 详情请看:4.3.2 OneShot配置 .enableCustomGreeting(true) // 是否使用自定义欢迎语功能,默认 true .enableWakeupInAsr(true) // 识别过程中是否响应唤醒,默认 true .setAecMode("internal") // AEC(回声消除)模式,设置 external 后,单麦播放 tts 时唤醒节点不会停止,打断唤醒词仍可使用 .setWakeupRouter("dialog"); // 唤醒后的路由,唤醒后是否进入对话。取值:dialog(唤醒默认进入识别及对话) or partner(唤醒后不会主动进入识别及对话,同时唤醒后会收到sys.wakeup.result的message消息)

4.3.1 MicType 说明

setMicType 配置说明

设置硬件麦克风采集模组的类型

默认使用 DUI 控制台上产品的对应配置项。若客户端通过SDK配置此项后,则以客户端配置为准

取值:

  • 0:单麦(1路音频)默认
  • 1:单麦回消(1路音频+1路参考音)
  • 2:线性四麦(4路音频)
  • 3:环形六麦(6路音频)
  • 4:车载双麦(废弃)
  • 5:家居双麦(2路音频)
  • 6:环麦4麦(4路音频)
  • 7:车载双麦(2路音频)
  • 8:线性六麦(6路音频)
  • 10:线性八麦(8路音频)

4.3.2 OneShot配置

setOneshotMidTime 和 setOneshotEndTime 配置说明

  • 取值:毫秒
  • 若MIDTIME为0 & ENDTIME为0,唤醒后进入识别;若VAD检测超时,则直接退出对话
  • 若MIDTIME为0 & ENDTIME不为0,唤醒后进入识别;若ENDTIME超时,则直接退出对话
  • 若MIDTIME不为0 & ENDTIME为0,唤醒后进入识别;若MIDTIME超时,则播放欢迎语,继续识别(默认)
  • 若MIDTIME不为0 & ENDTIME不为0,唤醒后进入识别;若MIDTIME超时,则播放欢迎语,继续识别,若ENDTIME超时,则直接退出对话
  • 默认值:MIDTIME=500ms,ENDTIME=0ms

4.4 对话配置

对话相关配置

  • 设置对话模式是半双工还是全双工配置已经删除,请用 Agent.setDuplexMode(final DuplexMode duplexMode) 接口设置
ddsConfigBuilder.createDmBuilder() .enableVadInFullduplex(true) // 全双工模式下是否使用 vad,默认 true .enableTipsInFullduplex(true) // 全双工中是否使用消息的提示音,默认 true .enableLocalDDS(false) // 是否使用本地对话,默认 false .setCustomTips(customTips) // 指定对话错误码的 TTS 播报内容 .setClickMode(0) // 点击模式,调用 Agent#avatarClick 后sdk的响应模式。 .setDmTimeout(10000) // 对话响应超时时间。单位 毫秒。默认 10000 ms .setNativeApiTimeout(10000); // nativeApi 超时时间。单位 毫秒。默认 10000 ms

4.5 TTS、播放 配置

ddsConfigBuilder.createTtsBuilder() .enableExoPlaySpeakUrl(false) // 是否使用 exoPlayer 播报 tts 的 url,默认 false .enableOptmization(false) // 本地 TTS 是否开启内存优化,默认 false .setAudioFocusMode("internal") // 音频焦点处理模式。internal sdk内部处理(默认),external 使用方式见:3.16.4 音频焦点处理模式 .setTtsMode("internal") // TTS播报模式,internal sdk内部合成播报, external 用户负责合成播报,使用方式见:3.9 外部TTS引擎 .setStreamType(AudioManager.STREAM_MUSIC) // 内置播放器的 STREAM 类型,默认 AudioManager.STREAM_ALARM .setAudioUsage(AudioAttributes.USAGE_ALARM) // 内置播放器的 USAGE 类型,默认 AudioAttributes.USAGE_ALARM .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION); // 内置播放器的 contentType 类型,默认 AudioAttributes.CONTENT_TYPE_SONIFICATION

4.6 识别配置

包括云端服务的 asr 配置和本地 asr 配置

ddsConfigBuilder.createAsrBuilder() .enableStreamUrl(true) // 对话接口里是否返回 tts 的 speakUrl,默认 true .setNotDropWakeupInAsr(false) // 识别时不要过滤唤醒词,默认 false .setCloudVadPauseTime(500) // 云端VAD在结束语音时静音时长,默认500ms .enableAudioDetection(false) // 噪声检测配置,开启后可过滤噪声识别结果,默认 false .enableCloudAsrpp(false) // 云端asrpp参数,设置为true后云端识别返回性别/年龄/情绪等字段,默认 false .enableNumberConvert(false) // 中文数字转阿拉伯数字,默认 false .enablePunctuation(false) // 识别结果是否带标点符号,默认 false .enableSignalVad(false) // 是否禁用VAD单字打断功能,默认 false .enableTone(false) // 识别结果的拼音是否带音调,默认 false .setCustomWakeupScore("12.5") // 自定义唤醒词阈值,该分数一般是通过测试集调整。默认值:无 .setAsrPermanentSkill("{\"skillId\":\"123123\"}") // 技能锁定接口,让用户说的话只落在锁定的技能里 .setCustomAsrConf("[{\"name\":\"宝宝睡觉模式\",\"confidence\":\"0.61\"}]")// 设置指定语料的识别置信度,防止某些语料识别置信度过低被抛弃 .enableWakeNetwork(false) // 弱网环境配置,如使用的是弱网环境,则配置为true,默认 false .setAsrRouter("dialog") // 区分识别后是否进入对话。默认 dialog 进入对话,partner 不进入对话,可通过ASREngine相关方法获取识别结果 .setCustomAsrBin("net.bin") // 预置本地识别 net.bin 资源 .setVadBin("vad.bin") // VAD资源 .enableOptGramBuild(false) // 优化离线gram编译逻辑,默认 false .enableTipsBeforeAsr(false) // 开启后识别前都会有叮咚声,默认 false .enableAsrTips(false); // 是否开启识别完成音效,默认 false

4.7 录音配置

ddsConfigBuilder.createRecorderBuilder() .setRecorderMode("internal") // 录音模式, internal SDK内部录音, external sdk外部录音,通过 Agent().feedPcm() 方法传入音频 .setAudioSource(MediaRecorder.AudioSource.DEFAULT) // 录音机参数: audioSource 录音机数据源类型 .setAudioSamplerate(16000) // 录音机参数: sampleRateInHz 录音时音频采样率 .setAudioChannelConf(AudioFormat.CHANNEL_IN_MONO) // 录音机参数:channelConfig 录音机频道源类型 .setAudioFormat(AudioFormat.ENCODING_PCM_16BIT) // 录音机参数:audioFormat 每个采样大小 .setAudioBufferSizeInByte(192000) // 录音机参数:bufferSizeInBytes 录音机的缓存大小 .setAudioChannelCount(1) // 音频通道数 单麦是1 .setAudioBufferSize(3200) // 录音机每次读取音频的 buffer 大小, 单麦每 100ms 是 3200B .setReversAudioChannel(false) // 是否翻转单麦 1mic+1ref 音频通道,默认 false .enableHighVolume(false); // 是否使用大音量检测,默认 false

五、错误码描述

error id 错误描述 原因及解决办法
1 解压dds.bin失败 系统IO错误
2 内核执行出错 请检查配置是否正确,或者联系DUI客服
3 系统启动超时 请检查配置是否正确,或者联系DUI客服
4 存储空间不足 请检查/data/目录剩余大小是否充足
070302 解码失败 联系DUI客服
070303 未发现新的版本
070304 请先升级您的客户端(SDK) 因为产品选择的dui内核和sdk不匹配,需要升级sdk
070305 网络异常, 无法连接至服务器 请检查网络是否正常或者配置是否正确
070306 解码失败 联系DUI客服
070307 补丁安装失败 存储空间不够
070308 备份压缩文件不存在 用户手动删掉了app私有空间的文件
070309 提取压缩文件失败 存储空间不够
070310 未设置产品ID 需要设置产品id
070311 无效客户端版本 请检查配置是否正确
070312 无效用户ID 需要设置用户id
070313 无效设备ID 设备id不正确
070314 资源地址无效 请检查配置是否正确
070315 无效的产品分支 需要设置产品分支
070316 无法找到资源 服务端资源生成有问题,请尝试重新发布一下,或者请提供产品id反馈给DUI客服,去后台查询根本原因
070317 资源校验失败 下载资源过程中资源被纂改
070319 当前产品中内核资源版本过低,请前往DUI控制台对应的产品中,选择最新的内核资源版本并重新发布产品 当前产品中内核资源版本过低,请前往DUI控制台对应的产品中,选择最新的内核资源版本并重新发布产品。
070320 存储空间不足 系统存储空间不足,请在错误的详细信息中查看需要的存储空间和现有的存储空间
070601 设备注册过程中无法连接到授权服务器 请检查网络是否畅通,或者productId与api key是否匹配
070602 设备注册过程无法颁发有效的profile文件 请检查产品ID或与客服联系
070603 API Key无效 请到产品授权配置页面查看api key信息是否正确,请确认签名证书是否匹配,包名是否匹配
070604 无效的产品ID 无效的产品ID,请确认是否未设置产品ID
070606 profile文件被禁用 请重新进行注册或者与客服联系
070607 profile文件过期 请重新进行注册或者与客服联系
070609 设备注册无法保存有效的profile文件 检查文件系统是否异常或者重新进行设备注册
070612 dns resolve failed: no answers(DNS失败) 请检查网络是否畅通
070613 dns resolve failed: exceed retry count(DNS失败) 请检查网络是否畅通
070614 dns resolve failed: network is unreachable(DNS失败) 请检查网络是否畅通
070615 dns resolve timeout(DNS超时) 请检查网络是否畅通
070616 connect ip failed: reasons for uncertainty(建立连接失败) 请检查网络是否畅通
070617 connect ip failed: network is unreachable(建立连接失败) 请检查网络是否畅通
070618 connect ip timeout(建立连接超时) 请检查网络是否畅通
070619 websocket handshake failed: reasons for uncertainty(websocket握手失败) 请检查网络是否畅通
070620 recv timeout(接收超时) 请检查网络是否畅通
070621 send timeout(发送超时) 请检查网络是否畅通
070622 orderly shutdown(发完数据正常关闭) 请检查网络是否畅通
071301 TTS合成超时 请检查网络是否正常
071302 对话超时 请检查网络是否正常
071303 对话连接断开 请检查网络是否正常
071304 识别结果为空 未检测到用户说法
071305 语义结果为空 用户的说法没有命中产品中的非闲聊技能
071306 WebAPI错误 调用WebAPI时发生错误
071307 NativeAPI错误 调用NativeAPI时发生错误
071308 进入闲聊技能 用户的首轮说法没有命中产品中的非闲聊技能
071309 异常重试达到最大次数 达到了产品错误处理的最大重试次数
071310 命中退出词 用户说了产品的退出词
071311 NativeAPI未注册 请检测是否注册并实现NativeAPI
071312 本地识别结果置信度低 网络离线时本地识别结果置信度较低
071313 自定义技能响应超时 联系技能开发者
071314 自定义技能内部错误 联系技能开发者
071315 自定义技能返回为空 联系技能开发者
071316 当前场景下不支持这么说 该技能关闭了智能调度
071317 进入兜底技能 用户说法进入了产品中的兜底技能
071318 进入知识型技能 用户说法进入了产品中的知识型技能,包括新闲聊、新百科
071319 未授权 请调用doAuth
071320 在使用oauth授权方式的情况下,access token过期或者不合法 请使用oauth sdk刷新access token,然后用dds sdk的接口传入access token
071321 自定义技能speaklist播报超时 请检查音频链接是否正常
071322 鉴权失败的情况下请求对话 请检查授权错误信息
071300 对话服务内部错误 联系DUI客服
072101 ASREngine VAD超时 超过8s未检测到人声
072102 ASREngine 识别为空 未检测到用户说法
072103 ASREngine 识别服务超时 请检查网络是否正常
072104 ASREngine 识别过程发生错误 联系DUI客服
072201 唤醒词个数超过限制 请清除之前的唤醒词后重试

六、常见问题

6.1 SDK的软硬件配置要求

  1. 需要Android 4.1及其以上的系统;
  2. /data分区剩余空间至少200MB。

6.2 授权失败

关于070603授权错误的问题,请做以下检查:

  1. 平台上生成的apikey和本地使用的apikey是否一致;
  2. 平台上release debug的sha256值是否和本地的对应;
  3. gradle文件配置的signingConfigs,只针对generate生成的apk;
  4. 如果是点run运行的程序,debug的sha256要填的.android目录下的debug.keystore(最常见)

6.3 客户端没收到Command/NativeAPI回调

常见原因如下,可以自上而下确认:

  1. 技能修改后,未发布;
  2. 技能发布后,产品没有选择该技能的最新版本;
  3. 产品未发布;
  4. 客户端配置的K_ALIAS_KEY和产品发布的分支不一致;
  5. 客户端没有注册对应的Command/NativeAPI;
  6. 客户端注册Command/NativeAPI的逻辑没走到;
  7. 客户端注册的Command/NativeAPI没填对,把?后面的参数也填进去了;
  8. 客户端注册Command/NativeAPI后,又重新初始化了DDS(release+init)

6.4 智能家居refreshToken 断网2年后导致SDK授权失败且无法恢复的问题

1)通过订阅token_refresh.fail消息,如果token刷新失败,SDK会抛出这个消息,应用收到该消息后,重新走以下流程:requestAuthCode() – setAuthCode()
2)setAuthCode授权成功后,将智能家居绑定流程再重新走一遍;确保DCA绑定成功。