Содержание статьи
Однажды вечером в одном из чатов я поинтересовался: нет ли у кого на примете сообществ, посвященных электронной музыке? В тот момент очень хотелось вплести в ткань обыденности не только сами треки, но и живые обсуждения — что нового вышло, кому что нравится, интересные факты о музыкантах.
Дискуссия не задалась, но один из участников скинул мне в приват ссылку на самопальную интернет‑радиостанцию. Минималистичный дизайн (включить‑выключить), простенькая визуализация, список треков и — новая для меня в этом жанре функция — возможность ставить лайки музыке прямо в эфире. Станцию я включил, но через полтора часа она замолчала и не выходила в эфир еще очень долго.
Во время обсуждения всплыл интересный факт: сегодня довольно сложно найти круглосуточное вещание, где, помимо музыки, был бы диджей, который комментирует эфир, ведет умные беседы про лейблы и вспоминает годы издания того или иного шедевра. На следующий день у меня сформировалась идея — сделать собственную интернет‑радиостанцию с нейродиджеем. В тот момент я еще точно не знал, насколько сложно это будет реализовать, но примерную архитектуру уже представлял, так как успел поработать с каждым компонентом будущей станции по отдельности.
Индексируем коллекцию
В первую очередь мне понадобилось собрать базу данных из тегов моей музыкальной коллекции. То есть сделать ровно то, что делает большинство популярных плееров при сканировании директорий. Небольшой 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 и запускались в нужное время. Но когда я решил вывести радиостанцию в глобальную сеть, начались нюансы.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»
