Я играю на пианино с детства. Классика, саундтреки, импровизации – репертуар меняется, но привычка спонтанно сесть за клавиши остается. Лучший способ отдохнуть, переключить голову после сложной работы или вообще просто так!  На моей домашней рабочей станции установлен профессиональный синтезатор фортепиано Pianoteq от Modartt — он полностью меня устраивает: не только реалистичностью звучания, но и возможностью экспериментировать со звуком.

Достаточно давно  я собрал умную дачу на базе ПЛК под Linux и даже написал об этом статью. Контроллер исправно управляет освещением, водоснабжением и отоплением. Потом я прочитал статью о запуске Doom на этом контроллере и задумался: смогу ли приспособить его под свое хобби? Получится ли играть на даче не хуже, чем дома? 

Doom — задача хоть и культовая, но все-таки относительно простая. А справится ли контроллер с real-time-синтезом аудио? Здесь недостаточно просто воспроизводить заранее записанный звук — нужно в реальном времени рассчитывать его по физической модели инструмента. 

Сборки Pianoteq существуют под разные архитектуры, но чаще всего его используют на студийных Mac и рабочих станциях — на сцене, в студиях и при работе над киномузыкой.

Итак, для своего эксперимента я привез на дачу USB-ЦАП, MIDI-клавиатуру с педалью и подключил их к контроллеру…

Постановочные фото: я достал контроллер из щита, когда проводил тестирование
Постановочные фото: я достал контроллер из щита, когда проводил тестирование
Дополнительные фото

Железные подробности

Wiren Board 8 — контроллер на DIN-рейку с 4-ядерным SoC Allwinner T507, 4 ГБ ОЗУ и 64 ГБ eMMC. По производительности он примерно соответствует Raspberry Pi 3 Model B+. 

Что именно я подключил к контроллеру:

  • FiiO KA13 — USB-ЦАП с поддержкой USB Audio Class. Подойдет почти любой USB-ЦАП: Linux работает с  устройствами без драйверов через стандартный snd-usb-audio. KA13 просто оказался под рукой.

  • Roland EDIROL UM-1 — старый и проверенный USB-MIDI-адаптер. Через 5-pin DIN к нему подключено цифровое пианино с MIDI-выходом.

  • Korg nanoKONTROL2 — компактный MIDI-контроллер с фейдерами и кнопками. С его помощью я менял параметры синтезатора на лету, без GUI.

  • HDMI-монитор через WBE2-HDMI (тот самый, что и в статье про Doom) — пригодился ровно один раз, чтобы настроить Pianoteq.

Контроллер Wiren Board 8 в щите. Расположение MIDI-клавиатуры ограничивается USB-удлинителем
Контроллер Wiren Board 8 в щите. Расположение MIDI-клавиатуры ограничивается USB-удлинителем
Дополнительные фото
Можно играть рядом со щитом, но это неудобно. Так что без USB-удлинителя метров на пять не обойтись
Можно играть рядом со щитом, но это неудобно. Так что без USB-удлинителя метров на пять не обойтись

Pianoteq: за что приходится платить процессором

Большинство «программных фортепиано» (Kontakt, Vienna Symphonic Library …) работают как сэмплеры. На диске у них лежат десятки гигабайт записей живого инструмента: каждая клавиша, каждая градация громкости, отдельно с педалью и отдельно без нее. Нажал клавишу — программа выбрала ближайший подходящий сэмпл и воспроизвела его. Такой подход дает реалистичное звучание, почти не нагружает CPU, но требует много места на диске. 

Pianoteq устроен принципиально иначе. Он не хранит звук, а вычисляет его по математической модели фортепиано. Каждая нота — это численное решение системы уравнений. При стандартных настройках 48 кГц синтезатор выполняет 48 000 таких вычислений в секунду. Частоту дискретизации можно понизить, пожертвовав верхними обертонами, — на слабом железе это иногда становится единственным выходом. 

Кроме вычислительной мощности нужен быстрый отклик. Для комфортной игры задержка между нажатием клавиши и звуком не должна превышать примерно 10 мс.

Pianoteq мой Mac с M2 не нагружает. Если он нормально запустится и заиграет на Cortex-A53, появится повод задуматься, какие еще задачи можно перенести на контроллер.

План и реальность

План выглядел просто: установить Pianoteq со всеми зависимостями, один раз настроить устройства через GUI по X-forwarding, а дальше запускать синтезатор headless, без монитора. Если Cortex-A53 не справится — подкрутить настройки производительности в самом Pianoteq. 

Я исходил из простого предположения: Debian 11, штатный образ Wiren Board 8 и USB MIDI Class должны работать из коробки. На практике все оказалось намного сложнее, и суровая реальность съела половину выходных.

Облом первый: ALSA не отдаёт MIDI

Установка Pianoteq прошла скучно: я скачал ARM64-архив с сайта Modartt и распаковал его в /mnt/data/pianoteq. Root-раздел на Wiren Board 8 занимает всего 2 ГБ, а на /mnt/data свободно около 56 ГБ. И тут начались первые проблемы. 

Pianoteq — десктопная программа на JUCE. Ее бинарник еще на этапе сборки прилинковали к X11, OpenGL, freetype и еще десятку библиотек. В штатном образе Wiren Board ничего этого нет: контроллер использует серверную конфигурацию без графики. 

При запуске динамический загрузчик еще до старта подгружает все обязательные для бинарного файла библиотеки   — до того, как программа успевает прочитать аргументы командной строки. Нет libx11-6 — получите segfault раньше, чем Pianoteq поймет, что его запустили с --headless. 

После нескольких итераций методом проб и ошибок я собрал такой набор пакетов: 

apt install -y \
    libgl1 libx11-6 libxext6 libxrender1 libxcursor1 libxrandr2 \
    libxinerama1 libxi6 libfreetype6 libfontconfig1 \
    libxcb-keysyms1 libxcb-icccm4 libxcb-randr0 libxcb-render-util0 \
    libxcb-image0 libxcb-shape0 libxcb-xkb1 libxcb-xinerama0 \
    libxkbcommon-x11-0 xauth

Список выглядит длинным, но в целом логичен:

  • libgl1, libx11-6, libxext6 — базовый GL+X11;

  • набор libxcb-* — low-level X-протокол, на котором работают Qt и JUCE;

  • libfreetype6 и libfontconfig1 нужны для рендера шрифтов: Pianoteq предзагружает их еще на старте;

  • xauth пригодится для ssh -X.

К Pianoteq это уже не относится, но для воспроизводимости эксперимента я сразу поставил еще пару утилит, без которых дальше будет неудобно:

apt install -y p7zip-full alsa-utils
  • p7zip-full нужен для распаковки pianoteq_linux_v843.7z;

  • alsa-utils приносит amidi, aconnect, aplay и другие утилиты для работы с ALSA — стандартной звуковой подсистемой Linux, которая отвечает за воспроизведение и запись аудио, управление громкостью и обработку MIDI-событий. 

После этого Pianoteq уже запускался:

/mnt/data/pianoteq/'Pianoteq 8'/arm-64bit/'Pianoteq 8' --headless --serve :8081

И в первой же строке лога я увидел:

ALSA lib seq_hw.c:466:(snd_seq_hw_open) open /dev/snd/seq failed:
    No such file or directory

В настройках Pianoteq — ноль MIDI-устройств. Хотя lsusb и amidi -l честно показывали все подключенные USB-MIDI-устройства. Где-то они потерялись. 

Где именно теряются MIDI-устройства

ALSA предоставляет два интерфейса для работы с MIDI.

Rawmidi

Низкоуровневый интерфейс. Устройства появляются как /dev/snd/midiCXDY, где X — номер звуковой карты, Y — номер устройства. По сути это просто байтовый поток туда-обратно.

Rawmidi работает всегда, если подключено USB MIDI Class-устройство. Его включает опция Linux-ядра:

CONFIG_SND_RAWMIDI

Sequencer

Это уже уровень выше — маршрутизация MIDI-событий между приложениями и устройствами.

Именно sequencer показывает aconnect -l, и через него большинство Linux-аудиоприложений получает MIDI. Для него нужна опция:

CONFIG_SND_SEQUENCER

Смотрим конфиг штатного ядра Wiren Board:

CONFIG_SND=y
CONFIG_SND_USB_AUDIO=y        ← USB-аудио работает
CONFIG_SND_RAWMIDI=y          ← raw MIDI работает
# CONFIG_SND_SEQUENCER is not set

Логично: промышленному контроллеру sequencer обычно не нужен. 

Почему не получилось обойтись модулем 

Сначала решение казалось элегантным: собрать только snd-seq*.ko out-of-tree, положить модули в /lib/modules/..., загрузить через modprobe — и готово.

Так я и сделал.

Модули собрались. modprobe snd-seq отработал без ошибок. /dev/snd/seq появился. aconnect -l начал отвечать.

Но MIDI-устройства в sequencer так и не зарегистрировались.

Причина нашлась в исходниках ALSA:

#if IS_ENABLED(CONFIG_SND_SEQUENCER)
    if (!rmidi->ops || !rmidi->ops->dev_register) {
        if (snd_seq_device_new(...)) {
            ...
        }
    }
#endif

Ключевая проблема — #if IS_ENABLED(CONFIG_SND_SEQUENCER). 

Когда ядро Wiren Board собирали без sequencer-а, весь код регистрации seq-устройств просто не попал в vmlinuz. А rawmidi собрали встроенным (=y) и включили прямо в ядро. 

В итоге загружать модулем оказалось нечего: код, который должен вызывать snd_seq_device_new() при подключении MIDI-устройства, физически отсутствовал в работающем ядре.

С Doom ситуация была проще: uinput представлял собой отдельный модуль. Здесь же conditional compilation идет на уровне всей подсистемы ALSA.

Поэтому перед тем как тратить время на out-of-tree-модули, стоит проверить исходники на конструкции вида:

#if IS_ENABLED(...)

Если они есть — пересборки ядра, скорее всего, не избежать. 

Пересборка ядра 

У Wiren Board 8 процедура стандартная и почти не отличается от пересборки под Doom

На рабочем ПК с Ubuntu или Debian:

sudo apt install -y build-essential bison flex bc libssl-dev libelf-dev cpio rsync kmod gcc-aarch64-linux-gnu
git clone --depth 1 --branch v6.8.0-wb153 https://github.com/wirenboard/linux.git linux-wb
cd linux-wb
git submodule update --init --depth 1 drivers/net/wireless/realtek/rtl8733bu

Тег v6.8.0-wb153 соответствует ядру на контроллере. Проверить версию можно через uname -r.

Дальше я просто добавил несколько строк в конфиг ядра:

cat >> arch/arm64/configs/wb8.config <<'EOF'
CONFIG_SND_SEQUENCER=m
CONFIG_SND_SEQ_DUMMY=m
CONFIG_SND_SEQ_MIDI=m
CONFIG_SND_SEQ_MIDI_EVENT=m
CONFIG_SND_SEQ_MIDI_EMUL=m
EOF

Сборка:

KERNEL_FLAVOUR=wb8 FORCE_DEFAULT=y VERSION_SUFFIX="+sndseq" CORES=$(nproc) ./scripts/package/wb/do_build_deb.sh

VERSION_SUFFIX="+sndseq" нужен, чтобы официальный пакет linux-image-wb8 потом не затер нашу сборку через apt upgrade.

На выходе получаем три пакета:

linux-image-wb8_6.8.0-wb153+sndseq_arm64.deb
linux-headers-wb8_6.8.0-wb153+sndseq_arm64.deb
linux-libc-dev_6.8.0-wb153+sndseq_arm64.deb

Установка на контроллер с рабочего компьютера: 

scp linux-image-wb8_6.8.0-wb153+sndseq_arm64.deb wb225:/tmp/
ssh wb225 "dpkg -i /tmp/linux-image-wb8_6.8.0-wb153+sndseq_arm64.deb && reboot"

После перезагрузки:

$ ls /dev/snd/seq
/dev/snd/seq
$ aconnect -l
client 16: 'UM-1' [type=kernel,card=0]
    0 'UM-1 MIDI 1'
client 28: 'nanoKONTROL2' [type=kernel,card=3]
    0 'nanoKONTROL2 nanoKONTROL2 _ CTR'

Все устройства появились на месте. Pianoteq тоже зарегистрировался как seq-клиент и увидел их в своем списке. 

Однократная настройка через X-forwarding

На Linux Pianoteq не умеет выбирать MIDI-устройства из командной строки — только через GUI. В headless-режиме это тоже не меняется. 

Но помогает старый трюк с X11-forwarding: 

ssh -X wb225 "cd '/mnt/data/pianoteq/Pianoteq 8/arm-64bit' && ./'Pianoteq 8'"

Окно Pianoteq открывается на локальном X-сервере ноутбука, как будто программа запущена прямо там. 

В Options → Devices я выбрал:

  • Audio Output → FiiO KA13, USB Audio

  • режим Direct hardware device without any conversions

  • MIDI Input → UM-1 и nanoKONTROL2

После закрытия окна настройки сохраняются в:

/root/.config/Modartt/Pianoteq84.prefs

Дальше Pianoteq уже можно запускать полностью headless. 

Облом второй: играет, но дребезжит

После пересборки ядра я снова запустил на контроллере ту же команду: 

sudo /mnt/data/pianoteq/'Pianoteq 8'/arm-64bit/'Pianoteq 8' --headless --serve :8081

И Pianoteq действительно заиграл с первого старта. Уже неплохо.

Но вместе с музыкой появились щелчки, редкие подмерзания на полсекунды и общее ощущение, что контроллер работает на пределе. Типичные xrun — ситуации, когда CPU не успевает вовремя заполнить звуковой буфер и в аудиопотоке возникает разрыв.

На этом этапе начинается самое интересное — или самое мучительное, в зависимости от настроения. Нужно настраивать real-time-аудио на ARM.

CPU governor: главный виновник

Штатное ядро Wiren Board 8 собрано с поддержкой CPUfreq. Ядро умеет динамически менять частоту процессора: 

$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
ondemand userspace performance schedutil
$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
288000 480000 720000 936000 1008000 1104000 1200000 1320000 1416000
$ cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
schedutil
schedutil
schedutil
schedutil

По умолчанию контроллер использует governor schedutil. Для промышленного ПЛК это абсолютно разумное решение: низкая нагрузка — ниже энергопотребление и температура.

Но для real-time-аудио такая стратегия оказалась проблемой.

Как работает CPUfreq 

У ядра Linux есть несколько алгоритмов управления частотой CPU. 

Governor

Что делает

performance

Всегда держит максимальную частоту

userspace

Частоту задает userspace

ondemand

Быстро поднимает частоту под нагрузкой

schedutil

Использует данные планировщика задач Linux

На Wiren Board 8 schedutil работает поверх механизма PELT (Per-Entity Load Tracking) — это сглаженная оценка загрузки CPU. 

Для обычных задач все отлично. Для аудио — нет. 

Проблема №1: governor реагирует слишком медленно 

Аудиопоток Pianoteq просыпается примерно каждые 2,7 мс:

128 сэмплов / 48 кГц ≈ 2,7 мс

За это время он должен:

  1. проснуться;

  2. вычислить следующую порцию звука;

  3. отправить ее в ALSA;

  4. снова заснуть.

Но schedutil оценивает нагрузку с инерцией. PELT сглаживает пики активности, а сам governor ограничен параметром:

rate_limit_us = 10000

То есть чаще одного раза в 10 мс менять частоту процессора он вообще не может.

В результате Pianoteq просыпается, а CPU все еще работает на пониженной частоте. Пока governor «понимает», что нагрузка выросла, звуковая карта уже требует следующий буфер.

Получаем xrun.

Проблема №2: сами переключения частоты тоже стоят времени

На Allwinner T507 смена частоты и напряжения должна занимать примерно 250 мкс.

В этот момент CPU фактически функционирует на bypass-частоте около 24 МГц, пока PLL заново захватывает синхронизацию.

На буфере размером 128 сэмплов одно такое переключение съедает примерно десятую часть времени callback-а.

Само по себе это немного. Но когда система и так работает на пределе, нескольких таких переключений в секунду хватает, чтобы появились щелчки.

Особенно хорошо их слышно после коротких пауз: governor успевает снизить частоту, следующий callback просыпается уже на ней и не укладывается в дедлайн.

Лекарство: performance

Решение оказалось простым — зафиксировать CPU на максимальной частоте.

Разово:

for c in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
    echo performance | sudo tee "$c" >/dev/null
done

Проверяем:

$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
1416000

Все четыре ядра теперь постоянно работают на 1,416 ГГц. Автоматизация через systemd

Чтобы не выполнять команду вручную после каждой перезагрузки, я сделал systemd-сервис:

tee /etc/systemd/system/cpu-performance.service <<'EOF'
[Unit]
Description=Lock CPU governor to performance for real-time audio
After=multi-user.target
[Service]
Type=oneshot
ExecStart=/bin/sh -c 'for c in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do echo performance > $c; done'
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
systemctl enable --now cpu-performance.service

Здесь важен параметр:

RemainAfterExit=yes

Без него systemd помечает oneshot-юнит как inactive сразу после завершения команды, и потом создается ощущение, будто сервис не работает. 

С RemainAfterExit=yes статус остается active, и вопросов не возникает.

Альтернативный вариант — cpufrequtils и:

GOVERNOR=performance

в /etc/default/cpufrequtils.

Можно пойти еще ниже и передать:

cpufreq.default_governor=performance

через bootargs U-Boot. Тогда governor переключится еще на этапе инициализации CPUfreq, без короткого окна между загрузкой ядра и стартом systemd. 

А не перегреется?

Фиксация CPU на максимальной частоте звучит немного тревожно, но реальные температуры оказались вполне комфортными.

Под полной нагрузкой:

$ for z in /sys/class/thermal/thermal_zone*/temp; do
    echo "$z: $(cat $z)"
done
/sys/class/thermal/thermal_zone1/temp: 53603
/sys/class/thermal/thermal_zone2/temp: 53684
/sys/class/thermal/thermal_zone3/temp: 55709
/sys/class/thermal/thermal_zone4/temp: 53198

То есть примерно 53–56 °C на пассивном охлаждении.

Allwinner T507 начинает троттлить ближе к 90 °C, поэтому запас огромный.

В обычном щите ситуация вряд ли будет хуже. Хотя летом в полностью закрытом шкафу без вентиляции температуру все-таки стоит контролировать.

А что с энергопотреблением?

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

Я повесил шунт в цепь питания и измерил ток осциллографом в нескольких режимах работы. На холостом ходу с governor schedutil контроллер потребляет около 1,99 Вт, с performance — 2,05 Вт. Прибавка составляет меньше 0,1 Вт.

Во время игры картина почти не меняется: 2,40 Вт против 2,47 Вт. При полной загрузке всех четырех ядер оба governor выходят примерно на одинаковый уровень — около 3,2 Вт.

Приоритеты реального времени

После фиксации частоты ситуация заметно улучшилась, но редкие xrun все равно оставались. Причем появлялись они в случайные моменты, а не на пиках нагрузки. Было очевидно, что процессору хватает мощности, но кто-то периодически отбирает у аудиопотока несколько миллисекунд процессорного времени.

Причина оказалась в планировщике Linux. По умолчанию аудиопоток Pianoteq работает как обычная задача CFS с nice-приоритетом 0. А значит, любой wb-mqtt-serial, journald или системный таймер может ненадолго вытеснить его с процессора.

Для real-time-аудио этого уже достаточно, чтобы получить очередной xrun.

Сначала нужно поднять лимиты ресурсов. Без этого ядро не разрешит программе запрашивать real-time-приоритет (rtprio) и блокировать память в ОЗУ (memlock). Я прописал эти лимиты для группы audio — стандартной группы Debian для аудиозадач — и для root, поскольку в моем случае Pianoteq запускается именно от него.

tee /etc/security/limits.d/99-audio.conf <<'EOF'
@audio   -  rtprio     95
@audio   -  memlock    unlimited
@audio   -  nice       -19
root     -  rtprio     95
root     -  memlock    unlimited
EOF

Здесь особенно важны две вещи:

  • rtprio 95 — разрешает real-time-приоритеты;

  • memlock unlimited — запрещает выгружать память Pianoteq в swap.

Это важно, потому что на Wiren Board по умолчанию есть swap-раздел на 256 МБ. Если ядро внезапно выгрузит часть аудиодвижка на eMMC, щелчок в наушниках гарантирован.

Дальше — переводим процесс в класс real-time задач и составляем небольшой launcher:

tee /usr/local/bin/pianoteq <<'EOF'#!/bin/sh
exec chrt -f 80 /mnt/data/pianoteq/'Pianoteq 8'/arm-64bit/'Pianoteq 8' "$@"
EOF
chmod +x /usr/local/bin/pianoteq

Ключ утилиты:

chrt -f 80

задает процессу политику планирования SCHED_FIFO и real-time-приоритет 80 — после этого ядро выделяет Pianoteq процессор сразу после пробуждения аудиопотока и не вытесняет его, пока тот не завершит работу или не появится задача с более высоким приоритетом. 

Почему не 99? Потому что это уже опасно.

Выше 80 остаются:

  • аппаратные IRQ,

  • системные потоки ядра,

  • migration/*, которые переносят задачи между ядрами.

Если забрать процессор вообще у всех, контроллер можно легко повесить намертво.

После запуска:

$ ps -eo pid,pri,rtprio,policy,pcpu,comm | grep Pianoteq
17952 120 80 FF 212 Pianoteq

POL=FF означает SCHED_FIFO, а CPU Usage уверенно переваливает за 200% — Pianoteq распределяет вычисления сразу по нескольким ядрам и больше не спотыкается о планировщик Linux.

Последние настройки: буфер, голоса, симпатический резонанс 

Дальше я перешел к настройкам самого Pianoteq. Именно они в итоге добили оставшиеся xrun и сделали систему пригодной для нормальной игры. 

Размер аудиобуфера

audioDeviceBufferSize: 128 → 512

Штатное значение оказалось слишком агрессивным. Буфер 128 сэмплов при 48 кГц оставляет всего около 2,7 мс на расчет очередной порции звука.

Для Cortex-A53 на 1,4 ГГц этого уже мало.

Буфер 512 сэмплов увеличивает окно до примерно 10,7 мс — почти в четыре раза больше времени на callback.

На слух задержка около 10 мс ощущается как «чуть-чуть с залипанием», но все еще остается в зоне комфортной игры.

Прямой вывод в ALSA

audioOutputDeviceName → Direct hardware device without any conversions

По умолчанию Pianoteq использует dmix — программный микшер ALSA.

Это удобно, когда к одной звуковой карте подключаются несколько приложений. Но здесь Pianoteq — единственный аудиоклиент.

Прямой вывод через hw: сокращает путь аудиопотока, уменьшает количество пробуждений ядра и снижает вероятность джиттера.

Ограничение полифонии

voices: 48 → 32

Стандартная полифония для Cortex-A53 избыточна.

32 голосов хватает практически для всего — кроме, пожалуй, «Трансцендентных этюдов» Листа.

Sympathetic resonance: главный пожиратель CPU 

Финальная проблема пряталась во вкладке Performance.

Там находится опция:

Sympathetic resonance

Она моделирует симпатический резонанс струн: когда звучание одной струны через деку приводит к звучанию остальных.

На мощном ПК эффект звучит великолепно. Инструмент становится заметно объемнее и богаче.

Но на Cortex-A53 именно этот DSP-блок забирал последние 20–30% CPU.

Пока эффект оставался включенным, Pianoteq периодически не успевал уложиться в дедлайн аудиобуфера.

Выключаешь — xrun исчезают.

Fidelity 

Еще одна полезная настройка: 

Fidelity → на одну ступень ниже

На слух разницу заметить почти невозможно, а нагрузка на CPU падает вполне ощутимо. 

Заключение

Pianoteq на Wiren Board 8 действительно работает.

И не просто запускается, а нормально играет:

  • полифония до 32 голосов;

  • латентность около 10 мс;

  • без щелчков и xrun;

  • с real-time-планировщиком и USB-MIDI.

До концертной рабочей станции такой системе, конечно, далеко. Но играть можно вполне комфортно.

Во время исполнения Pianoteq держит загрузку процессора в районе 180–230%, то есть стабильно занимает примерно два ядра из четырех. При этом запас для штатных сервисов Wiren Board остается: wb-mqtt-serial, Modbus-опрос и остальная автоматика продолжают работать в фоне. 

Оцените запись сами:

Получается довольно необычная картина: можно играть Шопена и одновременно опрашивать датчики по RS-485. 

За музыкальным сюжетом здесь, как мне кажется, скрывается и практический смысл. Pianoteq выступил в роли стресс-теста: контроллер справился с синтезом звука по физической модели в реальном времени, с латентностью около 10 мс и без заметных срывов. Это не доказывает, что на нем заработает любая вычислительно сложная задача, но показывает, что у платформы есть запас производительности для приложений, которым нужны предсказуемые задержки и стабильная работа в режиме, близком к реальному времени.