Synthèse Vocale Locale (TTS Local)
Voix par défaut : Qwen3-TTS 1.7B CustomVoice, locuteur aiden, GGUF 8 bits (Q8_0), piloté par
qwentts.cpp (C++/ggml, MIT) avec cette instruction de style :
Chaleureux, clair et curieux, comme un narrateur sympathique de vidéo explicative technique. Rythme détendu et régulier.
Il a remporté un test d'écoute à l'aveugle face à Kokoro, Chatterbox, PyTorch Qwen3-TTS, VoxCPM2 et Phonon
(voir references/benchmarks.md). Sur un GPU RTX 4070 Laptop 8 Go, il fonctionne à environ 4x la vitesse réelle, chargement du modèle compris : une narration de 18 minutes prend ~4,5 minutes.
Configuration (une fois par machine)
Exécutez python -I <skill>/scripts/say.py --check d'abord. S'il affiche OK, ignorez cette section.
Sinon, configurez le moteur, ou demandez à l'utilisateur de le faire. Cela prend 10 à 20 minutes et environ 2,5 Go d'espace disque.
- Construire qwentts.cpp. Il n'existe pas de binaire Windows précompilé, donc construisez-le à partir des sources :
Sous Windows, installez les outils de build VS 2022 (ou plus récent) avec la charge de travail C++, CMake, Ninja et le CUDA Toolkit, puis exécutezgit clone --recurse-submodules https://github.com/ServeurpersoCom/qwentts.cpp ~/tools/qwentts.cpp cd ~/tools/qwentts.cpp ./buildcuda.sh # NVIDIA (Linux) ; ./buildvulkan.sh pour AMD/Intel ; ./buildcpu.sh pour CPU uniquement (ex. macOS)buildcuda.cmddepuis le dépôt. Sivcvars64.batse trouve ailleurs, modifiez le chemin en haut de ce script. Pour cibler un seul GPU, ajoutez-DCMAKE_CUDA_ARCHITECTURES=89(RTX 40xx ; 86 = RTX 30xx, 120 = RTX 50xx) à la ligne cmake. - Télécharger les poids (~2 Go) depuis Hugging Face
Serveurperso/Qwen3-TTS-GGUF:pip install -U huggingface_hub hf download Serveurperso/Qwen3-TTS-GGUF qwen-talker-1.7b-customvoice-Q8_0.gguf qwen-tokenizer-12hz-Q8_0.gguf --local-dir ~/tools/qwen-gguf - Pointer say.py vers eux si vous avez utilisé d'autres dossiers : définissez
QWEN_TTS_HOME(le checkout),QWEN_TTS_EXE(le binaire) ouQWEN_TTS_MODELS(le dossier .gguf). Sous Windows,say.pyajoute%CUDA_PATH%\bin\x64au PATH pour vous (remplacez parCUDA_BIN). say.py --checkà nouveau, puis testez une ligne vocale.
Matériel : 8 Go de VRAM suffisent pour Q8_0 (~3 Go utilisés). Sans GPU, une version CPU fonctionne environ un quart de la vitesse. GGML_BACKEND=CPU force une version GPU sur le CPU.
Avant de générer
- Quelque chose d'autre utilise-t-il le GPU ? Un jeu ou une autre application gourmande en GPU rend la génération ~4x plus lente et fait bégayer cette application. Demandez avant de lancer un long travail. Si l'utilisateur nomme de telles applications, définissez
TTS_BUSY_APPS=app1,app2etsay.pyprévient quand l'une d'elles est en cours. - Un modèle plus récent mérite-t-il d'être vérifié ? Voir "Continuez à chercher de meilleurs modèles" ci-dessous. Si la dernière vérification date de plus de ~6 semaines et que la tâche est une véritable narration (pas un test d'une ligne), faites d'abord l'analyse rapide.
- Le texte sonne-t-il humain ? Une voix lit le langage IA avec la même intonation à chaque fois, donc le motif ressort plus qu'à l'écrit. Pour toute narration de plus de quelques lignes, lisez
natural-narration.mddepuis la compétencewhiteboard-explainer(même plugin,references/) et exécutez sonscripts/aispeak_scan.py, qui prend un script.py ou un fichier texte brut.
Générer
Le script utilise uniquement la bibliothèque standard, donc n'importe quel Python 3.10+ fonctionne. <skill> est le dossier de cette compétence.
python -I <skill>/scripts/say.py "Première ligne." "Deuxième ligne." -o out.wav # un seul WAV, intervalles de 0,35 s
python -I <skill>/scripts/say.py --file lines.txt -o clips/ # 000.wav, 001.wav, ...
Options :
| Option | Effet |
|---|---|
--speaker | aiden (par défaut) ; ryan est l'autre voix anglaise native |
--instruct | Texte de style, ex. "Excité, rapide" ou "Calme et sérieux" |
--seed N | Donne une prise reproductible |
--gap S | Secondes de silence entre les lignes dans un fichier joint |
--srt F | Écrit également des sous-titres synchronisés avec le WAV joint (voir Sous-titres) |
--quant | Q8_0 (par défaut, meilleur) ou Q4_K_M (plus petit, un peu plus plat ; téléchargez-le d'abord) |
--check | Rapporte le moteur, les modèles et les dossiers CUDA trouvés |
Comment ça marche :
- Chaque ligne passe par un seul processus, donc le modèle ne se charge qu'une fois (3–8 s). Passez les lignes en bloc, pas un appel par ligne.
- La sortie est un WAV 24 kHz mono 16 bits.
Les prises sont échantillonnées, donc chaque exécution diffère :
- Gardez les clips que vous aimez. Mise en cache par ligne, indexée par texte et paramètres.
- Refaites une ligne si sa longueur est très éloignée d'environ 0,07 s par caractère. Cela signifie que des mots ont été supprimés ou inventés.
Pour une narration vidéo, aussi :
- Réduisez le silence de chaque clip à environ 50 ms de padding, puis placez les lignes sur la timeline avec vos propres intervalles.
- Normalisez le volume de la piste finale :
ffmpeg -af loudnorm=I=-16:TP=-1.5:LRA=11.
Sous-titres (.srt) : proposez-les avec chaque vidéo narrée
Vous savez déjà quand chaque ligne commence et se termine, donc il n'y a rien à transcrire. Construisez les sous-titres à partir de ces timings.
- Avec
say.py: ajoutez--srt narration.srt(nécessite un.wav-o). Les sous-titres correspondent exactement à la piste jointe. - Dans votre propre pipeline : appelez
scripts/srt.py,to_srt([(start_s, end_s, text), ...], display=[...]), avec début/fin en temps vidéo par ligne parlée. Ou utilisez la CLI :python srt.py timings.tsv -o video.srt. - Corrigez les orthographes vocales. Le texte de narration est souvent écrit pour la voix : "D I L", noms réécrits phonétiquement, "runner point HTML", nombres en toutes lettres. Ramenez-les avec
display/--display display.tsv(parléécrit). Prenez les formes écrites de ce qui est affiché à l'écran, et utilisez des chiffres pour les nombres ("232 $", "83 %"). - Vérifiez le résultat. Cherchez les majuscules espacées restantes (
[A-Z] [A-Z]) et " point ". - Règles appliquées par le module :
- au maximum 2 lignes de 42 caractères par sous-titre
- coupe à la phrase, puis à la proposition, puis au mot
- les phrases courtes sont fusionnées pour que rien ne clignote
- le temps est réparti proportionnellement à la longueur
- Nommez-le comme la vidéo (
Nom Vidéo.srtà côté deNom Vidéo.mp4) pour que les lecteurs le chargent automatiquement. Dans VLC,--sub-file=force un fichier.
Le pipeline complet de la compétence whiteboard-explainer dans template/narration/build_vo.py est un exemple complet : mise en cache par ligne, reprises, réduction du silence, modelage des pauses, alignement des mots, table de repères et enveloppe de synchronisation labiale.
Pièges à éviter
- Les DLL CUDA doivent être dans le PATH (Windows). CUDA 13 les garde dans
...\CUDA\v13.x\bin\x64. Sans ce dossier dans le PATH,qwen-tts.exese ferme silencieusement sans sortie.say.pyl'ajoute pour vous. --stream-by-line -o -écrit des en-têtes WAV en streaming (tailles 0x7fffffff), une par ligne. Séparez àRIFF....WAVEfmtet supprimez chaque en-tête de 44 octets. Ne vous fiez pas aux champs de taille.- Aiden lit mal les lettres seules et les jetons étranges. Découvert par un audit Whisper non provoqué :
- "h star" est sorti comme "a hap" ou "our star", "y t" comme "VT", "y" comme "we", et "arXiv" comme "ArcSireFolk".
- Ces solutions fonctionnent : "H star" majuscule / "under H", "why tee" / "why tilde", "archive", et "SMILES check".
- "aitch" est erratique. Un nom peut être correct seul et déformé au milieu d'une phrase : testez-le dans la ligne entière.
- Un K en début de phrase peut disparaître ("Kwenputer" → "When Pooter") : commencez ces lignes par "And" ou "So".
- Auditez en transcrivant chaque clip sans le script comme invite initiale, puis en comparant avec le script. Un Whisper amorcé "entend" les mots prévus et cache les erreurs. Outils :
whiteboard-explainer/template/narration/audit_vo.pyettts_try.py.
- Pauses aux deux-points. Qwen marque une pause de 0,6 à 0,8 s à un deux-points et parfois en milieu de phrase. Dans une narration, limitez chaque silence par la ponctuation qui le précède (
shape()dans build_vo.py du template) et évitez les lignes fragmentées par deux-points ("Premier : ..."). - Solution de secours CPU : Kokoro 82M (
kokoro-onnx) est plat mais rapide sur CPU, idéal pour les brouillons pendant que le GPU est occupé.
Continuez à chercher de meilleurs modèles
La TTS locale s'améliore rapidement, et le choix actuel sera dépassé. Traitez-le comme le champion actuel, pas la réponse finale. Cherchez des modèles plus récents et meilleurs, surtout avant un gros travail de narration, ou lorsque la dernière vérification ci-dessous date de plus de ~6 semaines.
- Analysez. Recherchez sur le web des modèles TTS à poids ouverts publiés ou mis à jour depuis la dernière vérification. Consultez :
- TTS Arena / Artificial Analysis speech leaderboards
- Hugging Face tendances des modèles text-to-speech
- r/LocalLLaMA
- Versions GitHub des moteurs ci-dessous
- Filtrez. Ne gardez un candidat que s'il :
- peut être utilisé localement gratuitement (poids ouverts, licence permissive ou usage personnel)
- fonctionne sur la mémoire GPU et le système d'exploitation de l'utilisateur
- parle la langue requise
- bat le champion en qualité, ou l'égale tout en étant beaucoup plus rapide
- Testez. Générez
eval/lines.txtavec le candidat. Notez-le contre le champion avecscripts/voice_metrics.py(pip install numpy librosa; les enregistrements du champion et de référence sont danseval/) :- Écart-type de la hauteur ≥ ~5 demi-tons et étendue ≥ ~17 demi-tons sont nécessaires pour sonner naturel.
- Mesurez la vitesse en secondes d'audio par seconde de génération.
- Écoutez. Demandez à l'utilisateur de choisir à l'oreille entre le candidat et
eval/champion_*.wav(ffplay -nodisp -autoexit file.wav). Les métriques ne font qu'établir une présélection ; l'oreille décide. - Enregistrez. Mettez à jour la ligne "Dernière vérification" ci-dessous et
references/benchmarks.md, que quelque chose ait changé ou non. Si le champion change, remplacezeval/champion_*.wavet mettez à joursay.py, et demandez avant de supprimer l'ancien modèle.
Dernière vérification : 2026-11-02 (enquête web plus essais locaux VoxCPM2 Q8 et Phonon 31m/Miles). Aiden a été préféré à l'oreille par rapport aux deux. Pas encore testés localement : Phonon 40m, OmniVoice, Breeze TTS 2, Drama 3, Orpheus, Dia, Sesame CSM, Spark-TTS, IndexTTS-2, VibeVoice, Higgs Audio, Kyutai TTS et Fish/OpenAudio. Les détails sont dans references/benchmarks.md.