Russian narration for the Enshrouded notes: mod 0.4.0, installer, generation pipeline
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
# Генерация озвучки
|
||||
|
||||
Как из файлов игры получается мод, чем меряется качество и что крутить, чтобы стало лучше.
|
||||
Ни одна записка не прослушивается человеком: все решения принимаются по метрикам, а слушают уже в игре.
|
||||
|
||||
## Конвейер
|
||||
|
||||
```
|
||||
игра (enshrouded.kfc + .dat)
|
||||
│ extract kfc-parser: дескрипторы озвучки, звука, локализации, журнала
|
||||
│ EN-звук (frameless QOA 48 кГц) → work/en24/*.wav, RU-текст → текст для голоса
|
||||
▼
|
||||
work/notes.json ── refs ──► work/refs/*.wav референсы диктора (целые короткие записки и первые абзацы)
|
||||
│ work/centroid.npy тембр диктора (средний x-вектор WavLM) — цель QC
|
||||
│ work/en_pauses.json паузы диктора
|
||||
│ segments lexicon.pre → абзацы ≤ 380 знаков → RUAccent (+) → lexicon.post → U+0301
|
||||
▼
|
||||
work/segs.json ── render ──► work/takes/<id>_<hash>_s<seed>.wav 3 сида на кусок
|
||||
│ qc GigaAM-v3 (WER), meter2 (ударения), WavLM-SV (тембр), UTMOSv2 (MOS), темп
|
||||
│ pick фильтр + счёт; без проходного дубля → render --weak (ещё 2 сида) → qc → pick
|
||||
│ assemble обрезка, паузы диктора, 24→48 кГц soxr VHQ, громкость по оригиналу, лимитер, QOA
|
||||
▼
|
||||
work/qoa ── mod ──► mod/ru_voice_notes ── install.py ──► <игра>/mods + dbghelp.dll (EML)
|
||||
```
|
||||
|
||||
`python run.py all` выполняет всё по порядку. Каждый шаг можно повторить отдельно: дубли и оценки
|
||||
кэшируются по хэшу «референс + текст», поэтому после правки лексикона заново синтезируются и
|
||||
проверяются только затронутые куски.
|
||||
|
||||
## Метрики
|
||||
|
||||
| Метрика | Чем | Что ловит |
|
||||
|---|---|---|
|
||||
| `wer`, `hyp` | GigaAM-v3 RNN-T по кускам ≤ 20 с | пропуски, оговорки, «протечку» референса (английский в начале) |
|
||||
| `stress_ok / stress_n` | meter2 из модели ru-stress-cf | ударение не там, где поставил RUAccent или лексикон |
|
||||
| `sim` | WavLM-SV, косинус к центроиду диктора | уход тембра |
|
||||
| `mos` | UTMOSv2 | неестественность, артефакты |
|
||||
| `cps` | знаков в секунду | затянутость или скороговорку, обрыв |
|
||||
| `f0` (`qc --f0`) | разброс высоты тона, полутона | монотонность (медленно, на CPU) |
|
||||
|
||||
Отбор (`pick.py`): дубль проходит, если темп 7–19 знаков/с и WER ≤ 0,12 или ошибка не больше одного
|
||||
слова (у кусков до 3 слов — CER ≤ 0,35: ASR коверкает одиночные имена). Среди прошедших —
|
||||
максимум `6·stress + 0,5·MOS + 2·SIM − 3·WER`. Ударения весят больше всего: неверное ударение
|
||||
слушатель слышит сразу.
|
||||
|
||||
Сравнивать версии — по `work/pick_report.json` и по одним и тем же целым запискам (`qc --jobs`).
|
||||
|
||||
## История: от 0.3 к 0.4 (4 итерации)
|
||||
|
||||
0.3.0 — вся записка одним дублем, референс `amethystHermit` (10 с), 3 сида, линейная интерполяция до 48 кГц.
|
||||
|
||||
| Итерация | Что изменено | Чем подтверждено |
|
||||
|---|---|---|
|
||||
| 1. Текст | лексикон имён (`Во́ргот`, `Э́мбервэйл`, `Ла́хлан`…), апострофы в именах (`Кра'Тау` → `Кра Тау`), сокращения (`ок.`, `мм`, `в т.ч.`), числа в падеже; ударения переносятся по словам — посимвольный перенос портил 4 % текстов («в го́дод», «))») | сверка текстов: 0 расхождений после правки |
|
||||
| 2. Тембр | 8 кандидатов-референсов × 16 абзацев × 2 сида | победил `AlwaysTogether_Page_01__p1`: WER 3,7 % против 6,6 %, MOS 3,43 против 3,24, F0 5,30 против 4,52 |
|
||||
| 3. Синтез | абзацы ≤ 380 знаков вместо целой записки, лучший дубль на каждый абзац, 2 доп. сида для слабых | без проходного дубля: 82 из 2660 (заголовки и имена, ложные) |
|
||||
| 4. Сведение | паузы диктора (0,45 / 0,7 / 0,9 с вместо 0,3 / 0,7), soxr VHQ, мягкий лимитер | — |
|
||||
|
||||
Итог на 60 случайных записках, целиком:
|
||||
|
||||
| | 0.3.0 | 0.4.0 |
|
||||
|---|---|---|
|
||||
| WER | 3,2 % | 2,4 % |
|
||||
| MOS | 3,27 | 3,45 (лучше в 41 из 60) |
|
||||
| F0, разброс | 4,80 | 5,69 |
|
||||
| SIM | 0,838 | 0,840 |
|
||||
|
||||
## Что крутить
|
||||
|
||||
1. **Лексикон** (`ensvoice/lexicon.py`) — самый дешёвый рычаг. `PRE` — замены в обычном тексте
|
||||
(сокращения, числа, опечатки оригинала), `STRESS` — ударения игровых имён: регулярное выражение
|
||||
основы и номер ударной гласной. Подсказки, что чинить: поле `bad` в `work/qc/*.json` (слова с
|
||||
неверным ударением) и `hyp` против текста у дублей с высоким WER.
|
||||
2. **Референс голоса** (`voice.reference`). `python run.py refbench` озвучивает 16 абзацев каждым из
|
||||
9 кандидатов, затем `qc --jobs …` и `refbench --report` дают таблицу. Референс с обрубленным концом
|
||||
«протекает»: дубли начинаются с английских слов, у таких кандидатов много `bad` по WER.
|
||||
3. **Сиды** (`voice.seeds`, `extra_seeds`): больше сидов — выше шанс на чистый дубль, время растёт линейно.
|
||||
4. **Паузы** (`PAUSE` в `assemble.py`) и порог нарезки (`MAXC` в `segments.py`).
|
||||
5. **Отбор** (`passes` и `score` в `pick.py`).
|
||||
|
||||
Любую правку проверять числами: `pick_report.json` до и после и выборочные записки целиком.
|
||||
|
||||
## Время и ресурсы
|
||||
|
||||
Измерено на RTX 5080 16 ГБ (локальный бэкенд, батч 8):
|
||||
|
||||
| Шаг | Время |
|
||||
|---|---|
|
||||
| `extract` | 1–2 мин (CPU) |
|
||||
| `refs` | 1–2 мин |
|
||||
| `segments` | 2–5 мин (RUAccent на CPU, в основном загрузка моделей) |
|
||||
| `render` | около 2 мин на 2500 знаков × сид → около 4 ч на сид, около 12 ч на 3 сида |
|
||||
| `qc` | около 2,5 с на дубль → около 5 ч на 8000 дублей в один процесс; шарды `--shard i/n` параллельно |
|
||||
| `assemble` | около 20 мин (QOA-кодер на чистом Python, CPU) |
|
||||
|
||||
Автор генерировал 0.4.0 на Tesla V100 через сервер `media-node` (qwentts.cpp, GGUF Q8_0): там синтез
|
||||
примерно в 13 раз быстрее (RTF 0,05 против 0,6). Этот бэкенд (`tts.backend = "media_node"`) нужен, только если у вас есть такой сервер.
|
||||
|
||||
## Формат игры (для справки)
|
||||
|
||||
- `enshrouded.kfc`: заголовок KFC4 с 17 таблицами; ключи содержимого `(size, hash0, hash1, hash2)` →
|
||||
смещение в `enshrouded_NNN.dat`.
|
||||
- `VoiceoverResource.languages` — фиксированные слоты EN/DE/FR/ES/ZH, русского нет: мод подменяет EN.
|
||||
- `SoundResource`: `FramelessQOA`, 48 кГц моно, `frameCount`, `duration` в нс, `streamedData.hash`.
|
||||
- Frameless QOA: срезы QOA без заголовков, u64 little-endian на 20 сэмплов; LMS стартует с весов
|
||||
`[0, 0, −8192, 16384]` и не сбрасывается.
|
||||
- Локализация: `LocaTagCollectionResource`, записи по 24 байта (id, смещение строки от своего поля + 4, длина).
|
||||
- `JournalRegistryResource` связывает озвучку записки с id её названия и текста.
|
||||
Reference in New Issue
Block a user