Skip to main content
Glama

FireRedTTS3 — багатомовний TTS (24 мови, вкл. українську)

Локальний сервіс синтезу мовлення на FireRedTTS3 з HTTP-API і MCP-сервером, на порту :8020.

Модель вийшла 13.08.2026, ліцензія Apache 2.0, ваги публічні.

Що варто знати до старту

Пресетних голосів немає. Порожня бібліотека = синтезувати нічим. Спершу заводиш голос із будь-якого запису мовлення (add_firered_voice), далі вживаєш його за іменем. Модель клонує zero-shot прямо під час синтезу.

Потрібен транскрипт референсу. Моделі мало самого аудіо — треба ще текст того, що в ньому звучить. Не передаси — розпізнаємо Whisper'ом, але свій текст завжди точніший.

Наголосів немає. Ні словника, ні ByT5-фолбека, ні ручного Му+дрого. Наголоси модель ставить сама з контексту й на омографах (за́мок/замо́к) плутатиме. Це принципове обмеження багатомовної моделі.

Встановлення

cd /Users/admin/Projects/firered-tts
./setup.sh                    # venv + залежності + апстрім + патч + ваги

setup.sh робить чотири речі: піднімає .venv з Python 3.11 і torch 2.8.0 під MPS/CPU, клонує апстрім у vendor/FireRedTTS3 на запиненому комміті 00570ad, патчить його під Apple Silicon (див. нижче) і тягне ваги base+redae (~11.4 ГБ) у pretrained_models/.

Ваги окремо (довго — краще detached):

nohup ./download-weights.sh base > data/download.log 2>&1 &
./download-weights.sh instruct   # +7.9 ГБ, для дизайну голосу й редагування

Навіщо патч

Апстрім написаний виключно під NVIDIA і на Mac не запускається взагалі: flash_attn на Metal не збирається в принципі, пристрій зашитий як cuda, autocast прибитий цвяхами до CUDA. Патч замінює flash_attention_2 на sdpa (працює скрізь, у т.ч. на NVIDIA), робить пристрій динамічним і додає оптимізації з розділу про швидкість. Повний список — у шапці src/patch_upstream.py.

Патч ідемпотентний і падає, якщо заміна не влучила — якщо апстрім змінився, ти дізнаєшся про це одразу, а не CUDA-помилкою на першому синтезі.

Запуск

./run.sh                      # http://localhost:8020

Окремо запускати не обов'язково — MCP-сервер підніме бекенд сам. Бекенд вимикається після простою і звільняє пам'ять.

HTTP API

Метод

Ендпойнт

Що робить

GET

/health

статус, пристрій, яка модель у пам'яті

GET

/voices

імена голосів (фільтри gender, language; full=1 → з метаданими)

GET

/languages

24 мови + 21 діалект

POST

/clone_voice

референс + транскрипт → голос

DELETE

/voices/{name}

видалити голос (оригінал запису не чіпається)

POST

/tts

текст → аудіо-байти у відповіді

POST

/synthesize

текст → файл у DATA_DIR, JSON зі шляхом

POST

/voice_design

опис голосу → аудіо (instruct)

POST

/edit

правка запису: semantic | acoustic (instruct)

# 1) завести голос
curl -X POST localhost:8020/clone_voice -H 'Content-Type: application/json' -d '{
  "audio": "prompts/зразок.wav", "name": "Богдан",
  "prompt_text": "Це зразок мого голосу для клонування.",
  "language": "Ukrainian", "gender": "male"}'

# 2) озвучити
curl -X POST localhost:8020/tts -H 'Content-Type: application/json' -d '{
  "text": "Сьогодні чудова погода, ходімо гуляти в парк.",
  "voice": "Богдан", "format": "mp3"}' -o out.mp3

# Або one-shot — без реєстрації голосу: передай reference_audio замість voice,
# транскрипт зробить Whisper (перший виклик +~30с, далі кешується)
curl -X POST localhost:8020/tts -H 'Content-Type: application/json' -d '{
  "text": "Сьогодні чудова погода.", "reference_audio": "prompts/зразок.mp3",
  "format": "mp3"}' -o out.mp3

MCP

Див. mcp_server/README.md. Коротко:

claude mcp add firered-tts -- /Users/admin/Projects/firered-tts/.venv/bin/python \
  /Users/admin/Projects/firered-tts/mcp_server/server.py

Інструменти: firered_backend_status, list_firered_voices, add_firered_voice, delete_firered_voice, synthesize_firered_speech, design_firered_voice, edit_firered_speech.

Пам'ять

Mac mini M4, 16 ГБ. Ваги на диску: base (7.9) + redae (3.5) = 11.4 ГБ. У пам'яті менше — LLM-бекбон живе у half (4.2 замість 8.4 ГБ), — але на 16 ГБ це все одно впритул. Наслідки, закладені в код:

  • у пам'яті живе рівно одна модель — base або instruct; перемикання = повне перезавантаження (хвилини, голосно логується);

  • синтез серіалізований глобальним локом — два паралельні запити на 16 ГБ дають OOM, а не прискорення;

  • автозупинка за простоєм 1800с — довга навмисно: рестарт коштує ~40с компіляції шейдерів, тож тримати процес живим вигідніше;

  • Whisper для автотранскрипту — int8 на CPU, вивантажується одразу після розпізнавання.

Швидкість і чотири оптимізації

Наївний запуск апстріму на M4 давав ×186 реального часу — 3 секунди української рахувались 9 хвилин. Після чотирьох виправлень стало ×3.0, тобто у ~60 разів швидше. Що саме було не так (усе заміряно tools/profile_steps.py):

1. Autocast на кроці AR-циклу — головна біда. Апстрім вішає @torch.autocast декоратором на _backbone_one_step, тобто регіон відкривається й закривається на КОЖНОМУ кроці авторегресії. Кеш перекастів ваг у torch живе рівно всередині регіону, тож 1.7B параметрів fp32 переганялись у half щокроку. Крок бекбону: 19000 → 2710 мс. Тепер autocast вимкнений, а каст робиться явно на межі бекбону.

2. Бекбон у fp32. Ваги збережені у float32 (3.0B параметрів), що на 16 ГБ означає своп. Переводимо в half лише LLM-бекбон (4.2 ГБ замість 8.4), а redae і flow-декодер лишаємо fp32 — half ламає там MPS-matmul. Крок: 2710 → 1387 мс.

3. Референс кодувався заново на кожне речення. generate() викликається на кожне речення тексту, і кожен виклик проганяв redae-енкодер по тому самому референсному запису: 5.6с щоразу, незалежно від довжини тексту. Тепер кеш із ключем за вмістом аудіо. На 6.6 хв аудіо — 30 влучань проти 2 промахів, ~11% часу прогону.

4. Компіляція Metal-шейдерів. Найбільша частина «повільності» виявилась разовою: Metal компілює ядра при першому виконанні. Поштучні заміри кроку бекбону: 9873, 2104, 120, 93, 96, 97… мс. Тому сервіс робить прогрів на старті (FIRERED_WARMUP=1) і має довгий таймаут простою.

Заміряно на Mac mini M4 / 16 ГБ, 3.0с української, прогріта модель:

n_timesteps

Час

×реального часу

Якість

10 (дефолт)

12 с

×4.0

еталон

6

10 с

×3.0

різниця ледь чутна

4

7 с

×2.3

помітно грубіше

2

6 с

×1.9

помітні артефакти

Профіль прогрітої генерації: flow-декодер 57%, бекбон 18%, redae 11% — найдієвіший важіль саме n_timesteps. Real-time немає, але це вже робочий інструмент, а не «запусти на ніч».

Text normalization

Вбудований TN (wetext) знає лише китайську й англійську, тож для української він марний і ми його не ставимо. 19:30, 250 грн, 2026 р. модель озвучить як вийде. Два виходи: писати текст одразу словами, або увімкнути LLM-TN — FIRERED_TN_API_URL / _API_KEY / _MODEL у .env. Годиться будь-який OpenAI-сумісний ендпойнт, включно з локальним (llama.cpp / vLLM / Ollama) — тоді все лишається офлайн.

Ліцензія

Код і ваги — Apache 2.0. У README апстріму окремо написано, що zero-shot клонування «solely for academic research purposes» — це суперечить Apache 2.0 і на комерційне використання клонованих голосів дивись обережно. Для домашнього використання питання не стоїть.