Audio in Echtzeit generieren

Dieser Leitfaden zeigt Ihnen, wie Sie Audio über eine WebSocket-Verbindung in Echtzeit generieren.

WebSocket-Streaming ist eine Methode zum Senden und Empfangen von Daten über eine einzige, langlebige Verbindung. Sie eignet sich für Echtzeitanwendungen, bei denen Audiodaten gestreamt werden müssen, sobald sie verfügbar sind.

Wenn Sie schnell die Latenz (Zeit bis zum ersten Byte) einer WebSocket-Verbindung zur ElevenLabs Text-to-Speech API testen möchten, können Sie elevenlabs-latency über npm installieren und den Anweisungen hier folgen.

WebSockets sind für Text to Speech und die Agents Platform verfügbar. Dieser Leitfaden behandelt den Text to Speech WebSocket (/v1/text-to-speech/{voice_id}/stream-input). Dieser Endpunkt unterstützt nicht das Modell eleven_v3. Informationen zu Eleven v3-Dialogen über einen WebSocket finden Sie unter Echtzeit-Text zu Dialog und Text to Speech vs. Text zu Dialog-WebSockets.

Voraussetzungen

  • Ein ElevenLabs-Konto mit API-Schlüssel (so können Sie Ihren API-Schlüssel finden).
  • Python oder Node.js (oder eine andere JavaScript-Laufzeit) auf Ihrem Computer installiert

Einrichtung

Installieren Sie die erforderlichen Abhängigkeiten:

pip install python-dotenv
pip install websockets

Erstellen Sie als Nächstes eine .env-Datei in Ihrem Projektverzeichnis und fügen Sie Ihren API-Schlüssel hinzu:

.env
ELEVENLABS_API_KEY=your_elevenlabs_api_key_here

WebSocket-Verbindung herstellen

Wählen Sie eine Stimme aus der Stimmbibliothek und das gewünschte Text-to-Speech-Modell aus. Stellen Sie dann eine WebSocket-Verbindung zur Text-to-Speech API her.

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:
...

Eingabetext senden

Sobald die WebSocket-Verbindung geöffnet ist, legen Sie zuerst die Stimmeinstellungen fest. Senden Sie anschließend die Textnachricht an die 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": ""}))

Audio in Datei speichern

Lesen Sie die eingehende Nachricht aus der WebSocket-Verbindung und schreiben Sie die Audio-Chunks in eine lokale Datei.

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))

Skript ausführen

Führen Sie das Skript mit dem folgenden Befehl in Ihrem Terminal aus. Eine MP3-Audiodatei wird im Verzeichnis output gespeichert.

python text-to-speech-websocket.py

Erweiterte Konfiguration

WebSockets bieten einige erweiterte Einstellungen, mit denen Sie Ihre Echtzeit-Audiogenerierung fein abstimmen können.

Pufferung

Bei der Echtzeit-Audiogenerierung sind zwei Konzepte wichtig: Time To First Byte (TTFB) und Pufferung. Um hochwertige Audiodaten zu erzeugen und Kontext abzuleiten, benötigt das Modell eine bestimmte Menge an Eingabetext. Je mehr Text über eine WebSocket-Verbindung gesendet wird, desto besser ist die Audioqualität. Wird dieser Schwellenwert nicht erreicht, fügt das Modell den Text einem Puffer hinzu und generiert Audio, sobald der Puffer voll ist.

Bei der Latenz bezeichnet TTFB die Zeit, die vergeht, bis das erste Audio-Byte an den Client gesendet wird. Das ist wichtig, weil es die wahrgenommene Audiolatenz beeinflusst. Sie können daher die Puffergröße steuern, um Qualität und Latenz auszubalancieren.

Verwenden Sie dazu den Parameter chunk_length_schedule, wenn Sie die WebSocket-Verbindung initialisieren oder Text senden. Dieser Parameter ist ein Array von Ganzzahlen, das die Anzahl der Zeichen angibt, die an das Modell gesendet werden, bevor Audio generiert wird. Wenn Sie beispielsweise chunk_length_schedule auf [120, 160, 250, 290] setzen, generiert das Modell Audio, nachdem jeweils 120, 160, 250 und 290 Zeichen gesendet wurden.

So funktioniert dies mit den Standardeinstellungen für chunk_length_schedule:

Im obigen Diagramm wird Audio erst generiert, nachdem die zweite Nachricht an den Server gesendet wurde. Die erste Nachricht liegt unter dem Schwellenwert von 120 Zeichen, während die zweite Nachricht die Gesamtzahl der Zeichen über diesen Schwellenwert hebt. Die dritte Nachricht liegt über dem Schwellenwert von 160 Zeichen. Daher wird Audio sofort generiert und an den Client zurückgegeben.

Sie können beim Initialisieren der WebSocket-Verbindung oder beim Senden von Text einen benutzerdefinierten Wert für chunk_length_schedule festlegen.

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,
}))

Wenn Sie die sofortige Rückgabe des Audios erzwingen möchten, können Sie mit flush: true den Puffer leeren und die Generierung des gepufferten Texts erzwingen. Das kann beispielsweise nützlich sein, wenn Sie das Ende eines Dokuments erreicht haben und Audio für den letzten Abschnitt generieren möchten.

Dies kann pro Nachricht festgelegt werden, indem Sie in der Nachricht flush: true setzen.

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

Darüber hinaus erzwingt das Schließen des WebSockets automatisch die Generierung aller gepufferten Texte.

Stimmeinstellungen

Beim Initialisieren der WebSocket-Verbindungen können Sie die Stimmeinstellungen für nachfolgende Generierungen festlegen. So steuern Sie Geschwindigkeit, Stabilität und weitere Stimmeigenschaften des generierten Audios.

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

Sie können dies pro Nachricht überschreiben, indem Sie in der Nachricht andere voice_settings angeben.

Aussprachewörterbücher

Mit Aussprachewörterbüchern können Sie die Aussprache bestimmter Wörter oder Wortgruppen steuern. Das ist nützlich, um die korrekte Aussprache bestimmter Wörter sicherzustellen oder bestimmte Wörter oder Wortgruppen hervorzuheben.

Anders als voice_settings und generation_config müssen Aussprachewörterbücher in der Nachricht „Initialize Connection“ angegeben werden. Weitere Informationen finden Sie in der API-Referenz.

Bei der Verwendung phonembasierter Aussprachewörterbücher mit WebSockets müssen Sie enable_ssml_parsing=true als Abfrageparameter zur WebSocket-URI hinzufügen. Beispiel:

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

Best Practice

  • Wir empfehlen die Standardeinstellung für chunk_length_schedule in generation_config.
  • Bei der Entwicklung einer Echtzeit-Konversationsagent-Anwendung empfehlen wir, am Ende eines Gesprächszugs flush: true zusammen mit dem Text zu verwenden, um eine zeitnahe Audiogenerierung sicherzustellen.
  • Wenn die Standardeinstellung für Ihren Anwendungsfall keine optimale Latenz bietet, können Sie chunk_length_schedule anpassen. Beachten Sie jedoch, dass eine geringere Latenz durch diese Anpassung zulasten der Qualität gehen kann.

Tipps

  • Die WebSocket-Verbindung wird nach 20 Sekunden Inaktivität automatisch geschlossen. Um die Verbindung offen zu halten, können Sie ein einzelnes Leerzeichen " " senden. Beachten Sie, dass diese Zeichenfolge ein Leerzeichen enthalten muss, da eine vollständig leere Zeichenfolge, "", den WebSocket schließt.
  • Senden Sie eine leere Zeichenfolge, um die WebSocket-Verbindung nach der letzten Textnachricht zu schließen.
  • Mit alignment können Sie Zeitstempel auf Wortebene für jedes Wort im Text abrufen. Dies kann nützlich sein, um Audio und Text in einem Video abzugleichen oder für andere Anwendungen, die präzises Timing erfordern. Weitere Informationen finden Sie in der API-Referenz.

Nächste Schritte