Downloads · 30 days
2
9% of all-time downloads
marcosremar2/tts-service-kokoro
tts-service-kokoro is a machine learning model from marcosremar2. Use it for the machine learning task on the model card, and read the license before you ship it in a product.
- TTFA (Time To First Audio): ~150ms com Stream2Sentence ⚡ - Latência Kokoro Direto: ~360ms primeiro chunk - Stream2Sentence: 2.1x mais rápido que Kokoro direto - RTF (Real-Time Factor): 0.02x - 0.10x (excelente) - Wa…
Downloads · 30 days
2
9% of all-time downloads
All-time downloads
23
Public
Repo size
—
Likes
0
Public
Click a slice to open those files.
.py593 KB · 84%
From the Hugging Face model README
tts-service/
├── server.py # Servidor gRPC principal (porta 50054)
├── integrations/
│ ├── base_tts.py # Classe base para engines TTS
│ ├── kokoro_direct.py # Integração direta com Kokoro (100% qualidade)
│ └── tts_manager.py # Gerenciador unificado de estratégias
├── engines/
│ └── kokoro/ # Kokoro TTS Engine (82M params, não-autoregressivo)
│ └── kokoro_server.py # Servidor persistente do Kokoro
├── config/
│ ├── dependencies.py # Injeção de dependências
│ └── tts_config.py # Configurações centralizadas
└── protos/ # Definições gRPC
from stream2sentence import generate_sentences
from kokoro import KPipeline
# Dividir texto em sentenças para streaming incremental
sentences = list(generate_sentences(text))
for sentence in sentences:
# Processar cada sentença independentemente
generator = model(sentence, voice="pf_dora", speed=1.0)
for gs, ps, audio in generator:
yield audio
Resultado:
# warmup.py - Pré-carrega modelos e vozes
def warmup_kokoro():
# 1. Carregar Kokoro
model = KPipeline(lang_code="p", repo_id="hexgrad/Kokoro-82M")
# 2. Carregar Stream2Sentence
from stream2sentence import generate_sentences
# 3. Pré-aquecer ambas estratégias
# Kokoro direto: ~700ms primeiro chunk
# Stream2Sentence: ~90ms primeiro chunk
# 4. Cache de vozes comuns
voices = ["pf_dora", "af_heart", "af_bella"]
for voice in voices:
model("a", voice=voice) # Pré-carrega voz
Resultado:
# install.sh - Script único para setup completo
./install.sh
# Instala automaticamente:
- espeak-ng (binários pré-compilados)
- Kokoro e dependências
- Stream2Sentence
- Configura paths e permissões
Resultado: Setup completo em ~5 minutos
., ?, !)Texto curto (39 chars):
- Kokoro Direto: TTFA: 693ms | RTF: 0.26x
- Stream2Sentence: TTFA: 144ms | RTF: 0.05x ✅
Texto médio (108 chars):
- Kokoro Direto: TTFA: 204ms | RTF: 0.03x
- Stream2Sentence: TTFA: 186ms | RTF: 0.05x ✅
Texto longo (282 chars):
- Kokoro Direto: TTFA: 183ms | RTF: 0.02x
- Stream2Sentence: TTFA: 187ms | RTF: 0.03x ✅
Stream2Sentence é 2.1x mais rápido no primeiro chunk!
O servidor expõe os seguintes métodos via gRPC na porta 50054:
Síntese com streaming de áudio em tempo real.
Request (SynthesisRequest):
message SynthesisRequest {
string text = 1; // Texto para sintetizar (obrigatório)
string voice_id = 2; // ID da voz (default: "pf_dora")
string session_id = 3; // ID da sessão (opcional)
float speed = 4; // Velocidade da fala (1.0 = normal)
float pitch = 5; // Tom da voz (1.0 = normal)
map<string, string> metadata = 6; // Metadados adicionais
}
Response Stream (AudioResponse):
message AudioResponse {
bytes audio_data = 1; // Chunk de áudio PCM float32
int32 samples_count = 2; // Número de samples
bool is_final_chunk = 3; // Indica fim do stream
}
Síntese completa sem streaming.
Request: SynthesisRequest (mesmo do streaming)
Response: AudioResponse único com áudio completo
Lista vozes disponíveis.
Response (VoiceListResponse):
message VoiceListResponse {
repeated VoiceInfo voices = 1;
}
message VoiceInfo {
string id = 1; // ID da voz
string name = 2; // Nome amigável
string language = 3; // Idioma (pt-BR, en-US)
}
Verifica saúde do serviço.
Response (HealthCheckResponse):
message HealthCheckResponse {
bool healthy = 1;
repeated EngineStatus engines = 2;
}
import grpc
import tts_pb2
import tts_pb2_grpc
# Conectar ao servidor
channel = grpc.insecure_channel('localhost:50054')
stub = tts_pb2_grpc.TTSServiceStub(channel)
# 1. Streaming (recomendado para tempo real)
def stream_tts(text, voice='pf_dora'):
request = tts_pb2.SynthesisRequest(
text=text,
voice_id=voice,
session_id='session_123'
)
# Receber stream de áudio
audio_chunks = []
for response in stub.StreamingSynthesize(request):
if response.audio_data:
audio_chunks.append(response.audio_data)
print(f"Recebido chunk: {response.samples_count} samples")
if response.is_final_chunk:
print("Stream finalizado")
break
# Combinar chunks
full_audio = b''.join(audio_chunks)
return full_audio
# 2. Síntese simples (sem streaming)
def simple_tts(text, voice='pf_dora'):
request = tts_pb2.SynthesisRequest(
text=text,
voice_id=voice
)
response = stub.Synthesize(request)
return response.audio_data
# 3. Listar vozes
def list_voices():
response = stub.GetAvailableVoices(tts_pb2.Empty())
for voice in response.voices:
print(f"{voice.id}: {voice.name} ({voice.language})")
# Exemplo de uso
audio = stream_tts("Olá! Como posso ajudar você hoje?", "pf_dora")
# Listar serviços
grpcurl -plaintext localhost:50054 list
# Ver vozes disponíveis
grpcurl -plaintext localhost:50054 tts.TTSService/GetAvailableVoices
# Sintetizar texto
grpcurl -plaintext -d '{
"text": "Olá mundo!",
"voice_id": "pf_dora"
}' localhost:50054 tts.TTSService/StreamingSynthesize
# Health check
grpcurl -plaintext localhost:50054 tts.TTSService/HealthCheck
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
// Carregar proto
const packageDef = protoLoader.loadSync('tts.proto');
const tts = grpc.loadPackageDefinition(packageDef).tts;
// Criar cliente
const client = new tts.TTSService(
'localhost:50054',
grpc.credentials.createInsecure()
);
// Streaming TTS
function streamTTS(text, voice = 'pf_dora') {
const stream = client.StreamingSynthesize({
text: text,
voice_id: voice
});
const chunks = [];
stream.on('data', (response) => {
if (response.audio_data) {
chunks.push(response.audio_data);
}
});
stream.on('end', () => {
const fullAudio = Buffer.concat(chunks);
// Processar áudio...
});
}
# GPU (detectada automaticamente)
export CUDA_VISIBLE_DEVICES=0
# Configuração de porta (via config.yaml)
tts_service:
port: 50054
max_workers: 10
max_message_size: 10485760 # 10MB
| ID | Tipo | Gênero | Estratégia | Qualidade | TTFA |
|---|---|---|---|---|---|
pf_dora | PT-BR | Feminina | Stream2Sentence | Alta | ~150ms |
pm_alex | PT-BR | Masculina | Stream2Sentence | Alta | ~150ms |
af_heart | PT-BR | Feminina | Stream2Sentence | Alta | ~150ms |
af_bella | PT-BR | Feminina | Stream2Sentence | Alta | ~150ms |
| ID | Tipo | Gênero | Motor | Qualidade | TTFA |
|---|---|---|---|---|---|
af_nicole | EN-US | Feminina | Kokoro | 95% | ~90ms |
am_michael | EN-US | Masculina | Kokoro | 95% | ~90ms |
| Parâmetro | Tipo | Range | Default | Descrição |
|---|---|---|---|---|
text | string | - | (obrigatório) | Texto para sintetizar |
voice_id | string | Ver tabela | pf_dora | ID da voz |
speed | float | 0.5-2.0 | 1.0 | Velocidade da fala |
pitch | float | 0.5-2.0 | 1.0 | Tom da voz |
session_id | string | - | default | ID para tracking |
Saída: PCM int16, 24kHz, mono
# Converter para WAV
import wave
import numpy as np
def save_as_wav(audio_bytes, filename):
# Kokoro retorna PCM int16 diretamente
# Não precisa conversão!
with wave.open(filename, 'wb') as wav:
wav.setnchannels(1) # Mono
wav.setsampwidth(2) # 16-bit (int16)
wav.setframerate(24000) # 24kHz
wav.writeframes(audio_bytes) # Usar dados diretos
# Se precisar converter int16 para float32:
def int16_to_float32(audio_bytes):
audio_int16 = np.frombuffer(audio_bytes, dtype=np.int16)
audio_float32 = (audio_int16.astype(np.float32) / 32768.0).tobytes()
return audio_float32
⚠️ IMPORTANTE:
Execute o teste completo:
cd /workspace/ultravox-pipeline/tests
python3 test_tts_benchmark.py
python3 benchmark_final.py
# Resultados:
ESTRATÉGIA 1º CHUNK RTF STATUS
------------------------------------------------
Stream2Sentence 144ms 0.05x ✅ VENCEDOR
Kokoro Direto 360ms 0.10x ✅
Métricas:
- Stream2Sentence: 2.1x mais rápido no primeiro chunk
- RTF < 0.1x = Síntese em tempo real excelente
- 100% de sucesso na geração de áudio
# 1. Clonar repositório
git clone https://huggingface.co/spaces/marcosvservidor/tts-service-kokoro
cd tts-service-kokoro
# 2. Executar instalação automatizada
./install.sh
# 3. Executar warmup (opcional, melhora latência)
python3 warmup.py
python3 server.py
# Logs esperados:
# 🔥 WARMUP OTIMIZADO - TTS SERVICE KOKORO
# ✅ Kokoro carregado em 5.2s
# ✅ Stream2Sentence carregado
# 🚀 Sistema pronto para requisições com latência mínima!
# Ideal para chatbots e assistentes
def chat_response(text):
# Usar pf_dora para baixa latência
return stream_tts(text, voice='pf_dora')
# TTFA: ~81ms ✅
# Para audiobooks ou narrações longas
def narrate_text(text):
# Usar pm_alex para qualidade máxima
return simple_tts(text, voice='pm_alex')
# Qualidade: 100% ✅
async def llm_to_speech(llm_stream):
"""Converte stream de LLM direto para voz"""
# Acumular até pontuação
buffer = ""
async for token in llm_stream:
buffer += token
# Detectar fim de sentença
if any(p in buffer for p in '.?!'):
# Sintetizar sentença completa
audio = stream_tts(buffer, 'pf_dora')
yield audio
buffer = ""
# Sintetizar resto
if buffer:
yield stream_tts(buffer, 'pf_dora')
def detect_and_speak(text):
# Detectar idioma
if is_portuguese(text):
voice = 'pf_dora' # PT-BR
else:
voice = 'af_nicole' # EN-US
return stream_tts(text, voice)
Causas e Soluções:
Cold start: Servidor precisa de warm-up
Texto sem pontuação: Aguarda texto completo
# Ruim: texto longo sem quebras
text = "Este é um texto muito longo sem pontuação que vai demorar"
# Bom: com pontuação
text = "Este é um texto. Com pontuação. Vai ser rápido."
GPU não detectada: Usando CPU
nvidia-smiexport CUDA_VISIBLE_DEVICES=0Verificações:
# Verificar se servidor está rodando
ps aux | grep server.py
# Verificar porta
netstat -tlnp | grep 50054
# Reiniciar servidor
cd tts-service
pkill -f server.py
python3 server.py
# Cliente com logs detalhados
import logging
logging.basicConfig(level=logging.DEBUG)
# Verificar latência
import time
start = time.time()
request = tts_pb2.SynthesisRequest(text="teste")
for response in stub.StreamingSynthesize(request):
if response.audio_data and not first_chunk:
ttfa = (time.time() - start) * 1000
print(f"TTFA: {ttfa:.0f}ms")
first_chunk = True
# Pré-carregar vozes mais usadas
voices_cache = {
'pf_dora': load_voice('pf_dora'),
'pm_alex': load_voice('pm_alex')
}
# Para alta concorrência
from concurrent.futures import ThreadPoolExecutor
pool = ThreadPoolExecutor(max_workers=10)
channels = [
grpc.insecure_channel('localhost:50054')
for _ in range(10)
]
# Processar múltiplos textos
async def batch_tts(texts, voice='pf_dora'):
tasks = [
stream_tts(text, voice)
for text in texts
]
return await asyncio.gather(*tasks)
benchmark_final.py com métricas detalhadas1. espeak-ng phontab não encontrado
2. RealtimeTTS complexidade desnecessária
3. Latência alta no primeiro chunk
4. Instalação manual complexa
Última atualização: Dezembro 2025 Mantido por: Equipe Ultravox Pipeline