From 09885dd2573a8ffb29b9954c12c78d35b5ad7a18 Mon Sep 17 00:00:00 2001 From: Xulilong Date: Wed, 15 Jul 2026 17:53:53 +0800 Subject: [PATCH] Use sliding window for realtime ASR preview --- README.md | 1 + asr/main.py | 22 ++++++++++++++++------ config.env | 1 + 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d090827..9cbd6a6 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,7 @@ VOICEPRINT_STORE=/data/asr_voiceprints.json VOICEPRINT_MATCH_THRESHOLD=0.45 REALTIME_PARTIAL_MIN_SECONDS=1.5 REALTIME_PARTIAL_INTERVAL_SECONDS=2.0 +REALTIME_PARTIAL_WINDOW_SECONDS=6.0 ``` 说明: diff --git a/asr/main.py b/asr/main.py index 6e0b0c6..8c17c54 100644 --- a/asr/main.py +++ b/asr/main.py @@ -103,6 +103,7 @@ VOICEPRINT_STORE = os.environ.get("VOICEPRINT_STORE", "/tmp/asr_voiceprints.json VOICEPRINT_MATCH_THRESHOLD = float(os.environ.get("VOICEPRINT_MATCH_THRESHOLD", "0.45")) REALTIME_PARTIAL_INTERVAL_SECONDS = float(os.environ.get("REALTIME_PARTIAL_INTERVAL_SECONDS", "2.0")) REALTIME_PARTIAL_MIN_SECONDS = float(os.environ.get("REALTIME_PARTIAL_MIN_SECONDS", "1.5")) +REALTIME_PARTIAL_WINDOW_SECONDS = float(os.environ.get("REALTIME_PARTIAL_WINDOW_SECONDS", "6.0")) def _voiceprint_key(database_name: str, collection_name: str) -> str: @@ -261,6 +262,12 @@ def _write_pcm_wav(audio_bytes: bytes) -> str: return tmp_path +def _recent_pcm(audio_chunks: list[bytes], window_seconds: float) -> bytes: + max_bytes = max(int(window_seconds * 32000), 32000) + audio_bytes = b"".join(audio_chunks) + return audio_bytes[-max_bytes:] + + def _text_delta(previous: str, current: str) -> str: previous = previous or "" current = current or "" @@ -298,7 +305,7 @@ async def run_asr_websocket(websocket: WebSocket): last_partial_at = now partial_path = None try: - partial_path = _write_pcm_wav(b"".join(audio_chunks)) + partial_path = _write_pcm_wav(_recent_pcm(audio_chunks, REALTIME_PARTIAL_WINDOW_SECONDS)) result = await run_asr(partial_path, language="zh", response_format="json") partial_text = result.get("text", "") if isinstance(result, dict) else "" if partial_text and partial_text != last_partial_text: @@ -341,11 +348,14 @@ async def run_asr_websocket(websocket: WebSocket): if isinstance(result, dict): text = result.get("text", "") - await websocket.send_json({ - "mode": "2pass-offline", - "text": text, - "is_final": True, - }) + try: + await websocket.send_json({ + "mode": "2pass-offline", + "text": text, + "is_final": True, + }) + except Exception as e: + logger.warning(f"Skip final transcript because websocket is closed: {e}") finally: if tmp_path and os.path.exists(tmp_path): os.unlink(tmp_path) diff --git a/config.env b/config.env index 6c35916..b535128 100644 --- a/config.env +++ b/config.env @@ -31,6 +31,7 @@ VOICEPRINT_STORE=/data/asr_voiceprints.json VOICEPRINT_MATCH_THRESHOLD=0.45 REALTIME_PARTIAL_MIN_SECONDS=1.5 REALTIME_PARTIAL_INTERVAL_SECONDS=2.0 +REALTIME_PARTIAL_WINDOW_SECONDS=6.0 # fastapi_wss legacy/local ASR-with-speaker helper ASR_LOCAL_URL=http://asr-test:59805/asr