Кто из нас хотя бы однажды не меч­тал стать дид­жеем на радио? Эту меч­ту мож­но осу­щес­твить — с помощью собс­твен­ной интернет‑ради­останции, в которой эфир будет вес­ти дид­жей с искусс­твен­ным интеллек­том. Он ста­нет объ­являть тре­ки, рас­ска­зывать об исполни­телях и беседо­вать о музыке. Как все это нас­тро­ить и запус­тить — в нашей сегод­няшней статье.

Од­нажды вечером в одном из чатов я поин­тересо­вал­ся: нет ли у кого на при­мете сооб­ществ, пос­вящен­ных элек­трон­ной музыке? В тот момент очень хотелось вплес­ти в ткань обы­ден­ности не толь­ко сами тре­ки, но и живые обсужде­ния — что нового выш­ло, кому что нра­вит­ся, инте­рес­ные фак­ты о музыкан­тах.

Дис­куссия не задалась, но один из учас­тни­ков ски­нул мне в при­ват ссыл­ку на самопаль­ную интернет‑ради­останцию. Минима­лис­тичный дизайн (вклю­чить‑вык­лючить), прос­тень­кая визу­али­зация, спи­сок тре­ков и — новая для меня в этом жан­ре фун­кция — воз­можность ста­вить лай­ки музыке пря­мо в эфи­ре. Стан­цию я вклю­чил, но через пол­тора часа она замол­чала и не выходи­ла в эфир еще очень дол­го.

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

 

Индексируем коллекцию

В пер­вую оче­редь мне понадо­билось соб­рать базу дан­ных из тегов моей музыкаль­ной кол­лекции. То есть сде­лать ров­но то, что дела­ет боль­шинс­тво популяр­ных пле­еров при ска­ниро­вании дирек­торий. Неболь­шой 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.cpp или Ollama.

Что­бы ней­росеть не высасы­вала фак­ты из паль­ца, промпт (помимо наз­вания тре­ка и артиста) нуж­но было обо­гатить реаль­ными дан­ными. На ум приш­ло нес­коль­ко решений для получе­ния биог­рафий музыкан­тов: Discogs, MusicBrainz и Deezer. Discogs ока­зал­ся нерабо­чим вари­антом (хотя в дру­гих обсто­ятель­ствах он иде­ален). MusicBrainz боль­ше заточен под клас­сифика­цию жан­ров, а Deezer вооб­ще не завел­ся, ода­рив меня загадоч­ными сооб­щени­ями и веч­ной заг­рузкой стра­ницы вхо­да.

В ито­ге я вспом­нил, с чего вооб­ще начинал свое зна­комс­тво с интернет‑музыкой, и пошел на last.fm. Сайт открыл­ся, пароль десяти­лет­ней дав­ности подошел, API-ключ выдал­ся без проб­лем, а докумен­тация ока­залась пре­дель­но понят­ной. На нем и оста­новил­ся.

Для получе­ния информа­ции (Bio) я исполь­зовал два метода API: artist.search для поис­ка по име­ни и artist.getinfo для получе­ния дан­ных по пер­вому сов­падению. Ошиб­ками на этом эта­пе я решил пре­неб­речь — кол­лекция моя, боль­шинс­тво артистов я и так знаю. Last.fm отда­ет в фор­мате JSON ответ, из которо­го я вытас­киваю биог­рафию и под­мешиваю ее в промпт к локаль­ной LLM-модели mistralai/ministral-3-14b-reasoning.

 

Голос: заставляем нейросеть говорить

Мо­дель 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, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    1 Комментарий
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии