生成字计时
您可以使用 IBM Watson® Text to Speech 服务的 WebSocket 接口来获取用户指定位置(例如词边界)或输入文本中所有词的计时信息:
- 在输入文本中包含 SSML
<mark>
元素,以标识标记在音频中出现的时间。 - 在 JSON 文本消息中指定
timings
参数,可获取输入文本中所有字符串的计时信息。
计时信息对于同步音频和输入文本非常有用。 例如,您可以将头像或机器人的手势与合成语音的内容进行协调。
<mark>
元素和 timings
参数仅适用于 WebSocket 接口,而不适用于 HTTP 接口。 而且,日语输入文本不支持 timings
参数。
服务如何返回词计时
要返回标记或词计时信息,服务会多路复用独立二进制流和文本流以构造其响应:
- 对于每个
<mark
> 元素,服务会返回一条 JSON 文本消息。 每条消息指示从合成音频开头起,遇到 mark 时的确切时间。 - 对于所有字符串的词计时,服务会返回一条或多条 JSON 文本消息。 每条消息都包含词的数组,以及从合成音频开头起,这些词的开始时间和结束时间。
服务发送的二进制流和文本流是独立的。 因此,服务对其传递的音频块数,以及对用户接收文本和音频消息的时间几乎没有控制权。 例如,如果音频合成速度比压缩速度更快,那么所有文本消息可能都会先于任何音频到达。
实际上,服务可以发送任意数量的音频块,包括在每个文本消息之前和之后发送多个音频块。 还可以使单个二进制块包含位于 mark 或词的计时信息之前和之后的音频数据。
但是,包含计时信息的文本消息始终会先于包含相应音频的二进制块到达。 此外,音频消息始终按顺序到达,以便您可以从二进制结果构造合成文本的完整和准确音频。
指定 SSML mark
可选 SSML <mark>
元素是一个空标记,用于将标记放入要合成的文本中。 当已合成 <mark>
元素之前的所有文本时,将通知客户机。
此元素接受单个 name
属性,用于指定唯一标识 mark 的字符串。 名称必须以字母数字字符开头。 服务会返回名称,以及从合成音频的开头起出现 mark 的时间。 可以在输入文本中包含任意数量的 mark。
以下 JavaScript 代码片段包含名为 here
的 <mark>
元素的实例:
function onOpen(evt) {
var message = {
text: 'Hello <mark name="here"/> world',
accept: '*/*'
};
websocket.send(JSON.stringify(message));
}
完成对 mark 前面的文本的合成时,服务会发送一条文本消息,其中指明 mark 的名称以及在音频中遇到该 mark 的时间(以秒为单位):
{
"marks": [
["here", 0.501]
]
}
包含计时信息的文本消息始终会先于包含 mark 位置的音频块到达。
请求所有词的词计时
针对请求传递给服务的 JSON 对象的可选 timings
参数会返回输入文本中所有字符串的计时信息。 此便利性使您无需为输入的每个词指定 SSML <mark>
元素。 请传递包含字符串 words
的数组来请求词计时。 传递空数组或省略该参数时,不会收到任何计时信息。
该服务通过 WebSocket 连接返回词计时的方式与它返回各个 <mark>
元素的计时信息的方式相同。 服务会返回一条或多条 JSON 文本消息。 每条消息都包含词的数组,以及从合成音频开头起,这些词的开始时间和结束时间(以秒为单位)。 例如,以下示例将请求词计时信息:
function onOpen(evt) {
var message = {
text: 'I have a pet bird.',
accept: '*/*',
timings: ['words']
};
websocket.send(JSON.stringify(message));
}
在响应中,服务可以返回以下文本消息:
{
"words": [
[
"I", 0.0, 0.157
],
[
"have", 0.157, 0.321
],
[
"a", 0.321, 0.406
]
]
}
{
"words": [
[
"pet", 0.406, 0.731
],
[
"bird.", 0.731, 1.049
]
]
}
该响应仅仅是示例。 服务可能会返回一条或多条文本消息,其中包含输入的计时信息。 它还可以针对输入的每个词返回单独的文本消息。 此外,消息还可能与包含二进制音频块的响应交错在一起。 但是,包含词计时信息的文本消息始终会先于包含该词的音频块到达。
纯文本计时
服务的合成过程涉及文本规范化步骤,在此步骤中会拼读数字、日期、时间、金额、首字母缩略词和缩写。 结果对应于如何读此类字符串。 例如,字符串 $200 会读作三个词:two、hundred 和 dollars。 由于词计时信息用于将音频与输入文本同步,因此服务会返回与输入的非规范化拼写对应的计时信息。
例如,假设有以下输入文本:
The coldest recorded temperature is -89.2 degrees Celsius in Antarctica on July 21, 1983!
服务将返回以下字符串的音频计时:
"The"、"coldest"、"recorded"、"temperature"、"is"、"-89.2"、"degrees"、"Celsius"、"in"、"Antarctica"、"on"、"July"、"21,"、"1983!"
虽然 "-89.2" 在音频中读作 5 个独立的词(minus、eighty、nine、point 和 two),但文本消息将此字符串作为一个单元提供其计时信息,开始时间为读 minus 的时间,结束时间为读 two 的时间。
与上一个示例一样,非规范化字符串也可以包含标点符号。 服务在其返回的文本消息中会包含词前面或后面的标点以及计时。 例如,字符串 "21 ," 和 "1983!" 包含服务在其文本消息中返回的标点符号。 虽然标点会生成静默,但该词的音频计时不会包含该静默时间。
例如,假设输入文本包含以下条件语句:
If it is sunny, I will go to the beach.
服务将返回输入的所有字符串 (包括 "sunny ," 和 "Beach.") 的计时信息,这两个字符串都以生成静默的标点符号结尾。 但是, "sunny ," 的计时信息不包含逗号生成的静默,而 "Beach." 的计时信息不包含时间段的静默。 这些信息仅反映所读字符串的计时。
SSML 文本计时
服务合成纯文本时,会返回除空格以外的所有输入字符,以作为词计时响应中字符串的一部分。 但对于 SSML 却并非如此,因为某些 SSML 元素不会生成音频。 以下列表概述了可能影响词计时信息的 SSML 元素:
-
<say-as>
指示在规范化步骤中如何处理在打开和关闭<say-as>
标记之间包含的文本。 各属性指定如何读嵌入文本。 以下示例指示如何读日期:The baby was born on <say-as interpret-as="date" format="mdy">3/4/2016</say-as>.
服务会返回以下字符串的计时信息:"The"、"baby"、"was"、"born"、"on" 和 "3/4/2016."。 服务会将字符串 "3/4/2016" 规范化为 "march fourth two thousand sixteen"。 该字符串的词计时信息反映的开始时间是读 "march" 的时间,结束时间是读 "sixteen" 的时间。
以下示例指示要拼读词
Hello
:<say-as interpret-as="letters">Hello</say-as>.
服务会返回字符串 "Hello." 的计时信息。 服务在规范化步骤中,将逐字母拼读该词。 响应中的词计时信息反映的开始时间是读字母 "h" 的时间,结束时间是读字母 "o" 的时间。
-
<phoneme>
提供了包含在开始和结束<phoneme>
标记中的文本的发音。 但文本和结束标记都是可选的。 以下示例包括嵌入文本和结束标记:The <phoneme alphabet="ibm" ph=".0tx.1me.0fo">tomato</phoneme> was ripe.
服务会返回以下字符串的计时信息:"The"、"tomato"、"was" 和 "ripe."
相反,以下示例提供了一个没有嵌入文本和结束标记的一元
<phoneme>
元素:The <phoneme alphabet="ibm" ph=".0tx.1me.0fo"/> was ripe.
在这种情况下,服务将返回以下字符串的计时信息: "The" , "<phoneme>" , "是" , "成熟。"
-
<sub>
将元素的alias
属性中包含的文本替换为在语音音频中的开头和结尾<sub>
标记之间包含的文本。 例如,以下输入包含单个<sub>
标记:I work at <sub alias="International Business Machines">IBM</sub>.
该服务生成以下字符串的计时信息: "I" , "work" , "at" , "IBM."。 服务会将字符串 "IBM" 规范化为 "International Business Machines"。 该字符串的词计时信息反映的开始时间是读 "International" 的时间,结束时间是读 "Machines" 的时间。
-
<break>
在口语文本中插入暂停。 该服务会将词计时中产生的静默反映为<break>
元素之前的词的结束时间与该元素之后的词的开始时间之间的间隔。 -
<paragraph>
(或<p>
) 可以向音频添加静默。 服务不会返回静默的计时信息。 -
<sentence>
(或<s>
) 可以向音频添加静默。 服务不会返回静默的计时信息。
列表中未提及的 SSML 元素不会影响词计时信息。 有关服务对 SSML 的支持的更多信息,请参阅 了解 SSML。
使用 mark 元素的示例
以下示例说明了客户机与服务之间的简单 WebSocket 会话。 这些示例侧重于数据交换,而不是打开连接。 客户机发送包含两个名为 SIMPLE
和 EXAMPLE
的 <mark>
元素的文本消息,并请求以 WAV 格式返回音频:
{
"text": "This is a <mark name=\"SIMPLE\"/>simple <mark name=\"EXAMPLE\"/> example.",
"accept": "audio/wav"
}
服务首先会发送消息来确认音频格式。 然后,会发送包含结果的多条消息。 服务无法保证发送给客户机的音频块数,也无法保证传递文本和音频消息的顺序。
因此,以下两种响应都是可能的。 无论生成哪种响应,服务都会发送两条文本消息,用于标识 mark 在二进制流中的位置。 但是,服务会发送任意数量的包含音频的二进制消息。 mark 的计时信息始终会先于包含 mark 位置的音频块到达。
-
在第一个示例响应中, 文本消息与多条音频消息交织在一起:
{ "binary_streams": [ { "content_type": "audio/wav" } ] } ... One or more chunks of binary audio. All audio precedes the SIMPLE mark.... { "marks": [ [ "SIMPLE", 0.784 ] ] } ... One or more chunks of binary audio audio can precede and follow the SIMPLE mark. All audio precedes the EXAMPLE mark.... { "marks": [ [ "EXAMPLE", 1.003 ] ] } ... One or more chunks of binary audio. Audio can precede and follow the EXAMPLE mark....
-
在第二个示例响应中, 文本消息在任何音频消息之前到达:
{ "binary_streams": [ "content_type": "audio/wav"} ] } { "marks": [ [ "SIMPLE", 0.784 ] ] } { "marks": [ [ "EXAMPLE", 1.003 ] ] } ... One or more chunks of binary audio....