fix(voix): intonation stable, lecture vérifiée et SRT sans mots coupés

Intonation :
- Gemini reçoit une température basse et une graine fixe (réglables) : la
  lecture ne change plus de ton d'une génération à l'autre
- SRT : tout le texte est lu d'une seule traite puis découpé phrase par
  phrase, au lieu d'une génération (et d'une intonation) par sous-titre ;
  un seul traitement de niveau, plus de sauts de volume entre les phrases

Mots coupés :
- La voix est découpée dans ses vrais silences (silencedetect), plus aux
  bornes des mots estimées par Whisper qui rognaient les syllabes ; fondus
  de 10 ms aux coupes
- Whisper n'est plus amorcé avec le texte attendu : il hallucinait des
  répétitions qui masquaient les fins tronquées

Fiabilité :
- Chaque voix Gemini est réécoutée : mots sautés, fin tronquée ou consigne
  lue à voix haute déclenchent une nouvelle génération (3 au plus), puis
  repli sur Edge avec avertissement
- Consigne de lecture intégrale ajoutée au prompt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Ze4bs7tpF1KGWUk6ZZSZ4
This commit is contained in:
Claude committed 2026-09-28 09:04:08 +00:00
1 parent f3113a0f5a
commit 44899055b2
11 files changed
+454 -127

No files matched your search

+7 -4
View File
@@ -116,12 +116,15 @@ def _model():
return WhisperModel(config.WHISPER_MODEL, device="cpu", compute_type=config.WHISPER_COMPUTE_TYPE)
def _transcribe_sync(audio: Path, hint: str | None) -> list[Word]:
def _transcribe_sync(audio: Path) -> list[Word]:
# Pas de texte attendu en amorce (initial_prompt) ni d'enchaînement sur le
# segment précédent : les deux font halluciner Whisper, qui répète la phrase
# et masque une fin tronquée. On veut entendre ce qui est réellement dit.
segments, _ = _model().transcribe(
str(audio),
language="fr",
word_timestamps=True,
initial_prompt=hint or None,
condition_on_previous_text=False,
vad_filter=False,
beam_size=5,
)
@@ -133,6 +136,6 @@ def _transcribe_sync(audio: Path, hint: str | None) -> list[Word]:
]
async def transcribe(audio: Path, hint: str | None = None) -> list[Word]:
async def transcribe(audio: Path) -> list[Word]:
"""Mots prononcés et leurs instants (Whisper, exécuté hors de la boucle async)."""
return await asyncio.to_thread(_transcribe_sync, audio, hint)
return await asyncio.to_thread(_transcribe_sync, audio)