49 lines
1.8 KiB
Python
49 lines
1.8 KiB
Python
"""Базовый интерфейс 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:
|
|
"""Освободить ресурсы (по умолчанию — ничего).""" |