67 lines
3.0 KiB
Python
67 lines
3.0 KiB
Python
"""Real inference using ONLY explicitly generated synthetic Chinese speech."""
|
|
from pathlib import Path
|
|
import hashlib
|
|
import io
|
|
import json
|
|
import socket
|
|
import sys
|
|
import time
|
|
from unittest import mock
|
|
|
|
sys.dont_write_bytecode = True
|
|
sys.path.insert(0, r"C:\wechat_rpa")
|
|
import av
|
|
import numpy as np
|
|
import pysilk
|
|
import voice_transcription as voice
|
|
|
|
root = Path(__file__).resolve().parent
|
|
wav = root / "synthetic-chinese.wav"
|
|
silk = root / "synthetic-chinese.silk"
|
|
amr = root / "synthetic-chinese.amr"
|
|
audio = voice._decode_audio(wav.read_bytes())
|
|
pcm = (np.clip(audio, -1, 1) * 32767).astype("<i2")
|
|
with silk.open("wb") as target:
|
|
pysilk.encode(io.BytesIO(pcm.tobytes()), target, 16000, 24000, tencent=True)
|
|
with av.open(str(amr), "w", format="amr") as target:
|
|
stream = target.add_stream("libopencore_amrnb", rate=8000)
|
|
stream.bit_rate = 12200
|
|
stream.layout = "mono"
|
|
downsampled = pcm[::2]
|
|
for offset in range(0, len(downsampled), 160):
|
|
samples = downsampled[offset:offset + 160]
|
|
if len(samples) < 160:
|
|
samples = np.pad(samples, (0, 160 - len(samples)))
|
|
frame = av.AudioFrame.from_ndarray(np.ascontiguousarray(samples).reshape(1, -1), format="s16", layout="mono")
|
|
frame.sample_rate = 8000
|
|
for packet in stream.encode(frame):
|
|
target.mux(packet)
|
|
for packet in stream.encode(None):
|
|
target.mux(packet)
|
|
|
|
attempts = []
|
|
def no_network(*args, **kwargs):
|
|
attempts.append("network")
|
|
raise AssertionError("Synthetic offline ASR forbids network")
|
|
|
|
report = {"input_kind": "local Microsoft Huihui Desktop synthetic speech",
|
|
"input_text": "这是自动语音识别测试。请问今天下午三点可以预约吗?我想了解预约流程。",
|
|
"network_attempts": attempts, "results": []}
|
|
recognizer = voice.OfflineRecognizer(Path(r"C:\wechat_rpa\assets\asr\faster-whisper-base"))
|
|
with mock.patch.object(socket.socket, "connect", no_network), mock.patch.object(socket.socket, "connect_ex", no_network), mock.patch.object(socket, "getaddrinfo", no_network):
|
|
for path in (wav, silk, amr):
|
|
started = time.monotonic()
|
|
try:
|
|
result = voice.transcribe_file(path, allowed_roots=[root], recognizer=recognizer)
|
|
result["meaning_verified"] = "预约" in result["text"] and any(value in result["text"] for value in ("三点", "3点"))
|
|
except voice.VoiceTranscriptionError as exc:
|
|
result = {"status": "error", "error_code": exc.code, "meaning_verified": False}
|
|
result["format"] = path.suffix
|
|
result["elapsed_seconds"] = round(time.monotonic() - started, 3)
|
|
result["input_sha256"] = hashlib.sha256(path.read_bytes()).hexdigest()
|
|
report["results"].append(result)
|
|
print(json.dumps(result, ensure_ascii=False), flush=True)
|
|
report["ok"] = not attempts and all(row["status"] == "ready" and row["meaning_verified"] for row in report["results"])
|
|
(root / "real-offline-asr.json").write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
raise SystemExit(0 if report["ok"] else 1)
|