Stream realtime ASR in non-overlapping chunks

This commit is contained in:
Xulilong 2026-07-15 17:57:31 +08:00
parent 09885dd257
commit d6bce1ef6a
3 changed files with 8 additions and 15 deletions

View File

@ -98,7 +98,6 @@ 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
```
说明:

View File

@ -103,7 +103,6 @@ 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:
@ -262,12 +261,6 @@ 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 ""
@ -286,8 +279,8 @@ def _text_delta(previous: str, current: str) -> str:
async def run_asr_websocket(websocket: WebSocket):
await websocket.accept(subprotocol="binary")
audio_chunks = []
partial_chunks = []
last_partial_at = 0.0
last_partial_text = ""
try:
while True:
@ -295,28 +288,30 @@ async def run_asr_websocket(websocket: WebSocket):
if "bytes" in message and message["bytes"]:
audio_chunks.append(message["bytes"])
partial_chunks.append(message["bytes"])
audio_seconds = sum(len(chunk) for chunk in audio_chunks) / 32000.0
partial_seconds = sum(len(chunk) for chunk in partial_chunks) / 32000.0
now = asyncio.get_running_loop().time()
if (
audio_seconds >= REALTIME_PARTIAL_MIN_SECONDS
partial_seconds >= REALTIME_PARTIAL_MIN_SECONDS
and now - last_partial_at >= REALTIME_PARTIAL_INTERVAL_SECONDS
):
last_partial_at = now
partial_audio = b"".join(partial_chunks)
partial_chunks = []
partial_path = None
try:
partial_path = _write_pcm_wav(_recent_pcm(audio_chunks, REALTIME_PARTIAL_WINDOW_SECONDS))
partial_path = _write_pcm_wav(partial_audio)
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:
if partial_text:
await websocket.send_json({
"mode": "2pass-online",
"text": partial_text,
"timestamp": str(int(audio_seconds * 1000)),
"is_full_text": True,
"is_final": False,
})
last_partial_text = partial_text
except Exception as e:
logger.warning(f"实时部分识别失败: {e}")
finally:

View File

@ -31,7 +31,6 @@ 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