Gere áudio em tempo real

Este guia mostra como gerar áudio em tempo real por uma conexão WebSocket.

O streaming por WebSocket é um método de envio e recebimento de dados em uma única conexão de longa duração. Esse método é útil para aplicações em tempo real nas quais você precisa transmitir dados de áudio assim que eles ficam disponíveis.

Se quiser testar rapidamente a latência (tempo até o primeiro byte) de uma conexão WebSocket com a API de conversão de texto em voz da ElevenLabs, você pode instalar elevenlabs-latency via npm e seguir as instruções aqui.

Os WebSockets estão disponíveis para Text to Speech e para a Plataforma de Agentes. Este guia aborda o WebSocket de Text to Speech (/v1/text-to-speech/{voice_id}/stream-input). Esse endpoint não oferece suporte aos modelos eleven_v3 nem eleven_v4. Para diálogos com Eleven v3 ou Eleven v4 por WebSocket, consulte Texto em Tempo Real para Diálogo e WebSockets de Text to Speech vs. Texto para Diálogo.

Requisitos

  • Uma conta ElevenLabs com uma chave de API (veja como encontrar sua chave de API).
  • Python ou Node.js (ou outro ambiente de execução JavaScript) instalado na sua máquina

Configuração

Instale as dependências necessárias:

pip install python-dotenv
pip install websockets

Em seguida, crie um arquivo .env no diretório do projeto e adicione sua chave de API:

.env
ELEVENLABS_API_KEY=your_elevenlabs_api_key_here

Inicie a conexão WebSocket

Depois de escolher uma voz na Voice Library e o modelo de conversão de texto em voz que deseja usar, inicie uma conexão WebSocket com a API de conversão de texto em voz.

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

Envie o texto de entrada

Quando a conexão WebSocket estiver aberta, configure primeiro as definições de voz. Em seguida, envie a mensagem de texto para a 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": ""}))

Salve o áudio em um arquivo

Leia a mensagem recebida da conexão WebSocket e grave os blocos de áudio em um arquivo local.

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

Execute o script

Você pode executar o script rodando o comando a seguir no terminal. Um arquivo de áudio mp3 será salvo no diretório output.

python text-to-speech-websocket.py

Configuração avançada

O uso de WebSockets oferece algumas configurações avançadas que você pode usar para ajustar a geração de áudio em tempo real.

Bufferização

Ao gerar áudio em tempo real, é importante considerar dois conceitos: Tempo Até o Primeiro Byte (TTFB) e bufferização. Para produzir áudio de alta qualidade e deduzir o contexto, o modelo exige uma determinada quantidade mínima de texto de entrada. Quanto mais texto for enviado em uma conexão WebSocket, melhor será a qualidade do áudio. Se essa quantidade mínima não for atingida, o modelo adicionará o texto a um buffer e gerará o áudio quando o buffer estiver cheio.

Em termos de latência, o TTFB é o tempo necessário para que o primeiro byte de áudio seja enviado ao cliente. Isso é importante porque afeta a latência percebida do áudio. Portanto, talvez você queira controlar o tamanho do buffer para equilibrar qualidade e latência.

Para gerenciar isso, você pode usar o parâmetro chunk_length_schedule ao inicializar a conexão WebSocket ou ao enviar texto. Esse parâmetro é uma matriz de números inteiros que representa a quantidade de caracteres que será enviada ao modelo antes da geração do áudio. Por exemplo, se você definir chunk_length_schedule como [120, 160, 250, 290], o modelo gerará áudio depois que 120, 160, 250 e 290 caracteres forem enviados, respectivamente.

Veja como isso funciona com as configurações padrão de chunk_length_schedule:

No diagrama acima, o áudio só é gerado após a segunda mensagem ser enviada ao servidor. Isso acontece porque a primeira mensagem está abaixo do limite de 120 caracteres, enquanto a segunda eleva o total de caracteres acima desse limite. A terceira mensagem está acima do limite de 160 caracteres, então o áudio é gerado imediatamente e retornado ao cliente.

Você pode especificar um valor personalizado para chunk_length_schedule ao inicializar a conexão WebSocket ou ao enviar texto.

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

Caso queira forçar o retorno imediato do áudio, você pode usar flush: true para limpar o buffer e forçar a geração de qualquer texto armazenado nele. Isso pode ser útil, por exemplo, quando você chega ao fim de um documento e quer gerar o áudio da seção final.

Isso pode ser especificado por mensagem ao definir flush: true na mensagem.

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

Além disso, fechar o websocket forçará automaticamente a geração de qualquer texto armazenado no buffer.

Definições de voz

Ao inicializar as conexões WebSocket, você pode especificar as definições de voz para as gerações subsequentes. Isso permite controlar a velocidade, a estabilidade e outras características de voz do áudio gerado.

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

Isso pode ser substituído por mensagem ao especificar voice_settings diferente na mensagem.

Dicionários de pronúncia

Você pode usar dicionários de pronúncia para controlar a pronúncia de palavras ou frases específicas. Isso pode ser útil para garantir que certas palavras sejam pronunciadas corretamente ou para dar ênfase a determinadas palavras ou frases.

Diferentemente de voice_settings e generation_config, os dicionários de pronúncia devem ser especificados na mensagem “Initialize Connection”. Consulte a referência da API para mais informações.

Ao usar dicionários de pronúncia baseados em fonemas com WebSockets, você deve adicionar enable_ssml_parsing=true como parâmetro de consulta ao URI do WebSocket. Por exemplo:

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

Boas práticas

  • Sugerimos usar a configuração padrão de chunk_length_schedule em generation_config.
  • Ao desenvolver uma aplicação de agente conversacional em tempo real, recomendamos usar flush: true junto ao texto no fim de cada turno da conversa para garantir a geração de áudio no momento certo.
  • Se a configuração padrão não oferecer a latência ideal para seu caso de uso, você pode modificar chunk_length_schedule. No entanto, lembre-se de que reduzir a latência com esse ajuste pode comprometer a qualidade.

Dicas

  • A conexão WebSocket será fechada automaticamente após 20 segundos de inatividade. Para mantê-la aberta, você pode enviar um único caractere de espaço " ". Observe que essa string deve incluir um espaço, pois enviar uma string totalmente vazia, "", fechará o WebSocket.
  • Envie uma string vazia para fechar a conexão WebSocket após enviar a última mensagem de texto.
  • Você pode usar alignment para obter os timestamps de cada palavra no texto. Isso pode ser útil para alinhar o áudio ao texto em um vídeo ou em outras aplicações que exigem temporização precisa. Consulte a referência da API para mais informações.

Próximos passos