声如其闻,DuerOS中的声音播放

2020-10-10 15:02:14 浏览数 (1)

智能语音设备的特点是语音交互成为人机交互的重要方式,智能语音设备的声音输出摆脱了预置的逻辑,成为了响应式反馈,尤其是对声音输入的响应。在智能语音设备上,基于语音交互的服务被称为技能,通过诸如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)
  • 感知人工智能操作系统

0 人点赞