単語のタイミングの生成

単語境界などのユーザー指定の位置や、入力テキストのすべての単語に関して、IBM Watson® Text to Speech サービスの WebSocket インターフェースを使用してタイミング情報を取得することができます。

  • 音声でマーカーが出現する時刻を識別するために、入力テキストに SSML の<mark>要素を含めます。
  • 入力テキストのすべてのストリングのタイミング情報を取得する場合は、JSON テキスト・メッセージの timings パラメーターを指定する。

タイミング情報は、音声と入力テキストを同期する際に役立ちます。 例えば、アバターのジェスチャーやロボットのジェスチャーを合成音声のコンテンツと整合させることができます。

<mark>エレメントとtimingsパラメーターは、WebSocket インターフェースでのみ使用できます。HTTP インターフェースでは使用できません。 また、日本語の入力テキストでは、timings パラメーターはサポートされていません。

サービスが単語のタイミングを戻す仕組み

マークまたは単語のタイミング情報を戻すために、サービスは、独立したバイナリー・ストリームとテキスト・ストリームを多重送信して応答を構成します。

  • <mark> エレメントごとに、サービスは JSON テキスト・メッセージを返します。 各メッセージには、合成音声の開始時を基点として、マークが出現する正確な時間が示されています。
  • すべてのストリングの単語のタイミングを戻す場合は、サービスは JSON テキスト・メッセージを 1 つ以上戻します。 各メッセージには、各単語およびその (合成音声の開始時を基点とした) 開始時間と終了時間を示す配列が含まれています。

サービスから送信されるバイナリー・ストリームとテキスト・ストリームは互いに独立しています。 そのため、サービスから送信する音声チャンクの数や、ユーザーがテキスト・メッセージと音声メッセージを受け取るタイミングをサービスで制御することはほとんどできません。 例えば、音声合成が音声圧縮よりも速い場合は、音声がまったく到着していないのにテキスト・メッセージがすべて到着する可能性があります。

実際問題として、サービスから送信される音声チャンクの数は予測不能です。各テキスト・メッセージの前後に複数の音声チャンクが送信される場合もあります。 また、特定のマークまたは単語のタイミング情報の前と後に出現する音声データが、単一のバイナリー・チャンクに含まれる場合もあります。

ただし、タイミング情報を含むテキスト・メッセージは、必ず、対応する音声を含むバイナリー・チャンクより先に到着します。 さらに、音声メッセージは常に順番に到着するため、バイナリー結果から合成されたテキストの完全で正確な音声を構成することができます。

SSML マークの指定

オプションの SSML<mark> 要素は、合成されるテキストにマーカーを配置する空のタグです。 <mark>要素の前にあるすべてのテキストが合成されると、クライアントに通知されます。

この要素は、マークを一意的に識別するストリングを指定する単一の name 属性を受け入れます。 この名前の先頭は英数字にする必要があります。 サービスは、マークの名前と、合成音声の開始時を基点としてマークが出現する時間を返します。 入力テキストには任意の数のマークを指定できます。

以下の JavaScript コードのスニペットには、hereという名前の<mark>エレメントのインスタンスが含まれています。

function onOpen(evt) {
  var message = {
    text: 'Hello <mark name="here"/> world',
    accept: '*/*'
  };
  websocket.send(JSON.stringify(message));
}

マークの前にあるテキストの合成が終了すると、サービスは、マークの名前とそのマークが音声の中で出現する時間 (秒単位) を示す、テキスト・メッセージを送信します。

{
  "marks": [
    ["here", 0.501]
  ]
}

タイミング情報を含むテキスト・メッセージは、必ず、そのマークの位置を含む音声チャンクより先に到着します。

すべての単語のタイミング情報の要求

要求で JSON オブジェクトのオプションの timings パラメーターをサービスに渡すと、入力テキストのすべてのストリングのタイミング情報が返されます。 この利便性により、入力の単語ごとに SSML の<mark>要素を指定する必要がなくなります。 ストリング words を含む配列を渡して、単語のタイミング情報を要求します。 空の配列を渡したり、パラメーターを省略したりすると、タイミング情報は返されません。

サービスは、個々の<mark>エレメントのタイミング情報を返すのと同じ方法で、WebSocket 接続を介して単語のタイミングを返します。 1 つ以上の 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
    ]
  ]
}

この応答は例にすぎません。 サービスは、入力のタイミング情報を含むテキスト・メッセージを 1 つ以上返すことがあります。 また、入力の単語ごとに別個のテキスト・メッセージを返すこともできます。 さらには、複数のメッセージの間に、バイナリーの音声チャンクの応答を挟む場合もあります。 ただし、単語のタイミング情報を含むテキスト・メッセージ送信は、必ず、その単語を含む音声チャンクより先に到着します。

プレーン・テキストのタイミング

サービスの合成プロセスには、数値、日付、時間、通貨額、頭字語、および略語を読み上げるテキスト正規化ステップが含まれます。 この結果は、これらのストリングがどのように発話されるかに対応します。 例えば、ストリング $200twohundred、および dollars の 3 つの単語として発話されます。 単語のタイミング情報は、音声を入力テキストと同期するために使用されるため、サービスは、入力の正規化されていないスペルに対応するタイミング情報を返します。

例えば、以下の入力テキストについて考えます。

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 つの個別の単語 (minuseightyninepointtwo) で発話されますが、テキスト・メッセージには、単一ユニットとしてストリングのタイミング情報である 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" のタイミング情報を返します。 サービスは、正規化ステップでこの単語を 1 文字ずつつづります。 応答に含まれる単語のタイミング情報には、文字 "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.
    

    この場合、サービスは、ストリング「**」、「<phoneme>」、「は、」、「熟しています。」のタイミング情報を返します。

  • <sub>は、要素のalias属性に含まれるテキストを、発話音声の開始<sub>タグと終了タグで囲まれたテキストに置換します。 例えば、以下の入力には、単一の<sub>タグが含まれています。tag:

    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 についてを参照してください。

マーク要素の例

ここでは、クライアントとサービスの間のシンプルな WebSocket セッションを示す例を紹介します。 これらの例は、接続を開く操作ではなく、データの交換に焦点を当てています。 クライアントは、SIMPLEEXAMPLEという名前の 2 つの<mark>要素を含むテキスト・メッセージを送信し、音声を WAV 形式で返すように要求します。

{
  "text": "This is a <mark name=\"SIMPLE\"/>simple <mark name=\"EXAMPLE\"/> example.",
  "accept": "audio/wav"
}

まず、サービスは音声フォーマットを確認するメッセージを送信します。 次に、結果と一緒に複数のメッセージを送信します。 サービスからクライアントに送信される音声チャンクの数や、テキスト・メッセージと音声メッセージの送信順序をサービスで保証することはできません。

次に示す両方の応答が考えられます。 どちらの場合も、サービスは、バイナリー・ストリーム中のマークの位置を示すテキスト・メッセージを 2 つ送信します。 ただし、音声を含むバイナリー・メッセージが送信される数は予測不能です。 マークのタイミング情報は、必ず、そのマークの位置を含む音声チャンクより先に到着します。

  • *最初の応答例では、以下のようになります。*テキスト・メッセージには、複数の音声メッセージが混在しています。

    {
      "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....
    
  • *2 番目の応答例では、*テキスト・メッセージは、音声メッセージの前に到着します。

    {
      "binary_streams": [
        "content_type": "audio/wav"}
      ]
    }
    {
      "marks": [
        [
          "SIMPLE", 0.784
        ]
      ]
    }
    {
      "marks": [
        [
          "EXAMPLE", 1.003
        ]
      ]
    }
    ... One or more chunks of binary audio....