Of FOSDEM Mixers and Men
FOSDEM 2025 се случи. За първа година използваме и домашен хардуер от щастливи кокошки (ние сме кокошките), при това до голяма степен успешно.
Тук е описано какво успяхме да направим, и какви мотики настъпихме.
Първо искам да отбележа че цялото това приключение нямаше да се случи без Martijn Braam и Ангел Ангелов, които направиха самия хардуер. Даже и лекция направиха, може да я видите тук (на английски).
Martijn има и по-дълъг блогпост за самия дизайн и процеса по създаването на аудио платката.
Резултатът
Кутията за FOSDEM 2025 съдържа:
- аудио платката
- 5-портов кОмУтАтОр (дизайн от Martijn)
- захранване (дизайн от Ангел)
- няколко HAT платки, за връзките между компонентите (Ангел)
- HDMI capture карта – от магазина :)
- SBC - Radxa X4
- дисплей платка с три екранчета
Има и къстъм MTA100 кабели, малко USB, малко UTP… И само два кабела, които не работиха и трябваше да срежем.
![]() |
![]() |
---|---|
ВенциБокс (с разни стари части) | Готова кутия |
Началото
В NOC-а миналата година се появи Martijn и каза “ами, беше ми скучно, та тука направих едно нещо…” и нещото беше първата ревизия на аудио платката.
След това се сдобихме с първата FOSDEM-подходяща ревизия (т.е. с каквито ни трябват входове и изходи), и Ангел помогна за създаването на текущата.
![]() |
---|
FOSDEM Audio Interface rev. A |
С налична полуработеща, с-ужасен-noise-floor платка, започна кодописането на основните неща:
- да можем да пипаме силата на микрофоните
- и независимо да управляваме изходите към залата и стрийма
Около Коледа получихме финалните платки, та си взех една за вкъщи… И започна мъката:
![]() |
---|
Mom: We have a mixer at home // The mixer at home: |
Светото Писание
Що е то OSC и има ли то по̀чва у нас
Какво има едно ардуино? Сериен порт!
Изначално тръгнахме с текст през серийния порт… което беше окей, докато не се наложи да го управляваме от друг софтуер, понеже синхронизирането с таймаути е болезнено бавно. Та се хванахме и минахме към OSC-over-SLIP. Добавихме и втори сериен порт (наистина плейнтекст) за дебъгване.
И отново греда - за да можем да мултиплексираме комуникациите с миксера трябва да знаем дали ще получим отговор (или да чакаме за такъв), което отново ни върна в мъката с таймаутите. Workaround: ако получим команда, или връщаме данни, или връщаме същия пакет като ACK. Вече това да не получим отговор от платката означава или бъг, или че е commit-нала seppuku :S
Огромният main.cpp… и кой реформатира кода?
Накрая стигнахме до 600-редов спагети файл, та още преди да имаме стабилна версия стигнахме до рефакторинг.
С внимателна мозъчна хирургия достигнахме до отделни файлове (разбира се, последния възможен месец преди FOSDEM)… и платката спря да тръгва изобщо.
Упс, EEPROM пачовете викали AudioMixer4::gain()
със смислената стойност NaN
.
Оказа се и че Teensy-Audio-генератор-уеб-нещото не харесва правилно форматиран код, та трябваше да се погрижим завинаги да си остане грозен.
Отделихме го в собствено файлче teensyaudio_generated.cpp
с този прекрасен ред най-отгоре:
// clang-format off
Врътки и копченца
Първоначално фърмуера (дело на @Martijn) ни позволяваше директно да задаваме ниво от всеки вход до всеки изход, във вида на матрица 6 на 6:
\ | OUT1 | OUT2 | OUT3 | OUT4 | OUT5 | OUT6 |
---|---|---|---|---|---|---|
IN1 | g11 | g12 | g13 | g14 | g15 | g16 |
IN2 | g21 | g22 | g23 | g24 | g25 | g26 |
IN3 | g31 | g32 | g33 | g34 | g35 | g36 |
IN4 | g41 | g42 | g43 | g44 | g45 | g46 |
IN5 | g51 | g52 | g53 | g54 | g55 | g56 |
IN6 | g61 | g62 | g63 | g64 | g65 | g66 |
Така можем от звука на да пратим към , където значи заглушен, а – неусилен спрямо входа. Избрахме подходяща стойност по подразбиране и вкарахме такава матричка в EEPROM-а.
Това все още работи, но въпреки че е изоморфно на истински миксер, е неприятно за хора с аудио опит, понеже изглежда твърде различно…
Съответно вече има и малко по-удобен интерфейс за контролирането с променливи (псевдо) gain за всеки вход и volume за всеки изход, както и mute копчета (хубаво е да го пазим този state, за да можем да ънмютнем канал при желание; ако имахме само матрицата нямаше да е възможно).
В крайна сметка работим и пазим следните неща:
- матрицата от по-горе:
- гейн (input multipliers):
- волУме (output multipliers):
- още една матрица за мУте-та (така де, чисто технически е packet bitfield):
В крайна сметка казваме на ардуиното че искаме .
Само дето всички слайдове са линейни, което мислихме да оправим в уеб уй-я (и не стигнахме дотам, разбира се, та хората искаха да ме бият…).
Мотики!
Къде е екрана?
Ангел каза че след рестарт на платката дисплея не работи, освен ако не я убием от тока за няколко секунди.
Един светодиод на RST пина за дисплея си призна проблема:
![]() |
---|
The LED that did not blink |
Оказа се че от пестене на jumper кабелчета, в прототипите този пин е бил директно вързан на 5V, ама на истинската платка е стоял floating, защото никой не е казал на библиотеката какво да управлява… След това вече се сдобихме с работещ екран.
USB Audio? Here comes the segfault…
Понеже иди^Wпрекрасните хора, писали аудио библиотеката, са решили да поддържат само CD quality (44.1 kHz, 16-bit), ползваме разни пачове да подкараме 48 kHz семплиране.
И някъде удряме дерефериране на nullptr
… Това оставям да го дебъгва някой с повече C/C++ опит от мен :)
На кого му е притрябвал автоматичен рестарт при crash?
Таз’ мотика я настъпихме петък, понеже две платки не тръгнаха. Като добавихме дебъг порта, сме направили да чака някой да го отвори преди да тръгне платката, и сме забравили да го махнем…
Контрол от големия лош Интернет
Питоня
Аз всеки път мрънкам, ама като се наложи да правя нещо смислено на Python, се подсещам защо не го понасям…
CI и дебиан пакети
Една от причините да изберем Python беше да оставим OS package manager-а да се оправя с dependencies към други библиотеки.
Мързеливият начин беше да вкараме всичко в пакета (било то vendor
, node_modules
или venv
директория), но това не е The Sysadmin Way (TM), и настръхвам само от мисълта да тръгнем натам.
Наложи се да пакетираме една външна библиотека (мрън, дебианци не са я сглобили) – pythonosc, която отговаря за четенето и писането на OSC пакетите.
Забележка: Някой (аз) успя да осер^Wобърка debian/control
файла, та apt-get install
на UI пакетите не ъпдейтваше библиотеката до съвместима версия, което, разбира се, разбрахме петък…
Цъкалките
Искахме да имаме два интерфейса за управление - нещо конзолно през ssh, и някаква цъкалка (за екипите по сградите). Тоест задължително трябваше да напишем нещо, което да следи OSC пакетите и да ги рутира към правилния клиент (първата версия на това нещо написа Martijn, после добавих multiprocessing заради болки с библиотеката за серийния порт).
Имаме REST API (късичък FastAPI проект) и CLI (късичко click файлче). Умното в цялата схема е отделено в библиотека, та да може лесно да се напише по-екзотичен интерфейс за контрол (MIDI!)
И, разбира се, отново, защото питона не обича да си говори със серийни портове, та asyncio не ни свърши работа, и пак се намеси multiprocessing в уеб уй-я.
ЖабаСкрипт!
Сътворих този красив уй (за някои стойности на “красив”)
![]() |
---|
Mixer UI, courtesy of yours truly |
Общо взето един client-side JavaScript, въпреки че има известно количество PHP за да работи избора на вярната зала.
И автентикацията е направена по най-лесния начин – nginx поддържа basic auth :)
Поне на теория, не е необходим цял FOSDEM setup за миксера, достатъчно е:
- да си намерим аудио платка
- да инсталираме
fosdem-mixer-api
от FOSDEM-ското репо, или да си го сглобим сами - да извадим API-то към Интернет (всичко необходимо е в
/etc/mixerapi.conf
) - да качим някъде
mixer.js
,mixer.css
, и да направим страничка стилvocto.php
(файлове) - да пуснем аудио и да се радваме на blinkenlights
Note: още не съм вкарал последните неща в infrastructure, трябва да го свърша тия дни…
Мотиките
Висша математика
Вместо всеки 10 ms, нивата виждахме на 2 минути…
Понеже дърпането на данни в API-то трябва да става на що-годе фиксирани интервали, някой (пак аз) успя да обърка математиката, която се занимава с тайминга.
Как да използваме повече CPU от ffmpeg
Четенето от дескриптор байт по байт винаги е яло много процесорно време, но Python-а го прави още по-грозно.
Неделя го “оправихме” с малкия страничен ефект че успяваме да издропим половината от по-големите пакети. Всичко работеше де, само логовете изглеждаха… ужасно.
Ако InfluxDB падне, вече няма InfluxDB
Периодично пращаме данни към InfluxDB, за да виждаме какви са били нивата по входове/изходи по-назад във времето. Но след като убихме базата на няколко пъти (с несвързани неща)… миксерите спряха да изпращат данни.
Всъщност проблемният ред изглежда много просто:
requests.post(url, data=data.encode())
Ми, това означава да чака безкрайно ако не получи отговор… А като influxdb ритне камбаната, няма отговор. Тоест си висим там и никога повече няма да пратим данни.
Успяхме!
В крайна сметка се получи! Имаше още някакви дребни проблеми, но FOSDEM се случи, и повечето лекции са стриймнати и записани успешно.