Un serveur TTS compatible OpenAI sur votre propre ordinateur
Beaucoup d’applications parlent via l’API vocale d’OpenAI : Open WebUI, SillyTavern et tout ce qui repose sur le SDK OpenAI vous laissent changer l’URL de base et envoyer POST /v1/audio/speech à un autre serveur. Pointez-les vers votre propre serveur et leurs voix tournent sur votre ordinateur, gratuitement et hors ligne.
Ce guide construit ce serveur en environ 70 lignes de Python avec le moteur gratuit SuperClonica. Nous l’avons testé avec le client Python officiel d’OpenAI : il renvoie du MP3, du WAV ou du PCM brut et vérifie une clé facultative.
Installation
Il faut Python 3.10 ou plus récent. Installez le moteur, le serveur web et l’encodeur audio :
pip install superclonica-cpu fastapi uvicorn av
Au premier lancement, le moteur télécharge le modèle Supertonic 3, environ 400 Mo ; ensuite il fonctionne hors ligne sur un processeur ordinaire, et une carte graphique NVIDIA ne fait que l’accélérer.
Le serveur
Enregistrez ceci sous 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)}Lancez-le, il répond sur le port 8880 :
uvicorn openai_tts:app --host 127.0.0.1 --port 8880
Le champ voice accepte l’une des dix voix de base, M1 à M5 et F1 à F5 ; un nom inconnu revient à F1, donc un client qui envoie alloy reçoit quand même une réponse. Réglez TTS_LANGUAGE sur la langue de votre application : le moteur parle 31 langues, mais pas encore le chinois. Définissez TTS_KEY si d’autres que vous peuvent atteindre le port.
Connecter Open WebUI
Ouvrez Admin Panel, Settings, Audio. Réglez Text-to-Speech Engine sur OpenAI, API Base URL sur http://localhost:8880/v1, TTS Model sur superclonica et TTS Voice sur une voix de base. Open WebUI exige une clé API non vide : saisissez votre TTS_KEY, ou n’importe quel texte si vous l’avez laissée vide. Si Open WebUI tourne dans Docker, lancez le serveur avec --host 0.0.0.0 et utilisez http://host.docker.internal:8880/v1; sous Linux, le conteneur a besoin de --add-host=host.docker.internal:host-gateway. Laissez Response Splitting sur punctuation pour que les longues réponses commencent à parler après la première phrase.
Connecter SillyTavern
Ouvrez Extensions, TTS et choisissez le fournisseur OpenAI Compatible. Indiquez http://localhost:8880/v1/audio/speech comme endpoint, superclonica comme modèle, votre clé, et tapez vous-même la liste des voix, car ce fournisseur ne la récupère pas : M1,M2,M3,M4,M5,F1,F2,F3,F4,F5. Donnez ensuite à chaque personnage et à votre persona sa propre voix dans la carte des voix, puis cliquez sur Apply.
Voix de personnages et autres clients
Pour utiliser une voix du catalogue SuperClonica, téléchargez son fichier de voix et ajoutez une ligne comme VOICES["narrator"] = "/path/to/the/file" ; le nom narrator fonctionne alors dans tous les clients, y compris la liste de voix de SillyTavern. Tout programme basé sur le SDK OpenAI fonctionne aussi : créez le client avec base_url http://localhost:8880/v1 et appelez audio.speech.create comme d’habitude.