Files
Blind/modules/tts/test_tts.py
T
2026-09-11 19:47:15 +03:00

107 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Тест TTS-модуля (Модуль 3).
Запуск:
python modules/tts/test_tts.py # синтез фразы по умолчанию, сохранить wav
python modules/tts/test_tts.py --play # то же + воспроизвести
python modules/tts/test_tts.py --text "Своё предложение"
python modules/tts/test_tts.py --voice ru-RU-DmitryNeural --play
python modules/tts/test_tts.py --rate -10 # другая скорость
python modules/tts/test_tts.py --provider piper --model models/piper/ru_RU-dmitri-medium.onnx
python modules/tts/test_tts.py --listen # интерактивно: вводишь текст — слышишь
Критерии приёмки Модуля 3:
1. Русская фраза звучит естественно, скорость на ~15% медленнее обычной.
2. Несколько предложений — с заметными паузами между ними.
3. Синтез короткой фразы (до 100 символов) — <= 3 c.
"""
from __future__ import annotations
import argparse
import sys
import time
from pathlib import Path
try:
from modules.audio_io import Player
from modules.audio_io.codec import decode_audio_bytes # noqa: F401 (использование ниже)
from modules.tts import get_provider
except ImportError:
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from modules.audio_io import Player
from modules.audio_io.codec import decode_audio_bytes # noqa: F401
from modules.tts import get_provider
SAMPLES_DIR = Path(__file__).resolve().parent / "samples"
DEFAULT_TEXT = (
"Здравствуйте! Это проверка голоса ассистента. "
"Сейчас проверим, насколько понятна речь. Погода сегодня хорошая."
)
def parse_args() -> argparse.Namespace:
p = argparse.ArgumentParser(description="Тест TTS-модуля")
p.add_argument("--text", default=DEFAULT_TEXT, help="текст для синтеза")
p.add_argument("--provider", default="edge", choices=["edge", "piper"])
p.add_argument("--voice", default="ru-RU-SvetlanaNeural",
help="голос edge-tts (ru-RU-DmitryNeural — мужской)")
p.add_argument("--rate", default=-15, help="скорость речи, %% (−15 по ТЗ)")
p.add_argument("--model", default="models/piper/ru_RU-dmitri-medium.onnx",
help="путь к .onnx модели Piper")
p.add_argument("--pause", type=float, default=0.35, help="пауза между предложениями, с")
p.add_argument("--per-sentence", action="store_true",
help="режим «предложение = запрос» (сравнение латентности)")
p.add_argument("--play", action="store_true", help="воспроизвести результат")
p.add_argument("--listen", action="store_true",
help="интерактивно: вводишь текст — слышишь звук")
p.add_argument("--save", default=str(SAMPLES_DIR / "tts_out.wav"))
return p.parse_args()
def speak_once(tts, text: str, play: bool, save: Path) -> None:
result = tts.synthesize(text)
print(result)
save = Path(save)
save.parent.mkdir(parents=True, exist_ok=True)
import soundfile as sf
sf.write(str(save), result.audio, result.samplerate)
print(f"Сохранено: {save} ({len(result.audio) / result.samplerate:.1f} c)")
if play:
player = Player()
print("Воспроизвожу...")
player.play(result.audio, result.samplerate, blocking=True)
def listen_loop(tts) -> None:
player = Player()
print("Режим «слушать»: вводи текст, Enter — озвучить, пустая строка — выход.")
while True:
text = input("текст> ").strip()
if not text:
return
result = tts.synthesize(text)
print(f" синтез {result.generation_sec:.2f} c, аудио {result.duration_sec:.1f} c")
player.play(result.audio, result.samplerate, blocking=True)
def main() -> int:
args = parse_args()
kwargs = {"sentence_pause_sec": args.pause}
if args.provider == "edge":
kwargs.update(voice=args.voice, rate=int(args.rate), per_sentence=args.per_sentence)
else:
kwargs.update(model_path=args.model)
tts = get_provider(args.provider, **kwargs)
if args.listen:
listen_loop(tts)
else:
speak_once(tts, args.text, args.play, args.save)
return 0
if __name__ == "__main__":
sys.exit(main())