[本地语音降噪]:完成本地语音链路和噪音过滤,包含降噪模型、本地ASR和实时字幕稳定策略

This commit is contained in:
mkbk
2026-06-17 22:55:33 +08:00
parent a77a172412
commit 8c75fc5baf
22 changed files with 803 additions and 64 deletions
+46 -13
View File
@@ -17,12 +17,28 @@ from .models import AudioFrame, AudioSegment, ErrorCode, ProviderError, Transcri
from .speech_models import stt_model_paths
_MEANINGFUL_TEXT = re.compile(r"[\w\u4e00-\u9fff]", re.UNICODE)
_PARTIAL_MIN_MEANINGFUL_CHARS = 3
def is_valid_transcript_text(text: str) -> bool:
return bool(_MEANINGFUL_TEXT.search(text.strip()))
def _meaningful_text_length(text: str) -> int:
return len(_MEANINGFUL_TEXT.findall(text.strip()))
def should_emit_partial_transcript(text: str, last_text: str) -> bool:
normalized = text.strip()
if not is_valid_transcript_text(normalized) or _meaningful_text_length(normalized) < _PARTIAL_MIN_MEANINGFUL_CHARS:
return False
if normalized == last_text:
return False
if not last_text:
return True
return normalized.startswith(last_text)
class MetadataSttProvider:
def __init__(self, language: str = "zh") -> None:
self.language = language
@@ -73,7 +89,7 @@ class MetadataRealtimeTranscriptSession:
or frame.metadata.get("transcript")
or ""
).strip()
if not is_valid_transcript_text(text) or text == self._last_text:
if not should_emit_partial_transcript(text, self._last_text):
return None
self._last_text = text
return Transcript(
@@ -197,7 +213,11 @@ class SherpaOnnxSttProvider:
"stt",
)
paths = stt_model_paths(self.model_path)
missing = [name for name, path in paths.items() if name != "model_dir" and not path.exists()]
missing = [
name
for name, path in paths.items()
if name not in {"type", "model_dir"} and isinstance(path, Path) and not path.exists()
]
if missing:
raise ProviderError(
ErrorCode.STT_MODEL_MISSING,
@@ -219,16 +239,29 @@ class SherpaOnnxSttProvider:
"stt",
) from exc
try:
self._recognizer = sherpa_onnx.OnlineRecognizer.from_transducer(
tokens=str(paths["tokens"]),
encoder=str(paths["encoder"]),
decoder=str(paths["decoder"]),
joiner=str(paths["joiner"]),
num_threads=1,
decoding_method="greedy_search",
enable_endpoint_detection=True,
provider="cpu",
)
model_type = str(paths.get("type", "sherpa-onnx-streaming-transducer"))
if model_type == "sherpa-onnx-streaming-zipformer2-ctc":
self._recognizer = sherpa_onnx.OnlineRecognizer.from_zipformer2_ctc(
tokens=str(paths["tokens"]),
model=str(paths["model"]),
num_threads=1,
sample_rate=16000,
feature_dim=80,
enable_endpoint_detection=True,
decoding_method="greedy_search",
provider="cpu",
)
else:
self._recognizer = sherpa_onnx.OnlineRecognizer.from_transducer(
tokens=str(paths["tokens"]),
encoder=str(paths["encoder"]),
decoder=str(paths["decoder"]),
joiner=str(paths["joiner"]),
num_threads=1,
decoding_method="greedy_search",
enable_endpoint_detection=True,
provider="cpu",
)
except Exception as exc:
raise ProviderError(
ErrorCode.STT_TRANSCRIBE_FAILED,
@@ -331,7 +364,7 @@ class SherpaOnnxRealtimeTranscriptSession:
"sherpa-onnx-stt",
"stt",
) from exc
if not is_valid_transcript_text(text) or text == self._last_text:
if not should_emit_partial_transcript(text, self._last_text):
return None
self._last_text = text
return Transcript(