mirror of
https://github.com/R0m1k3/LiveFlow.git
synced 2026-10-15 11:19:31 +02:00
Micro : amplification serveur avant le VAD + diarisation stabilisée
Captation micro : - Plus AUCUN test/bascule de micro automatique (choix manuel uniquement) - Amplification adaptative (AGC) déplacée dans le segmenteur, AVANT la détection de parole : un micro faible (casque) était sous le seuil du VAD, aucune phrase n'était détectée. Validé : signal crête 1473 -> 16600, capté. - Le client envoie l'audio brut (plus de gain navigateur qui se battait avec) ; vumètre affiché amplifié pour rester lisible Diarisation (locuteurs éclatés en 3, 4, 5... pour une même personne) : - Seuil abaissé (0.70 -> 0.35) + zone morte large : un nouveau locuteur n'est créé que si le score est nettement bas ET le segment assez long (>=1.2s) - Centroïde plus stable (EMA alpha 0.15) + adhérence au dernier locuteur - Validé : 1 personne reste 1 locuteur même très bruitée ; 2-3 voix séparées https://claude.ai/code/session_01YHMp3EKzr4s6o8w1ygxuUe
This commit is contained in:
4 files changed
+85
-61
No files matched your search
@@ -9,6 +9,8 @@ silence prolongé ou une durée maximale.
|
||||
from collections import deque
|
||||
from dataclasses import dataclass
|
||||
|
||||
import audioop
|
||||
|
||||
import webrtcvad
|
||||
|
||||
SAMPLE_RATE = 16000
|
||||
@@ -22,6 +24,15 @@ SILENCE_END_MS = 700 # silence qui clôt un segment
|
||||
MIN_SPEECH_MS = 300 # en dessous, le segment est ignoré (bruit)
|
||||
MAX_SEGMENT_S = 25 # coupe forcée pour garder une latence raisonnable
|
||||
|
||||
# Contrôle de gain automatique appliqué AVANT la détection de parole : sans lui,
|
||||
# un micro faible (casque...) reste sous le seuil du VAD et aucune phrase n'est
|
||||
# détectée. On amène le niveau crête vers une cible exploitable.
|
||||
AGC_TARGET_PEAK = 11000 # niveau crête visé (échelle int16)
|
||||
AGC_NOISE_FLOOR = 120 # en dessous : du silence, on n'amplifie pas
|
||||
AGC_MAX_GAIN = 60.0 # amplification maximale
|
||||
AGC_ATTACK = 0.5 # vitesse de montée du gain (0-1)
|
||||
AGC_RELEASE = 0.2 # vitesse de descente du gain (0-1)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Segment:
|
||||
@@ -30,9 +41,29 @@ class Segment:
|
||||
t1: float
|
||||
|
||||
|
||||
class _AutoGain:
|
||||
"""Amplification adaptative du flux pour fiabiliser la détection de parole."""
|
||||
|
||||
def __init__(self):
|
||||
self.gain = 1.0
|
||||
|
||||
def process(self, frame: bytes) -> bytes:
|
||||
peak = audioop.max(frame, 2)
|
||||
if peak >= AGC_NOISE_FLOOR:
|
||||
desired = min(AGC_MAX_GAIN, AGC_TARGET_PEAK / peak)
|
||||
rate = AGC_ATTACK if desired > self.gain else AGC_RELEASE
|
||||
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)
|
||||
return frame
|
||||
|
||||
|
||||
|
||||
class SpeechSegmenter:
|
||||
def __init__(self):
|
||||
self._vad = webrtcvad.Vad(VAD_AGGRESSIVENESS)
|
||||
self._agc = _AutoGain()
|
||||
self._pending = bytearray()
|
||||
self._ring: deque[tuple[bytes, bool]] = deque(maxlen=PREROLL_FRAMES)
|
||||
self._frame_index = 0
|
||||
@@ -49,6 +80,7 @@ class SpeechSegmenter:
|
||||
while len(self._pending) >= FRAME_BYTES:
|
||||
frame = bytes(self._pending[:FRAME_BYTES])
|
||||
del self._pending[:FRAME_BYTES]
|
||||
frame = self._agc.process(frame) # amplifie avant le VAD
|
||||
seg = self._process_frame(frame)
|
||||
if seg is not None:
|
||||
segments.append(seg)
|
||||
|
||||
Reference in new issue
Block a user