
智能语音设备的声如其闻特点是语音交互成为人机交互的重要方式,智能语音设备的声音声音输出摆脱了预置的逻辑,成为了响应式反馈,播放尤其是声如其闻对声音输入的响应。
那么,声音 技能是播放在智能语音设备上如何播放声音的呢?
在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,使端上关闭麦克风。消息示例如下:
{ "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指令用于播放器界面的渲染,消息样例如下:
{ "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的语音播报指令,表示本次返回结果中需要播报的语音信息,消息样例如下:
...... "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的定义一致,消息样例如下:
"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事件给技能,消息样例如下:
{ "type": "StreamResponse.SendPart", "token": "{ { STRING}}" } 技能在收到StreamResponse.NextRequired事件后,返回计划内的下一个响应结果, request中事件样例如下:
{ "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=%E5%BA%A6%E7%A7%98%E6%8F%90%E9%86%92%E4%BD%A0%E4%BB%8A%E5%A4%A9%E6%98%AF%E5%A6%88%E5%A6%88%E7%9A%84%E7%94%9F%E6%97%A5&from=标明技能来源&expire=86400
其中参数需要urlencode, php使用rawurlencode函数即可,其他编程语言类似。
text:文本长度5k以内 from: botid,需要传相关来源 expire: 音频地址的保存时间,最多不超过7天,单位是秒 使用每天转换播放长文本的示例如下:
{ "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=%25E9%2598%25AE%25E6%2588%2590%25E5%258F%2591%25E8%25B5%25B4%25E7%2591%259E%25E4%25B8%25BD%25E7%259D%25A3%25E5%25AF%25BC%25E6%25A3%2580%25E6%259F%25A5%25E7%2596%25AB%25E6%2583%2585%25E9%2598%25B2%25E6%258E%25A7%25EF%25BC%259A%25E5%258A%25A1%25E5%25BF%25853%25E6%2597%25A5%25E5%2586%2585%25E5%25AE%258C%25E6%2588%2590%25E5%2585%25A8%25E5%2591%2598%25E6%25A0%25B8%25E9%2585%25B8%25E6%25A3%2580%25E6%25B5%258B%25E3%2580%2582%25E6%258E%258C%25E4%25B8%258A%25E6%2598%25A5%25E5%259F%258E%25E8%25AE%25AF9%25E6%259C%258814%25E6%2597%25A5%25EF%25BC%258C%25E4%25BA%2591%25E5%258D%2597%25E7%259C%2581%25E7%259C%2581%25E5%25A7%2594%25E5%2589%25AF%25E4%25B9%25A6%25E8%25AE%25B0%25E3%2580%2581%25E7%259C%2581%25E9%2595%25BF%25E3%2580%2581%25E7%259C%2581%25E5%25A7%2594%25E7%259C%2581%25E6%2594%25BF%25E5%25BA%259C%25E5%25BA%2594%25E5%25AF%25B9%25E6%2596%25B0%25E5%2586%25A0%25E8%2582%25BA%25E7%2582%258E%25E7%2596%25AB%25E6%2583%2585%25E5%25B7%25A5%25E4%25BD%259C%25E9%25A2%2586%25E5%25AF%25BC%25E5%25B0%258F%25E7%25BB%2584%25E7%25BB%2584%25E9%2595%25BF%25E9%2598%25AE%25E6%2588%2590%25E5%258F%2591%25E8%25B5%25B6%25E8%25B5%25B4%25E5%25BE%25B7%25E5%25AE%258F%25E5%2582%25A3%25E6%2597%258F%25E6%2599%25AF%25E9%25A2%2587%25E6%2597%258F%25E8%2587%25AA%25E6%25B2%25BB%25E5%25B7%259E%25E7%2591%259E%25E4%25B8%25BD%25E5%25B8%2582%25E7%259D%25A3%25E5%25AF%25BC%25E6%25A3%2580%25E6%259F%25A5%25E7%2596%25AB%25E6%2583%2585%25E9%2598%25B2%25E6%258E%25A7%25E5%25B7%25A5%25E4%25BD%259C%25EF%25BC%258C%25E5%25AF%25B9%25E5%25BD%2593%25E5%2589%258D%25E7%2596%25AB%25E6%2583%2585%25E9%2598%25B2%25E6%258E%25A7%25E5%25BD%25A2%25E5%258A%25BF%25E8%25BF%259B%25E8%25A1%258C%25E5%2586%258D%25E5%2588%2586%25E6%259E%2590%25E3%2580%2581%25E5%2586%258D%25E7%25A0%2594%25E5%2588%25A4%25EF%25BC%258C%25E5%25AF%25B9%25E8%25BE%25B9%25E5%25A2%2583%25E7%25AE%25A1%25E6%258E%25A7%25E5%2592%258C%25E9%2598%25B2%25E6%258E%25A7%25E6%258E%25AA%25E6%2596%25BD%25E8%25BF%259B%25E8%25A1%258C%25E5%2586%258D%25E7%25A0%2594%25E7%25A9%25B6%25E3%2580%2581%25E5%2586%258D%25E5%25AE%2589%25E6%258E%2592%25E3%2580%2582%25E5%25BD%2593%25E6%2599%259A%25EF%25BC%258C%25E9%2598%25AE%25E6%2588%2590%25E5%258F%2591%25E4%25B8%258E%25E5%259B%25BD%25E5%25AE%25B6%25E5%258D%25AB%25E7%2594%259F%25E5%2581%25A5%25E5%25BA%25B7%25E5%25A7%2594%25E8%25B5%25B4%25E4%25BA%2591%25E5%258D%2597%25E4%25B8%2593%25E5%25AE%25B6%25E7%25BB%2584%25E8%25BF%259B%25E8%25A1%258C%25E5%25B7%25A5%25E4%25BD%259C%25E5%25BA%25A7%25E8%25B0%2588%25EF%25BC%258C%25E4%25BB%258B%25E7%25BB%258D%25E8%25BE%25B9%25E5%25A2%2583%25E7%2596%25AB%25E6%2583%2585%25E9%2598%25B2%25E6%258E%25A7%25E6%2583%2585%25E5%2586%25B5%25E5%258F%258A%25E5%25A2%2583%25E5%25A4%2596%25E7%2596%25AB%25E6%2583%2585%25E8%25BE%2593%25E5%2585%25A5%25E5%25A4%2584%25E7%25BD%25AE%25E5%25BA%2594%25E5%25AF%25B9%25E5%25B7%25A5%25E4%25BD%259C%25EF%25BC%258C%25E5%2590%25AC%25E5%258F%2596%25E4%25B8%2593%25E5%25AE%25B6%25E7%25BB%2584%25E5%25AF%25B9%25E5%25BA%2594%25E6%2580%25A5%25E5%2593%258D%25E5%25BA%2594%25E3%2580%2581%25E5%2585%25A8%25E5%2591%2598%25E6%25A0%25B8%25E9%2585%25B8%25E6%25A3%2580%25E6%25B5%258B%25E3%2580%2581%25E4%25BA%25BA%25E5%2591%2598%25E7%25AE%25A1%25E6%258E%25A7%25E3%2580%2581%25E6%25B5%2581%25E8%25A1%258C%25E7%2597%2585%25E5%25AD%25A6%25E8%25B0%2583%25E6%259F%25A5%25E3%2580%2581%25E5%258C%25BB%25E7%2596%2597%25E6%2595%2591%25E6%25B2%25BB%25E3%2580%2581%25E4%25BF%25A1%25E6%2581%25AF%25E6%258A%25A5%25E9%2580%2581%25E7%25AD%2589%25E6%2596%25B9%25E9%259D%25A2%25E5%25B7%25A5%25E4%25BD%259C%25E7%259A%2584%25E6%2584%258F%25E8%25A7%2581%25E5%25BB%25BA%25E8%25AE%25AE%25E3%2580%2582&speaker=1100&speed=5&audio=http%253A%252F%252Fdueros-short-video.bj.bcebos.com%252Fshort-video%252Fstop_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地址。
小结
声音的播放是智能语音设备的基础能力,直接播放音视频资源是一种直接的声音播放方式,面向动态文本内容的声音播放一般采用TTS合成的方式实现。outputSpeech 是DuerOS中的TTS合成指令,repromt指令是outputSpeech 指令在特定场景下的变种。对于长文本的播放,DuerOS 提供了分段连续播放和媒体在线转换两种解决方案,DuerOS中的SSML 播放则可以相对有效地将媒体资源播放和TTS语音播放结合起来,从而使DuerOS 技能给用户带来更好的用户体验。
除了SSML之外,如何进一步提高语音合成的表现力呢?家中的孩童能否在“小度系列产品”上听到爸爸声音讲的故事呢?一系列与声音播放相关的新功能特性已经在路上了,DuerOS,值得期待!
【本文来自专栏作者“老曹”的原创文章,作者微信公众号:喔家ArchiSelf,id:wrieless-com】
戳这里,看该作者更多好文