Un servidor TTS compatible con OpenAI en tu propio ordenador
Muchas apps hablan a través de la API de voz de OpenAI: Open WebUI, SillyTavern y todo lo hecho con el SDK de OpenAI te dejan cambiar la URL base y enviar POST /v1/audio/speech a otro servidor. Apúntalas a un servidor tuyo y sus voces funcionarán en tu ordenador, gratis y sin conexión.
Esta guía crea ese servidor en unas 70 líneas de Python con el motor gratuito de SuperClonica. Lo probamos con el cliente oficial de OpenAI para Python: devuelve MP3, WAV o PCM sin procesar y comprueba una clave opcional.
Instalación
Necesitas Python 3.10 o posterior. Instala el motor, el servidor web y el codificador de audio:
pip install superclonica-cpu fastapi uvicorn av
En el primer arranque el motor descarga el modelo Supertonic 3, unos 400 MB; después funciona sin conexión en un procesador normal, y una tarjeta gráfica NVIDIA solo lo acelera.
El servidor
Guarda esto como openai_tts.py:
import io, os, wave
import av
import numpy as np
from fastapi import FastAPI, Header, HTTPException
from fastapi.concurrency import run_in_threadpool
from fastapi.responses import Response
from pydantic import BaseModel
from superclonica import Superclonica
VOICES = {name: name for name in ["M1", "M2", "M3", "M4", "M5", "F1", "F2", "F3", "F4", "F5"]}
# a catalog voice: VOICES["narrator"] = "/path/to/voice-file-from-the-catalog"
LANGUAGE = os.environ.get("TTS_LANGUAGE", "en")
KEY = os.environ.get("TTS_KEY", "") # leave empty on a private machine
app = FastAPI()
tts = Superclonica()
class Speech(BaseModel):
input: str
voice: str = "F1"
model: str = "superclonica"
response_format: str = "mp3"
def to_wav(pcm, rate):
buffer = io.BytesIO()
with wave.open(buffer, "wb") as file:
file.setnchannels(1)
file.setsampwidth(2)
file.setframerate(rate)
file.writeframes(pcm.tobytes())
return buffer.getvalue()
def to_mp3(pcm, rate):
buffer = io.BytesIO()
with av.open(buffer, "w", format="mp3") as container:
stream = container.add_stream("libmp3lame", rate=rate, layout="mono")
frame = av.AudioFrame.from_ndarray(pcm.reshape(1, -1), format="s16", layout="mono")
frame.rate = rate
for packet in stream.encode(frame):
container.mux(packet)
for packet in stream.encode(None):
container.mux(packet)
return buffer.getvalue()
def speak(request):
voice = VOICES.get(request.voice, "F1")
audio, rate = tts.synthesize(request.input, voice=voice, lang=LANGUAGE)
pcm = (np.clip(np.asarray(audio, dtype=np.float32).reshape(-1), -1, 1) * 32767).astype(np.int16)
if request.response_format == "wav":
return to_wav(pcm, rate), "audio/wav"
if request.response_format == "pcm":
return pcm.tobytes(), "audio/pcm"
return to_mp3(pcm, rate), "audio/mpeg"
@app.post("/v1/audio/speech")
async def speech(request: Speech, authorization: str = Header("")):
if KEY and authorization != "Bearer " + KEY:
raise HTTPException(401)
audio, media_type = await run_in_threadpool(speak, request)
return Response(audio, media_type=media_type)
@app.get("/v1/models")
def models():
return {"object": "list", "data": [{"id": "superclonica", "object": "model"}]}
@app.get("/v1/audio/voices")
def voices():
return {"voices": list(VOICES)}Arráncalo y responderá en el puerto 8880:
uvicorn openai_tts:app --host 127.0.0.1 --port 8880
El campo voice acepta una de las diez voces de serie, de M1 a M5 y de F1 a F5; los nombres desconocidos pasan a F1, así que un cliente que envíe alloy también recibe respuesta. Pon en TTS_LANGUAGE el idioma de tu app: el motor habla 31 idiomas, pero aún no chino. Define TTS_KEY si alguien más puede llegar al puerto.
Conectar Open WebUI
Abre Admin Panel, Settings, Audio. Pon Text-to-Speech Engine en OpenAI, API Base URL en http://localhost:8880/v1, TTS Model en superclonica y TTS Voice en una de las voces de serie. Open WebUI pide una clave API no vacía: escribe tu TTS_KEY, o cualquier texto si la dejaste vacía. Si Open WebUI corre en Docker, arranca el servidor con --host 0.0.0.0 y usa http://host.docker.internal:8880/v1; en Linux el contenedor necesita --add-host=host.docker.internal:host-gateway. Deja Response Splitting en punctuation para que las respuestas largas empiecen a sonar tras la primera frase.
Conectar SillyTavern
Abre Extensions, TTS y elige el proveedor OpenAI Compatible. Escribe http://localhost:8880/v1/audio/speech como endpoint, superclonica como modelo, tu clave, y escribe tú la lista de voces, porque este proveedor no la descarga: M1,M2,M3,M4,M5,F1,F2,F3,F4,F5. Luego da a cada personaje y a tu persona su propia voz en el mapa de voces y pulsa Apply.
Voces de personajes y otros clientes
Para usar una voz del catálogo de SuperClonica, descarga su archivo de voz y añade una línea como VOICES["narrator"] = "/path/to/the/file"; el nombre narrator funcionará en todos los clientes, incluida la lista de voces de SillyTavern. También sirve cualquier programa con el SDK de OpenAI: crea el cliente con base_url http://localhost:8880/v1 y llama a audio.speech.create como siempre.