mirror of
https://github.com/R0m1k3/LiveFlow.git
synced 2026-10-21 22:18:16 +02:00
Fiabilité : la diarisation ne peut plus bloquer ni casser la transcription
- Timeout de 5 s sur l'identification du locuteur (asyncio.wait_for) : si le modèle ECAPA tarde ou se bloque sur CPU, on ignore le locuteur mais le texte est transcrit et affiché quand même - AGC du segmenteur protégé : une trame problématique renvoie l'audio brut au lieu d'interrompre le découpage Testé : texte transcrit malgré un diariseur qui se bloque indéfiniment. https://claude.ai/code/session_01YHMp3EKzr4s6o8w1ygxuUe
This commit is contained in:
2 files changed
+19
-11
No files matched your search
+6
-3
@@ -283,13 +283,16 @@ async def ws_transcribe(ws: WebSocket):
|
|||||||
f"({len(seg.pcm) / 2 / SAMPLE_RATE:.1f}s d'audio, niveau crête {peak}"
|
f"({len(seg.pcm) / 2 / SAMPLE_RATE:.1f}s d'audio, niveau crête {peak}"
|
||||||
f"{', amplifié' if pcm is not seg.pcm else ''}), transcription...", flush=True)
|
f"{', amplifié' if pcm is not seg.pcm else ''}), transcription...", flush=True)
|
||||||
|
|
||||||
# Diarisation (~30-80 ms CPU, dans un thread pour ne pas bloquer)
|
# Diarisation (~30-80 ms CPU). Strictement isolée : ni un blocage
|
||||||
|
# ni une erreur ne doivent empêcher la transcription qui suit.
|
||||||
speaker = ""
|
speaker = ""
|
||||||
if diarizer is not None:
|
if diarizer is not None:
|
||||||
try:
|
try:
|
||||||
speaker = await asyncio.to_thread(diarizer.identify, pcm)
|
speaker = await asyncio.wait_for(
|
||||||
|
asyncio.to_thread(diarizer.identify, pcm), timeout=5.0
|
||||||
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print(f"[réunion {meeting_id}] diarisation échouée : {exc}", flush=True)
|
print(f"[réunion {meeting_id}] diarisation ignorée : {exc}", flush=True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
text = await transcribe(pcm)
|
text = await transcribe(pcm)
|
||||||
|
|||||||
+13
-8
@@ -48,14 +48,19 @@ class _AutoGain:
|
|||||||
self.gain = 1.0
|
self.gain = 1.0
|
||||||
|
|
||||||
def process(self, frame: bytes) -> bytes:
|
def process(self, frame: bytes) -> bytes:
|
||||||
peak = audioop.max(frame, 2)
|
# Toute erreur ici ne doit JAMAIS interrompre le découpage : on
|
||||||
if peak >= AGC_NOISE_FLOOR:
|
# renvoie la trame d'origine en cas de souci.
|
||||||
desired = min(AGC_MAX_GAIN, AGC_TARGET_PEAK / peak)
|
try:
|
||||||
rate = AGC_ATTACK if desired > self.gain else AGC_RELEASE
|
peak = audioop.max(frame, 2)
|
||||||
self.gain += (desired - self.gain) * rate
|
if peak >= AGC_NOISE_FLOOR:
|
||||||
self.gain = max(1.0, min(AGC_MAX_GAIN, self.gain))
|
desired = min(AGC_MAX_GAIN, AGC_TARGET_PEAK / peak)
|
||||||
if self.gain > 1.01:
|
rate = AGC_ATTACK if desired > self.gain else AGC_RELEASE
|
||||||
return audioop.mul(frame, 2, self.gain)
|
self.gain += (desired - self.gain) * rate
|
||||||
|
self.gain = max(1.0, min(AGC_MAX_GAIN, self.gain))
|
||||||
|
if self.gain > 1.01:
|
||||||
|
return audioop.mul(frame, 2, self.gain) # audioop sature, ne déborde pas
|
||||||
|
except Exception:
|
||||||
|
return frame
|
||||||
return frame
|
return frame
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user