リアルタイムでオーディオを生成

このガイドでは、WebSocket接続を介してリアルタイムでオーディオを生成する方法を説明します。

WebSocketストリーミングは、単一の長時間維持される接続を介してデータを送受信する方法です。この方法は、利用可能になった音声データをストリーミングする必要があるリアルタイムアプリケーションに役立ちます。

ElevenLabsテキスト読み上げAPIへのWebSocket接続のレイテンシー(最初のバイトが届くまでの時間)をすぐにテストしたい場合は、npmでelevenlabs-latencyをインストールし、こちらの手順に従ってください。

WebSocketはテキスト読み上げとAgents Platformで利用できます。このガイドでは、テキスト 読み上げWebSocket(/v1/text-to-speech/{voice_id}/stream-input)について説明します。このエンドポイントは eleven_v3モデルをサポートしていません。WebSocket経由のEleven v3ダイアログについては、Realtime Text to DialogueおよびText to Speech vs Text to Dialogue WebSocketsを参照してください。

要件

  • APIキーを持つElevenLabsアカウント(APIキーの確認方法はこちら)。
  • PythonまたはNode.js(または別のJavaScriptランタイム)がマシンにインストールされていること

セットアップ

必要な依存関係をインストールします。

pip install python-dotenv
pip install websockets

次に、プロジェクトディレクトリに.envファイルを作成し、APIキーを追加します。

.env
ELEVENLABS_API_KEY=your_elevenlabs_api_key_here

WebSocket接続を開始する

ボイスライブラリからボイスを選び、使用するテキスト読み上げモデルを決めたら、テキスト読み上げAPIへのWebSocket接続を開始します。

import os
from dotenv import load_dotenv
import websockets
# Load the API key from the .env file
load_dotenv()
ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY")
voice_id = 'Xb7hH8MSUJpSbSDYk0k2'
# For use cases where latency is important, we recommend using the 'eleven_flash_v2_5' model.
model_id = 'eleven_flash_v2_5'
async def text_to_speech_ws_streaming(voice_id, model_id):
uri = f"wss://api.elevenlabs.io/v1/text-to-speech/{voice_id}/stream-input?model_id={model_id}"
async with websockets.connect(uri) as websocket:
...

入力テキストを送信する

WebSocket接続が開いたら、まずボイス設定を行います。次に、テキストメッセージをAPIに送信します。

async def text_to_speech_ws_streaming(voice_id, model_id):
async with websockets.connect(uri) as websocket:
await websocket.send(json.dumps({
"text": " ",
"voice_settings": {"stability": 0.5, "similarity_boost": 0.8, "use_speaker_boost": False},
"generation_config": {
"chunk_length_schedule": [120, 160, 250, 290]
},
"xi_api_key": ELEVENLABS_API_KEY,
}))
text = "The twilight sun cast its warm golden hues upon the vast rolling fields, saturating the landscape with an ethereal glow. Silently, the meandering brook continued its ceaseless journey, whispering secrets only the trees seemed privy to."
await websocket.send(json.dumps({"text": text}))
# Send empty string to indicate the end of the text sequence which will close the WebSocket connection
await websocket.send(json.dumps({"text": ""}))

音声をファイルに保存する

WebSocket接続から受信するメッセージを読み取り、音声チャンクをローカルファイルに書き込みます。

import asyncio
async def write_to_local(audio_stream):
"""Write the audio encoded in base64 string to a local mp3 file."""
with open(f'./output/test.mp3', "wb") as f:
async for chunk in audio_stream:
if chunk:
f.write(chunk)
async def listen(websocket):
"""Listen to the websocket for audio data and stream it."""
while True:
try:
message = await websocket.recv()
data = json.loads(message)
if data.get("audio"):
yield base64.b64decode(data["audio"])
elif data.get('isFinal'):
break
except websockets.exceptions.ConnectionClosed:
print("Connection closed")
break
async def text_to_speech_ws_streaming(voice_id, model_id):
async with websockets.connect(uri) as websocket:
...
# Add listen task to submit the audio chunks to the write_to_local function
listen_task = asyncio.create_task(write_to_local(listen(websocket)))
await listen_task
asyncio.run(text_to_speech_ws_streaming(voice_id, model_id))

スクリプトを実行する

ターミナルで次のコマンドを実行すると、スクリプトを実行できます。outputディレクトリにmp3音声ファイルが保存されます。

python text-to-speech-websocket.py

高度な設定

WebSocketには、リアルタイム音声生成を微調整するための高度な設定があります。

バッファリング

リアルタイム音声を生成する際には、Time To First Byte(TTFB)とバッファリングという2つの重要な概念を考慮する必要があります。高品質な音声を生成し、コンテキストを推測するために、モデルには一定量の入力テキストが必要です。WebSocket接続で送信するテキストが多いほど、音声品質は向上します。しきい値に達していない場合、モデルはテキストをバッファに追加し、バッファが満たされると音声を生成します。

レイテンシーの観点では、TTFBは最初の音声バイトがクライアントに送信されるまでの時間です。これは音声の体感レイテンシーに影響するため重要です。そのため、品質とレイテンシーのバランスを取るために、バッファサイズを制御したい場合があります。

これを管理するには、WebSocket接続の初期化時またはテキスト送信時にchunk_length_scheduleパラメーターを使用します。このパラメーターは、音声を生成する前にモデルへ送信する文字数を表す整数の配列です。たとえば、chunk_length_scheduleを[120, 160, 250, 290]に設定すると、120、160、250、290文字がそれぞれ送信された後にモデルが音声を生成します。

以下は、chunk_length_scheduleのデフォルト設定での動作例です。

上の図では、2つ目のメッセージがサーバーに送信された後にのみ音声が生成されます。これは、最初のメッセージが120文字のしきい値未満である一方、2つ目のメッセージによって合計文字数がしきい値を超えるためです。3つ目のメッセージは160文字のしきい値を超えているため、音声がすぐに生成され、クライアントに返されます。

WebSocket接続の初期化時またはテキスト送信時に、chunk_length_scheduleのカスタム値を指定できます。

await websocket.send(json.dumps({
"text": text,
"generation_config": {
# Generate audio after 50, 120, 160, and 290 characters have been sent
"chunk_length_schedule": [50, 120, 160, 290]
},
"xi_api_key": ELEVENLABS_API_KEY,
}))

音声をすぐに返したい場合は、flush: trueを使用してバッファをクリアし、バッファ内のテキストを強制的に生成できます。たとえば、ドキュメントの末尾に到達し、最後のセクションの音声を生成したい場合に便利です。

これは、メッセージ内でflush: trueを設定することで、メッセージごとに指定できます。

await websocket.send(json.dumps({"text": "Generate this audio immediately.", "flush": True}))

また、WebSocketを閉じると、バッファ内のテキストが自動的に強制生成されます。

ボイス設定

WebSocket接続を初期化する際、後続の生成に適用するボイス設定を指定できます。これにより、生成される音声の速度、安定性、その他のボイス特性を制御できます。

await websocket.send(json.dumps({
"text": text,
"voice_settings": {"stability": 0.5, "similarity_boost": 0.8, "use_speaker_boost": False},
}))

メッセージ内で異なるvoice_settingsを指定することで、メッセージごとに上書きできます。

発音辞書

発音辞書を使用すると、特定の単語やフレーズの発音を制御できます。特定の単語を正しく発音させたり、特定の単語やフレーズを強調したりする際に役立ちます。

voice_settingsやgeneration_configとは異なり、発音辞書は「Initialize Connection」メッセージで指定する必要があります。詳細はAPIリファレンスを参照してください。

WebSocketで音素ベースの発音辞書を使用する場合は、WebSocket URIのクエリパラメーターとしてenable_ssml_parsing=trueを追加する必要があります。例:

wss://api.elevenlabs.io/v1/text-to-speech/{voice_id}/stream-input?model_id={model_id}&enable_ssml_parsing=true

ベストプラクティス

  • generation_config内のchunk_length_scheduleには、デフォルト設定を使用することをおすすめします。
  • リアルタイム会話エージェントアプリケーションを開発する際は、音声を適切なタイミングで生成するため、会話ターンの最後のテキストとともにflush: trueを使用することをおすすめします。
  • デフォルト設定でユースケースに最適なレイテンシーが得られない場合は、chunk_length_scheduleを変更できます。ただし、この調整でレイテンシーを短縮すると、品質が低下する可能性があります。

ヒント

  • WebSocket接続は、20秒間操作がないと自動的に閉じます。接続を維持するには、半角スペース1文字の" "を送信します。完全な空文字列""を送信するとWebSocketが閉じるため、この文字列にはスペースを含める必要があります。
  • 最後のテキストメッセージを送信した後、WebSocket接続を閉じるには空文字列を送信します。
  • alignmentを使用すると、テキスト内の各単語について単語レベルのタイムスタンプを取得できます。これは、ビデオ内で音声とテキストを同期させる場合や、正確なタイミングが必要なその他の用途で役立ちます。詳細はAPIリファレンスを参照してください。

次のステップ