[本地语音降噪]:完成本地语音链路和噪音过滤,包含降噪模型、本地ASR和实时字幕稳定策略
This commit is contained in:
+46
-13
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user