Я пять лет вожу настольные ролевые игры как мастер и думаю, что музыка это 50% эффекта погружения в процесс игры который работает в обе стороны.

Сколько раз повторялась одна и та же ситуация: партия торгуется в подземелье с контрабандистом, напряжение на столе настоящее — и тут трек просто заканчивается. Тишина. Или автоплей подсовывает что‑то бодрое из соседнего плейлиста. Магия момента разбита, а я судорожно переключаю вкладки, а игроки уже скроля ленту далеко вне этого подземелья..

Видеоигры решили эту проблему десятилетия назад. В любой RPG музыка города крутится, пока ты в городе, а стоит спуститься в склеп — тема мрачнеет вместе с тобой, точно в долю. Композиторы пишут игровую музыку секциями с loop‑точками и маркерами переходов, а движки вроде FMOD и Wwise собирают из этого «музыку, которая реагирует». Но моя фонотека — это обычные mp3. У них есть начало и конец, и им плевать на мою партию. Да можно брать тематические саундтреки по часу, но в них отсутствует развитие как в обычных треках.

Так родился проект: инструмент, который берёт любой трек и заставляет его вести себя как игровой саундтрек. На основании проделанного ресерча и разработки я решил написать эту статью — вся дорога: от теории музыки и алгоритма Фута из 2000 года до нейросетей 2025-го. С честными цифрами, включая место, где новая нейросеть проиграла старой.

Как устроена адаптивная музыка в играх

Прежде чем резать чужие треки, стоило понять, как это делают «по‑настоящему». В игровом аудио два базовых приёма.

Horizontal re‑sequencing — трек состоит из секций, каждая умеет зацикливаться. Переход между секциями происходит не когда захотелось, а на музыкальной границе: конец лупа, граница такта или фразы. Хвост уходящей секции (реверберация, тарелки) при этом может доозвучивать поверх следующей — в Wwise это называется post‑exit, и именно эта деталь отличает «склейку» от перехода, который звучит задуманным.

Vertical layering — секция одна, но инструменты лежат отдельными слоями: перкуссия, бас, мелодия. Слои включаются по «интенсивности» сцены: разведка — только фон, начался бой — подключились барабаны.

Оба приёма предполагают, что музыку написали под них. Моя задача — получить то же самое поверх готовой записи. Значит, нужно: найти в треке музыкальную структуру, сделать каждую секцию бесшовным лупом и научиться переходить между секциями так, чтобы не было слышно склейку. Опционально добавить разложение по слоям — барабаны, вокал, мелодия, фон.

Шаг 1. Ищем структуру: биты, такты, новизна

Первая версия анализатора — классический MIR (music information retrieval) на librosa, без нейросетей.

Биты. Алгоритм Эллиса (2007): огибающая онсетов плюс динамическое программирование. На выходе — темп и позиция каждого удара.

Сильные доли. Предполагаем размер 4/4 и подбираем фазу: из четырёх возможных сдвигов выбираем тот, при котором онсеты на каждом четвёртом бите в среднем самые сильные. Наивно, но для большинства современной музыки работает.

Границы секций. Здесь — алгоритм Фута (2000), красивый до сих пор. Для каждого бита считаем признаки: хрому (какие ноты звучат — это про гармонию), MFCC (тембр) и громкость. Затем строим матрицу самоподобия: элемент (i, j) показывает, насколько бит i похож на бит j. Куплеты и припевы проявляются на ней характерными «квадратами» — участками, где музыка похожа сама на себя. А границы между секциями находит свёртка с «шахматным» ядром вдоль диагонали: она даёт кривую новизны, пики которой — и есть места смены секций.

Вот как это выглядит на реальном треке:

Матрица самоподобия и кривая новизны. «Квадраты» на матрице - секции трека; голубые линии - найденные границы.
Матрица самоподобия и кривая новизны. «Квадраты» на матрице — секции трека; голубые линии — найденные границы.

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

Шаг 2. Граница должна не «слышаться», а «замыкаться»

Ключевое озарение пришло из Infinite Jukebox — проекта Пола Ламера, который бесконечно «продлевал» любую песню, прыгая между похожими битами внутри неё. Его вывод: для лупа важно не то, где переход слышен, а то, где стык замыкается чисто. Поэтому после базовой нарезки каждая граница «доводится»: она пробует сдвинуться на соседние сильные доли (в пределах пары тактов) и выбирает позицию, которая максимизирует сразу несколько вещей:

  1. Замыкаемость лупа — насколько конец секции спектрально похож на её начало (буквально: «насколько чисто этот кусок склеится сам с собой»);

  2. Фразовость — бонус, если длина секции получается 4 или 8 тактов: так строится почти вся музыка;

  3. Слышимость перехода — сильный онсет и скачок громкости на границе (признак, что здесь действительно вступает новая партия);

  4. Новизна — тот самый пик из матрицы самоподобия, как якорь.

Про якорь — отдельный урок. Без него оптимизатор радостно утаскивал границу с правильного места на «случайно чистый» стык в двух тактах от настоящей смены секции. Локальная метрика без глобального сигнала оптимизирует не то, что нужно.

Вторая находка этого этапа — детект проигровок. Нарастания (build‑ups, райзеры) зацикливать нельзя: напряжение растёт‑растёт и резко сбрасывается на стыке — звучит как сломанный чайник. Распознаются они по форме: у обычной секции громкость гуляет вокруг среднего, у проигровки — направленно ползёт вверх, и вместе с ней растёт «яркость» звука (спектральный центроид).

Слева - обычная секция: громкость колеблется вокруг тренда, наклон почти нулевой.                               Справа - проигровка: направленный рост. Такие секции играют один раз, как драматический мостик.
Слева — обычная секция: громкость колеблется вокруг тренда, наклон почти нулевой. Справа — проигровка: направленный рост. Такие секции играют один раз, как драматический мостик.

Шаг 3. Плеер: бесшовные лупы в браузере

Фронтенд — React и Web Audio API. Web Audio позволяет планировать воспроизведение с точностью до сэмпла: каждый проход лупа — отдельный источник со своим временем старта на аудио‑часах браузера. По кнопке «Дальше» следующий запланированный кусок просто берётся из следующей секции — и переход попадает ровно на границу лупа.

Дальше — набор приёмов, каждый из которых по отдельности мелочь, а вместе они превращают «склейку» в переход:

  • Микро‑фейды. Любой срез волны не в нуле даёт щелчок. Шесть миллисекунд фейда на каждом стыке стал давать бесплатно хороший результат.

  • Правильный кроссфейд. Наивный линейный кроссфейд (один трек линейно затухает, другой нарастает) проседает по громкости в середине — на слух это заметный «провал». Правильные кривые — косинус и синус: их квадраты в сумме дают единицу, и суммарная мощность остаётся постоянной.

Почему DJ-пульты и игровые движки используют equal-power кроссфейд: у линейного - провал громкости в середине, у косинусного суммарная мощность постоянна.
Почему DJ‑пульты и игровые движки используют equal‑power кроссфейд: у линейного — провал громкости в середине, у косинусного суммарная мощность постоянна.
  • Дозвучивающий хвост. Тот самый post‑exit из Wwise: старая секция не обрубается на границе, а дозвучивает секунду‑полторы поверх новой, медленно затухая. Реверберация и тарелки доживают своё и шов исчезает. Из всех приёмов этот даёт самый заметный эффект.

  • Бас‑своп. Приём из DJ‑практики: два одновременных баса при кроссфейде дают кашу. Поэтому низ входящей секции включается жёстко на сильной доле, а середина и верх кроссфейдятся плавно. В любой момент времени звучит ровно один бас. Забавно, что нейросеть DJtransGAN от Sony, обучаясь имитировать переходы живых диджеев, «выучила» ровно этот же приём.

  • Стингеры. Одиночные акценты на переходах вроде тарелки, низкого удара, райзера. Тонкость в таймингах: удар должен начаться на границе, а райзер — закончиться на ней. Назначаются на конкретную секцию: партия входит в склеп — бум. Игроки вздрагивают, десять из десяти.

Шаг 4. Потолок эвристики и нейросети

Я разметил вручную несколько треков совершенно разных стилей от классического Грига, до тяжелых рифов и эпической киберпанковой электронщины — расставил границы так, как их слышу я, — и посчитал, насколько автоматика в них попадает (F‑мера с допуском в один такт). Эвристика выдала 0.45. На простой электронике — прилично, на оркестровой музыке с плавающим темпом не очень хорошо. Дальше был долгий ресёрч, что произошло в задаче анализа музыкальной структуры за последние годы. Самое подходящее что нашел:

  1. All‑In‑One (2023) — трансформер, обученный на Harmonix Set: 912 треков, размеченных живыми музыкантами. Предсказывает темп, биты, сильные доли, границы и — главное — функциональные названия секций: intro, verse, chorus, bridge. Внутри трюк, который многое объясняет: перед анализом трек разделяется на инструментальные слои, и границы куплетов сеть «видит» по вокальному слою гораздо лучше, чем по общему миксу.

  2. SongFormer (октябрь 2025) — свежая модель на фундаментных аудио‑эмбеддингах, обученная уже на 14 тысячах треков. Заметно быстрее и проще в установке. Для битов в пару к ней — Beat This! (2024), актуальный бит‑трекер.

Честное сравнение

На моих размеченных треках (точность попадания границ, ±1 такт):

  • Поп‑баллада: эвристика 0.82 → All‑In‑One 0.90 → SongFormer 0.89

  • Электроника (extended mix): эвристика 0.20 → All‑In‑One 0.36 → SongFormer 0.50

  • Оркестровый эпик: эвристика 0.32 → All‑In‑One 0.44 → SongFormer 0.19

  • В среднем: 0.45 → 0.57 → 0.53

Вывод получился нюансным — и это, пожалуй, самое интересное. SongFormer новее, быстрее и заметно лучше на поп/рок/электронике. Но оркестровый трек он провалил: первые полторы минуты пометил одним куском «intro». Модель обучена на поп‑музыке — классика и киношные саундтреки для неё чужая территория. Старый All‑In‑One со своими инструментальными слоями там оказался устойчивее. Поэтому в проекте живут три движка: быстрая эвристика (две секунды, работает на любом железе), All‑In‑One и SongFormer — пользователь выбирает под свой материал. А поверх любого движка накладываются наши луп‑метрики: замыкаемость, детект проигровок, привязка границ к тактам. Слои интенсивности (vertical layering) при этом делает Demucs — нейросеть разделения источников от Meta: трек раскладывается на барабаны, бас, вокал и остальное, каждый слой получает свой регулятор. Одна тема закрывает сцену от «спокойной разведки» до «полного боя» парой чекбоксов.

Что получилось

Локальное веб‑приложение: Python на бэке, браузерный плеер на фронте, три движка анализа, редактор границ, слои, стингеры, экспорт лупов в WAV. На моих сессиях это выглядит так: при подготовке загружаю трек на локацию, ИИ размечает (пару границ правлю мышкой), в игре — одна клавиша «Дальше», когда партия двигается по сюжету. Разница не в том, что музыка стала лучше. Разница в том, что о ней больше не думаешь во время игры.

Репозиторий: github.com/Siziff/musslop — установка в три команды под любые системы, ИИ‑движки опциональны (но крайне рекомендуемые). Буду рад фидбеку по нарезке ваших треков и особенно — рассказам, как проблему музыки на сессиях решаете вы.


Ссылки: Ellis 2007 — Beat Tracking by Dynamic Programming · Foote 2000 — Automatic Audio Segmentation · All‑In‑One · SongFormer · Beat This! · Demucs · The Infinite Jukebox