# 一. 集成准备 ## 1.1 获取产品ID 集成SDK之前,您首先需要[到DUI控制台](https://www.duiopen.com/console/product/index.html)创建产品,获取如下的产品ID。  ## 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 不同版本 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)  2. 快捷唤醒中的命令响应  当您在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  当您在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。
1.2 下载和导入SDK
- 目前Android SDK只支持Android Studio开发环境
- Android Demo 存放在 gitee:dui-dds-android-sdk-samples 项目
- 可以前往SDK下载页下载最新的SDK包,sdk 包内包含 aar duicore javadoc 等
- java doc 还可在线查看 javadoc在线文档
- 如需SDK历史版本,请前往Android_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 文件夹,内含:
- mini.py // 精简脚本
- mini.config // 精简脚本的配置文件
脚本使用方法:
- 修改 mini/mini.config 配置文件,0表示不使用, 1表示使用
- 在mini目录下执行python脚本: python3 mini.py
- 在mini目录下生成精减的 aar 与 duicore
!!!强烈建议客户使用匹配的 duicore 和 aar, 支持客户自己根据需要裁剪,一般可以减少一半大小
二. 初始化 SDK
初始化包含
- 授权
- 资源、产品的初始化
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 配置规则:
-
如果duicore.zip的放置位置在assets目录下,那么就可以使用如代码中的写法,直接使用文件名:duicore.zip
-
如果duicore.zip没有放置在assets目录下,就必须使用duicore.zip文件的绝对路径:/sdcard/dds/duicore.zip
-
在 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 版本授权流程:
- 授权操作需要连接到互联网, 在 SDK 初始化时设置 DDSAuthListener 来监听授权的情况
- SDK 初始化完毕后,您可以使用
DDS.getInstance().isAuthSuccess()
来判断当前是否授权成功。 - 授权成功会回调
DDSAuthListener.onAuthSuccess()
,授权失败会收到回调DDSAuthListener.onAuthFailed()
,需要重新调用DDS.getInstance().doAuth() - 授权错误码从 "070601“ – “070622”,详见 五、错误码描述。
- v1.X.X.X 版本的授权和 init 回调没有顺序关系
-
在SDK初始化时,可以设置 DDSAuthListener 来监听授权的情况。
-
SDK初始化完毕后,您可以使用 DDS.getInstance().isAuthSuccess() 来判断当前是否授权成功。
-
授权成功会回调 DDSAuthListener.onAuthSuccess,授权失败会收到回调DDSAuthListener.onAuthFailed,需要重新调用DDS.getInstance().doAuth()。
-
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
- 客户端动作的执行
详细的command编写说明,请参见客户端动作(command)编写说明
- 快捷唤醒中的命令响应
当您在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
当您在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 内部处理,也可交由外部处理
- 配置 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")
- 设置回调并在回调里处理音频焦点
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 音频回调
- 配置 VOIP 信息
config.addConfig(DDSConfig.K_VOIP_TYPE, "line_2");
- 设置 VOIP 回调
/**
* 设置voip监听器, 时时回调voip音频
*
* @param listener
*/
public void setVoipListener(VoipListener listener)
- 启动 VOIP 功能
// 调用 setEnableVoip(true) 时 voipNode 才会注册消息,接收音频数据
DDS.getInstance().getAgent().setEnableVoip(true);
-
录音后就会从回调里回调 voip 音频
-
取消 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播报时说分词容易误选择问题,比如导航列表有“天安门”“北京天安门”“天安门地铁站“,以前用唤醒说”天安门地铁站“会响应”天安门“
使用方法
- 初始化
- 打开增强唤醒功能
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();
- 优先级设置
- 如果可见即可说和全局免唤醒词有相同的词,可见即可说优先
DDS.getInstance().getAgent().getWakeupEngine().setIncrementPriority(WakeupPriorityType.HOTWORD_FIRST);
- 如果可见即可说和全局免唤醒词有相同的词,全局免唤醒优先
DDS.getInstance().getAgent().getWakeupEngine().setIncrementPriority(WakeupPriorityType.GLOBALWORD_FIRST);
- 使用全局免唤醒词
可使用增强唤醒的唤醒词类型:
* 命令唤醒词(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();
- 使用可见即可说
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 获取性别/年龄信息
- 在初始化DDS SDK的时候加入如下配置:
config.addConfig(DDSConfig.K_USE_GENDER, "true");
config.addConfig(DDSConfig.K_USE_AGE, "true");
- 然后订阅 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 全双工不支持接口与功能
-
由于全双工模式为全程识别状态, 所以开启识别和 startListening 不可同时使用 [3.13开启/终止识别](#3.13 仅开启识别)
-
由于全双工模式为全程识别状态, 在模拟半双工下的打断唤醒由识别代替,主唤醒词正常开启下一轮对话
-
由于全双工模式下暂时没有快捷指令的使用场景, 所以快捷指令在全双工模式下不可用 [3.21 快捷指令](#3.21 快捷指令)
-
全双工模式下默认使用 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 声纹
声纹注册、验证的词也是主唤醒词,唤醒后会使用唤醒音频做声纹的注册、验证等操作
- 打开声纹配置
config.addConfig(DDSConfig.K_VPRINT_ENABLE, "true"); // 是否开启声纹功能
DDS.getInstance().getAgent().getWakeupEngine().useVprintInWakeUp(true) // 动态设置是否在唤醒中启用声纹
- 使用声纹
// 注册声纹监听器
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 包含很多配置,现按功能分为以下子类:
- ddsConfigBuilder.createCommonBuilder() [4.1 设备信息、使用目录、VOIP功能 等通用配置](#4.1 设备信息、使用目录、VOIP功能 等通用配置)
- ddsConfigBuilder.createNodeBuilder() [4.2 sdk 功能开关](#4.2 sdk 功能开关)
- ddsConfigBuilder.createWakeupBuilder() [4.3 麦克风类型、唤醒、声纹、asrpp、Oneshot 配置](#4.3 麦克风类型、唤醒、声纹、asrpp、Oneshot 配置)
- ddsConfigBuilder.createDmBuilder() [4.4 对话配置](#4.4 对话配置)
- ddsConfigBuilder.createTtsBuilder() [4.5 TTS、播放 配置](#4.5 TTS、播放 配置)
- ddsConfigBuilder.createAsrBuilder() [4.6 识别配置](#4.6 识别配置)
- 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 系统目录配置
使用方式如下:
- assets 目录配置方式: setWakeupBin(“wakeup.bin”)
- 系统目录配置方式: 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的软硬件配置要求
- 需要Android 4.1及其以上的系统;
- /data分区剩余空间至少200MB。
6.2 授权失败
关于070603授权错误的问题,请做以下检查:
- 平台上生成的apikey和本地使用的apikey是否一致;
- 平台上release debug的sha256值是否和本地的对应;
- gradle文件配置的signingConfigs,只针对generate生成的apk;
- 如果是点run运行的程序,debug的sha256要填的.android目录下的debug.keystore(最常见)
6.3 客户端没收到Command/NativeAPI回调
常见原因如下,可以自上而下确认:
- 技能修改后,未发布;
- 技能发布后,产品没有选择该技能的最新版本;
- 产品未发布;
- 客户端配置的K_ALIAS_KEY和产品发布的分支不一致;
- 客户端没有注册对应的Command/NativeAPI;
- 客户端注册Command/NativeAPI的逻辑没走到;
- 客户端注册的Command/NativeAPI没填对,把?后面的参数也填进去了;
- 客户端注册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。
1.2 下载和导入SDK
- 目前Android SDK只支持Android Studio开发环境
- Android Demo 存放在 gitee:dui-dds-android-sdk-samples 项目
- 可以前往SDK下载页下载最新的SDK包,sdk 包内包含 aar duicore javadoc 等
- java doc 还可在线查看 javadoc在线文档
- 如需SDK历史版本,请前往Android_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 文件夹,内含:
- mini.py // 精简脚本
- mini.config // 精简脚本的配置文件
脚本使用方法:
- 修改 mini/mini.config 配置文件,0表示不使用, 1表示使用
- 在mini目录下执行python脚本: python3 mini.py
- 在mini目录下生成精减的 aar 与 duicore
!!!强烈建议客户使用匹配的 duicore 和 aar, 支持客户自己根据需要裁剪,一般可以减少一半大小
二. 初始化 SDK
初始化包含
- 授权
- 资源、产品的初始化
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 配置规则:
-
如果duicore.zip的放置位置在assets目录下,那么就可以使用如代码中的写法,直接使用文件名:duicore.zip
-
如果duicore.zip没有放置在assets目录下,就必须使用duicore.zip文件的绝对路径:/sdcard/dds/duicore.zip
-
在 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 版本授权流程:
- 授权操作需要连接到互联网, 在 SDK 初始化时设置 DDSAuthListener 来监听授权的情况
- SDK 初始化完毕后,您可以使用
DDS.getInstance().isAuthSuccess()
来判断当前是否授权成功。 - 授权成功会回调
DDSAuthListener.onAuthSuccess()
,授权失败会收到回调DDSAuthListener.onAuthFailed()
,需要重新调用DDS.getInstance().doAuth() - 授权错误码从 "070601“ – “070622”,详见 五、错误码描述。
- v1.X.X.X 版本的授权和 init 回调没有顺序关系
-
在SDK初始化时,可以设置 DDSAuthListener 来监听授权的情况。
-
SDK初始化完毕后,您可以使用 DDS.getInstance().isAuthSuccess() 来判断当前是否授权成功。
-
授权成功会回调 DDSAuthListener.onAuthSuccess,授权失败会收到回调DDSAuthListener.onAuthFailed,需要重新调用DDS.getInstance().doAuth()。
-
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
- 客户端动作的执行
详细的command编写说明,请参见客户端动作(command)编写说明
- 快捷唤醒中的命令响应
当您在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
当您在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 内部处理,也可交由外部处理
- 配置 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")
- 设置回调并在回调里处理音频焦点
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 音频回调
- 配置 VOIP 信息
config.addConfig(DDSConfig.K_VOIP_TYPE, "line_2");
- 设置 VOIP 回调
/**
* 设置voip监听器, 时时回调voip音频
*
* @param listener
*/
public void setVoipListener(VoipListener listener)
- 启动 VOIP 功能
// 调用 setEnableVoip(true) 时 voipNode 才会注册消息,接收音频数据
DDS.getInstance().getAgent().setEnableVoip(true);
-
录音后就会从回调里回调 voip 音频
-
取消 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播报时说分词容易误选择问题,比如导航列表有“天安门”“北京天安门”“天安门地铁站“,以前用唤醒说”天安门地铁站“会响应”天安门“
使用方法
- 初始化
- 打开增强唤醒功能
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();
- 优先级设置
- 如果可见即可说和全局免唤醒词有相同的词,可见即可说优先
DDS.getInstance().getAgent().getWakeupEngine().setIncrementPriority(WakeupPriorityType.HOTWORD_FIRST);
- 如果可见即可说和全局免唤醒词有相同的词,全局免唤醒优先
DDS.getInstance().getAgent().getWakeupEngine().setIncrementPriority(WakeupPriorityType.GLOBALWORD_FIRST);
- 使用全局免唤醒词
可使用增强唤醒的唤醒词类型:
* 命令唤醒词(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();
- 使用可见即可说
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 获取性别/年龄信息
- 在初始化DDS SDK的时候加入如下配置:
config.addConfig(DDSConfig.K_USE_GENDER, "true");
config.addConfig(DDSConfig.K_USE_AGE, "true");
- 然后订阅 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 全双工不支持接口与功能
-
由于全双工模式为全程识别状态, 所以开启识别和 startListening 不可同时使用 [3.13开启/终止识别](#3.13 仅开启识别)
-
由于全双工模式为全程识别状态, 在模拟半双工下的打断唤醒由识别代替,主唤醒词正常开启下一轮对话
-
由于全双工模式下暂时没有快捷指令的使用场景, 所以快捷指令在全双工模式下不可用 [3.21 快捷指令](#3.21 快捷指令)
-
全双工模式下默认使用 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 声纹
声纹注册、验证的词也是主唤醒词,唤醒后会使用唤醒音频做声纹的注册、验证等操作
- 打开声纹配置
config.addConfig(DDSConfig.K_VPRINT_ENABLE, "true"); // 是否开启声纹功能
DDS.getInstance().getAgent().getWakeupEngine().useVprintInWakeUp(true) // 动态设置是否在唤醒中启用声纹
- 使用声纹
// 注册声纹监听器
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 包含很多配置,现按功能分为以下子类:
- ddsConfigBuilder.createCommonBuilder() [4.1 设备信息、使用目录、VOIP功能 等通用配置](#4.1 设备信息、使用目录、VOIP功能 等通用配置)
- ddsConfigBuilder.createNodeBuilder() [4.2 sdk 功能开关](#4.2 sdk 功能开关)
- ddsConfigBuilder.createWakeupBuilder() [4.3 麦克风类型、唤醒、声纹、asrpp、Oneshot 配置](#4.3 麦克风类型、唤醒、声纹、asrpp、Oneshot 配置)
- ddsConfigBuilder.createDmBuilder() [4.4 对话配置](#4.4 对话配置)
- ddsConfigBuilder.createTtsBuilder() [4.5 TTS、播放 配置](#4.5 TTS、播放 配置)
- ddsConfigBuilder.createAsrBuilder() [4.6 识别配置](#4.6 识别配置)
- 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 系统目录配置
使用方式如下:
- assets 目录配置方式: setWakeupBin(“wakeup.bin”)
- 系统目录配置方式: 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的软硬件配置要求
- 需要Android 4.1及其以上的系统;
- /data分区剩余空间至少200MB。
6.2 授权失败
关于070603授权错误的问题,请做以下检查:
- 平台上生成的apikey和本地使用的apikey是否一致;
- 平台上release debug的sha256值是否和本地的对应;
- gradle文件配置的signingConfigs,只针对generate生成的apk;
- 如果是点run运行的程序,debug的sha256要填的.android目录下的debug.keystore(最常见)
6.3 客户端没收到Command/NativeAPI回调
常见原因如下,可以自上而下确认:
- 技能修改后,未发布;
- 技能发布后,产品没有选择该技能的最新版本;
- 产品未发布;
- 客户端配置的K_ALIAS_KEY和产品发布的分支不一致;
- 客户端没有注册对应的Command/NativeAPI;
- 客户端注册Command/NativeAPI的逻辑没走到;
- 客户端注册的Command/NativeAPI没填对,把?后面的参数也填进去了;
- 客户端注册Command/NativeAPI后,又重新初始化了DDS(release+init)
6.4 智能家居refreshToken 断网2年后导致SDK授权失败且无法恢复的问题
1)通过订阅token_refresh.fail消息,如果token刷新失败,SDK会抛出这个消息,应用收到该消息后,重新走以下流程:requestAuthCode() – setAuthCode()
2)setAuthCode授权成功后,将智能家居绑定流程再重新走一遍;确保DCA绑定成功。