聲如其聞,DuerOS中的聲音播放
智能語音設(shè)備的特點是語音交互成為人機(jī)交互的重要方式,智能語音設(shè)備的聲音輸出擺脫了預(yù)置的邏輯,成為了響應(yīng)式反饋,尤其是對聲音輸入的響應(yīng)。
那么, 技能是在智能語音設(shè)備上如何播放聲音的呢?
在DuerOS 中,主要提供了三種聲音的播放方式:音視頻媒體播放,TTS 合成的語音播放 和利用TTS合成的媒體及文本混合播放。
音視頻媒體播放
點播和資源類的技能都采用音視頻媒體播放的方式。當(dāng)技能收到用戶請求的意圖時,技能在響應(yīng)中返回音視頻媒體播放的指令和媒體資源的url,以及其他的相關(guān)內(nèi)容,在智能語音設(shè)備收到技能響應(yīng)的內(nèi)容后,即可播放技能制定的資源。
基于AudioPlayer的音頻播放
音頻資源既可以在有屏語音設(shè)備(如小度智能屏系列),也可以在無屏語音設(shè)備(如小度音箱系列)上播放,主要是通過AudioPlayer 相關(guān)指令完成的。
AudioPlayer指令從技能發(fā)送給DuerOS,對端上進(jìn)行音頻資源的播放控制。在DuerOS收到該指令后,會經(jīng)過一系列處理解析為端上對應(yīng)的播放指令。在云端下發(fā)AudioPlayer指令后,端上開始音頻資源播放操作。音頻資源播放會觸發(fā)一系列事件上報到云端。這些指令和事件分別是:
指令或事件 | 功能解釋 |
---|---|
AudioPlayer.Play指令 | 播放 |
AudioPlayer.Stop指令 | 停止 |
AudioPlayer.PlaybackStarted事件 | 播放開始 |
AudioPlayer.PlaybackStopped事件 | 播放暫停。 |
AudioPlayer.PlaybackFinished事件 | 播放結(jié)束,即一首歌播放完后上報的事件 |
AudioPlayer.PlaybackNearlyFinished事件 | 在播放即將結(jié)束時上報的事件 |
AudioPlayer.PlaybackFailed事件 | 當(dāng)設(shè)備端播放audio item發(fā)生錯誤時上報此事件 |
AudioPlayer.Play指令是驅(qū)動播放音頻的核心指令,一般地,使用Play指令時,response需要返回shouldEndSession值為true,使端上關(guān)閉麥克風(fēng)。消息示例如下:
- {
- "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}},
- ......
- ]
- }
- }
- }
當(dāng)持續(xù)播放音頻資源的時候,技能需要監(jiān)聽AudioPlayer.PlaybackNearlyFinished事件,通過playBehavior參數(shù)對播放的音頻隊列進(jìn)行調(diào)整,DuerOS 是通過資源迭代的方式完成多個音頻資源間連續(xù)播放的。
當(dāng)然,使用全雙工特性時,DuerOS的處理稍有不同,詳情可以參見《聊聊“全雙工”》。
基于VideoPlayer 的視頻播放
視頻資源只可以在有屏語音設(shè)備(如小度智能屏系列)上播放,主要是通過VideoPlayer 相關(guān)指令完成的。
VideoPlayer提供了VideoPlayer指令接口和VideoPlayer事件接口。VideoPlayer指令是技能向DuerOS發(fā)送的,對視頻進(jìn)行控制的指令,如播放指令、停止播放指令等。DuerOS收到指令后會轉(zhuǎn)化成端上能識別的播放指令,對視頻進(jìn)行相應(yīng)的控制。VideoPlayer事件是指在視頻播放過程中觸發(fā)一系列事件,DuerOS會將這些事件上報給技能,請求技能進(jìn)行處理。這些指令和事件分別是:
指令或事件 | 功能解釋 |
---|---|
VideoPlayer.Play指令 | 播放 |
VideoPlayer.Stop指令 | 停止 |
VideoPlayer.ClearQueue指令 | 用于清除video_player播放隊列 |
VideoPlayer.PlaybackStarted事件 | 播放開始 |
VideoPlayer.PlaybackStopped事件 | 播放暫停 |
VideoPlayer.PlaybackFinished事件 | 播放結(jié)束,即一個視頻播放完后上報的事件 |
VideoPlayer.PlaybackNearlyFinished事件 | 在播放即將結(jié)束時上報的事件。 |
VideoPlayer.ProgressReportIntervalElapsed事件 | 本事件用來統(tǒng)計播放情況。如果Play指令有progressReportIntervalInMilliseconds,則在播放對應(yīng)video item時,每隔此時間上報本事件。 |
VideoPlayer.ProgressReportDelayElapsed事件 | 本事件用來統(tǒng)計播放情況。如果Play指令中有progressReportDelayInMilliseconds,則對應(yīng)video item播放此時間長后會上報本事件 |
VideoPlayer.PlaybackStutterStarted事件 | 在PlaybackStarted事件之后,如果設(shè)備端緩沖視頻數(shù)據(jù)慢于播放速度時,會上報此事件。 |
VideoPlayer.PlaybackStutterFinished事件 | PlaybackStutterStarted事件之后,緩沖恢復(fù)到正常狀態(tài),可重新開始播放時端上會上報本事件。 |
VideoPlayer.PlaybackPaused事件 | 視頻播放時,如果發(fā)生對話/鬧鐘等行為,則視頻播放會暫停,此時,端上會上報PlaybackPaused事件和PlaybackResumed事件。 |
VideoPlayer.PlaybackResumed事件 | 視頻播放時,如果發(fā)生對話/鬧鐘等行為,則視頻播放會暫停,此時,端上會上報PlaybackPaused事件和PlaybackResumed事件。 |
VideoPlayer.PlaybackQueueCleared事件 | 設(shè)備端處理完ClearQueue指令后會上報此事件。 |
技能在返回VideoPlayer.Play指令時,對于show設(shè)備中控需要補(bǔ)充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持續(xù)播放視頻資源的方法與音頻資源持續(xù)播放的方式類似。
音視頻媒體播放是技能播放聲音的重要方法,在非資源類技能中也有著廣泛的應(yīng)用。
基于TTS 的語音播放
音視頻資源有著很好的表現(xiàn)力,但面向動態(tài)播報的內(nèi)容預(yù)先錄制的成本比較高,對于各種各樣的文本播報,一般采用TTS 合成的方式完成語音播放。
TTS 的語音播報指令——outputSpeech
outputSpeech是基于TTS的語音播報指令,表示本次返回結(jié)果中需要播報的語音信息,消息樣例如下:
- ......
- "response" : {
- "outputSpeech" : {
- "type" : "{{STRING}}",
- "text" : "{{STRING}}",
- "ssml" : "{{STRING}}",
- },
- "card" : {},
- "directives" : [],
- "expectSpeech": {{BOOLEAN}},
- "shouldEndSession" : {{BOOLEAN}}
- }
在outputSpeech的指令機(jī)構(gòu)中,TTS合成有如下的類型:
- PlainText:普通文本。
- SSML:一種結(jié)構(gòu)化語言,用于輔助描述語音發(fā)音聲調(diào)。
當(dāng)type取值為PlainText時,該字段為必選字段。長度不能超過256個字符。當(dāng)type為SSML時,該字段為必選字段,長度不能超過256個字符,SSML 會在下一節(jié)“基于TTS的媒體和文本合成播放”中在進(jìn)行描述。
outputSpeech 的變種——reprompt
在需要用戶輸入時,如果用戶離開了,麥克風(fēng)沒有進(jìn)行語音的輸入,或用戶輸入的語音請求系統(tǒng)無法解析成技能的任何意圖,則可以使用reprompt來播放預(yù)置的內(nèi)容。
reprompt.outputSpeech參數(shù)定義與上述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指令的變種,是一種應(yīng)用在特定場景的TTS 語音播報。
長文本的播放
TTS 播報中對文本長度的限制往往被人們所詬病。話術(shù)太長往往會被截斷,或者在播放的過程中,由于長時間沒有用戶交互導(dǎo)致session 被關(guān)閉,現(xiàn)象就是在TTS 播放一段時間后,技能退出了,用戶需要重新打開技能才能繼續(xù)在技能中交互。
針對長文本的播報,目前DuerOS 提供了兩種解決方案:分段持續(xù)播放和媒體轉(zhuǎn)換播放。
分段持續(xù)播放
既然長文本在播放過程中無法維持session, 那么我們可以把長文本分割成多個短文本,在前一個短文本播放完畢之后,在繼續(xù)播放下一個短文本,如此類推,實現(xiàn)長文本的持續(xù)完整播放。
具體的方式是應(yīng)用DuerOS 的StreamResponse 特性,StreamResponse 定義了技能在執(zhí)行過程中,下發(fā)部分結(jié)果到端上執(zhí)行,在端執(zhí)行完這部分結(jié)果后,上報事件以獲取技能的其它結(jié)果,適應(yīng)于以下場景:技能返回shouldEndSession=false和StreamResponse.SendPart指令。
StreamResponse.SendPart指令告知DuerOS平臺本次只返回部分滿足結(jié)果,期望在StreamResponse.NextRequired事件時繼續(xù)返回結(jié)果。技能在返回本指令時,如果shouldEndSession=true,DuerOS平臺將忽略本指令。技能在返回本指令時,如果shouldEndSession=false,DuerOS平臺在本次結(jié)果執(zhí)行后,下發(fā)StreamResponse.NextRequired事件給技能,消息樣例如下:
- {
- "type": "StreamResponse.SendPart",
- "token": "{{STRING}}"
- }
技能在收到StreamResponse.NextRequired事件后,返回計劃內(nèi)的下一個響應(yīng)結(jié)果, request中事件樣例如下:
- {
- "type": "StreamResponse.NextRequired",
- "requestId": "{{STRING}}",
- "timestamp": "{{STRING}}",
- "token": "{{STRING}}"
- }
StreamResponse.SendPart指令 和StreamResponse.NextRequired事件組成了一種類似迭代器的機(jī)制,使技能可以完成長文本的分段發(fā)送。
當(dāng)然,StreamResponse的應(yīng)用場景還有很多,長文本的TTS播報只是其中的一個典型應(yīng)用而已。
媒體轉(zhuǎn)換播放
既然長文本的TTS播報受限于長度,那能否將長文本實時在線轉(zhuǎn)換為音頻資源,然后采用AudioPlayer.play 指令播放來突破文本長度的限制呢?答案是肯定的。
DuerOS 提供一種在線將文本轉(zhuǎn)化成音頻資源的方法,在技能返回Play指令的時候,content為指定schema的tts,DuerOS會進(jìn)行及時的批量替換。
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=標(biāo)明技能來源&expire=86400
其中參數(shù)需要urlencode, php使用rawurlencode函數(shù)即可,其他編程語言類似。
- text:文本長度5k以內(nèi)
- from: botid,需要傳相關(guān)來源
- expire: 音頻地址的保存時間,最多不超過7天,單位是秒
使用每天轉(zhuǎn)換播放長文本的示例如下:
- {
- "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"
- }
- }
- }
通過分段持續(xù)播放和媒體轉(zhuǎn)換播放這兩種解決方案,在DuerOS上可以相對有效地解決TTS 文本播放的問題。
基于TTS 的媒體與文本合成播放
音視頻資源可以有效地提升聲音的品質(zhì)和效果,TTS 文本播報能解決動態(tài)內(nèi)容的播放問題,二者結(jié)合可以進(jìn)一步提升技能的表現(xiàn)效果,提升服務(wù)的用戶體驗。
目前,DuerOS 提供的可行方式是在技能中使用SSML。DuerOS支持基礎(chǔ)標(biāo)簽和擴(kuò)展標(biāo)簽兩種:基礎(chǔ)標(biāo)簽里的所有標(biāo)簽都是SSML標(biāo)準(zhǔn)標(biāo)簽,相當(dāng)于SSML標(biāo)簽的子集;擴(kuò)展標(biāo)簽指DuerOS使用標(biāo)準(zhǔn)SSML語言定制的標(biāo)簽。
基礎(chǔ)標(biāo)簽?zāi)壳鞍?種:
- speak:根標(biāo)簽
- audio:根據(jù)url合成已有音頻
- say-as:設(shè)置數(shù)字、符號等的讀法
- sub:替換目標(biāo)單詞
- silence:設(shè)置靜音,在文本播報的開頭或者結(jié)尾增加靜音片段,最大10s
- phoneme:多音字注音
對于audio標(biāo)簽而言,音頻以服務(wù)器可以訪問的的地址給出,目前支持16K采樣和24K采樣,16bit,單聲道,44字節(jié)頭的wave格式文件。出于性能的約束,要求必須將相應(yīng)的音頻文件上傳到百度云bos平臺,使用bos提供的地址。單個請求最大限制3個并列的audio資源,單個audio資源大小限制為3M。
擴(kuò)展標(biāo)簽?zāi)壳鞍?種:
- background:設(shè)置背景聲
- say-as:在屬性interpret-as加入兩個新值,僅對英文有效
- poem:設(shè)置詩詞,屬性值 “wuyan”代表五言詩;“qiyan”代表七言詩;“songci”代表宋詞
- space:在所包含文本的空格處生成停頓
其中background標(biāo)簽與audio 標(biāo)簽具有類似的性質(zhì),要求必須將相應(yīng)的音頻文件上傳到百度云bos平臺,使用bos提供的資源url地址。
小結(jié)
聲音的播放是智能語音設(shè)備的基礎(chǔ)能力,直接播放音視頻資源是一種直接的聲音播放方式,面向動態(tài)文本內(nèi)容的聲音播放一般采用TTS合成的方式實現(xiàn)。outputSpeech 是DuerOS中的TTS合成指令,repromt指令是outputSpeech 指令在特定場景下的變種。對于長文本的播放,DuerOS 提供了分段連續(xù)播放和媒體在線轉(zhuǎn)換兩種解決方案,DuerOS中的SSML 播放則可以相對有效地將媒體資源播放和TTS語音播放結(jié)合起來,從而使DuerOS 技能給用戶帶來更好的用戶體驗。
除了SSML之外,如何進(jìn)一步提高語音合成的表現(xiàn)力呢?家中的孩童能否在“小度系列產(chǎn)品”上聽到爸爸聲音講的故事呢?一系列與聲音播放相關(guān)的新功能特性已經(jīng)在路上了,DuerOS,值得期待!
【本文來自51CTO專欄作者“老曹”的原創(chuàng)文章,作者微信公眾號:喔家ArchiSelf,id:wrieless-com】