Stream realtime ASR in non-overlapping chunks
This commit is contained in:
parent
09885dd257
commit
d6bce1ef6a
@ -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
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
21
asr/main.py
21
asr/main.py
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user