Содержание статьи
Однажды вечером в одном из чатов я поинтересовался: нет ли у кого на примете сообществ, посвященных электронной музыке? В тот момент очень хотелось вплести в ткань обыденности не только сами треки, но и живые обсуждения — что нового вышло, кому что нравится, интересные факты о музыкантах.
Дискуссия не задалась, но один из участников скинул мне в приват ссылку на самопальную интернет‑радиостанцию. Минималистичный дизайн (включить‑выключить), простенькая визуализация, список треков и — новая для меня в этом жанре функция — возможность ставить лайки музыке прямо в эфире. Станцию я включил, но через полтора часа она замолчала и не выходила в эфир еще очень долго.
Во время обсуждения всплыл интересный факт: сегодня довольно сложно найти круглосуточное вещание, где, помимо музыки, был бы диджей, который комментирует эфир, ведет умные беседы про лейблы и вспоминает годы издания того или иного шедевра. На следующий день у меня сформировалась идея — сделать собственную интернет‑радиостанцию с нейродиджеем. В тот момент я еще точно не знал, насколько сложно это будет реализовать, но примерную архитектуру уже представлял, так как успел поработать с каждым компонентом будущей станции по отдельности.
Индексируем коллекцию
В первую очередь мне понадобилось собрать базу данных из тегов моей музыкальной коллекции. То есть сделать ровно то, что делает большинство популярных плееров при сканировании директорий. Небольшой Python-скрипт с библиотекой mutagen создал базу данных (SQLite) с основной таблицей tracks:
-
id -
title -
artist_id -
album -
year -
genre -
path
Вспомогательная таблица Artists содержала только ID и имя. В следующей итерации нужно было создать механизм воспроизведения: сделать рандомный запрос в БД, вытащить трек и путь до него в локальной файловой системе, а затем скормить этот путь плееру mpg123. В атомарном виде это уже и есть автоматическая система, электронный диджей. Не хватало только самого вещания, но об этом я пока не беспокоился — знал, что существует Icecast и проблем с ним быть не должно.
Мозги станции: LLM и борьба с галлюцинациями
Второй частью проекта был собственно сам диджей. Он должен выходить в эфир и говорить о музыке, причем желательно говорить правду, а не галлюцинировать и рассказывать именно о том треке, который сейчас звучит.
На локальном хосте у меня была поднята LM Studio, которая впритык поместилась в 4 Гбайт VRAM моей мобильной GTX 1650. У LM Studio есть функция сервера с совместимым OpenAI API. Я решил использовать ее просто потому, что она уже была настроена, но с таким же успехом можно взять llama. или Ollama.
Чтобы нейросеть не высасывала факты из пальца, промпт (помимо названия трека и артиста) нужно было обогатить реальными данными. На ум пришло несколько решений для получения биографий музыкантов: Discogs, MusicBrainz и Deezer. Discogs оказался нерабочим вариантом (хотя в других обстоятельствах он идеален). MusicBrainz больше заточен под классификацию жанров, а Deezer вообще не завелся, одарив меня загадочными сообщениями и вечной загрузкой страницы входа.
В итоге я вспомнил, с чего вообще начинал свое знакомство с интернет‑музыкой, и пошел на last.fm. Сайт открылся, пароль десятилетней давности подошел, API-ключ выдался без проблем, а документация оказалась предельно понятной. На нем и остановился.
Для получения информации (Bio) я использовал два метода API: artist. для поиска по имени и artist. для получения данных по первому совпадению. Ошибками на этом этапе я решил пренебречь — коллекция моя, большинство артистов я и так знаю. Last.fm отдает в формате JSON ответ, из которого я вытаскиваю биографию и подмешиваю ее в промпт к локальной LLM-модели mistralai/.
Голос: заставляем нейросеть говорить
Модель Mistral работает на моем железе почти на грани разумного минимума — 5–6 токенов в секунду (что примерно равно скорости слепой печати текста). Понятно, что генерировать «отбивки» (реплики диджея) в реальном времени не выйдет. Поэтому запрос в LLM отправляется заранее, пока звучит блок из 3–5 необъявляемых треков, случайно выбранных из базы.
Пока играет музыка, LLM генерирует текст на основе тегов и биографии с last.fm. Далее за дело берется модель Text-to-Speech (TTS) f5-tts. Поскольку оригинальная f5-tts англоязычная, я нашел на Hugging Face форк f5-tts_russian. Если попытаться озвучить русский текст чистой английской моделью, результат выйдет плачевным — в ней просто нет весов для русских фонем, и диджей заговорит с дичайшим акцентом.
F5-tts — это zero-shot-модель. Чтобы она заговорила нужным голосом, ее не надо долго тренировать. Достаточно скормить ей аудиосемпл (10–20 с) и его текстовую транскрипцию. На выходе получаем озвучку сгенерированного текста нужным тембром. Семпл я нашел на бирже актеров озвучки — взял голос с чистым британским акцентом. Мне хотелось, чтобы мой диджей говорил по‑русски так, словно этот язык для него не совсем родной или немного подзабытый. И это было правильное решение: треки в коллекции в основном англоязычные, и их названия модель произносит безупречно.
Эфир: Icecast, FFmpeg и магия пайпов
Пока радио работало локально, проблем не возникало: треки крутились по очереди через mpg123, чанки нейроозвучки предварительно склеивались в FFmpeg и запускались в нужное время. Но когда я решил вывести радиостанцию в глобальную сеть, начались нюансы.
Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
