Настройка пульта для встроенного ИК-датчика Orange Pi

Дано: Orange Pi PC Plus с установленным LibreELEC 11, которым хотелось бы управлять с помощью пульта. Инфракрасный приёмник у Orange Pi имеется, поэтому нужно просто научить систему понимать с этого пульта сигналы.

Прежде всего нужно купить какой-нибудь пульт или взять уже имеющийся, у которого есть кнопки стрелок, старт/пауза, стоп, ОК и тому подобные, чтобы можно было нормально управлять медиацентром. Я купил пульт для телевизора Supra RS41-MOUSE за 200 рублей.

Заходим по SSH в систему. В инструкции по настройке пультов сначала рекомендуют подбирать совместимые конфигурации из списка по пути /usr/lib/udev/rc_keymaps, но их там полторы сотни и нет ничего похожего по названию, так что я не стал возиться, а сразу перешёл к созданию собственной конфигурации, описанному в разделе Advanced.

Выводим список поддерживаемых протоколов управления (supported kernel protocols):

OrangePiPCPlus:~ # ir-keytable
Found /sys/class/rc/rc0/ with:
        Name: sunxi-ir
        Driver: sunxi-ir
        Default keymap: rc-empty
        Input device: /dev/input/event0
        LIRC device: /dev/lirc0
        Attached BPF protocols:
        Supported kernel protocols: lirc rc-5 rc-5-sz jvc sony nec sanyo mce_kbd rc-6 sharp xmp imon rc-mm
        Enabled kernel protocols: lirc
        bus: 25, vendor/product: 0001:0001, version: 0x0100
        Repeat delay = 500 ms, repeat period = 125 ms

Нужно подобрать протокол, с которым совместим пульт. В моём случае подошёл nec, после включения которого в консоли начали отображаться коды кнопок при их нажатии на пульте:

OrangePiPCPlus:~ # ir-keytable -p nec -t
Protocols changed to nec
Testing events. Please, press CTRL-C to abort.
1018.929441: lirc protocol(necx): scancode = 0x710205
1018.984459: lirc protocol(necx): scancode = 0x710205 repeat
1021.562363: lirc protocol(necx): scancode = 0x710205
1024.293455: lirc protocol(necx): scancode = 0x710204
1024.348472: lirc protocol(necx): scancode = 0x710204 repeat
1028.355072: lirc protocol(necx): scancode = 0x710268
1028.410087: lirc protocol(necx): scancode = 0x710268 repeat
1028.517783: lirc protocol(necx): scancode = 0x710268 repeat
1030.043425: lirc protocol(necx): scancode = 0x710262
1030.098426: lirc protocol(necx): scancode = 0x710262 repeat

Отлично, теперь нужно нарисовать карту кнопок (keymap), где прописывается протокол пульта и соответствие кодов кнопок с их функциями. Список функций можно посмотреть с помощью команды irrecord -l | grep ^KEY или в секции <remote device="devinput"> файла /usr/share/kodi/system/Lircmap.xml.

В итоге у меня получилась конфигурация, приведённая ниже. Закомментированы кнопки, к которым я не нашёл близкой функции, а кнопка POWER была чуть позже изменена на ENTER, потому что одноплатник выключался, но включить его потом с пульта было нельзя.

# table supra_rs41, type: nec
# 0x710202 KEY_POWER
0x710202 KEY_ENTER
# 0x71020f source
0x710220 KEY_RED
0x710234 KEY_GREEN
0x71022b KEY_YELLOW
0x71022c KEY_BLUE
0x710227 KEY_MUTE
0x710225 KEY_ZOOM
# 0x710200 freeze
0x710228 KEY_TEXT
0x710203 KEY_FAVORITES
0x710232 KEY_SUBTITLE
0x710240 KEY_AUDIO
0x710255 KEY_RECORD
0x710226 KEY_REWIND
0x71021e KEY_FORWARD
0x710239 KEY_PREVIOUS
0x710213 KEY_NEXT
0x71021a KEY_PLAY
0x710201 KEY_STOP
0x710260 KEY_UP
0x710261 KEY_DOWN
0x710265 KEY_LEFT
0x710262 KEY_RIGHT
0x710268 KEY_ENTER
0x71022d KEY_MENU
0x71021f KEY_ESC
0x710207 KEY_VOLUMEUP
0x71020b KEY_VOLUMEDOWN
0x710222 KEY_HOME
# 0x710221 mouse
0x710212 KEY_CHANNELUP
0x710210 KEY_CHANNELDOWN
0x710204 KEY_1
0x710205 KEY_2
0x710206 KEY_3
0x710208 KEY_4
0x710209 KEY_5
0x71020a KEY_6
0x71020c KEY_7
0x71020d KEY_8
0x71020e KEY_9
0x710211 KEY_0
0x710223 KEY_DISPLAYTOGGLE
# 0x710250 return

Теперь надо создать файл с нашей картой кнопок и запустить его:

OrangePiPCPlus:~ # nano /storage/.config/rc_keymaps/supra_rs41
OrangePiPCPlus:~ # ir-keytable -c -w /storage/.config/rc_keymaps/supra_rs41
Read supra_rs41 table
Old keytable cleared
Wrote 39 keycode(s) to driver
Protocols changed to nec

Пульт сразу же начинает работать. Осталось добавить эту конфигурацию в автозагрузку:

OrangePiPCPlus:~ # echo "* * supra_rs41" > /storage/.config/rc_maps.cfg

Смена движка блога

Некоторое время назад, зайдя в админку Вордпресса, на котором работал этот блог, я обнаружил в разделе рекомендаций, что производительность оценивается как довольно низкая, сайт отвечает на запросы с задержкой и неплохо бы добавить ещё механизм кэширования объектов на базе Redis.

Честно говоря, Wordpress с течением времени нравился мне всё меньше. Он превратился в какую-то оболочку для плагинов, из которых многие имеют платную версию; в бесплатной же функционал урезан и она постоянно напоминает плашками в админке о том, что хорошо бы купить платную. Сам Вордпресс постоянно обрастает бирюльками вроде вышеупомянутых «рекомендаций о здоровье сайта».

Редактор «Гутенберг», появившийся в 5-й версии Вордпресса, был очень удобен, но каждый раз он показывал окно «Добро пожаловать в редактор блоков!», которое должно было появиться только единожды при первом запуске. В интернете есть статьи по решению этой проблемы, но попробовав один метод, который не сработал, я оставил эти попытки. Не хотелось что-либо поломать, а пишу в блог я не так часто.

Для резервного копирования Вордпресса опять же нужен плагин, потому что можно, конечно, бэкапить его обычным способом как файлы веб-каталога и дамп базы, но потом, если нужно будет восстановить это где-то на другом сервере под другим адресом, то сделать это будет очень непросто — Вордпресс прописывает адрес сайта прямо в базе данных в очень многих местах и необходимо править эту базу, чтобы заставить сайт работать в другом расположении.

Теперь, значит, рекомендуется поставить Redis, чтобы ускорить работу. Ладно. Помимо контейнера с Redis опять-таки ставится очередной плагин, причём настраивать его приходится из консоли, редактируя wp-config.php.

После настройки я запустил Wordpress и получил неработающий сайт, который не поднялся даже после удаления изменений, внесённых в конфигурацию. Восстановить работоспособность удалось только удалив все плагины, но из-за этого поехала вёрстка, в частности, блоки кода начали выглядеть искажённо даже после установки плагина подсветки заново.

После этого мне надоело и я решил перейти на другую CMS, желательно без базы данных, как DokuWiki. От движка нужна поддержка:

  • Галерей картинок;
  • Аудио-плейлистов (в Вордпрессе они были, но их убрали);
  • Подсветки синтаксиса;
  • Видео (Youtube и локальных файлов);
  • По возможности, RSS и типографики.

Перебрав около десятка статических CMS (Grav, HTMLy, Pico и т. п.), я остановился на Datenstrom Yellow, для работы которого требуется только веб-сервер и PHP с минимумом дополнительных модулей (php-curl, php-gd, php-mbstring, php-zip). Для сравнения, в PHP Вордпресса было больше 20 модулей. Записи в блоге делаются в формате Markdown, можно использовать и HTML-код. Переезжал я с помощью wordpress-export-to-markdown, который сконвертировал XML-экспорт из Вордпресса в файлы Markdown и скачал картинки; последующая доработка делалась с помощью Powershell и вручную.

Мелкие недостатки есть:

  1. В Вордпрессе можно было указать у галереи, сколько будет картинок в ряду, здесь указывается точный размер превью.
  2. Не очень удобный способ заполнения альтернативного текста для картинок в галереях.
  3. Видео (локальное или ссылка) не адаптируется к размеру колонки.
  4. Не самый удобный способ написания постов, но это для регулярно имеющего дело с кодом и командной строкой человека не проблема.

Итог: блог хорошо и быстро работает, всё, что нужно, имеется, резервные копии делать удобно, т. к. весь сайт — это просто набор статических файлов, потребление ресурсов абсолютно минимально. Движемся дальше.

Компьютер грузится только с подключенным монитором

Мой сайт уже без малого 2 года крутится на неттопе с установленной Ubuntu 20.04 LTS. С некоторых пор неттоп начал испытывать проблемы при загрузке: например, ставишь обновления, перезагружаешь систему — и она уже не возвращается. Подключаешь монитор, чтобы посмотреть, в чём дело — и система загружается нормально. Какой-то бред, как будто железка издевается над тобой.

Я проверял настройки BIOS несколько раз, проверял блок питания на предмет нехватки мощности (хотя как в этом случае помогло бы подключение монитора?) — никаких проблем не обнаружил. Думал даже о покупке нового неттопа. Сегодня, когда в очередной раз, используя непарламентские выражения, я подключал монитор, чтобы загрузить железку после очередного невозврата из перезагрузки, решил поискать в интернете, мало ли, у кого-то такая же дурацкая проблема?

И нашёл! Оказалось, дело в настройках загрузчика GRUB. Нужно выключить у него графический режим и потом применить настройку:

sudo sed -i '/GRUB_TERMINAL=/c GRUB_TERMINAL=console' /etc/default/grub
sudo update-grub

Теперь перезагружается без проблем. И покупать ничего не потребовалось.

Демонстрации Yamaha W7 и автоматическое деление аудиофайла на части

Обнаружил сайт demodb.org, где можно послушать демки со старых синтезаторов, меня интересовала Yamaha W7.

Сайт этот скачать аудиофайлы не даёт: при попытке открыть консоль разработчика в браузере закрывает свою вкладку или переадресует на другой сайт, а если всё-таки через плагин браузера выцепляешь прямой путь к аудиофайлу, то скачивается заглушка, где говорится, что по прямым ссылкам качать нельзя.

Оставался единственный вариант — запись на локальном компьютере. Я взял аудиоредактор Wavosaur, отрегулировал входной сигнал, выключил микрофон веб-камеры, чтобы он не фонил, записал все нужные мне треки, нормализовал всё до 0 дБ. Получилось следующая картина:

Теперь нужно было порезать получившуюся запись на части. Конечно, мне сразу же захотелось это автоматизировать. Очевидно, разделителями являются периоды тишины, и после недолгого поиска я обнаружил, что в великом и могучем ffmpeg обнаружением тишины в аудиосигнале занимается фильтр silencedetect.

Экспериментальным способом выяснилось, что подходящим порогом срабатывания является -50 дБ, и silencedetect выдаёт такое:

[silencedetect @ 0000025b206a7740] silence_start: 0
[silencedetect @ 0000025b206a7740] silence_end: 4.11882 | silence_duration: 4.11882
[silencedetect @ 0000025b206a7740] silence_start: 213.267
[silencedetect @ 0000025b206a7740] silence_end: 218.22 | silence_duration: 4.95304
[silencedetect @ 0000025b206a7740] silence_start: 342.852
[silencedetect @ 0000025b206a7740] silence_end: 354.239 | silence_duration: 11.387
[silencedetect @ 0000025b206a7740] silence_start: 530.661
[silencedetect @ 0000025b206a7740] silence_end: 540.122 | silence_duration: 9.4607
[silencedetect @ 0000025b206a7740] silence_start: 670.007
[silencedetect @ 0000025b206a7740] silence_end: 680.196 | silence_duration: 10.1893
[silencedetect @ 0000025b206a7740] silence_start: 853.138
[silencedetect @ 0000025b206a7740] silence_end: 867.126 | silence_duration: 13.9884

Полезный сигнал начинается с silence_end, а заканчивается на silence_start, поэтому нужно выбросить первый silence_start и последний silence_end, итого 5 треков. Перед началом трека делается отступ в 0,25 сек, а в конце добавляется 1 сек, чтобы треки в списке не игрались attacca и между ними была какая-то пауза. В ffmpeg указывается не конечное время, а длительность нужного куска, поэтому нужно для этого из конечного времени вычесть начальное.

Иногда, например, у альбомов на Youtube, звук начинается сразу и первая метка silence_end оказывается уже на втором треке, поэтому нужно предусмотреть такие ситуации. Здесь я предположил, что если метка позже 30-й секунды, то вставлять в начало списка silence_end ноль и не удалять первый silence_start, т. к. нужно знать, где первая дорожка кончается. Соответственно, команда ffmpeg будет без указания стартовой позиции.

# Исходный файл
$file = 'D:\Музыка\Yamaha W7 demos.wav'
# Отступ до и после полезного сигнала (чтобы треки не начинались сразу один за другим)
$preSec = 0.25
$postSec = 1
# Формат файлов на выходе
$outputExt = ".mp3"

$file = get-item -literalpath $file
$log = (& ffmpeg -i $file.FullName -af silencedetect=n=-50dB:d=1 -f null - 2>&1) -match '^\[silencedetect'

$starts,$ends = $log.where({$_ -match 'silence_end'}, 'Split')
[regex]$replOut =  '.*?: (\d+\.\d+).*'
$starts = $starts -replace $replOut,'$1' |select -SkipLast 1
$ends = $ends -replace $replOut,'$1'
# Если в начале тишины нет (первое начало позднее 30-й секунды)
if ([double]$starts[0] -gt 30) {
    $starts = ,"0" + $starts
}
else {
    $ends = $ends |select -Skip 1
}

$c = 0
$starts |% {
    if ($_ -eq 0) {
        & ffmpeg -y -hide_banner `
        -t ([double]$ends[$c] + $postSec) `
        -i $file.FullName `
        ($file.DirectoryName + "\" + ($c+1).tostring("00") + " $($file.BaseName)" + $outputExt)
    }
    else {
        & ffmpeg -y -hide_banner `
        -ss ([double]$starts[$c] - $preSec) `
        -t ([double]$ends[$c] - [double]$starts[$c] + $postSec) `
        -i $file.FullName `
        ($file.DirectoryName + "\" + ($c+1).tostring("00") + " $($file.BaseName)" + $outputExt)
    }
    $c++
}

Получается просто отлично — больше ничего делать не потребовалось. Чуть позже я нашёл запись этих же демонстраций на Youtube, и скрипт так же хорошо работает и на ней.

Надо сказать, что тех демонстраций, которые я выкладывал, когда рассказывал о своей неудачной карьере аранжировщика, я нигде не нашёл; видимо, они были на какой-то дополнительно приобретаемой дискете и их никто не записал. Зато нашёл другие, которые я тоже вспомнил, и они великолепны, а Isn’t it hip и Halftime просто, я бы сказал, исключительны. Структура композиций, динамика, гармонизация, подголоски, выбор тембров и их обработка — всё на высшем уровне.

Yamaha - Isn’t It Hip (Yamaha W5/W7 demo, 1994)

Неделя велопоездок на работу в цифрах

С погодой повезло и получилось ездить на работу всю неделю без пропусков.

Время - 16 часов Пройденное расстояние Средняя скорость Максимальная скорость Расход калорий Сокращено выбросов CO2 (кг)

Upd: а вот ещё одна неделя в конце августа. В принципе, то же самое. Различие в километраже связано не с неточностью счётчика, а с тем, что я один или два раза проезжал одну остановку на трамвае.

Время - 15 ч 56 мин Пройденное расстояние Средняя скорость Максимальная скорость Расход калорий Сокращено выбросов CO2 (кг)