61 lines
2.2 KiB
Python
61 lines
2.2 KiB
Python
"""Базовый интерфейс STT-модуля (речь → текст).
|
||
|
||
Любой распознаватель (faster-whisper, vosk, ...) реализует SttProvider,
|
||
чтобы остальной проект не зависел от конкретной библиотеки.
|
||
Замена провайдера = правка одной строки в конфиге.
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from abc import ABC, abstractmethod
|
||
from dataclasses import dataclass
|
||
from pathlib import Path
|
||
from typing import Union
|
||
|
||
import numpy as np
|
||
|
||
# Что умеет принимать transcribe(): путь к файлу ЛИБО готовый массив float32
|
||
AudioInput = Union[str, Path, np.ndarray]
|
||
|
||
# Все провайдеры ждут 16 кГц mono float32 ([-1 .. 1])
|
||
SAMPLE_RATE = 16000
|
||
|
||
|
||
@dataclass
|
||
class TranscriptResult:
|
||
"""Результат распознавания одной фразы."""
|
||
|
||
text: str
|
||
duration_sec: float # длина аудио в секундах
|
||
processing_sec: float # сколько времени считали
|
||
language: str = "ru"
|
||
language_probability: float = 0.0
|
||
|
||
@property
|
||
def realtime_factor(self) -> float:
|
||
"""processing / duration. Значение < 1.0 — быстрее реального времени."""
|
||
if self.duration_sec <= 0:
|
||
return float("inf")
|
||
return self.processing_sec / self.duration_sec
|
||
|
||
def __str__(self) -> str:
|
||
return (
|
||
f"[{self.duration_sec:.1f} c аудио → {self.processing_sec:.2f} c, "
|
||
f"RTF {self.realtime_factor:.2f}] {self.text!r}"
|
||
)
|
||
|
||
|
||
class SttProvider(ABC):
|
||
"""Интерфейс провайдера распознавания речи."""
|
||
|
||
name: str = "base"
|
||
|
||
@abstractmethod
|
||
def transcribe(self, audio: AudioInput, sample_rate: int = SAMPLE_RATE) -> TranscriptResult:
|
||
"""Распознать речь.
|
||
|
||
audio — путь к файлу (wav/mp3/flac, декодирование внутри) ИЛИ
|
||
numpy-массив float32 mono с частотой sample_rate.
|
||
"""
|
||
|
||
def close(self) -> None:
|
||
"""Освободить ресурсы (по умолчанию — ничего).""" |