# Технические заметки

## Почему base64 PCM вместо WAV/Opus

- **Низкая задержка**: не нужно ждать формирования заголовка WAV или энкодинга Opus.
- **Простота клиента**: Python-агент может напрямую скормить PCM в `pyaudio` / `sounddevice` / `alsaaudio`.
- **Компромисс**: трафик больше, чем с Opus, но в локальной сети это некритично.
- Opus можно добавить позже как опциональный формат.

## Почему один TTS worker

- CUDA context не любит параллельные вызовы из разных потоков.
- Один worker с `asyncio.Queue` гарантирует последовательный доступ к GPU и предсказуемое потребление VRAM.
- Если в будущем понадобится масштабирование — можно запустить несколько независимых инстансов.

## Управление остановкой

`stop_event` — центральный механизм:

- `SessionState.stop()` устанавливает флаг.
- Все длительные операции (TTS, отправка аудио) могут его проверять.
- После `stop` новое сообщение `text` автоматически сбрасывает флаг и начинает новую фразу.

## Сегментация

Цель — найти баланс между:
- задержкой (короткие сегменты синтезируются быстрее),
- качеством (TTS лучше звучит на целых предложениях),
- реалтаймом (не ждём слишком долго).

Параметры по умолчанию:
- `min_segment_length = 30`
- `max_segment_length = 200`
- `max_buffer_wait_ms = 500`

Для русского языка предложения обычно короче, чем на английском, поэтому `max_length` выбран консервативно.

## F5-TTS: особенности интеграции

- Используется готовая модель `F5TTS_v1_Base` через официальный пакет `f5-tts`.
- Модель автоматически загружается из Hugging Face при первом вызове `load()`;
  можно скачать заранее скриптом `scripts/download_f5_tts.py`.
- Референсное аудио транскрибируется автоматически, если `ref_text` не задан.
- Результат транскрипции и путь к обработанному аудио кэшируются по паре
  `(путь, эмоция)`, чтобы не повторять работу при смене сегментов.
- Скорость регулируется параметром `speed` инференса F5-TTS.
- Текущая целевая частота дискретизации — **24 kHz**; модель сама
  передискретирует референс при необходимости.
- Инференс выполняется в отдельном потоке (`asyncio.to_thread`) с
  `asyncio.Lock`, чтобы не блокировать event loop и не допускать
  параллельных CUDA-вызовов. Все `send_text` сериализуются через
  отдельный `_send_lock`, чтобы избежать deadlock внутри `websockets`.

### Замеры задержки (RTX 3090, Python 3.11, CUDA 12.6)

- Первый запуск без `DEFAULT_REF_TEXT`: ~5–6 с, большая часть уходит на Whisper-транскрипцию референса.
- С `DEFAULT_REF_TEXT` и `WARMUP=true`: warm-up занимает ~2 с (загрузка модели + один инференс).
- После warm-up с кэшированным референсом: первый audio-chunk ~1.1 с на коротком сегменте.
- 4 сегмента подряд: первый finished ~1.1 с, последний ~4.4 с.
- `stop` + возобновление работает без переподключения WebSocket.

## Мультиязычность

Базовая модель `F5TTS_v1_Base` поддерживает несколько языков из коробки.
Инференс на коротких тестовых фразах работает для:

- русского,
- английского,
- украинского,
- испанского,
- немецкого,
- французского.

При этом важные нюансы:

- **Акцент**: когда референс — русский спикер, английская и европейская речь
  может иметь сильный русский акцент.
- **Произношение**: для незнакомых фонем модель может «додумывать» звуки
  или пропускать сложные буквосочетания.
- **Скорость**: английские/романские фразы короче по токенам, поэтому
  инференс занимает ~1.0–1.2 с, в то время как русский/украинский — ~1.2–2.2 с
  на сегмент из 1–2 предложений.
- **Точность языка**: модель не использует явный `language` токен; язык
  определяется по тексту. Для смешанных текстов (ru + en) возможны
  переключения произношения.

### Что делать, если качество европейских языков недостаточно

1. **Отдельный референс на английском/европейском языке** —
   в протоколе уже поддерживаются `voice_refs` по эмоциям; можно добавить
   `language` в профиль голоса и выбирать референс в зависимости от языка.
2. **Fallback backend** — для «чужеродных» языков переключаться на
   `MeloTTS` или другой TTS, который лучше произносит конкретный язык.
3. **Fine-tuning F5-TTS** — самый трудоёмкий, но даёт лучший контроль
   над акцентом и языком.

Сейчас оставляем F5-TTS как единственный backend, но архитектура
(`TTSEngine` + `_BACKEND_MAP`) позволяет добавить fallback позже.

- F5-TTS может не идеально произносить украинский / европейские языки из коробки — возможно потребуется fine-tuning или fallback. Сейчас протокол и сегментатор не ограничивают язык; качество зависит от самой модели.
- RTX 3060 (12 GB) подойдёт для базовой модели, но batch-size и длина референса придётся ограничивать.
- Быстрый `stop` во время CUDA kernel не прервёт уже запущенный kernel, но предотвратит отправку результата.
- `main.py` создаёт engine до старта uvicorn; при `TTS_BACKEND=f5_tts` первый запуск может занять десяток секунд из-за загрузки модели и vocos.
