first commit
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
"""Тест 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())
|
||||
Reference in New Issue
Block a user