|
@@ -4,6 +4,24 @@ import tempfile
|
|
|
import os
|
|
import os
|
|
|
import numpy as np
|
|
import numpy as np
|
|
|
import time
|
|
import time
|
|
|
|
|
+import logging
|
|
|
|
|
+import os
|
|
|
|
|
+
|
|
|
|
|
+LOG_LEVEL = os.getenv("WHISPER_LOG_LEVEL", "INFO").upper()
|
|
|
|
|
+VAD_ENABLED = env_bool("WHISPER_VAD", True)
|
|
|
|
|
+
|
|
|
|
|
+logging.basicConfig(
|
|
|
|
|
+ level=getattr(logging, LOG_LEVEL, logging.INFO),,
|
|
|
|
|
+ format="%(asctime)s [%(levelname)s] %(message)s"
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+logger = logging.getLogger("whisper-ws")
|
|
|
|
|
+
|
|
|
|
|
+logger.info(f"Log level = {LOG_LEVEL}")
|
|
|
|
|
+logger.info(f"VAD enabled = {VAD_ENABLED}")
|
|
|
|
|
+
|
|
|
|
|
+def env_bool(name: str, default: bool = False) -> bool:
|
|
|
|
|
+ return os.getenv(name, str(default)).lower() in ("1", "true", "yes", "on")
|
|
|
|
|
|
|
|
app = FastAPI()
|
|
app = FastAPI()
|
|
|
|
|
|
|
@@ -52,6 +70,8 @@ MAX_AUDIO_SEC = 10.0
|
|
|
async def ws_transcribe(ws: WebSocket):
|
|
async def ws_transcribe(ws: WebSocket):
|
|
|
await ws.accept()
|
|
await ws.accept()
|
|
|
|
|
|
|
|
|
|
+ logger.info("WebSocket connecté !")
|
|
|
|
|
+
|
|
|
audio_buffer = []
|
|
audio_buffer = []
|
|
|
start_time = time.time()
|
|
start_time = time.time()
|
|
|
|
|
|
|
@@ -59,39 +79,53 @@ async def ws_transcribe(ws: WebSocket):
|
|
|
while True:
|
|
while True:
|
|
|
data = await ws.receive_bytes()
|
|
data = await ws.receive_bytes()
|
|
|
|
|
|
|
|
|
|
+ logger.debug(f"Audio reçu: {len(data)} bytes")
|
|
|
|
|
+
|
|
|
# PCM int16 → float32
|
|
# PCM int16 → float32
|
|
|
pcm = np.frombuffer(data, dtype=np.int16).astype(np.float32) / 32768.0
|
|
pcm = np.frombuffer(data, dtype=np.int16).astype(np.float32) / 32768.0
|
|
|
audio_buffer.append(pcm)
|
|
audio_buffer.append(pcm)
|
|
|
|
|
|
|
|
audio_len = sum(len(chunk) for chunk in audio_buffer) / SAMPLE_RATE
|
|
audio_len = sum(len(chunk) for chunk in audio_buffer) / SAMPLE_RATE
|
|
|
|
|
|
|
|
|
|
+ logger.debug(f"Buffer audio: {audio_len:.2f}s")
|
|
|
|
|
+
|
|
|
# Sécurité : reset si trop long
|
|
# Sécurité : reset si trop long
|
|
|
if audio_len > MAX_AUDIO_SEC:
|
|
if audio_len > MAX_AUDIO_SEC:
|
|
|
|
|
+ logger.warning("Buffer trop long, reset")
|
|
|
audio_buffer.clear()
|
|
audio_buffer.clear()
|
|
|
start_time = time.time()
|
|
start_time = time.time()
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
# Transcription dès qu'on a assez d'audio
|
|
# Transcription dès qu'on a assez d'audio
|
|
|
if audio_len >= MIN_AUDIO_SEC:
|
|
if audio_len >= MIN_AUDIO_SEC:
|
|
|
|
|
+ logger.info(f"Transcription ({audio_len:.2f}s)")
|
|
|
|
|
+
|
|
|
audio = np.concatenate(audio_buffer)
|
|
audio = np.concatenate(audio_buffer)
|
|
|
audio_buffer.clear()
|
|
audio_buffer.clear()
|
|
|
|
|
|
|
|
segments, _ = model.transcribe(
|
|
segments, _ = model.transcribe(
|
|
|
audio,
|
|
audio,
|
|
|
language="fr",
|
|
language="fr",
|
|
|
- vad_filter=True
|
|
|
|
|
|
|
+ vad_filter=VAD_ENABLED
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
text = " ".join(seg.text for seg in segments).strip()
|
|
text = " ".join(seg.text for seg in segments).strip()
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+ logger.info(f"Texte: '{text}'")
|
|
|
|
|
+
|
|
|
if text:
|
|
if text:
|
|
|
await ws.send_json({
|
|
await ws.send_json({
|
|
|
"type": "final",
|
|
"type": "final",
|
|
|
"text": text
|
|
"text": text
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+ logger.info("Résultat envoyé")
|
|
|
|
|
+
|
|
|
except WebSocketDisconnect:
|
|
except WebSocketDisconnect:
|
|
|
- pass
|
|
|
|
|
|
|
+ logger.info("WebSocket déconnecté")
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
|
|
+ logger.exception("Erreur de WebSocket")
|
|
|
|
|
+
|
|
|
await ws.send_json({"error": str(e)})
|
|
await ws.send_json({"error": str(e)})
|
|
|
await ws.close()
|
|
await ws.close()
|