mirror of
https://github.com/Alkatrazz24/Funk-lab.git
synced 2026-07-08 15:54:42 +02:00
feat(stt-client): stt --voices / --install-voice (changer de voix Piper) (#56)
Faciliter le changement de voix (le simple --update ne bascule rien : la config perso prime sur le défaut). - `stt --voices` : liste les voix Piper FR (homme/femme), URLs vérifiées sur HuggingFace rhasspy/piper-voices. - `stt --install-voice fr_FR-tom-medium` : télécharge .onnx + .onnx.json et SÉLECTIONNE la voix (écrit tts_engine=piper + piper_voice dans stt.toml, en préservant les autres réglages). Puis `stt --restart`. - doc : admin/ia/stt.md (note « pourquoi --update ne change pas la voix »). Client 0.16.0. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
610d32f1e9
commit
0b117916be
3 changed files with 97 additions and 1 deletions
|
|
@ -257,6 +257,12 @@ Côté client, pilotable depuis `stt/client/config/` + l'écran de réglages du
|
|||
démarrage et **retombe en silence** proprement si le modèle/paquet manque (`engine._synth_kokoro`).
|
||||
La synthèse est enfichable (`engine._synthesize`) — Piper reste le repli.
|
||||
|
||||
**Changer de voix Piper (homme/femme)** : `stt --voices` liste les voix FR ; `stt --install-voice
|
||||
fr_FR-tom-medium` télécharge la voix (`.onnx` + `.onnx.json` depuis HuggingFace rhasspy/piper-voices)
|
||||
**et la sélectionne** (`tts_engine=piper` + `piper_voice` écrits dans `stt.toml`). Puis `stt --restart`.
|
||||
> ⚠️ Changer la voix nécessite d'écrire `tts_engine`/`piper_voice` dans le `stt.toml` **existant** :
|
||||
> un simple `stt --update` ne bascule rien (la config perso prime sur le défaut) — d'où ces commandes.
|
||||
|
||||
---
|
||||
|
||||
## Prérequis / dépendances
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[project]
|
||||
name = "stt"
|
||||
version = "0.15.0"
|
||||
version = "0.16.0"
|
||||
description = "STT — client vocal Jarvis du homelab Funk (voix + HUD, parle au STT-server)"
|
||||
requires-python = ">=3.11"
|
||||
readme = "README.md"
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ def _running_stt_pids() -> list[int]:
|
|||
"--stop", "--start", "--restart", "--update", "--version", "--setup", "--text",
|
||||
"--install-service", "--uninstall-service",
|
||||
"--install-desktop", "--uninstall-desktop", "--install-kokoro",
|
||||
"--voices", "--install-voice",
|
||||
}
|
||||
pids: list[int] = []
|
||||
try:
|
||||
|
|
@ -472,6 +473,87 @@ def _cmd_install_kokoro() -> int:
|
|||
return 0
|
||||
|
||||
|
||||
# Voix Piper FR (vérifiées sur HuggingFace rhasspy/piper-voices). Nom → description.
|
||||
_PIPER_FR_VOICES = {
|
||||
"fr_FR-upmc-medium": "femme (UPMC) — défaut",
|
||||
"fr_FR-siwis-medium": "femme (SIWIS)",
|
||||
"fr_FR-tom-medium": "homme (Tom)",
|
||||
"fr_FR-gilles-low": "homme (Gilles)",
|
||||
"fr_FR-mls-medium": "multi-locuteurs (MLS)",
|
||||
}
|
||||
|
||||
|
||||
def _cmd_voices() -> int:
|
||||
print("Voix Piper FR — installe avec : stt --install-voice <nom>\n")
|
||||
for name, desc in _PIPER_FR_VOICES.items():
|
||||
print(f" • {name:22} {desc}")
|
||||
print("\nVoix Kokoro (plus naturelle, FR : ff_siwis) → stt --install-kokoro")
|
||||
print("Après installation d'une voix : stt --restart")
|
||||
return 0
|
||||
|
||||
|
||||
def _set_voice_config(updates: dict) -> None:
|
||||
"""Met à jour les clés [voice] dans ~/.config/stt/stt.toml (préserve les autres valeurs)."""
|
||||
import tomllib
|
||||
|
||||
import tomli_w
|
||||
from stt.config import CONFIG_DIR, CONFIG_PATH
|
||||
|
||||
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
||||
cfg: dict = {}
|
||||
if CONFIG_PATH.exists():
|
||||
with CONFIG_PATH.open("rb") as f:
|
||||
cfg = tomllib.load(f)
|
||||
cfg.setdefault("voice", {}).update(updates)
|
||||
with CONFIG_PATH.open("wb") as f:
|
||||
tomli_w.dump(cfg, f)
|
||||
|
||||
|
||||
def _cmd_install_voice(name: str) -> int:
|
||||
"""Télécharge une voix Piper FR (.onnx + .onnx.json) et la sélectionne dans la config."""
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
parts = name.split("-")
|
||||
if len(parts) != 3 or "_" not in parts[0]:
|
||||
print(f"Nom invalide : {name!r} (attendu ex. fr_FR-tom-medium — voir `stt --voices`).")
|
||||
return 1
|
||||
lc, speaker, quality = parts
|
||||
lang = lc.split("_")[0]
|
||||
base = ("https://huggingface.co/rhasspy/piper-voices/resolve/main/"
|
||||
f"{lang}/{lc}/{speaker}/{quality}/{name}")
|
||||
dest = Path.home() / ".local/share/piper"
|
||||
dest.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def hook(blocks: int, bsize: int, total: int) -> None:
|
||||
if total > 0:
|
||||
print(f"\r {min(100, blocks * bsize * 100 // total)}%", end="", flush=True)
|
||||
|
||||
for ext in (".onnx", ".onnx.json"):
|
||||
target = dest / f"{name}{ext}"
|
||||
if target.exists() and target.stat().st_size > 0:
|
||||
print(f"✓ {name}{ext} déjà présent")
|
||||
continue
|
||||
print(f"⬇ {name}{ext}…")
|
||||
try:
|
||||
urllib.request.urlretrieve(f"{base}{ext}", target, hook) # noqa: S310
|
||||
print()
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f"\n❌ {name}{ext} : HTTP {e.code} (voix introuvable ? `stt --voices`)")
|
||||
target.unlink(missing_ok=True)
|
||||
return 1
|
||||
except Exception as e: # noqa: BLE001
|
||||
print(f"\n❌ {name}{ext} : {e}")
|
||||
target.unlink(missing_ok=True)
|
||||
return 1
|
||||
|
||||
_set_voice_config({"tts_engine": "piper", "piper_voice": name})
|
||||
print(f"\n✓ Voix « {name} » installée et sélectionnée (tts_engine=piper).")
|
||||
print(" Applique : stt --restart")
|
||||
return 0
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
p = argparse.ArgumentParser(prog="stt", description="Client vocal Jarvis de Funk")
|
||||
p.add_argument("--setup", action="store_true", help="Écrit la config par défaut")
|
||||
|
|
@ -499,6 +581,10 @@ def main(argv: list[str] | None = None) -> int:
|
|||
help="Met à jour STT depuis git (pipx reinstall)")
|
||||
p.add_argument("--install-kokoro", action="store_true",
|
||||
help="Télécharge le modèle Kokoro (TTS naturel) dans ~/.local/share/kokoro")
|
||||
p.add_argument("--voices", action="store_true",
|
||||
help="Liste les voix Piper FR installables (homme/femme)")
|
||||
p.add_argument("--install-voice", metavar="NOM",
|
||||
help="Télécharge une voix Piper FR et la sélectionne (ex. fr_FR-tom-medium)")
|
||||
p.add_argument("--version", action="store_true", help="Affiche la version installée")
|
||||
args = p.parse_args(argv)
|
||||
|
||||
|
|
@ -510,6 +596,10 @@ def main(argv: list[str] | None = None) -> int:
|
|||
return _cmd_update()
|
||||
if args.install_kokoro:
|
||||
return _cmd_install_kokoro()
|
||||
if args.voices:
|
||||
return _cmd_voices()
|
||||
if args.install_voice:
|
||||
return _cmd_install_voice(args.install_voice)
|
||||
if args.start:
|
||||
return _cmd_start()
|
||||
if args.restart:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue