[本地语音降噪]:完成本地语音链路和噪音过滤,包含降噪模型、本地ASR和实时字幕稳定策略
This commit is contained in:
@@ -11,15 +11,20 @@ from .models import ErrorCode, ProviderError
|
||||
DEFAULT_VAD_URL = "https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx"
|
||||
DEFAULT_STT_URL = (
|
||||
"https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/"
|
||||
"sherpa-onnx-streaming-zipformer-zh-14M-2023-02-23.tar.bz2"
|
||||
"sherpa-onnx-streaming-zipformer-ctc-zh-int8-2025-06-30.tar.bz2"
|
||||
)
|
||||
DEFAULT_STT_DIR = "sherpa-onnx-streaming-zipformer-zh-14M-2023-02-23"
|
||||
DEFAULT_STT_DIR = "sherpa-onnx-streaming-zipformer-ctc-zh-int8-2025-06-30"
|
||||
DEFAULT_KWS_URL = (
|
||||
"https://github.com/k2-fsa/sherpa-onnx/releases/download/kws-models/"
|
||||
"sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01-mobile.tar.bz2"
|
||||
)
|
||||
DEFAULT_KWS_DIR = "sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01-mobile"
|
||||
DEFAULT_KWS_KEYWORDS = "x iǎo j ié x iǎo j ié @小杰小杰\n"
|
||||
DEFAULT_DENOISER_URL = (
|
||||
"https://github.com/k2-fsa/sherpa-onnx/releases/download/"
|
||||
"speech-enhancement-models/gtcrn_simple.onnx"
|
||||
)
|
||||
DEFAULT_DENOISER_PATH = "denoise/gtcrn_simple.onnx"
|
||||
|
||||
REQUIRED_MODEL_FILES = (
|
||||
f"wake/{DEFAULT_KWS_DIR}/tokens.txt",
|
||||
@@ -29,9 +34,8 @@ REQUIRED_MODEL_FILES = (
|
||||
"wake/keywords.txt",
|
||||
"vad/silero_vad.onnx",
|
||||
f"stt/{DEFAULT_STT_DIR}/tokens.txt",
|
||||
f"stt/{DEFAULT_STT_DIR}/encoder-epoch-99-avg-1.int8.onnx",
|
||||
f"stt/{DEFAULT_STT_DIR}/decoder-epoch-99-avg-1.onnx",
|
||||
f"stt/{DEFAULT_STT_DIR}/joiner-epoch-99-avg-1.int8.onnx",
|
||||
f"stt/{DEFAULT_STT_DIR}/model.int8.onnx",
|
||||
DEFAULT_DENOISER_PATH,
|
||||
)
|
||||
|
||||
|
||||
@@ -68,6 +72,7 @@ def default_manifest() -> dict[str, Any]:
|
||||
"wake": DEFAULT_KWS_URL,
|
||||
"vad": DEFAULT_VAD_URL,
|
||||
"stt": DEFAULT_STT_URL,
|
||||
"denoiser": DEFAULT_DENOISER_URL,
|
||||
},
|
||||
"providers": {
|
||||
"wake": {
|
||||
@@ -84,12 +89,14 @@ def default_manifest() -> dict[str, Any]:
|
||||
"path": "vad/silero_vad.onnx",
|
||||
},
|
||||
"stt": {
|
||||
"type": "sherpa-onnx-streaming-transducer",
|
||||
"type": "sherpa-onnx-streaming-zipformer2-ctc",
|
||||
"model_dir": f"stt/{DEFAULT_STT_DIR}",
|
||||
"tokens": f"stt/{DEFAULT_STT_DIR}/tokens.txt",
|
||||
"encoder": f"stt/{DEFAULT_STT_DIR}/encoder-epoch-99-avg-1.int8.onnx",
|
||||
"decoder": f"stt/{DEFAULT_STT_DIR}/decoder-epoch-99-avg-1.onnx",
|
||||
"joiner": f"stt/{DEFAULT_STT_DIR}/joiner-epoch-99-avg-1.int8.onnx",
|
||||
"model": f"stt/{DEFAULT_STT_DIR}/model.int8.onnx",
|
||||
},
|
||||
"denoiser": {
|
||||
"type": "sherpa-onnx-gtcrn",
|
||||
"path": DEFAULT_DENOISER_PATH,
|
||||
},
|
||||
},
|
||||
"required_files": list(REQUIRED_MODEL_FILES),
|
||||
@@ -127,6 +134,13 @@ def vad_model_path(models_dir: str | Path) -> Path:
|
||||
return root / str(path)
|
||||
|
||||
|
||||
def denoiser_model_path(models_dir: str | Path) -> Path:
|
||||
root = Path(models_dir)
|
||||
manifest = load_manifest(root)
|
||||
path = manifest.get("providers", {}).get("denoiser", {}).get("path", DEFAULT_DENOISER_PATH)
|
||||
return root / str(path)
|
||||
|
||||
|
||||
def wake_model_paths(models_dir: str | Path) -> dict[str, Path]:
|
||||
root = Path(models_dir)
|
||||
manifest = load_manifest(root)
|
||||
@@ -144,12 +158,21 @@ def wake_model_paths(models_dir: str | Path) -> dict[str, Path]:
|
||||
}
|
||||
|
||||
|
||||
def stt_model_paths(model_path: str | Path) -> dict[str, Path]:
|
||||
def stt_model_paths(model_path: str | Path) -> dict[str, Any]:
|
||||
root = Path(model_path)
|
||||
if (root / "manifest.json").exists() or (root / "stt").exists():
|
||||
manifest = load_manifest(root)
|
||||
stt = manifest.get("providers", {}).get("stt", {})
|
||||
stt_type = str(stt.get("type", "sherpa-onnx-streaming-transducer"))
|
||||
if stt_type == "sherpa-onnx-streaming-zipformer2-ctc":
|
||||
return {
|
||||
"type": stt_type,
|
||||
"model_dir": root / str(stt.get("model_dir", f"stt/{DEFAULT_STT_DIR}")),
|
||||
"tokens": root / str(stt.get("tokens", f"stt/{DEFAULT_STT_DIR}/tokens.txt")),
|
||||
"model": root / str(stt.get("model", f"stt/{DEFAULT_STT_DIR}/model.int8.onnx")),
|
||||
}
|
||||
return {
|
||||
"type": stt_type,
|
||||
"model_dir": root / str(stt.get("model_dir", f"stt/{DEFAULT_STT_DIR}")),
|
||||
"tokens": root / str(stt.get("tokens", f"stt/{DEFAULT_STT_DIR}/tokens.txt")),
|
||||
"encoder": root / str(stt.get("encoder", f"stt/{DEFAULT_STT_DIR}/encoder-epoch-99-avg-1.int8.onnx")),
|
||||
@@ -157,6 +180,7 @@ def stt_model_paths(model_path: str | Path) -> dict[str, Path]:
|
||||
"joiner": root / str(stt.get("joiner", f"stt/{DEFAULT_STT_DIR}/joiner-epoch-99-avg-1.int8.onnx")),
|
||||
}
|
||||
return {
|
||||
"type": "sherpa-onnx-streaming-transducer",
|
||||
"model_dir": root,
|
||||
"tokens": root / "tokens.txt",
|
||||
"encoder": root / "encoder-epoch-99-avg-1.int8.onnx",
|
||||
@@ -195,11 +219,23 @@ def model_status_errors(status: SpeechModelStatus) -> list[ProviderError]:
|
||||
"model-check",
|
||||
)
|
||||
)
|
||||
if status.missing_files:
|
||||
denoise_missing = tuple(item for item in status.missing_files if item.startswith("denoise/"))
|
||||
speech_missing = tuple(item for item in status.missing_files if not item.startswith("denoise/"))
|
||||
if speech_missing:
|
||||
errors.append(
|
||||
ProviderError(
|
||||
ErrorCode.STT_MODEL_MISSING,
|
||||
"missing speech model files: " + ", ".join(status.missing_files),
|
||||
"missing speech model files: " + ", ".join(speech_missing),
|
||||
False,
|
||||
"speech-models",
|
||||
"model-check",
|
||||
)
|
||||
)
|
||||
if denoise_missing:
|
||||
errors.append(
|
||||
ProviderError(
|
||||
ErrorCode.NOISE_FILTER_MODEL_MISSING,
|
||||
"missing denoiser model files: " + ", ".join(denoise_missing),
|
||||
False,
|
||||
"speech-models",
|
||||
"model-check",
|
||||
|
||||
Reference in New Issue
Block a user