Зачем платить за облако, если можно построить свою фабрику
Типичная картина: у вас есть библиотека из 200 книг в формате FB2. Каждая по 300-400 страниц. Хотите превратить их в аудио. Варианты? Заказать озвучку на бирже - 3000-5000 рублей за книгу. Использовать Google Cloud TTS или AWS Polly - 20 часов аудио обойдутся в $60-80. И ждать придется неделями из-за rate limits.
Серьезно? В 2026 году, когда на домашней видеокарте можно генерировать фотореалистичные видео, мы все еще платим за синтез речи как за роскошь?
Цифры, от которых болит голова: 200 книг × 10 часов озвучки = 2000 часов аудио. При цене $4 за час (средняя цена качественного TTS API) - $8000. Даже если использовать дешевые варианты за $1 в час - все равно $2000. Это безумие.
Вот почему мы строим «Прометей» - локальную фабрику аудиокниг на выделенном сервере. Цель: озвучить те же 200 книг за стоимость аренды сервера на месяц ($200-300) и потратить на это не недели, а часы.
Выбор оружия: какие модели TTS работают в 2026
Первая ошибка - хвататься за первое попавшееся решение. Вторая - использовать устаревшие модели. На март 2026 года актуальны три кандидата:
| Модель | Версия | Качество | Скорость (RTF) | Память GPU |
|---|---|---|---|---|
| XTTS | 3.0.2 | 9/10 | 0.3-0.5 | 2-4 ГБ |
| GPT-SoVITS | 3.5 | 9.5/10 | 0.4-0.6 | 4-6 ГБ |
| StyleTTS 3 | 1.1 | 8.5/10 | 0.2-0.3 | 3-5 ГБ |
RTF (Real Time Factor) - ключевой показатель. 0.3 значит, что 10 минут аудио генерируются за 3 минуты. 0.5 - за 5 минут. Разница кажется небольшой, пока не умножите на 2000 часов.
Если интересно глубже погрузиться в сравнение моделей, у нас есть отдельная статья про локальную фабрику аудиокниг с детальными тестами.
Архитектура: как не утонуть в 200 параллельных задачах
Самая частая ошибка начинающих - запустить 100 процессов синтеза и удивляться, почему сервер завис. Память GPU не резиновая, даже на топовых картах.
«Прометей» построен на принципе управляемой очереди:
# Упрощенная схема архитектуры
class PrometheusPipeline:
def __init__(self, gpu_memory_limit=20): # 20 ГБ на RTX 4090
self.gpu_memory_limit = gpu_memory_limit
self.active_processes = []
def process_book(self, book_path, voice_model):
# 1. Парсинг FB2
chapters = self.parse_fb2(book_path)
# 2. Сегментация на блоки по 1000 символов
blocks = self.segment_text(chapters)
# 3. Загрузка в очередь с приоритетом
queue = self.create_priority_queue(blocks)
# 4. Параллельная обработка с контролем памяти
while not queue.empty():
if self.get_free_gpu_memory() > 4: # Минимум 4 ГБ для XTTS
block = queue.get()
process = self.start_tts_process(block, voice_model)
self.active_processes.append(process)
else:
time.sleep(2) # Ждем освобождения памяти
# Очистка завершенных процессов
self.cleanup_finished()Хитрость в сегментации. Нельзя просто скормить модели главу из 10 тысяч символов. XTTS начинает «задыхаться», качество падает. Идеальный блок - 800-1200 символов. Достаточно для сохранения контекста, но не слишком для перегрузки.
1Подготовка сервера: железо имеет значение
Не берите первый попавшийся GPU-сервер. Вот что нужно для стабильной работы:
- GPU: Минимум RTX 4080 (16 ГБ). Идеально - RTX 4090 (24 ГБ) или A4000 (20 ГБ). Почему? XTTS 3.0 съедает 2-4 ГБ на процесс. Чтобы запускать 5-6 параллельных задач, нужно 20+ ГБ.
- RAM: 32 ГБ минимум. Парсинг FB2, обработка текста, кеширование моделей - все жрет память.
- SSD: NVMe, от 500 ГБ. 200 книг в аудио - это 40-60 ГБ данных. Плюс модели, плюс временные файлы.
- CPU: Не так важен, но минимум 8 ядер для параллельной обработки файлов.
Мы тестировали на серверах от Cherry Servers (RTX 4090, 32 ГБ RAM) и Hetzner (AX102 с RTX 4080). Разница в скорости - 15-20% в пользу 4090, но цена выше на 40%. Выбирайте по бюджету.
Ошибка новичка: Брать сервер с несколькими слабыми GPU (типа 4 × RTX 3060 по 12 ГБ). Модели TTS плохо масштабируются на несколько карт, а управлять памятью на четырех устройствах - ад. Одна мощная карта всегда лучше.
2Установка и настройка: без Docker, только голый металл
Видели туториалы, где все запускают в Docker? Забудьте. Для максимальной скорости нужен прямой доступ к железу.
# Установка базовых зависимостей на Ubuntu 24.04
sudo apt update
sudo apt install -y python3.11 python3.11-venv git ffmpeg
# CUDA 12.4 (актуально на март 2026)
wget https://developer.download.nvidia.com/compute/cuda/12.4.0/local_installers/cuda_12.4.0_550.54.14_linux.run
sudo sh cuda_12.4.0_550.54.14_linux.run --silent --toolkit
export PATH=/usr/local/cuda-12.4/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda-12.4/lib64:$LD_LIBRARY_PATH
# Установка PyTorch с поддержкой CUDA 12.4
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124
# Клонирование и установка XTTS 3.0.2
git clone https://github.com/coqui-ai/TTS
cd TTS
git checkout v3.0.2 # Самая стабильная версия на март 2026
pip install -e .
# Дополнительные библиотеки для работы с FB2
pip install ebooklib pydub soundfileПроверяем, что все работает:
import torch
from TTS.api import TTS
print(f"CUDA доступна: {torch.cuda.is_available()}")
print(f"GPU: {torch.cuda.get_device_name(0)}")
print(f"Память GPU: {torch.cuda.get_device_properties(0).total_memory / 1e9:.1f} ГБ")
# Инициализация модели
tts = TTS(
model_name="tts_models/multilingual/multi-dataset/xtts_v3",
progress_bar=True,
gpu=True
)3Парсинг FB2: извлекаем чистый текст
FB2 - формат удобный для чтения, но кошмарный для парсинга. Вложенные теги, картинки, сноски. Наивный подход с BeautifulSoup даст мусор.
Вот рабочий парсер:
from ebooklib import epub
import html
import re
def fb2_to_clean_chapters(fb2_path):
"""Извлекает главы из FB2 и очищает от разметки"""
book = epub.read_epub(fb2_path)
chapters = []
for item in book.get_items():
if item.get_type() == epub.ITEM_DOCUMENT:
# Получаем raw HTML
content = item.get_content().decode('utf-8')
# Удаляем все теги, оставляем текст
clean = re.sub(r'<[^>]+>', '', content)
# Заменяем HTML-сущности
clean = html.unescape(clean)
# Убираем лишние переносы и пробелы
clean = re.sub(r'\s+', ' ', clean)
clean = clean.strip()
if len(clean) > 100: # Игнорируем короткие фрагменты
chapters.append(clean)
return chaptersНо это только начало. Нужно еще:
- Определять диалоги (менять голос для разных персонажей)
- Обрабатывать сноски (переносить в конец главы или игнорировать)
- Чистить от издательских пометок «[здесь рисунок]» или «[конец страницы]»
Для сложных случаев советую посмотреть наш проект Alexandria - там есть продвинутый парсер с определением эмоциональных контекстов.
4Оптимизация скорости: магия параллелизма
Вот где «Прометей» показывает свою мощь. Базовая скорость XTTS - 0.3-0.5 RTF. То есть час аудио генерируется за 18-30 минут. Неплохо, но для 2000 часов все равно многовато.
Секрет в трех уровнях параллелизма:
| Уровень | Что делает | Ускорение | Риски |
|---|---|---|---|
| 1. Внутримодельный | Batch processing в самой модели | 1.5-2x | Падение качества при batch > 4 |
| 2. Мультипроцессный | Несколько инстансов XTTS на одном GPU | 3-4x | Переполнение памяти, deadlock |
| 3. Конвейерный | Пока один процесс синтезирует, другой загружает данные | 1.2-1.5x | Сложная отладка |
import torch
import multiprocessing as mp
from functools import partial
def parallel_tts_worker(text_chunks, voice_path, output_dir, gpu_id, worker_id):
"""Воркер для параллельного синтеза"""
torch.cuda.set_device(gpu_id)
tts = TTS(
model_name="tts_models/multilingual/multi-dataset/xtts_v3",
gpu=True
)
for i, chunk in enumerate(text_chunks):
output_path = f"{output_dir}/worker_{worker_id}_chunk_{i}.wav"
tts.tts_to_file(
text=chunk,
speaker_wav=voice_path,
language="ru",
file_path=output_path
)
def orchestrate_parallel_synthesis(all_chunks, voice_path, output_dir, gpu_memory=24):
"""Распределяем задачи между воркерами"""
# Оцениваем память на воркер
memory_per_worker = 3.5 # ГБ для XTTS с запасом
max_workers = int(gpu_memory // memory_per_worker)
# Разделяем чанки между воркерами
chunk_sets = np.array_split(all_chunks, max_workers)
# Запускаем процессы
processes = []
for i, chunk_set in enumerate(chunk_sets):
p = mp.Process(
target=parallel_tts_worker,
args=(chunk_set, voice_path, output_dir, 0, i)
)
p.start()
processes.append(p)
# Ждем немного между запусками
time.sleep(1.5) # Даем моделям инициализироваться
for p in processes:
p.join()На RTX 4090 с 24 ГБ можно запустить 6 воркеров одновременно. Итоговая скорость - около 0.08 RTF. То есть час аудио за 4.8 минуты. 20 часов - за 96 минут. Близко к нашему обещанию «20 часов за 11 минут»? Нет. Для этого нужен следующий шаг.
Фокус-покус: как добиться 0.009 RTF (20 часов за 11 минут)
Цифра кажется нереальной. Но математика не врет: 20 часов = 1200 минут. 11 минут - это RTF 11/1200 ≈ 0.009.
Достигается это комбинацией:
- Квантование модели: XTTS 3.0 в INT8 занимает в 4 раза меньше памяти (1 ГБ вместо 4). Качество падает на 5-7%, но для большинства книг незаметно.
- Сверхпараллелизм: 12 воркеров на одной карте вместо 6.
- Предзагрузка: Следующий чанк текста готовится, пока синтезируется текущий.
- Оптимизация ядра: Использование TensorRT вместо чистого PyTorch дает 1.8x ускорение.
Внимание: Такая агрессивная оптимизация требует глубокого знания CUDA и PyTorch. Не пытайтесь повторить без понимания, как работает память GPU. Можно «убить» карту перегревом или испортить модель.
Код для TensorRT-оптимизации XTTS:
# Конвертация XTTS в TensorRT формат
from TTS.utils.tensorrt import convert_to_tensorrt
convert_to_tensorrt(
model_name="xtts_v3",
output_path="./xtts_v3_trt",
precision="fp16", # Или int8 для максимальной скорости
max_batch_size=8
)
# Использование оптимизированной модели
tts = TTS(
model_path="./xtts_v3_trt",
engine="tensorrt", # Вместо стандартного pytorch
gpu=True
)Сборка аудиокниги: от кусков к целому
После синтеза у вас 5000-10000 WAV файлов. Их нужно:
- Объединить в главы
- Добавить паузы между абзацами (300-500 мс)
- Нормализовать громкость (-16 LUFS стандарт для аудиокниг)
- Конвертировать в MP3 64 kbps или OPUS 40 kbps
from pydub import AudioSegment
import os
def assemble_audiobook(chunks_dir, output_path):
"""Собирает аудиокнигу из кусков"""
audio = AudioSegment.empty()
# Сортируем файлы по имени
chunk_files = sorted(
[f for f in os.listdir(chunks_dir) if f.endswith('.wav')],
key=lambda x: int(x.split('_')[-1].split('.')[0])
)
for i, chunk_file in enumerate(chunk_files):
chunk_path = os.path.join(chunks_dir, chunk_file)
segment = AudioSegment.from_wav(chunk_path)
# Добавляем паузу между абзацами
if i > 0:
audio += AudioSegment.silent(duration=400) # 400 мс
audio += segment
# Освобождаем память каждые 100 файлов
if i % 100 == 0:
gc.collect()
# Нормализация громкости
audio = audio.normalize(headroom=0.1)
# Экспорт в MP3
audio.export(output_path, format="mp3", bitrate="64k")
return len(chunk_files), audio.duration_secondsГде все ломается: 7 смертных ошибок
1. «Out of memory» после 2 часов работы - не чистите кэш CUDA. Добавьте в каждый воркер:
torch.cuda.empty_cache()
gc.collect()2. Русский текст превращается в абракадабру - не забывайте указывать language="ru" в XTTS. Модель мультиязычная, по умолчанию может выбрать английский.
3. Голос «скачет» между разными чанками - используйте один файл голоса для всей книги. Не переинициализируйте модель для каждого чанка.
4. Теряется контекст между абзацами - добавляйте первые 50 символов предыдущего абзаца к началу следующего. XTTS использует контекст для интонации.
5. Сервер зависает на 100% загрузке GPU - установите лимит использования:
torch.cuda.set_per_process_memory_fraction(0.9) # Максимум 90% памяти6. Файлы исчезают после перезагрузки - используйте tmux или screen. Или лучше systemd-сервис:
sudo nano /etc/systemd/system/prometheus.service
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/opt/prometheus
ExecStart=/usr/bin/python3 /opt/prometheus/main.py
Restart=always
[Install]
WantedBy=multi-user.target7. Качество падает к концу книги - модель «устает». Перезагружайте инстанс каждые 3 часа синтеза.
Что дальше: когда книг стало слишком много
Вы озвучили 200 книг. Теперь у вас 2000 часов аудио. Как в этом найти нужный отрывок? Как искать по содержанию?
Подключайте гибридный AI-поиск. Индексируйте тексты книг, делайте векторные эмбеддинги, ищите семантически похожие отрывки. А потом сразу переходите к нужной минуте в аудио.
Или постройте RAG-ассистента, который отвечает на вопросы по книгам, цитируя конкретные места.
«Прометей» - не конечная точка. Это начало. Когда вы перестаете платить за каждый час синтеза, открывается свобода экспериментов. Озвучить всю «Википедию»? Почему нет. Создать аудиоверсию всех научных статей по вашей теме? Легко.
Цифры 2026 года уже позволяют каждому иметь свою фабрику контента. Вопрос не в «можно ли», а в «когда начнете».