Чем бы вы ни занимались, звуковое сопровождение играет довольно важную роль. Для учёбы или вдумчивой работы люди часто выбирают одноимённые плейлисты, состоящие по большей части из спокойной музыки, эмбиента или классики. Для бега тоже есть своя музыка, которая обычно куда динамичнее — многим нравится подбирать её в зависимости от скорости и интенсивности бега.
Но большинство треков, как правило, содержат слова, которые могут отвлекать от таких занятий, где хочется подумать или сконцентрироваться. И поэтому приходится тратить много времени на поиски подходящей подборки, но и там может попасться отвлекающее. Поэтому здорово, когда есть возможность подстроить звуковое окружение под себя.
Меня зовут Николай Глазырин, я старший разработчик в службе развития медиатехнологий. В этом посте я расскажу о том, как задумывалась и создавалась Нейромузыка: как мы обучили модель писать музыкальные лупы, сочетать их между собой и подстраивать музыкальный поток под каждого пользователя.
С чего мы начали
Нейромузыка — это бесконечный поток звуков, собранный алгоритмами на основе заранее сгенерированных лупов (коротких аудиофрагментов, которые можно бесшовно зациклить). В такой музыке выверен темпоритм, нет пауз и слов, чтобы слушатель не отвлекался. Но при этом её можно персонализировать: здесь вы тоже можете ставить лайки и дизлайки, помогая алгоритму лучше под вас подстраиваться.
Мы решили поэкспериментировать и сделать для аудитории Яндекс Музыки новый технологический продукт, чтобы вживую проверить, какие именно возможности открывают генеративные технологии в плане творчества.
В рамках этого эксперимента у нас сразу было несколько ограничений.
Все наши пользователи должны иметь возможность послушать нейромузыку.
Эта музыка должна быть им интересна, чтобы они возвращались послушать её ещё и ещё.
Она не должна прерываться и тормозить, должна быть доступна всем, но при этом у каждого пользователя она должна быть своя.
Нельзя ограничиться просто одним общим на всех потоком, как у обычных FM-радиостанций. Мы хотим, чтобы каждый пользователь получил свой персональный поток.
В свою очередь, сервер должен знать про все эти отдельные потоки и управлять их содержимым, чтобы включать каждому пользователю самую подходящую для него музыку и быстро реагировать на лайки и дизлайки.
Именно эта персонализированность во многом определила нашу архитектуру. Если вы занимались созданием музыки на компьютере, вы знаете, что это требует серьёзных ресурсов. VST-плагины (виртуальные синтезаторы и эффекты) с лёгкостью съедят весь ваш процессор и попросят ещё. Когда процессор не успевает обрабатывать все дорожки, в звуке появляется заметный треск. Если этот треск услышит пользователь, он выключит нашу музыку и никогда больше не включит.
Значит, нужно уменьшать потребление процессора. А как это сделать? Вот несколько вариантов:
Меньше дорожек, более простая музыка.
Не такие «тяжёлые» плагины, менее интересный звук.
Записываем дорожки в отдельные аудиофайлы вместо генерации в реальном времени.
Оказывается, что любая обработка аудио в реальном времени на сервере выходит очень дорогой — доступных нам вычислительных ресурсов не хватит, чтобы быстро обрабатывать много отдельных аудиопотоков. Если перенести такую обработку на конечные устройства, это не порадует ни батарею, ни разработчиков мобильных клиентов. Поэтому выход остаётся только один — заранее записать все партии и собирать итоговые потоки из готовых аудиофайлов.
Так мы можем обойтись весьма небольшим объёмом ресурсов. Клиенты при этом не делают ничего сложнее воспроизведения аудиопотока, что сильно облегчает разработку. Заодно мы получаем возможность без релиза клиента легко обновлять алгоритмы, по которым собирается музыка.
Собирать музыкальный аудиопоток из отдельных файлов можно на разных уровнях детальности: от записей отдельных нот до целых партий продолжительностью в минуты. Напомню, что мы хотим получить на выходе достаточно хорошую музыку, которая не должна сильно отличаться от обычной, то есть созданной людьми. Нужно, чтобы наш музыкальный поток постепенно и разумно менялся. На радио или в диджейском сете треки меняются каждые несколько минут, и мы будем стремиться к похожему поведению.
Собираем музыку
Далее нам нужно обеспечить сочетаемость записанных фрагментов друг с другом. Живой музыкант, собирая свой трек из сэмплов (аудиофрагментов), лупов (аудиофрагментов, которые можно гладко зациклить) и собственных партий, решает точно такую же задачу. Она, как и любая креативная задача, очень тяжело поддаётся компьютерной автоматизации. Нельзя «просто» взять какие-то аудиолупы, выровнять их по темпу, по времени начала, по тональности, по длительности, по громкости, и получить на выходе хорошую музыку. Лупы могут быть из разных жанров, с разным настроением, могут не подходить друг к другу по ритму или по тембру. К такой автоматизации мы стремимся, но пока стабильного результата оказалось проще достигнуть с человеческой помощью.
И сейчас мы озвучим эту схему. Так звучат отдельные кубики:
А это то, что получается, когда они складываются вместе:
Точно так же не стоит преуменьшать человеческий вклад и в создание сэмплов и лупов. Помимо легко кодируемых «технических» ограничений на темп, тональность и длительность лупа есть, собственно, звучащие в нём ноты, а также плохо формализуемый тембр звука.
Очень сложно объяснить компьютеру, какое звучание «хорошее», а какое «плохое». Зачастую «хороший» и «плохой» звуки по разным метрикам находятся ближе друг к другу, чем два «хороших». А какой-нибудь «плохой» звук может внезапно оказаться «хорошим» в подходящем окружении (условный дабстеп).
Обучать различные модели пониманию всех нюансов звука очень интересно, но долго, а нашей целью всё-таки является продукт в разумные сроки. Поэтому мы прибегли к помощи живых музыкантов и продюсеров из команды Monoleak. С их помощью мы создали много качественных музыкальных лупов в пяти разных жанрах: рок, поп, хип-хоп, электроника, эмбиент. Эти лупы протегированы по жанрам, тональностям и темпу и поделены на группы, внутри которых все лупы друг с другом сочетаются. Также для всех лупов у нас есть ноты в формате midi, которые в них звучат.
Обучаем нейросеть
С такой коллекцией лупов на руках мы не удержались от того, чтобы обучить на них нейросеть. Чтобы поддержать высокое качество исходного материала, мы ограничились обучением модели, которая позволяет генерировать нотные партии, подходящие к заданному контексту. Например, по нотам баса и гармонии мы можем сгенерировать мелодию, которая (вероятно) будет к ним подходить.
Стоит отметить, что на тех же самых данных можно было обучить и модель, которая сразу генерирует аудио. Вы наверняка слышали про Dance Diffusion, Riffusion, MusicLM и другие модели, которые позволяют генерировать звук напрямую. Эти модели сейчас активно развиваются, но пока не обеспечивают нужного качества звука на выходе. А управлять ими, чтобы получить нужный звук, гораздо сложнее, чем двигать ноты и крутить ручки в традиционных инструментах.
Чуть менее очевидная проблема состоит в том, что такие модели не смогут сгенерировать новые звуки, которых не было в их обучающем множестве. По этим причинам мы решили ограничиться моделью, которая работает с нотами, закодированными в текстовом виде. Подробнее про нашу модель и данные можно прочитать в этой статье.
На выходе этой модели получаются ноты, которые ещё надо хорошо озвучить. Сырой выход модели, озвученный простым midi-шрифтом, звучит не очень интересно. И здесь нам на помощь ещё раз пришёл живой человек, чтобы красиво озвучить полученные ноты. С правильно подобранным тембром получается куда лучше и прекрасно подходит к контексту.
Ноты, выданные нейросетью и озвученные простейшим фортепиано:
Те же ноты, но их озвучил продюсер хорошим инструментом:
Озвученные продюсером ноты в контексте реального трека:
Что мы получили
Сейчас мы можем собирать из лупов на лету полноценные композиции в разных жанрах. Также мы умеем делать плавные переходы от одной композиции к другой, чтобы не отвлекать наших пользователей резкой сменой трека. Вот пример такого перехода:
И даже плавные переходы между жанрами и со сменой темпа тоже возможны (хотя последнее уже требует той самой обработки звука в реальном времени). Это пример перехода между жанрами:
Наш сервер умеет раздавать генеративную музыку сотням тысяч слушателей одновременно. Каждый пользователь получает свой индивидуальный поток, персонализированный под его вкусы точно так же, как в Моей Волне. Про то, как устроен наш бэкенд и как стримится Нейромузыка, мы подробно рассказывали на конференциях VideoTech и PlayButton.
Нейромузыку можно лайкать и дизлайкать. Но здесь тоже есть интересный момент. Обычно музыкальные рекомендации работают с треками, артистами и альбомами, а реакция пользователя относится ко всему треку. А в генеративной музыке мы можем пойти дальше, на уровень отдельных лупов. Это позволяет работать буквально с отдельными составными частями треков и добиваться максимальной персонализации.
В результате у нас получилась достаточно сложная комбинация разных алгоритмов и человеческого труда. В процессе все мы ещё раз убедились в том, что пока избавиться от человека при создании хорошей музыки не получится.