"""Базовый интерфейс TTS-модуля (текст → звук). Провайдеры (edge-tts онлайн, piper офлайн) реализуют TtsProvider, чтобы остальной проект не зависел от конкретного синтезатора: если edge-tts отвалится (неофициальный эндпоинт Microsoft), переключение на Piper — одна строка в конфиге. """ from __future__ import annotations from abc import ABC, abstractmethod from dataclasses import dataclass import numpy as np @dataclass class SynthResult: """Результат синтеза одной фразы.""" text: str audio: np.ndarray # float32 mono [-1..1] samplerate: int generation_sec: float # время генерации (без воспроизведения) provider: str = "" sentence_count: int = 1 @property def duration_sec(self) -> float: if self.samplerate <= 0: return 0.0 return len(self.audio) / self.samplerate def __str__(self) -> str: return (f"[{self.provider}] текст {len(self.text)} симв. → " f"аудио {self.duration_sec:.1f} c, синтез {self.generation_sec:.2f} c, " f"{self.samplerate} Гц, предложений: {self.sentence_count}") class TtsProvider(ABC): """Интерфейс провайдера синтеза речи.""" name: str = "base" @abstractmethod def synthesize(self, text: str) -> SynthResult: """Синтезировать речь. text — уже очищенный от Markdown текст.""" def close(self) -> None: """Освободить ресурсы (по умолчанию — ничего)."""