リアルタイムで対話をストリーミング

このガイドでは、Text to Dialogue WebSocket経由でEleven v3の対話オーディオをストリーミングする方法を説明します。

Text to Dialogue WebSocket(/v1/text-to-dialogue/stream-input)では、ダイアログの行を送信しながらbase64エンコードされたオーディオチャンクを受信する間、1つの接続を開いたままにします。これはEleven v3のダイアログモデル専用です(model_idはeleven_v3で始まる必要があります)。

このガイドでは、Text to Dialogue WebSocketについて説明します。Flash、Multilingual v2、またはその他の v3以外のTTSモデルでは、Realtime TTS WebSocketを使用してください。両方のプロトコルを 並べて比較した概要は、Text to SpeechとText to Dialogueの WebSocketを参照してください。

要件

  • APIキーを持つElevenLabsアカウント(認証)。
  • APIキーにはText to Speech権限が必要です。
  • マシンにPythonまたはNode.jsがインストールされていること。

セットアップ

pip install python-dotenv websockets

.envファイルを作成します。

.env
ELEVENLABS_API_KEY=your_elevenlabs_api_key_here

ボイスライブラリからvoice IDを選択します。以下の例では、接続ごとに登録できるボイスが1つのeleven_v3_conversationalを使用します。

WebSocketを開く

model_idやoutput_formatなどのクエリパラメータを指定して、wss://api.elevenlabs.io/v1/text-to-dialogue/stream-inputに接続します。APIキーはxi-api-keyヘッダー、または最初のJSONメッセージで送信できます(ここでは言語間でパターンを統一するため、本文内で示します)。

import asyncio
import base64
import json
import os
from dotenv import load_dotenv
import websockets
load_dotenv()
ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY")
VOICE_ID = "21m00Tcm4TlvDq8ikWAM"
MODEL_ID = "eleven_v3_conversational"
URI = (
"wss://api.elevenlabs.io/v1/text-to-dialogue/stream-input"
f"?model_id={MODEL_ID}&output_format=mp3_44100_128"
)

ボイスを登録してテキストをストリーミングする

voices(必須)と、xi-api-keyヘッダーを設定していない場合はxi_api_keyを含む最初のメッセージを送信します。次に、inputsを含むフレームを1つ以上送信します。各項目にはtext、voice_id、オプションのnew_turnを指定します。

サーバーは十分なコンテキスト(約40文字かつ8語)が得られるまでテキストをバッファリングし、その後audioチャンクを送信します。レスポンスフィールドにはsnake_caseを使用します(例:is_final)。

async def stream_dialogue():
async with websockets.connect(URI) as websocket:
await websocket.send(
json.dumps(
{
"voices": [VOICE_ID],
"xi_api_key": ELEVENLABS_API_KEY,
}
)
)
line = (
"This is a longer line of dialogue used to exceed the minimum buffer so the model "
"starts generating streamed audio for the registered voice. "
)
await websocket.send(
json.dumps(
{
"inputs": [
{"text": line, "voice_id": VOICE_ID, "new_turn": False},
],
}
)
)
await websocket.send(json.dumps({"close_socket": True}))
os.makedirs("output", exist_ok=True)
out_path = "output/dialogue-ws.mp3"
with open(out_path, "wb") as audio_file:
while True:
raw = await websocket.recv()
msg = json.loads(raw)
if msg.get("error"):
raise RuntimeError(msg)
if msg.get("audio"):
audio_file.write(base64.b64decode(msg["audio"]))
if msg.get("is_final"):
break
print(f"Wrote {out_path}")
asyncio.run(stream_dialogue())

close_socketは、バッファリングされたテキストをすべてフラッシュし、残りのオーディオを送信した後、接続を閉じる前にis_final: trueを含む最後のフレームを送信します。行と行の間も接続を開いたままにするには、セッションが終了するまでclose_socketを省略します。接続を閉じずに短いバッファのオーディオを強制生成するには、flushを使用します。

スクリプトを実行する

python text-to-dialogue-websocket.py

output/配下にMP3ファイルが作成されます(ファイル名は上記の例のとおりです)。

動作に関する注意事項

バッファリング

TTS WebSocketのchunk_length_scheduleとは異なり、ダイアログストリーミングでは、最初の部分オーディオを送信する前にサーバー側で固定されたしきい値(文字数と単語数)が適用されます。短い行を送信して遅延が発生する場合は、inputsフレームごとに少し多めのテキストをまとめるか、flush: trueを送信してソケットを閉じずに生成を強制してください。

ターンとボイス

話者のターンが終了したら、プロソディが適切にリセットされるようnew_turn: trueを設定します。inputsエントリ間でvoice_idを変更した場合も、新しいターンが開始されます。eleven_v3_conversationalでは、voicesに登録するボイスは必ず1つにしてください。eleven_v3では、最大10個のボイスを登録できます。

非アクティブ状態

サーバーが20秒間クライアントメッセージを受信しないと、接続は終了します。オーディオを合成せずにタイマーをリセットするには、{"keep_alive": true}を送信します。

同時実行

各オープン接続は、開いている間、1つのダイアログセッションを保持します。このセッションは、プランの標準同時実行制限とは別の専用プールから割り当てられます。接続経由で生成されたオーディオは、標準同時実行数にはカウントされません。詳しくは、Text to Dialogueの同時実行を参照してください。

アラインメント

利用可能な場合にチャンクでalignmentオブジェクト(snake_caseのタイミング配列)を受信するには、クエリ文字列にsync_alignment=trueを追加します。APIリファレンスを参照してください。

次のステップ