Почему потоковый TTS и что такое VoXtream

Большинство моделей синтеза работают офлайн: получили весь текст, подумали, выдали аудио целиком. Для голосового ассистента или синхронного перевода это плохо — важна задержка до первого звука. LLM генерирует ответ токен за токеном, и от TTS хочется того же: начать говорить, пока текст ещё дописывается.

VoXtream (KTH, ICASSP 2026) — это full-stream TTS: он принимает текст инкрементально и начинает говорить с первого слова, с задержкой до первого пакета аудио ~100 мс на обычной GPU. Архитектура — три авторегрессионных трансформера поверх нейрокодека Mimi:

Архитектура VoXtream2: путь текста, промпта и управляющего сигнала темпа
Архитектура VoXtream2: путь текста, промпта и управляющего сигнала темпа

Цвета на схеме: зелёное — три трансформера, которые обучаются (именно они дообучались под русский); розовое — предобученные компоненты, чьи веса не трогаются (кодек Mimi и спикер-энкодер); жёлтое — необучаемая обработка вне модели (G2P и SRC); синее — входы.

  1. Phoneme Transformer (PT) — шаг за шагом кодирует поток фонем. Ключевая идея — ограниченный look-ahead: модель видит вперёд не всю фразу, а несколько фонем, поэтому может начинать генерацию, не дожидаясь конца предложения.

  2. Temporal Transformer (TT) — главный генератор. На каждом кадре (Mimi работает на 12.5 Гц, кадр = 80 мс) он предсказывает семантический токен кодека и duration-токен. Duration-токен — самое интересное место: он кодирует «остаёмся на текущей фонеме или двигаемся дальше, и сколько фонем помещается в кадр» (сдвиг 0/1/2 × 1/2 фонемы = 6 состояний). Получается монотонное выравнивание фонем и аудио, встроенное прямо в генерацию.

  3. Depth Transformer (DT) — по выходу TT и семантическому токену достраивает акустические токены остальных кодбуков Mimi (тембр, детали). Сюда же подмешивается эмбеддинг диктора (ReDimNet) — так работает клонирование голоса.

Далее Mimi-декодер превращает токены в звук, кадр за кадром.

Как можно было понять из текста выше, что модель учится на пофонемных длительностях. Чтобы обучить duration-токены, каждому кадру аудио нужно сопоставить конкретные фонемы. Значит, весь датасет должен быть пофонемно выровнен форс-алайнером - и качество этого выравнивания напрямую становится качеством ритма речи.

Чем VoXtream2 отличается от первого

VoXtream2 отличается от исходной VoXtream по нескольким ключевым параметрам. Для G2P (преобразования текста в фонемы) VoXtream использовала g2pE, который поддерживал только английский язык. VoXtream2 перешла на espeak-ng с IPA, что дало мультиязычность. Глубина look-ahead по фонемам выросла с 10 до 25, что улучшает просодию (при дообучении на русский эта величина не фиксировалась, а семплировалась на каждом шаге обучения: в 70% случаев — из плотной сетки 1–30 фонем, в 30% — из разреженной сетки дальних значений 40…450 и «вся фраза». Это одновременно аугментация и способ получить одну модель для двух режимов: потокового, где look-ahead мал, и офлайн, где текст известен целиком и просодия заметно лучше — в частности, вопросительная интонация). Число кодбуков Mimi увеличилось с 12 до 16, что повышает качество звука. Для текста промпта VoXtream требовала транскрипт с выравниванием, тогда как VoXtream2 использует prompt text masking — фонемы промпта помечаются как UNK. Пунктуация: VoXtream её не поддерживала, а VoXtream2 вводит знаки .,!? как отдельные фонемные токены. Управление темпом речи в VoXtream отсутствовало, а в VoXtream2 появился SRC — distribution matching по duration-токенам, позволяющий менять темп на лету. CFG (classifier-free guidance) в VoXtream не применялось, а VoXtream2 использует его на всех кондиционированиях. Наконец, задержка до первого звука снизилась со 102 до 74 мс, при этом генерация идёт в 4 раза быстрее реального времени.

Prompt text masking. В первой версии, чтобы клонировать голос, нужен был транскрипт промпта и его выравнивание. Во второй фонемы промпта просто заменяются на UNK-токены — модель кондиционируется на промпт чисто акустически. Следствие: промпт может быть на любом языке. Дал английскую запись — получил русскую речь тем же голосом.

SRC — speaking rate control. Duration-токены каждого шага образуют распределение. SRC сравнивает целевую гистограмму длительностей (для заданных «слогов в секунду») с накопленной за последние секунды генерации и мягко перевзвешивает логиты duration-токенов. Никакого переобучения — темп меняется прямо во время произнесения фразы.

Дообучение на русский

  1. Расширение словаря: единый espeak-IPA словарь с ударениями. Забавное инженерное ограничение: пайплайн пишет фонемные потоки в uint8, словарь обязан влезать в 255 токенов — редкие фонемы отсекаются порогом частоты. Итог — 166 токенов.

  2. Хирургия эмбеддингов: матрица phone_embeddings расширяется, новые русские строки инициализируются от ближайшей английской фонемы (tʲ ← t, ˈɑ ← ɑ…), спец-токены переезжают в конец. Всё остальное — веса трансформеров — берётся от английского чекпоинта как есть.

  3. Своя акустическая модель для MFA — готовые русские модели выравнивания используют другой фонемный алфавит, а нужен ровно espeak, иначе цикл «данные ↔ инференс» рвётся. Пришлось переобучить MFA на своём корпусе под espeak-словарь с ударениями.

  4. Ударения и омографы на инференсе — RUAccent (за́мок/замо́к решает контекстная модель, ручной + в тексте имеет приоритет). Отдельная мелочь: espeak читает + как «плюс», поэтому ударение из разметки нельзя подавать в G2P — оно переносится пост-обработкой, перестановкой ˈ на нужную гласную.

Дальше классика: сначала претрейн TT+PT с замороженным Depth Transformer (пока эмбеддинги новых фонем «встают на место», тембровую часть не трогаем), потом разморозка DT, потом специализированные стадии.

Данные

Русской речи в открытом доступе много, но она разного качества и в разных форматах. Все доступные наборы данных (~3.6 млн клипов, ~4500 часов) были приведены к единой схеме метаданных с помощью audiogear. Audiogear считает по каждому клипу набор метрик качества (нейро-MOS, SNR, разборчивость, полоса, согласие ASR с текстом). На выходе получилось ~1870 часов.

Основные моменты по фильтрации и предобработке аудио:

  • Доверенные студийные наборы использовались целиком, остальные по метрикам: DistillMOS ≥ 3.0–3.3, WADA-SNR ≥ 14, STOI ≥ 0.9, полоса ≥ 9 кГц. Для наборов с ASR-текстами — дополнительный фильтр по CER независимого ASR (GigaAM-v3 ≤ 0.10): если два распознавателя не согласны, то аудио отбрасываем.

  • Никакого денойза. Модель выучивает артефакты энхансеров очень быстро, поэтому апсемплились нейросетью только 16-кГц аудио.

  • Пунктуация из аудио. В некотором количестве аудио пунктуация отсутствует, поэтому пришлось прогоняеть через ASR для востановление пунктуации.

  • Ударения: Для ударений использовался RUAccent.

  • Цифры: клипов с цифрами в тексте всего 0.24% — имплицитно числительные не выучить (и тут не понятно как с mfa их разметить). Для работы с цифрами используем RUNorm.

  • Громкость: каждый клип приводится к −23 LUFS, и промпт на инференсе — к тому же уровню.

Как готовить данные для VoXtream

VoXtream учится на длинных последовательностях (55 секунд = 688 кадров Mimi), а корпус состоит из клипов по 3–15 секунд. Значит, клипы одного диктора нужно склеивать в группы. И эта склейка — не техническая формальность, а место, где вы программируете просодию будущей модели. Всё, что вы вклеите между фразами, модель выучит как «так люди делают паузы».

Склейка обучающей группы: промпт, клипы одного диктора, паузы с комнатным тоном и явные sil-токены в фонемном потоке
Склейка обучающей группы: промпт, клипы одного диктора, паузы с комнатным тоном и явные sil-токены в фонемном потоке

Паузы — из эмпирики, с правильной структурой дисперсии. Статистика с 4.5 млн реальных межфразовых пауз: медиана после точки 0.39 с, после вопроса 0.42, после запятой 0.27. Но вставлять всем одну медиану нельзя (робот), и семплировать каждую паузу независимо из общего распределения тоже нельзя — тогда внутри одной фразы разброс будет как между разными дикторами. Работает расщеплённая дисперсия: пауза = медиана_знака × темп_группы × шум, где темп группы — один лог-нормальный множитель на всю группу (межгрупповая вариация), а шум — покадровый (внутригрупповая, CV≈0.54, как у живого человека). И обязательно клампы на хвосты распределения: без них модель выучила редкие двухсекундные паузы и стала вставлять их где попало.

Тишина — это не ноль. Паузы нельзя заполнять цифровым нулём: в реальных записях там комнатный тон, и модель отлично слышит разницу. Первая версия «комнатного тона» тайлила зеркальным повтором 60-мс окно тишины — период 8 Гц слышался как вертолёт, и модель честно выучила вертолёт. Правильно: синтезировать стационарный шум со спектральной огибающей тихого участка самого клипа (случайные фазы + overlap-add). Автокорреляция упала с 0.97 до 0.03.

Фейды — только внутри собственной тишины клипа, raised-cosine до 40 мс. Двухмиллисекундный фейд в ноль слышен как обрыв; фейд, наезжающий на речь, смягчает согласные.

Не ломайте причинные связи аугментацией. Темпо-аугментация (копии клипов ×0.75 и ×1.3 скорости) — полезная вещь, но если такие копии попадают в один пул с оригиналами, модель видит группы, где промпт медленный, а продолжение быстрое — и делает логичный вывод: «темп промпта ничего не значит». Аугментированные версии — строго отдельными пулами. По той же причине группы собираются однородными по темпу (клипы бинуются по слогам-в-секунду).

Мелочи, которые оказались не мелочами: порядок клипов в группе = порядок записи (у аудиокниг это даёт естественные продолжения мысли); 10% коротких групп (5–35 с) лечат галлюцинации на коротких промптах; спикер-эмбеддинги считаются мульти-кропом (6 кропов разной длины с разных мест группы, при обучении семплируется один) — так покрытие вариативности реальных промптов гораздо лучше, чем от изотропного шума поверх одного эмбеддинга.

MFA: форс-алайнер для VoXtream — не вспомогательный инструмент, а источник ground truth: из пофонемной разметки берутся duration-токены (ритм!), позиции пауз, темп клипов, статистика для склейки групп. Почему MFA. Для русского языка, к сожалению, нет ни одного нормального выравнивателя кроме MFA.

Как кадры выравниваются по фонемам

Mimi работает на 12.5 кадрах в секунду — кадр 80 мс. Фонема в беглой речи длится 30–120 мс: в один кадр помещается одна или две фонемы, а долгая ударная гласная тянется на несколько кадров. Из TextGrid берутся только начала фонем; конец каждой — начало следующей (паузы sil — такие же фонемы), последняя тянется до конца слота. При склейке группы клипы кладутся на общую шкалу времени с учётом вставленных пауз. Дальше строится миллисекундная сетка — каждой миллисекунде приписан индекс фонемы, — и кадр i = окно [80i, 80i+80): множество фонем, попавших в окно, и есть «фонемы кадра». Их не больше двух: если из-за очень коротких звуков в кадр попало три, самая короткая выбрасывается из потока, чтобы выравнивание осталось монотонным с меньше или равно двум фонемами на кадр.

Пример: слово «мама» и пауза — интервалы MFA, кадры по 80 мс и duration-состояния каждого кадра
Пример: слово «мама» и пауза — интервалы MFA, кадры по 80 мс и duration-состояния каждого кадра

Duration-состояние кадра — пара (сдвиг, число фонем): сдвиг = индекс первой фонемы этого кадра минус индекс последней фонемы предыдущего (0 — остались на той же, 1 — перешли к следующей, 2 — перескочили одну совсем короткую), число фонем — 1 или 2. Итого шесть состояний. Именно их Temporal Transformer учится предсказывать совместно с семантическим токеном — один softmax над (словарь Mimi × 6), — а на инференсе предсказанное состояние двигает указатель по фонемной последовательности: так выравнивание генерируется вместе со звуком, и модель не может ни перескочить слово, ни зациклиться.

Две последние детали. Знаки препинания вставляются в поток отдельными токенами после последней фонемы слова — их видит Phoneme Transformer (контекст интонации), но перед Temporal Transformer они удаляются, так как знак препинания — не фонема, и кадр ему не нужен. Ударение переносится из accent_text перестановкой ˈ на нужную гласную внутри слова.

Какие проблемы были в словаре

  • Односимвольные предлоги записаны названиями букв: в → v ˈɛ («вэ»), к → k ˈɑ («ка»), с → ˈɛ s («эс»). MFA, доверяя словарю, впихивал несуществующий гласный в аудио: медиана длительности предлога «в» получалась 140 мс вместо реальных 40–80. Предлог «в» встречается в четверти клипов — то есть ~40% корпуса имело испорченный ритм. Фикс: варианты произношения с ассимиляцией звонкости (в → v|f, к → k|ɡ) — MFA сам выбирает вариант по акустике.

  • Междометия — тем же способом: «хм» = «ха-эм», «ммм» = «эм-эм-эм» (1858 вхождений). Жемчужина коллекции — статья ь, которая разворачивалась в транскрипцию целой фразы «мягкий знак».

  • А вот аббревиатуры (ссср, нтв, днк) по буквам — правильно, их трогать нельзя. Автоаудит на этот класс простой: слова без гласных букв, у которых в произношении есть гласные фонемы.

  • Потерянная ё. Корпус для MFA собирался из колонки text, где ё давно заменена на е. Модель училась говорить «мое» вместо «моё» — на 13.3% корпуса. Правильные статьи в словаре были всегда — не было правильных текстов. Пересборка из accent_text + числовой гейт: в клипах с ё фонема ɵ обязана присутствовать (после фикса — 92.9%).

  • Дефисные слова. Если слова «мюнди-баре» нет в словаре, MFA молча пишет в разметку ДВА слова; при этом 18 тысяч дефисных слов, которые в словаре есть, идут одним. Пословный сопоставитель на этом рассинхронизировался, и группа молча выбрасывалась из обучения — это была не ошибка MFA, а моя.

  • OOV-слова MFA выравнивает как spn — на месте редкого слова в потоке появляется «пауза», под которой на самом деле речь. Покрытие словаря у меня 99.97%, так что класс редкий, но знать о нём надо: клипы с цифрами до фикса выпадали целиком именно так.

Алгоритм создания датасета целиком — от сырых записей до обучения:

  1. Сбор и унификация. Все источники приводятся к единой схеме метаданных (путь, текст, спикер, длительность, метрики качества), аудио — к 24 кГц mono.

  2. Отбор. Фильтры по DistillMOS, SNR, STOI, полосе и CER независимого ASR; дедупликация; доверенные студийные наборы — целиком.

  3. Тексты. Пунктуация нормализуется до .,!?; знаки переносятся из ASR-транскрипта; RUAccent расставляет ударения и восстанавливает ё (accent_text); клипы с цифрами разворачиваются RUNorm с ASR-валидацией; словарь произношений проходит аудит.

  4. Корпус для MFA. На каждый клип — <спикер>/<id>.wav и .lab с текстом из accent_text (без +, в нижнем регистре, без пунктуации).

  5. Выравнивание. mfa align со словарём espeak-IPA и собственной акустической моделью — по-клиповое, на выходе TextGrid со словами и фонемами с таймингами.

  6. Статистика. По TextGrid снимаются распределения пауз по знакам (4.5 млн стыков) и темп каждого клипа (гласные фонемы / озвученное время) — из него темпо-корзины.

  7. Склейка групп по спикеру до 55 с: порядок записи, паузы = медиана знака × темп группы × шум (с клампами), комнатный тон из спектра тихого окна, фейды внутри тишины, −23 LUFS, 10% коротких групп, аугментации отдельными пулами.

  8. Фичи. Выравниваются mimi-коды (16 кодбуков), фонемные последовательности из TextGrid (sil сохраняются, знаки препинания вставляются после последней фонемы слова, ударения переносятся из accent_text), покадровые индексы фонем и duration-состояния, мульти-кроп спикер-эмбеддинги, SPS-бины. (TextGrid используются по-клиповые, до склейки: так паузы клипов остаются естественными)

Итоги по цифрам

Метрики получены на небольшом количестве аудио на незнакомых для модели спикеров:

CER (GigaAM-v3)

Паузы (медиана)

Ровность темпа (CV)

Переключения голоса

~0.010

0.4–0.5 с

0.16

0–2%

Ссылки:

Комментарии (4)


  1. n0isy
    27.08.2026 15:45

    Здорово! А можно мультиязычность (ну хотя бы ru+en, чтобы ITшную речь можно было диктовать)?


    1. monkey_llm Автор
      27.08.2026 15:45

      Да, но не скоро)


  1. Brakanier
    27.08.2026 15:45

    Автору спасибо! Пойду тестить.

    с задержкой до первого пакета аудио ~100 мс на обычной GPU

    На какой GPU замерялось?

    А то обычная GPU часто оказывается h100)


    1. monkey_llm Автор
      27.08.2026 15:45

      3090