Самодельный музыкальный центр с веб-интерфейсом

В этой заметке я расскажу про мой самопальный музыкальный центр с веб-интерфейсом и усилками, который я некоторое время поставил себе в кабинет на даче (или, эту комнату лучше будет назвать “лабораторией”?) Исходники всего этого безобразия прилагаются в конце заметки.

Собственно, что хотим получить?

Идея проста, как незнамо что. Одноплатник с линуксом, который хотим подключить к вайфаю и рулить воспроизведением музыки на большие динамики (достаточно большие, чтобы звучали громче работающего 3д-принтера). Для этого берем дешевую usb 5.1 звуковуху (У интегрированных в одноплатники только два канала обычно). Добавляем жесткий диск для храниния музыки. Выглядит неплохо? Ну, поехали.

Что нам потребуется

Все хозяйство вышло где-то в ~40-50$, наверное. Точно посчитать не смогу, так как все это дело валялось на чердаке годами, собирая пыль, и ничего не зная про курс доллара и инфляцию.

Корпус

Пожалуй, самая долгая в плане изготовления часть. Но необходимое, ибо не хочется получить порнуху из проводов где-то в углу? Короб состоит из трех этажей скрепленных вместе М3 винтами. по размерам они 150x150mm каждый и печатались что-то около десяти часов (каждый!). Это дело пришлось повторять несколько раз, пока я не выловил все ошибки. Начну с нижнего этажа. На рендере оно выглядит так:


А вот так выглядел в реальной жизни первый прототип (из зеленого пластика, который не пошел в итоге “в прождакшн”.

Содержит 30mm вентлятор (Лучше бы я сразу вешал 120mm вентилятор, это исчадие ада гудит так, что хоть вешайся!), USB<-->SATA мост для жесткого диска HDD, приклеенный термоклеем, так как китайцы поскупились на крепежные отверстия. А так же отверстия для крепления к стене. На жесткий диск печатается и крепится специальная скоба, за которую просто ухватиться, чтобы достать диск.

На втором этаже обитает мой usb хаб с интегрированным 12->5V DC-DC преобразователем и TV-стик. Плюс этихстиков в том, что из-за простой схематики их можно запитывать через usb хост разъем, что я и делаю тут. Так же в корпусе прорези для uSD карты и microusb разъема. Постоянно включенный порт хаба выведен наружу, чтобы в него можно было втыкать флешки и жесткие диски, и копировать с них музыку в коллекцию.

Вот так оно выглядит на рендере:А вот так в реальной жизни:

Наконец, на самом верхнем этаже у нас живет VMA2016 усилители. Вход line-in и mic-in выведены на корпус. (Чтобы можно было воткнуть, например, гитару или микрофон, на случай если я захочу отомстить соседям на даче за регулярное пьяное караоке в стиле “шансон”)

Наконец, вот так все это выглядит в сборе. Красить зеленый пластик я не хотел, потому финальный варант перепечатал в черном варианте:

Внизу специальная скоба для крепления фильтра от пыли. От него в итоге пришлось отказаться, как и от 30мм вентилятора, в пользу 120мм, и очень тихого, который я разместил на верхней крышке конструкции.

Для крепления к стене использовались обычные стальные уголки, к которым все это крепится М3 винтами.

А вот так оно выглядит, если закрепить на стене:

Софт

Вот примерный список программных компонентов, которые я использовал в этой поделке:

  • skyforge – собирает кастомизированную корневую фс на базе debian
  • groovebasin – Плеер с веб-интерфейсом, написанный на node.js. Дополнительно умеет прикидываться mpd
  • alsamixer-webui – веб интерфейс для микшера
  • nginx – служит обратным прокси для всего вышеуказанного
  • aura – моя библиотека для RPC c привязками к луа. lua скрипт под названием shard-ctl ее использует для того, чтобы щелкать питанием портов и релешками
  • Кучка баш скриптов и unit файлов для systemd, которые управляют охлаждемнием, запускают службы и т.п.

 

WIFI & Время

Вайфай настраивается штатным механизмом в debian через /etc/network/interfaces.d/. Так как у ТВ стика отсутствует RTC то сразу после настройки соединения я получаю точное время по NTP. Вот конфиг /etc/network/interfaces.d/wlan0

auto wlan0
iface wlan0 inet dhcp
        wpa-ssid frostgate
        wpa-psk  hackmedude
        post-up  ntpdate -s arvale.lab

N.B. Лучше заменить arvale.lab на ближайший ntp сервер. Я использую имеющийся в локальной сети OpenWRT роутер для этого дела.

Unit’ы для Systemd

Последовательность старта достаточно сложная. После того, как все стандартные службы вроде ssh и сети загрузились, надо подать питание на usb звуковуху и usb<->sata мост, дождатся, пока мост определится и подмонтировать диск с коллекцией музыки. Только после этого можно запускать groovebasin и подавать питание на усилители.

Это разруливается тремя unit файлами для systemd (которые были переделаны из скриптов с до-systemd эпохи):

groove-mount (Хреновое имя, осталось от до-systemd скриптов. Сейчас он только подает питание на диски)

[Unit]
Description=powers on disks

[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/groovemount
WorkingDirectory=/

[Install]
WantedBy=groovebasin.service

Вот так тупо выглядит скрипт /usr/local/bin/groovemount. Таймауты гарантируют, пиковый ток не будет большим.

#!/bin/bash -x
export PATH=$PATH:/usr/local/bin:/usr/bin/:/usr/sbin
 
shard-ctl --usb 1 on 
sleep 3
shard-ctl --usb 2 on
sleep 3
 
shard-ctl --relay 1 on
sleep 1
shard-ctl --relay 2 on
 
exit 0

srv.mount (Монтирует жесткий диск с коллекцией)

[Unit]
After=groove-mount.service

[Mount]
What=/dev/disk/by-uuid/2B234EA2293F197A
Where=/srv

[Install]
WantedBy=groovebasin.service
Wants=groove-mount.service

Наконец, groovebasin.service (В debian пакете оного скрипта не было, пришлось написать быстренько). Обращаю внимание на директиву working-directory которая указывает на каталог, где хранится config.json. Он у меня лежит на внешнем жестком диске.

[Unit]
Description=groovebasin music player
Documentation=http://groovebasin.com/

[Service]
User=root
ExecStart=/usr/bin/groovebasin
WorkingDirectory=/srv/groovebasin/

[Install]
WantedBy=multi-user.target

Эти скрипты связаны между собой зависимостями, и работает (на вскидку) надежнее, чем старые скрипты со времен до SystemD.

 

groovebasin config.json

 

{
    "host": "0.0.0.0",
    "port": 8081,
    "dbPath": "groovebasin.db",
    "musicDirectory": "/srv/",
    "mpdHost": "0.0.0.0",
    "mpdPort": 6600,
    "encodeQueueDuration": 8,
    "sslKey": null,
    "sslCert": null
}

Ничего особенного тут нет, я только прописал путь к музыке и еще я убрал отсюда API ключи. Их надо воткнуть свои, следуя руководству groovebasin.

/etc/asound.conf

Так как в большей части моей музыкальной коллекции всего два канала, а динамиков больше двух, то необходимо делать апмикс. Это решается прописыванием конфига asound.conf. До кучи, даже с отключенной в ядре встроенной звуковухой, usb звук регистрировался с индексом 1, а не 0. Потому из коробки, без прописывания asound.conf нифига не завелось.

Вот мой /etc/asound.conf который решает эти проблемы:

pcm.!default {
  type plug
  slave {
    pcm "ch51up"
  }
}

ctl.!default {
  type hw
  card 1
}


# for 5.1 speakers
pcm.ch51up {
         slave.pcm "hw:1,0"
         slave.channels 6
         type route
         ttable.0.0 1
         ttable.1.1 1
         ttable.0.2 1
         ttable.1.3 1
         ttable.0.4 1
         ttable.1.4 1
         ttable.0.5 1
         ttable.1.5 1
}

Управление вентилятором и усилителями

На моей платке-хабе 3 реле. Как и внутренними usb портами, ими можно управлять из консили через утилиту shard-ctl.

В моем случае реле 1 и 2 включают передние/сабвуфер усилители и задние соответственно. Третье реле управляет вентилятором.

Я использую smartctl, обрабатываю выхлоп башем и включаю/выключаю вентилятор в зависимости от полученных данных. Да, вот такой вот костыль, господа и дамы.

#!/bin/bash
TEMP=`smartctl -A /dev/sda|grep Temperature_Celsius|awk '{print $10}'`
if [ $TEMP -lt "35" ]; then
        shard-ctl --relay 3 off
fi
 
if [ $TEMP -gt "40" ]; then
        shard-ctl --relay 3 on
fi
 
echo "Current temp: $TEMP"

Этот скрипт вызывает cron каждую минуту.

Web морда

На девайсе работают две независимые апликухи: Groovebasin (написан на node.js) and alsa-mixer (написан на python). Чтобы заставить “ужа” и “ежа” делить между собой 80й порт, нам потребуется nginx в режиме reverse proxy.

OpenWRT роутер работающий у меня выдает этому устройству доменное имя ‘iceshard.lab’, а две строчки в конфигах заставили направлять на этот хост так же запросы к player.iceshard.lab и mixer.iceshard.lab.

Далее, остается только добавить несколько строчек в nginx.conf

Here’s my nginx.conf:

server {
        listen 80;

        server_name player.iceshard.lab;
        client_max_body_size 64m;

        location / {
                proxy_pass http://127.0.0.1:8081;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection 'upgrade';
                proxy_set_header Host $host;
                proxy_cache_bypass $http_upgrade;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

}

server {
        listen 80;

        server_name mixer.iceshard.lab;

        location / {
                proxy_pass http://127.0.0.1:8080;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection 'upgrade';
                proxy_set_header Host $host;
                proxy_cache_bypass $http_upgrade;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

}

После этого groovebasin доступен по адресу player.iceshard.lab, а alsamixer по адресу mixer.iceshard.lab.
 

Автоматическое отключение питания усилителей

Когда музыка не играет из динамиков можно услышать едва слышимый белый шум. Обычно его не слышно, а если включить вывод даже очень тихой музыки он пропадает. После замены блока питания на менее шумный различить шум стало практически невозможно (особенно если работает 3д-принтер или ЧПУ станок). Но когда в комнате тишина, то это раздражает и жутко. Пришлось сделать еще один скрипт. Он использует консольную утилиту mpc чтобы получать события от groovebasin и опрашивает его статус. Когда уже 120 секунд, как ничего не играет – выключаем усилители. Если что-то начало играть – включаем их снова.

Скриншоты веб-интерфейса

Десктоп приложение и “горячие” клавиши

Я использовал electron чтобы быстро сделать приложение, которое сидит в трее и выскакивает по сочетанию “Win+A”. Так же в выпадающей менюшке можно вызвать окно микшера. Electron штука стремная, прожорливая, и для чего-то более важного я стараюсь держаться от нее подальше. Но в данном случае это было самым быстрым и удобным решением проблемы.

Прочее

Эта заметка оказалась очень жирной, даже несмотря на то, что я документировал только моменты, которые могут вызвать проблемы. Заранее прошу извинить меня, что не документировал некоторые вещи подробнее. Тем не менее, если Вам захочется собрать себе примерно такой же бокс, и не лень улучшить что-то из того, что есть – pull реквесты на github’е всегда приветствуются 😉

Opensource

  • Скрипты, которые используются чтобы сделать корневую фс можно взять в этом репозитории на github. Skyforge соберет корневую файловую систему, установит в нее пакеты ядром, сгенерирует initramfs и положит в /boot готовый boot.scr. Результат будет в архиве, который можно будет распаковать на SD карту (См. linux-sunxi wiki, там описано как сделать загрузочную SD карту)
  • Electron приложение можно at найти тут
  • А все механические части корпуса у меня на thingiverse
  • А еще можно поставит пару лайков проекту на hackaday.io (Хотя я едва ли буду дальше улучшать его, так как эта штука делает ровно то, что должна и делает это хорошо)

 

Добавить комментарий