Исследование · июль 2026
Тексты с кодом, нотацией и терминами не читаются вслух так же, как читаются глазами. Пятнадцать раундов измерений: что помогло, что не помогло и почему очевидные ходы оказались ловушками.
Тренажёру подготовки к бэкенд-собеседованиям нужен аудиорежим — заниматься на прогулке, как слушают подкаст. Контент есть: 602 вопроса, у каждого разбор с эталонным ответом, ловушками и следующими вопросами. Итого 473 тысячи символов, около 9.5 часов речи.
Ограничения: генерация полностью локальная и офлайн, без провайдерских ключей на сервере. Язык — русский. Материал — инженерный: код, SQL, аннотации, аббревиатуры.
Проверено семь вариантов, все локальные. Скорость проблемой не оказалась ни у одного: даже самый медленный проходит корпус за ночь, а быстрые — за четверть часа.
Открытый лидерборд русского TTS показал, что Silero v4 стоит на последнем месте из 34 записей по натуральности — и объективно хуже собственной версии 3.1. То есть новее не значит лучше.
Пока дефект оставался впечатлением, чинить его было нечем. Решение: прогонять синтез обратно через распознавание и считать CER — долю потерянных символов.
Это оказалось не только измерением, но и защитой. Silero молча выбрасывает латиницу:
| Фраза | Ожидаемо | Silero выдал |
|---|---|---|
List<? extends Number> удобно читать как Number | ~3.4 с | 1.1 с |
Более безопасные варианты — ON DELETE RESTRICT | ~3.3 с | 1.7 с |
Три движка ведут себя на латинице по-разному, и это влияет на дизайн сборки:
Silero выбрасывает молча, Vosk падает с KeyError, Piper читает с русской фонетикой.
Худшее поведение — именно молчаливое.
Второй рычаг оказался неожиданно сильным. ESpeech клонирует голос по образцу на 10–13 секунд, и первые прогоны использовали образец, сгенерированный Silero — то есть модель просили копировать артикуляцию у робота, которого сама же работа и забраковала.
ruaccent поставил
ударение правильно (вопр+ос). Значит виноват был не текст — отрицательный результат,
сэкономивший время.Здесь чинилась не та проблема. Я считал, что беда в непроизносимости нотации, и написал слой нормализации: развернуть, кириллизовать, произнести. Замерил распространённость — паттерны есть у 72% вопросов корпуса:
Текст стал произносимым — и остался плохим, потому что это был тот же листинг, просто озвученный. Нотацию нужно переводить не в звук, а в смысл.
Появилось и то, чего в исходном тексте не было вообще: возврат к отложенной мысли. В начале — «запомни оговорку, вернёмся к ней», в конце про ловушку со стиранием типов — «вот здесь и срабатывает та самая оговорка». На бумаге избыточно: глаз вернётся абзацем выше. В ухе возврата нет, и это единственный способ связать части объяснения.
Полный свод правил — ADR 0002. Побочный эффект: аудио-версия примерно в 1.8 раза длиннее экранной, объём корпуса вырос с 9.5 до 16.5 часов.
Второе замечание того же раунда — монотонность. Плоская строка не умеет замедляться, поэтому единицей скрипта стала реплика с типом, несущая свой темп и паузы.
| Тип реплики | Скорость | Пауза до | Пауза после |
|---|---|---|---|
| повествование, связки | ×1.02 | 120 мс | 120 мс |
| термин, новый случай | ×0.88 | 420 мс | 260 мс |
| ключевая фраза | ×0.86 | 480 мс | 420 мс |
| вопрос | ×0.92 | 300 мс | 250 мс |
Таблица живёт в коде одним объектом: правка в одном месте меняет дикцию всего курса. Паузы вставляются реальными сэмплами при склейке, а не выпрашиваются у модели.
23 секунды осмысленной медлительности — и разборчивость не просела, а выросла. Замедление на терминах помогает не только человеку, но и распознавалке.
Замер огибающей на стыках показал механизм: перед паузой сигнал держится около 0.013 — это собственный шумовой фон референсной записи, примерно −30 dB от пика. В паузе — ровно 0.0000. Переход занимает один сэмпл.
Ухо слышит здесь не «говорящий замолчал», а «запись выключилась»: пропадает не только голос, но и воздух помещения.
| Вариант паузы | Скачок между сэмплами | Перепад громкости | CER |
|---|---|---|---|
| цифровой ноль | 0.00208 | ×25 | 6.3% |
| + затухание 45 мс | 0.00037 | ×23 | 4.9% |
| + фон комнаты | 0.00058 | ×12 | 4.6% |
Темп меряется в слогах в секунду — для русского это просто количество гласных. В нашем корпусе 2.37 слога на слово, что даёт перевод в привычные слова в минуту.
| Что | слог/с | слов/мин |
|---|---|---|
| Наша озвучка на ×1.0 | 5.6–6.3 | 142–160 |
| Средний темп русской речи | 4.73 | 120 |
| Технические презентации (англ. источники) | 2.5–3.25 | 100–130 |
| Учебное объяснение | 2.4–3.9 | 60–100 |
Инженерный текст озвучивался на скорости развлекательного подкаста. Две традиции меряют в разных единицах, но в слогах в секунду сходятся: 2.5–4.0 для материала, который нужно усвоить, а не прослушать.
Очевидный ход — покрутить ручку speed у модели. Она масштабирует предсказанные
длительности и выводит модель за пределы обучающего распределения: речь плывёт.
speed модели
растяжение atempo
больше пауз
При одном и том же темпе паузы и растяжение дают разборчивость в 2.6–4.4 раза лучше, чем ручка модели. Финальная комбинация — паузы ×3, затем растяжение 0.92: 96 слов/мин при CER 5.2%.
Порядок важен: паузы даются первыми, потому что они несут смысл — границу мысли. Растяжение остаётся косметической доводкой до целевой цифры. Ровно так делает живой преподаватель: он не растягивает слова, он молчит между мыслями.
Замер оказался хуже ожидаемого: 34% файла — тишина, медианная пауза 2.15 секунды, самая длинная — 9.3 секунды. И это при том, что предыдущий раунд уже занимался паузами и считал вопрос закрытым.
Ошибка была в модели, а не в числах:
pad_before и pad_after, поэтому промежуток
между двумя репликами получался суммой двух паддингов — величиной, которую никто
не задавал осознанно;beat;atempo растягивал тишину заодно с речью.Арифметика сходится в точку: 260 + 250 + 120 = 630 мс, ×3 = 1890, ×1.087 = 2055 мс — ровно наблюдавшаяся медиана. Пауза на подумать в 2.5 с тем же путём стала 9.3 с. А поскольку раздувалось всё одинаково, двухсекундная пауза вставала внутри одной мысли — отсюда обрыв на полуслове.
| Граница между репликами | Пауза |
|---|---|
| повествование продолжается | 140 мс — вдох, а не остановка |
| после термина идёт его объяснение | 260 мс |
| подводка к термину или ключевой фразе | 480 мс |
| после ключевой фразы | 700 мс |
| пауза на устный ответ | 2500 мс — единственная намеренно длинная |
Плюс два следствия: никаких глобальных множителей, и растяжение применяется к сегментам речи до сборки — так пауза длится ровно столько, сколько заявлено, при любом темпе.
Расстановка пауз была верной с самого начала. Максимальная пауза сократилась с 9.3 с до 2.9 с — и это ровно та единственная, что даётся на устный ответ.
atempo обрабатывал
вместе с речью и фон комнаты, добавляя артефакты.Мораль. Симптом «звучит неестественно» почти никогда не лечится подбором числа. Подкрутить множитель здесь означало бы получить то же самое, только тише. Настоящая ошибка была в том, что длительность промежутка вообще никем не задавалась осознанно — она получалась побочным эффектом сложения.
Это третий раз за исследование, когда правильным ходом оказалось поменять модель, а не параметр: до этого был текст (транслитерация вместо объяснения) и темп (ручка модели вместо пауз и постобработки).
Затухание 45 мс из шестого раунда применялось ко всему сегменту, включая речь. Замер энергии на краях: у отдельных реплик в первых 45 мс сигнал достигает 83% от собственного пика. Русский взрывной согласный длится 50–80 мс — то есть косинусная кривая просто стирала начальный звук.
Сначала я сделал «умнее»: находить реальные границы речи по энергии, обрезать лишнее и затухать только в тихой зоне. Стало хуже. Причина поучительная: глухие щелевые — «с», «ш», «ф», мягкое «ть» — энергетически лежат далеко ниже любого разумного порога. Порог срезал ровно те согласные, ради спасения которых всё и затевалось.
Базовая длительность теперь умножается на случайное число из 0.62–1.42, а граница уровня «вдох» с вероятностью 35% не получает паузы вовсе — фразы сливаются. Генератор засевается хешем текста соседних реплик, поэтому одинаковый контент даёт одинаковое аудио при любой пересборке.
CER после этих правок — 3.4–4.8% против 2.2–4.8% до них. Разброс между конфигурациями сопоставим с разбросом самого замера на одном образце, поэтому улучшение разборчивости здесь не заявляется.
Правка краёв обоснована прямым измерением сигнала, а не метрикой. Это тот случай, где CER показывает отсутствие вреда, но не способен показать пользу: он меряет, разобрано ли слово, а не то, звучит ли начало фразы обрубленным. Три раунда назад метрика поймала дефект, которого не слышало ухо; здесь ухо поймало дефект, которого не видит метрика.
Правильный вывод, и стоило прийти к нему раньше. Три итерации подряд я правил один и тот же механизм: паузы были слишком резкими, потом слишком длинными, потом слишком одинаковыми. Каждая правка улучшала замеры. Каждый результат на слух оставался искусственным.
Три раунда правок одного механизма — сигнал, что механизм не нужен, а не что он недонастроен.
Из конвейера ушли таблица длительностей, функция границы, разброс, пропуск части пауз и фон комнаты. Осталось затухание 8 мс на стыках — это анти-клик, а не пауза.
Сплошной синтез оказался вдвое хуже по разборчивости — при том, что пауз не вставляется ни там, ни там.
Причина: F5 сам режет длинный текст на куски по числу символов и сшивает кросс-фейдом. Его граница попадает в середину фразы, наша — на границу смысла. То есть разбиение на реплики полезно не только для темпа, но и как способ отобрать у модели право резать текст где попало.
Побочно объяснилось, почему сплошной вариант звучит безостановочно: 13 пауз против 34 и 1.3% тишины против 7.1%. Длинный непрерывный текст модель читает не дыша.
Мораль. Пауза теперь — свойство пунктуации в тексте, а не параметр сборки. Хочешь паузу — поставь точку и начни новую реплику. Это и проще, и честнее: длина паузы становится решением автора текста, а не подобранным числом в конфиге.
Да. atempo=0.66 — растяжение готового аудио в полтора раза. И это выходило
далеко за границу осторожности, которую я сам же записал двумя раундами раньше:
«сильнее 0.8 — осторожно, артефакты слышны, хотя CER их не показывает».
Вопрос вскрыл две ошибки подряд.
F5 — zero-shot модель. Она копирует из референса не только тембр, но и темп речи. Отбирая референс, я смотрел исключительно на CER и ни разу не померил, с какой скоростью говорит чтец.
Первые референсы вырезались окном в 13 секунд с отметки 45 с, чтобы проскочить дисклеймер LibriVox. Окно попадало в середину фразы, и транскрипт получался обрубком: «улиц губернского города о дело происходило в 1842 году».
F5 получает транскрипт референса дословно. Кривой транскрипт портит каждый синтезируемый сегмент, а не только начало. Именно поэтому Тургенев в первом отборе выглядел плохо и был отвергнут.
| Референс | нарезка | растяжение | слов/мин | пауз | CER |
|---|---|---|---|---|---|
| Лесков | по таймеру | ×0.66 | 97 | 35 | 3.2% |
| Тургенев | по таймеру | ×1.00 | 108 | 136 | 7.3% |
| Тургенев | по фразам | ×1.00 | 91 | 142 | 1.0% |
| Достоевский | по фразам | ×1.00 | 133 | 27 | 11.2% |
Одна только чистая нарезка того же клипа дала 7.3% → 1.0%, семикратное улучшение.
Мораль. Четыре раунда подряд я компенсировал следствие вместо причины: паузы правил затуханиями, темп — растяжением. Оба свойства принадлежали референсу, и оба чинились в постобработке. Признак, который стоило заметить раньше: если параметр приходится подкручивать всё сильнее, скорее всего его источник находится выше по конвейеру. С 45 мс затухания до 8 мс, с ×0.9 растяжения до ×0.66 — оба раза цифра уползала в крайность, и оба раза чинилось не то место.
Реплика короткая — 48 символов, внутреннему разбиению модели не подвергается. Значит разрывы приходят из просодии, а просодию модель берёт из референса.
Казалось бы, взять самого слитного — «Поездку в Полесье», ноль пауз в минуту. На полном тексте он дал CER 18.8%. Слитно и неразборчиво. Второй по слитности — 14.1%.
| Референс | слов/мин | тишины | CER |
|---|---|---|---|
| Полесье — самый слитный | 109 | 1.2% | 18.8% |
| Записки из подполья | 133 | 3.5% | 14.1% |
| Белые ночи | 100 | 9.1% | 1.7% |
«Слитный» и «разборчивый» оказались независимыми свойствами — ровно как до этого темп с разборчивостью. Третий раз за исследование одна и та же ловушка: улучшаешь один показатель референса, роняешь другой. Референс отбирается по трём измерениям сразу.
Реплики типов «ключевая фраза», «термин» и «вопрос» короткие по построению и должны звучать на одном вдохе — значит внутренних запятых в аудио-версии у них нет. Затронуто 6 реплик из 26. Это продолжение правила восьмого раунда: если пауза — свойство пунктуации, то и отсутствие паузы задаётся пунктуацией.
С хорошим референсом сплошной синтез перестал проигрывать разбивке по репликам — 1.0% против 1.2% — и оказался вдвое слитнее: 21 пауза против 38. Вывод восьмого раунда о превосходстве разбивки был получен на плохом референсе и держался именно на нём.
Конвейер после десяти итераций свёлся к текст → синтез одним проходом.
Ни вставленных пауз, ни растяжения, ни разбивки, ни единого параметра, подобранного
на слух. Осталось правильно написанный текст и правильно выбранные тринадцать секунд
референса.
Первое замечание вскрыло ошибку в самой процедуре. До сих пор при любой проблеме с манерой речи я менял чтеца целиком — вместе с тембром. Тургенев рвал фразы, я взял Достоевского, и тембр уехал вместе с манерой.
Тембр принадлежит голосу и постоянен. Манера принадлежит отрывку и меняется по ходу записи: один и тот же человек читает описание слитно, а диалог — рвано.
Скан всей записи — 777 секунд, 255 сегментов, 35 подходящих окон — показал, что плотность пауз у одного и того же чтеца гуляет от 6.7 до 20 с лишним в минуту. Прежний клип просто попал в рваный участок.
| Отрывок того же чтеца | пауз/мин у клипа | разрывов внутри фразы |
|---|---|---|
| прежний, 51 с | 13.3 | 3 |
| t0 — 51.5 с | 6.7 | 1 |
| t1 — 463 с | 11.4 | 3 |
| t2 — 193.5 с | 12.7 | 0 |
Отдельно поучительно: плотность пауз в клипе не предсказывает поведение на нашей фразе. У t2 этот показатель хуже, чем у t0, а фразу он читает слитно. Косвенный показатель не заменяет прямую пробу — контрольную фразу нужно синтезировать каждым кандидатом и считать разрывы в ней.
Процедура выбора стала двухступенчатой: голос выбирает человек по тембру, отрывок выбирает скрипт по пробе.
Правило «без внутренних запятых» из прошлого раунда касалось только ключевых фраз. Распространено на весь текст: одна мысль — одно предложение; вложенные придаточные разбиваются; запятая, тире и двоеточие внутри фразы не используются; подлежащее в начале.
Компромисс сместился, но не исчез: t2 звучит слитнее — медианная пауза вдвое короче, — а t0 точнее по метрике и попадает в темп без всякой обработки. Мягкое растяжение 0.88 вернулось как допустимая доводка: оно внутри безопасной зоны, в отличие от отвергнутого 0.66. Разница между инструментом и костылём здесь ровно в глубине.
Справедливо, и хуже, чем кажется на первый взгляд.
Первая. Отбор проверил семь движков и остановился, как только один заработал приемлемо. Дальше десять раундов улучшали выбранный движок, ни разу не спросив, не решает ли задачу другой лучше.
Вторая. Отбор опирался на открытый лидерборд русского TTS. При повторной проверке оказалось, что он не обновлялся с августа 2025 года. Всё, что вышло позже, отсутствует в нём по построению — а вышло немало:
| Модель | Дата | Лицензия |
|---|---|---|
| Qwen3-TTS (Alibaba) | январь 2026 | Apache-2.0 |
| VibeVoice (Microsoft) | январь 2026 | MIT |
| IndexTTS-2 | январь 2026 | — |
| Higgs Audio v3 (Boson AI) | 2026 | открытая |
| CosyVoice 3 | 2026 | открытая |
| Движок | слов/мин | пауз | тишины | медиана | CER | скорость |
|---|---|---|---|---|---|---|
| Qwen3-TTS, одним проходом | 114 | 38 | 1.8% | 72 мс | 0.8% | ×0.4 |
| Qwen3-TTS, по репликам | 109 | 80 | 4.2% | 75 мс | 1.2% | ×0.4 |
| ESpeech RL-V2 | 118 | 123 | 20.1% | 180 мс | 1.0% | ×4.1 |
| ESpeech RL-V1 | 118 | 118 | 20.3% | 225 мс | — | ×4.1 |
| ESpeech PODCASTER | 118 | 118 | 22.3% | 250 мс | 2.3% | ×4.1 |
Три разных чекпойнта ESpeech — RL-V2, RL-V1, PODCASTER — дают практически одинаковые числа. Разница между ними в пределах погрешности.
Qwen на том же референсе и том же тексте: 38 пауз вместо 120, 1.8% тишины вместо 20%, медианная пауза 72 мс вместо 180–250.
Значит рваность, с которой шла борьба четыре раунда подряд — вставленными паузами, подбором затуханий, перебором отрывков записи, переписыванием пунктуации, — была свойством архитектуры F5/DiT. Не настроек. Не референса. Не текста. Qwen3-TTS построен иначе: дискретная многокодбуковая языковая модель без DiT, и этой проблемы у него просто нет.
Четыре раунда лечили симптом чужого инструмента.
×0.4 к реальному времени против ×4.1 — вдесятеро медленнее. Корпус в 16.5 часов: около 41 часа генерации вместо четырёх. Разово это две ночи. Замер сделан без flash-attn, который рекомендует документация Qwen; с ним разрыв должен сократиться, насколько — не проверено.
Непроверенным осталось многое: VibeVoice, IndexTTS-2, Higgs Audio v3, CosyVoice 3, Chatterbox, XTTS-v2, а также веса ESpeech SFT-95K и SFT-256K и русский F5 от Misha24-10 (последние три не скачались из-за таймаутов зеркала). По этим архитектурам вывода нет.
Урок. Ошибка не в том, что первый выбор оказался неоптимальным — это нормально и неизбежно. Ошибка в том, что после выбора обзор ни разу не пересматривался, хотя работа шла десять раундов и раз за разом упиралась в одну и ту же проблему. Признак тот же, что и в девятом раунде, только на уровень выше: если несколько итераций подряд борются с одним симптомом и каждая помогает лишь частично, источник, скорее всего, вне конвейера вообще. И отдельно: внешний рейтинг — снимок на дату, и дату надо смотреть раньше, чем цифры.
Ограничение известное и документированное: Qwen3-TTS обучался без разметки ударений, поэтому пользовательские знаки ударения он не поддерживает. В репозитории модели по этому поводу открыты обсуждения.
Но «не поддерживает» — не то же, что «игнорирует». Проверил четыре нотации на пяти фразах, измеряя не «стало ли лучше» (это на слух), а не сломался ли текст — обратным распознаванием.
Надёжный способ — переписывать. Слово с неоднозначным ударением заменяется на однозначное:
| Проблемное | Замена |
|---|---|
| «этот вопрос стоит разобрать» | «этот вопрос надо разобрать» |
| «большая часть проверок» | «основная часть проверок» |
| «список параметризован типом» | «у списка задан тип элемента» |
Это продолжение принципа второго раунда: аудио-версия — отдельный жанр, и слова в ней выбираются по звучанию, а не только по смыслу. Омографы туда просто не попадают.
Второй способ — ЗАГЛАВНЫЕ, точечно. Единственная нотация, не портящая текст, и модель на неё реагирует: длительность фразы меняется с 2.56 до 2.88 с. Двигает ли она ударение в нужную сторону — надо слушать.
Хотел померить ударения автоматически. Написал детектор: границы слов из Whisper, внутри слова — деление огибающей на вокалические ядра, ударное — с наибольшим произведением громкости на длительность, ожидание — из RUAccent.
На записи Qwen он показал 61% расхождений. Цифра выглядела неправдоподобно, а ошибки подозрительно смещались к первому слогу — поэтому я прогнал детектор по контрольному материалу: живой записи чтеца, где ударения верны по определению.
Инструмент меряет не ударение, а положение взрывного согласного в начале слова. Его числа не используются нигде, включая те 61%. Скрипт лежит в репозитории как отрицательный результат. Помогло бы настоящее фонемное выравнивание вместо энергетической эвристики — не делалось.
Заметка про метод. Единственная причина, по которой ошибочные 61% не попали в отчёт, — детектор был прогнан по контролю прежде, чем ему поверили. Ровно этой проверки не было сделано десятью раундами раньше в отношении внешнего лидерборда: там не посмотрели дату, и это стоило проекту десяти раундов работы не с тем движком. Навык один и тот же — прежде чем верить измерению, проверь измеритель.
Продолжение — раунд 23. Позже модель научили слушаться знака ударения U+0301: дообучение LoRA на аудиокнигах и фразах с намеренно сдвинутыми ударениями. Исходная Qwen следует знаку на ненормативном слоге в 21% случаев, после дообучения — в 49%, голос и разборчивость те же. Абзац с омографами и пары «до / после» — на отдельной странице, решение — ADR 0023.
Предыдущий раунд собрал словарь из 79 терминов и озвучил его пачками для проверки на слух — по десять терминов в клипе, 93 секунды на весь просмотр. Этот подход отменяется целиком: любая схема, где решение выносит человек, не масштабируется на пополнение корпуса.
Значит нужна автоматическая проверка произношения — включая ударение, где я уже дважды провалился.
Оба провалившихся детектора искали ударный слог внутри сигнала по энергии. Прежде чем винить метод, проверил эталон: Silero действительно уважает метки — звук меняется от переноса ударения. Разметка была верной, провалился подход.
Сработало, когда задача перевернулась: ничего не сегментировать, а спрашивать, на какую из известных записей похожа неизвестная. Silero умеет ставить ударение в заданный слог — значит можно нарезать эталоны для всех позиций, а кандидата сравнивать по форме контура громкости, нормированной так, что тембр и темп сокращаются.
Первый полный прогон: 30 принято, 12 «исправлено», 37 не решено. Исправления — растяжки вроде «ээс-ку-эль». Посмотрел, что услышала распознавалка у непрошедших:
| Термин | Писали | Услышано | сходство |
|---|---|---|---|
| JVM | «джей-ви-эм» | «что такое JVM.» | 0.0 |
| HTTP | «эйч-ти-ти-пи» | «что такое HTTP.» | 0.0 |
| Spring Boot | «Спринг Бут» | «что такое Spring Boot.» | 0.0 |
Модель произнесла всё правильно. ASR узнал термин и записал его своим письмом — латиницей. А сравнение шло кириллицы с латиницей и давало ноль. Инструмент браковал верные произношения и подменял их мусором.
Починка: засчитывать совпадение и с английским исходником, разворачивать цифры
(«3Map» → «триMap»), сводить оба алфавита к общей фонетической записи, где «Спринг Бут»
и Spring Boot дают одну строку. Проверено без повторной генерации,
на уже собранных транскриптах: проходящих стало 78 из 79 вместо 30.
JWT я написал как «джей-ви-ти». Правильно — «джей-дабл-ю-ти».
Инструмент эту опечатку подтвердил, потому что проверяет соответствие звука
написанию, а не написания термину.
Вывод: аббревиатуры нельзя писать руками. Побуквенное чтение выводится из самого термина правилом, руками остаётся только список идиоматических исключений — тех, что давно стали словами: джейсон, рест, эс-ку-эль, си-джи-либ. Это константа языка, а не работа по корпусу. Правило исправило JWT, AOP и URL, и все три теперь распознаются со сходством 1.00.
Слева — то, что попало бы в озвучку: мои опечатки, заверенные сломанной проверкой, и её же «починки» растянутой гласной. Справа — то, что выбрал исправленный конвейер.
Последняя пара — самая показательная. Написание было верным с самого начала; сломанный измеритель объявил его негодным и «исправил», растянув гласную. После починки проверки конвейер вернулся к исходному варианту сам.
Чего автоматика не решает. Конвейер добивается того ударения, которое считает верным RUAccent. Где акцентуатор ошибается на жаргоне — на «дженерики» он даёт второй слог, а в речи разработчиков слышен первый — инструмент этого знать не может. Это вопрос нормы, а не измерения.
speed у диффузионной модели. Роняет разборчивость впятеро.
Главный отрицательный результат.ESpeech даже вылеченный не догоняет Vosk и Silero по чистоте — 5–7% против 1–3%. Это осознанная плата: диффузионная модель звучит естественнее и рискует сильнее.
Сама метрика тоже небезупречна. Whisper плохо разбирает транслитерированные термины, поэтому образец про дженерики показывает завышенный CER у всех движков одинаково. Метрика достоверна для сравнения вариантов на одном тексте и обманчива между разными.
Сырые json — в каталоге results/, скрипты — в scripts/,
по одному описанию на итерацию — в experiments/.
Всё локально: ESpeech-TTS-1 (Apache-2.0), Piper (MIT), Vosk-TTS (Apache-2.0), faster-whisper (MIT), референсы — общественное достояние (LibriVox). У Silero бесплатная лицензия ограничена образовательными целями.