智能语音设备的特点是语音交互成为人机交互的重要方式,智能语音设备的声音输出摆脱了预置的逻辑,成为了响应式反馈,尤其是对声音输入的响应。在智能语音设备上,基于语音交互的服务被称为技能,通过诸如DBP(DuerOS Bot Platform)开放平台开发技能,进而提供基于语音交互的服务,关于面向DuerOS的技能开发可以参考《面向接口/协议?看DuerOS的技能开发》。
那么, 技能是在智能语音设备上如何播放声音的呢?
在DuerOS 中,主要提供了三种声音的播放方式:音视频媒体播放,TTS 合成的语音播放 和利用TTS合成的媒体及文本混合播放。
音视频媒体播放
点播和资源类的技能都采用音视频媒体播放的方式。当技能收到用户请求的意图时,技能在响应中返回音视频媒体播放的指令和媒体资源的url,以及其他的相关内容,在智能语音设备收到技能响应的内容后,即可播放技能制定的资源。
基于AudioPlayer的音频播放
音频资源既可以在有屏语音设备(如小度智能屏系列),也可以在无屏语音设备(如小度音箱系列)上播放,主要是通过AudioPlayer 相关指令完成的。
AudioPlayer指令从技能发送给DuerOS,对端上进行音频资源的播放控制。在DuerOS收到该指令后,会经过一系列处理解析为端上对应的播放指令。在云端下发AudioPlayer指令后,端上开始音频资源播放操作。音频资源播放会触发一系列事件上报到云端。这些指令和事件分别是:
指令或事件 | 功能解释 |
---|---|
AudioPlayer.Play指令 | 播放 |
AudioPlayer.Stop指令 | 停止 |
AudioPlayer.PlaybackStarted事件 | 播放开始 |
AudioPlayer.PlaybackStopped事件 | 播放暂停。 |
AudioPlayer.PlaybackFinished事件 | 播放结束,即一首歌播放完后上报的事件 |
AudioPlayer.PlaybackNearlyFinished事件 | 在播放即将结束时上报的事件 |
AudioPlayer.PlaybackFailed事件 | 当设备端播放audio item发生错误时上报此事件 |
AudioPlayer.Play指令是驱动播放音频的核心指令,一般地,使用Play指令时,response需要返回shouldEndSession值为true,使端上关闭麦克风。消息示例如下:
代码语言:javascript复制{
"type": "AudioPlayer.Play",
"playBehavior": "{{STRING}}"
"audioItem": {
"audioItemId": "{{STRING}}",
"stream": {
"url": "{{STRING}}",
"outputSpeech": {
"type": "{{STRING}}",
"text": "{{STRING}}",
"ssml": "{{STRING}}",
"ttsTemplates": {{TTSTemplateStructure}}
},
"streamFormat": "{{STRING}}",
"offsetInMilliSeconds": {{LONG}},
"token": "{{STRING}}",
"progressReportIntervalMs": {{LONG}}
},
"playerInfo": {
"content": {{AudioPlayerInfoContentStructure}},
"controls": [
{{ControlStructure}},
{{ControlStructure}},
......
]
}
}
}
当持续播放音频资源的时候,技能需要监听AudioPlayer.PlaybackNearlyFinished事件,通过playBehavior参数对播放的音频队列进行调整,DuerOS 是通过资源迭代的方式完成多个音频资源间连续播放的。
当然,使用全双工特性时,DuerOS的处理稍有不同,详情可以参见《聊聊“全双工”》。
基于VideoPlayer 的视频播放
视频资源只可以在有屏语音设备(如小度智能屏系列)上播放,主要是通过VideoPlayer 相关指令完成的。
VideoPlayer提供了VideoPlayer指令接口和VideoPlayer事件接口。VideoPlayer指令是技能向DuerOS发送的,对视频进行控制的指令,如播放指令、停止播放指令等。DuerOS收到指令后会转化成端上能识别的播放指令,对视频进行相应的控制。VideoPlayer事件是指在视频播放过程中触发一系列事件,DuerOS会将这些事件上报给技能,请求技能进行处理。这些指令和事件分别是:
指令或事件 | 功能解释 |
---|---|
VideoPlayer.Play指令 | 播放 |
VideoPlayer.Stop指令 | 停止 |
VideoPlayer.ClearQueue指令 | 用于清除video_player播放队列 |
VideoPlayer.PlaybackStarted事件 | 播放开始 |
VideoPlayer.PlaybackStopped事件 | 播放暂停 |
VideoPlayer.PlaybackFinished事件 | 播放结束,即一个视频播放完后上报的事件 |
VideoPlayer.PlaybackNearlyFinished事件 | 在播放即将结束时上报的事件。 |
VideoPlayer.ProgressReportIntervalElapsed事件 | 本事件用来统计播放情况。如果Play指令有progressReportIntervalInMilliseconds,则在播放对应video item时,每隔此时间上报本事件。 |
VideoPlayer.ProgressReportDelayElapsed事件 | 本事件用来统计播放情况。如果Play指令中有progressReportDelayInMilliseconds,则对应video item播放此时间长后会上报本事件 |
VideoPlayer.PlaybackStutterStarted事件 | 在PlaybackStarted事件之后,如果设备端缓冲视频数据慢于播放速度时,会上报此事件。 |
VideoPlayer.PlaybackStutterFinished事件 | PlaybackStutterStarted事件之后,缓冲恢复到正常状态,可重新开始播放时端上会上报本事件。 |
VideoPlayer.PlaybackPaused事件 | 视频播放时,如果发生对话/闹钟等行为,则视频播放会暂停,此时,端上会上报PlaybackPaused事件和PlaybackResumed事件。 |
VideoPlayer.PlaybackResumed事件 | 视频播放时,如果发生对话/闹钟等行为,则视频播放会暂停,此时,端上会上报PlaybackPaused事件和PlaybackResumed事件。 |
VideoPlayer.PlaybackQueueCleared事件 | 设备端处理完ClearQueue指令后会上报此事件。 |
技能在返回VideoPlayer.Play指令时,对于show设备中控需要补充RenderVideoPlayerInfo指令用于播放器界面的渲染,消息样例如下:
代码语言:javascript复制{
"type": "VideoPlayer.Play",
"playBehavior": "{{STRING}}"
"videoItem": {
"videoItemId": "{{STRING}}",
"stream": {
"url": "{{STRING}}",
"offsetInMilliseconds": {{LONG}},
"stopPointsInMilliseconds":[
{{LONG}},
{{LONG}},
...
],
"expiryTime": {{STRING}}
"progressReport": {
"progressReportDelayInMilliseconds": {{LONG}},
"progressReportIntervalInMilliseconds": {{LONG}}
},
"token": "{{STRING}}",
"expectedPreviousToken": {{STRING}}
}
}
}
使用VideoPlayer持续播放视频资源的方法与音频资源持续播放的方式类似。
音视频媒体播放是技能播放声音的重要方法,在非资源类技能中也有着广泛的应用。
基于TTS 的语音播放
音视频资源有着很好的表现力,但面向动态播报的内容预先录制的成本比较高,对于各种各样的文本播报,一般采用TTS 合成的方式完成语音播放。
TTS 的语音播报指令——outputSpeech
outputSpeech是基于TTS的语音播报指令,表示本次返回结果中需要播报的语音信息,消息样例如下:
代码语言:javascript复制......
"response" : {
"outputSpeech" : {
"type" : "{{STRING}}",
"text" : "{{STRING}}",
"ssml" : "{{STRING}}",
},
"card" : {},
"directives" : [],
"expectSpeech": {{BOOLEAN}},
"shouldEndSession" : {{BOOLEAN}}
}
在outputSpeech的指令机构中,TTS合成有如下的类型:
- PlainText:普通文本。
- SSML:一种结构化语言,用于辅助描述语音发音声调。
当type取值为PlainText时,该字段为必选字段。长度不能超过256个字符。当type为SSML时,该字段为必选字段,长度不能超过256个字符,SSML 会在下一节“基于TTS的媒体和文本合成播放”中在进行描述。
outputSpeech 的变种——reprompt
在需要用户输入时,如果用户离开了,麦克风没有进行语音的输入,或用户输入的语音请求系统无法解析成技能的任何意图,则可以使用reprompt来播放预置的内容。
reprompt.outputSpeech参数定义与上述outputSpeech的定义一致,消息样例如下:
代码语言:javascript复制 "response" : {
"outputSpeech" : {
"type" : "{{STRING}}",
"text" : "{{STRING}}",
"ssml" : "{{STRING}}",
},
"reprompt" : {
"outputSpeech" : {
"type" : "{{STRING}}",
"text" : "{{STRING}}",
"ssml" : "{{STRING}}",
}
},
"card" : {},
"directives" : [],
"expectSpeech": {{BOOLEAN}},
"shouldEndSession" : {{BOOLEAN}}
}
repromt指令实际上是outputSpeech指令的变种,是一种应用在特定场景的TTS 语音播报。
长文本的播放
TTS 播报中对文本长度的限制往往被人们所诟病。话术太长往往会被截断,或者在播放的过程中,由于长时间没有用户交互导致session 被关闭,现象就是在TTS 播放一段时间后,技能退出了,用户需要重新打开技能才能继续在技能中交互。
针对长文本的播报,目前DuerOS 提供了两种解决方案:分段持续播放和媒体转换播放。
分段持续播放
既然长文本在播放过程中无法维持session, 那么我们可以把长文本分割成多个短文本,在前一个短文本播放完毕之后,在继续播放下一个短文本,如此类推,实现长文本的持续完整播放。
具体的方式是应用DuerOS 的StreamResponse 特性,StreamResponse 定义了技能在执行过程中,下发部分结果到端上执行,在端执行完这部分结果后,上报事件以获取技能的其它结果,适应于以下场景:技能返回shouldEndSession=false和StreamResponse.SendPart指令。
StreamResponse.SendPart指令告知DuerOS平台本次只返回部分满足结果,期望在StreamResponse.NextRequired事件时继续返回结果。技能在返回本指令时,如果shouldEndSession=true,DuerOS平台将忽略本指令。技能在返回本指令时,如果shouldEndSession=false,DuerOS平台在本次结果执行后,下发StreamResponse.NextRequired事件给技能,消息样例如下:
代码语言:javascript复制{
"type": "StreamResponse.SendPart",
"token": "{{STRING}}"
}
技能在收到StreamResponse.NextRequired事件后,返回计划内的下一个响应结果, request中事件样例如下:
代码语言:javascript复制{
"type": "StreamResponse.NextRequired",
"requestId": "{{STRING}}",
"timestamp": "{{STRING}}",
"token": "{{STRING}}"
}
StreamResponse.SendPart指令 和StreamResponse.NextRequired事件组成了一种类似迭代器的机制,使技能可以完成长文本的分段发送。
当然,StreamResponse的应用场景还有很多,长文本的TTS播报只是其中的一个典型应用而已。
媒体转换播放
既然长文本的TTS播报受限于长度,那能否将长文本实时在线转换为音频资源,然后采用AudioPlayer.play 指令播放来突破文本长度的限制呢?答案是肯定的。
DuerOS 提供一种在线将文本转化成音频资源的方法,在技能返回Play指令的时候,content为指定schema的tts,DuerOS会进行及时的批量替换。
schema示例如下:
以下是文本“度秘提醒你今天是妈妈的生日” tts://dueros.baidu.com?text=度秘提醒你今天是妈妈的生日&from=标明技能来源&expire=86400
其中参数需要urlencode, php使用rawurlencode函数即可,其他编程语言类似。
- text:文本长度5k以内
- from: botid,需要传相关来源
- expire: 音频地址的保存时间,最多不超过7天,单位是秒
使用每天转换播放长文本的示例如下:
代码语言:javascript复制{
"directive": {
"header": {
"messageId": "YXVkaW9fbmV3cysxNjAwMTU2ODI4Xzk2NmExampiMQ==",
"name": "Play",
"namespace": "ai.dueros.device_interface.audio_player"
},
"payload": {
"audioItem": {
"audioItemId": "9696687530282687670",
"stream": {
"expiryTime": "2020-10-01T00:00:00 08:00",
"offsetInMilliseconds": 0,
"progressReport": {
"progressReportIntervalInMilliseconds": 15000
},
"streamFormat": "AUDIO_MPEG",
"token": "eyJib3RfaWQiOiJhdWRpb19uZXdzIiwicmVzdWx0X3Rva2VuIjoiNjk0ODQ2MDY5Mjc0NmYwYjY3YTRhNzI5ZWMwZDE1ZGUiLCJib3RfdG9rZW4iOiI5Njk2Njg3NTMwMjgyNjg3NjcwIiwibGF1bmNoX2lkcyI6WyJhdWRpb19uZXdzIl19",
"url": "tts://dueros.baidu.com?text=%E9%98%AE%E6%88%90%E5%8F%91%E8%B5%B4%E7%91%9E%E4%B8%BD%E7%9D%A3%E5%AF%BC%E6%A3%80%E6%9F%A5%E7%96%AB%E6%83%85%E9%98%B2%E6%8E%A7%EF%BC%9A%E5%8A%A1%E5%BF%853%E6%97%A5%E5%86%85%E5%AE%8C%E6%88%90%E5%85%A8%E5%91%98%E6%A0%B8%E9%85%B8%E6%A3%80%E6%B5%8B%E3%80%82%E6%8E%8C%E4%B8%8A%E6%98%A5%E5%9F%8E%E8%AE%AF9%E6%9C%8814%E6%97%A5%EF%BC%8C%E4%BA%91%E5%8D%97%E7%9C%81%E7%9C%81%E5%A7%94%E5%89%AF%E4%B9%A6%E8%AE%B0%E3%80%81%E7%9C%81%E9%95%BF%E3%80%81%E7%9C%81%E5%A7%94%E7%9C%81%E6%94%BF%E5%BA%9C%E5%BA%94%E5%AF%B9%E6%96%B0%E5%86%A0%E8%82%BA%E7%82%8E%E7%96%AB%E6%83%85%E5%B7%A5%E4%BD%9C%E9%A2%86%E5%AF%BC%E5%B0%8F%E7%BB%84%E7%BB%84%E9%95%BF%E9%98%AE%E6%88%90%E5%8F%91%E8%B5%B6%E8%B5%B4%E5%BE%B7%E5%AE%8F%E5%82%A3%E6%97%8F%E6%99%AF%E9%A2%87%E6%97%8F%E8%87%AA%E6%B2%BB%E5%B7%9E%E7%91%9E%E4%B8%BD%E5%B8%82%E7%9D%A3%E5%AF%BC%E6%A3%80%E6%9F%A5%E7%96%AB%E6%83%85%E9%98%B2%E6%8E%A7%E5%B7%A5%E4%BD%9C%EF%BC%8C%E5%AF%B9%E5%BD%93%E5%89%8D%E7%96%AB%E6%83%85%E9%98%B2%E6%8E%A7%E5%BD%A2%E5%8A%BF%E8%BF%9B%E8%A1%8C%E5%86%8D%E5%88%86%E6%9E%90%E3%80%81%E5%86%8D%E7%A0%94%E5%88%A4%EF%BC%8C%E5%AF%B9%E8%BE%B9%E5%A2%83%E7%AE%A1%E6%8E%A7%E5%92%8C%E9%98%B2%E6%8E%A7%E6%8E%AA%E6%96%BD%E8%BF%9B%E8%A1%8C%E5%86%8D%E7%A0%94%E7%A9%B6%E3%80%81%E5%86%8D%E5%AE%89%E6%8E%92%E3%80%82%E5%BD%93%E6%99%9A%EF%BC%8C%E9%98%AE%E6%88%90%E5%8F%91%E4%B8%8E%E5%9B%BD%E5%AE%B6%E5%8D%AB%E7%94%9F%E5%81%A5%E5%BA%B7%E5%A7%94%E8%B5%B4%E4%BA%91%E5%8D%97%E4%B8%93%E5%AE%B6%E7%BB%84%E8%BF%9B%E8%A1%8C%E5%B7%A5%E4%BD%9C%E5%BA%A7%E8%B0%88%EF%BC%8C%E4%BB%8B%E7%BB%8D%E8%BE%B9%E5%A2%83%E7%96%AB%E6%83%85%E9%98%B2%E6%8E%A7%E6%83%85%E5%86%B5%E5%8F%8A%E5%A2%83%E5%A4%96%E7%96%AB%E6%83%85%E8%BE%93%E5%85%A5%E5%A4%84%E7%BD%AE%E5%BA%94%E5%AF%B9%E5%B7%A5%E4%BD%9C%EF%BC%8C%E5%90%AC%E5%8F%96%E4%B8%93%E5%AE%B6%E7%BB%84%E5%AF%B9%E5%BA%94%E6%80%A5%E5%93%8D%E5%BA%94%E3%80%81%E5%85%A8%E5%91%98%E6%A0%B8%E9%85%B8%E6%A3%80%E6%B5%8B%E3%80%81%E4%BA%BA%E5%91%98%E7%AE%A1%E6%8E%A7%E3%80%81%E6%B5%81%E8%A1%8C%E7%97%85%E5%AD%A6%E8%B0%83%E6%9F%A5%E3%80%81%E5%8C%BB%E7%96%97%E6%95%91%E6%B2%BB%E3%80%81%E4%BF%A1%E6%81%AF%E6%8A%A5%E9%80%81%E7%AD%89%E6%96%B9%E9%9D%A2%E5%B7%A5%E4%BD%9C%E7%9A%84%E6%84%8F%E8%A7%81%E5%BB%BA%E8%AE%AE%E3%80%82&speaker=1100&speed=5&audio=http%3A%2F%2Fdueros-short-video.bj.bcebos.com%2Fshort-video%2Fstop_1_24k.wav&from=news"
}
},
"playBehavior": "ENQUEUE"
}
}
}
通过分段持续播放和媒体转换播放这两种解决方案,在DuerOS上可以相对有效地解决TTS 文本播放的问题。
基于TTS 的媒体与文本合成播放
音视频资源可以有效地提升声音的品质和效果,TTS 文本播报能解决动态内容的播放问题,二者结合可以进一步提升技能的表现效果,提升服务的用户体验。
目前,DuerOS 提供的可行方式是在技能中使用SSML。DuerOS支持基础标签和扩展标签两种:基础标签里的所有标签都是SSML标准标签,相当于SSML标签的子集;扩展标签指DuerOS使用标准SSML语言定制的标签。
基础标签目前包括6种:
- speak:根标签
- audio:根据url合成已有音频
- say-as:设置数字、符号等的读法
- sub:替换目标单词
- silence:设置静音,在文本播报的开头或者结尾增加静音片段,最大10s
- phoneme:多音字注音
对于audio标签而言,音频以服务器可以访问的的地址给出,目前支持16K采样和24K采样,16bit,单声道,44字节头的wave格式文件。出于性能的约束,要求必须将相应的音频文件上传到百度云bos平台,使用bos提供的地址。单个请求最大限制3个并列的audio资源,单个audio资源大小限制为3M。
扩展标签目前包括4种:
- background:设置背景声
- say-as:在属性interpret-as加入两个新值,仅对英文有效
- poem:设置诗词,属性值 “wuyan”代表五言诗;“qiyan”代表七言诗;“songci”代表宋词
- space:在所包含文本的空格处生成停顿
其中background标签与audio 标签具有类似的性质,要求必须将相应的音频文件上传到百度云bos平台,使用bos提供的资源url地址。
关于在DuerOS 中如何使用SSML,以及SSML 的更多信息,可以参考《生动化你的表达——DuerOS中的SSML应用》。
小结
声音的播放是智能语音设备的基础能力,直接播放音视频资源是一种直接的声音播放方式,面向动态文本内容的声音播放一般采用TTS合成的方式实现。outputSpeech 是DuerOS中的TTS合成指令,repromt指令是outputSpeech 指令在特定场景下的变种。对于长文本的播放,DuerOS 提供了分段连续播放和媒体在线转换两种解决方案,DuerOS中的SSML 播放则可以相对有效地将媒体资源播放和TTS语音播放结合起来,从而使DuerOS 技能给用户带来更好的用户体验。
除了SSML之外,如何进一步提高语音合成的表现力呢?家中的孩童能否在“小度系列产品”上听到爸爸声音讲的故事呢?一系列与声音播放相关的新功能特性已经在路上了,DuerOS,值得期待!
(文中图片都来自网络,如有侵权,联系作者删除)
【关联阅读】
- https://dueros.baidu.com/dbp
- 聊聊“异步”
- 聊聊“全双工”
- 多模态交互之DPL 2.0
- 调试DuerOS的智能语音技能
- DuerOS的零编程技能实现
- 揭秘“语音交互”背后的AI硬核黑科技!
- 《智能语音时代》的读书笔记
- 再看语音交互设计
- 语音交互设计的一点认知
- 百度AI开发者大会之DuerOS 回顾
- AI开发者大会中的公开课解读——DuerOS技能开发与CFC编程
- AI开发者大会中的公开课解读——如何在DuerOS技能中实现用户支付购买
- DPL 来了——百度2019AI开发者大会DuerOS公开课解读之三
- 故事工厂在DuerOS技能开发中的应用——百度2019AI开发者大会DuerOS公开课解读之四
- 企业赋能 AI 服务生活
- DuerOS 走进初夏的成都
- 放心用吧!浅谈DuerOS的安全性
- 智能音箱场景下的性能优化
- 在校大学生能成为DuerOS 的独立开发者吗?
- 生动化你的表达——DuerOS中的SSML应用
- 用JavaScript打造AI应用-从Nodejs SDK 看DuerOS的技能开发
- 从Java SDK看DuerOS的技能开发
- 面向接口/协议?看DuerOS的技能开发
- 感知自然语言理解(NLU)
- 感知人工智能操作系统