Files
2026-09-11 19:47:15 +03:00

6.7 KiB
Raw Permalink Blame History

Модуль 3: TTS (текст → звук)

Два провайдера за общим интерфейсом TtsProvider: переключение = выбор в конфиге.

Провайдер Сеть Латентность (110 симв.) Голоса Роль
edge (provider_edge.py) онлайн, бесплатный сервис MS, без ключа 3.0 c (4 предл.), 1.7 c (короткая) Svetlana/Dmitry, отличное качество основной
piper (provider_piper.py) офлайн, локально на CPU 0.47 c dmitri/irina/ruslan, проще фолбэк без сети

Режимы edge-провайдера

  • Один запрос (по умолчанию): весь текст → один round-trip (~1.7–3 c). Паузы между предложениями удлиняются многоточиями («точка» → «…» — edge-tts читает как длинную паузу).
  • per_sentence=True: каждое предложение отдельным запросом + точная тишина sentence_pause_sec между ними. Медленнее (N запросов), нужен для тонкой подгонки пауз.

Замер 2026-09-11 (свежие соединения в каждом процессе): один запрос 110 симв = 3.0 c, короткая фраза 36 симв = 1.7 c, Piper 110 симв = 0.47 c. В живой программе соединение переиспользуется — будет быстрее. Цепочка целиком: STT ~1.2 c + LLM ~1–2 c + TTS ~1.7 c ≈ 4–5 c от отпускания клавиши до голоса (маскируется earcon «думаю»).

Использование из кода

from modules.tts import get_provider

tts = get_provider("edge")  # voice="ru-RU-DmitryNeural" — мужской
result = tts.synthesize("Привет! Как дела?")
# result.audio (float32), result.samplerate, result.generation_sec, result.duration_sec

Фолбэк при обрыве сети (switch = одна строка):

tts = get_provider("piper", model_path="models/piper/ru_RU-dmitri-medium.onnx")

Потоковая озвучка (стриминг, 2026-09-11)

tts.stream(text) — генератор: отдаёт чанки-предложения по мере готовности (параллельные запросы, отдача в порядке следования). Assistant играёт чанк сразу, не дожидаясь синтеза всего текста:

  • первое предложение звучит через ~0.5 c после готовности LLM (один round-trip);
  • длинные ответы больше не пропорционально тормозят (2-е/3-е предложение синтезируются, пока играет 1-е);
  • между чанками вставляется пауза sentence_pause_sec;
  • перебивание отцом гасит поток и синтез (счётчик поколений).

Плюс в Player.play() добавлен префикс тишины 0.1 c (lead_silence_sec) — защита от «съедания» первого слова при открытии аудио-потока (недозаполненный буфер ALSA/Pulse).

Настройка из .env (2026-09-11)

TTS_PROVIDER=edge            # edge | piper
TTS_VOICE=ru-RU-SvetlanaNeural   # мужской: ru-RU-DmitryNeural
TTS_RATE=+15                 # темп речи, % (+ = быстрее; ≈+30% к исходному −15)
PIPER_MODEL=models/piper/ru_RU-dmitri-medium.onnx

Выбранный конфиг юзером: edge + Svetlana, rate +15 («женский голос лучше и темп нормальный»). Piper остаётся офлайн-фолбэком (0.22 c синтез против 0.49 c у edge), при желании ускориться — одна строка в .env.

Фабрика: make_tts() из modules/tts — тесты ассистента читают эти параметры автоматически, смену голоса/скорости/провайдера можно делать без правки кода.

Файлы

Файл Назначение
base.py Интерфейс TtsProvider, результат SynthResult
provider_edge.py edge-tts: один запрос / сшивание предложений
provider_piper.py Piper офлайн (length_scale из rate автоматом в config)
config.py make_tts() — фабрика из .env (провайдер, голос, скорость)
text_split.py Разбивка на предложения; защита сокращений («т.д.», «т.к.»)
test_tts.py Тест-скрипт (синтез / воспроизведение / интерактив / провайдеры)

Тест-скрипт

.venv/bin/python modules/tts/test_tts.py --play                 # фраза по умолчанию + звук
.venv/bin/python modules/tts/test_tts.py --text "Своё предложение"
.venv/bin/python modules/tts/test_tts.py --voice ru-RU-DmitryNeural --play   # мужской
.venv/bin/python modules/tts/test_tts.py --listen               # вводишь текст — слышишь
.venv/bin/python modules/tts/test_tts.py --per-sentence         # медленный режим (сравнение)
.venv/bin/python modules/tts/test_tts.py --provider piper --model models/piper/ru_RU-dmitri-medium.onnx --play

Критерии приёмки Модуля 3

  1. ✅ Русский голос звучит естественно, скорость −15% — принято юзером на слух (Svetlana).
  2. ✅ Паузы между предложениями слышны (многоточия / точная тишина).
  3. ✅ Синтез короткой фразы ≤ 3 c: 1.7 c (edge), 0.47 c (piper).

Известные ограничения

  • edge-tts — неофициальный API Microsoft: исторически ломался (403). План Б — Piper, он уже в проекте.
  • Piper: голос скачивается один раз в models/piper/ (команда в docstring провайдера); качество проще edge, зато 0.5 c и полностью офлайн.
  • В provider_edge._run_async учтён будущий asyncio-контекст (aiogram в Модуле 6): синтез не сломает чужой event loop.