Я уже больше недели брожу вокруг да около мыслей о написании этой статьи. Правда, основная мотивация сначала была в том, что мне последнее время не хватало контента и хотелось немного разбавить коронавирусную повестку. Однако, потом появились статьи про робо-комбайн, взлом архива с биткоинами и прочая годнота и я, было, решил, что не время еще пускать в ход недозрелый материал.
Однако, сегодня нежданно вырвался из локдауна ментейнер героя сегодняшнего обзора, и, буквально несколько часов назад в PyPi ушел reapy v0.6.0: пакет, который позволяет полноценно использовать Python для написания расширений для Reaper DAW.
Итак: зачем нужен reapy, и что происходит с Python в REAPER.
Экспозиция
REAPER
Это DAW, который стоит несколько особняком от своих собратьев, доставляя достаточно уникальный пользовательский опыт. Многие мои коллеги испытывают к нему патологическое отвращение, многие — патологическую любовь. А дело все в том, что взаимодействие с REAPER выглядит примерно следующим образом:
Не нравится как работает — настрой. Вообще не работает — пиши код.
Собственно, огромная гибкость и широкий API для 4‑х ЯП позволили вырастить вокруг проприеритарного софта огромное open-source сообщество.
Писать можно на C++, eel, lua и Python; при этом, каждый язык интегрирован по-своему, и в зависимости от ситуации, предпочтительней писать то на одном, то на другом.
Допустим, в большинстве случаев плюсы — оверкилл, как в вопросах написания расширений, так и в вопросах их установки. По большому счету, в народе широко распространились только три расширения: SWS, ReaPack (это типа npm) и, с недавнего времени JS_ReaScript.
На eel писать почти также дорого, как и на C, т.к. там фактически ручное управление памятью, почти никаких абстракций, в т.ч. функций высшего порядка и вдобавок — почти все приходится писать самому: мало что можно использовать в качестве библиотек. Однако, eel обладает тремя шикарными свойствами: шустрый (в отличие от lua с Python), интерпретируемый (в отличие от C++), и общается с рипером лучше всех, за счет расширенного API и общего адресного пространства. Кроме того, на нем можно очень дешево писать DSP и MIDI плагины (типа VST, только JSFX).
Lua де-факто стандарт для написания расширения общего назначения: для него уже написан ряд хороших библиотек, он ведет себя одинаково на всех платформах, устанавливается через ReaPack без танцев с бубном. Однако, при том, что я небольшой фанат этого языка в целом, Reaper умудрился его еще и поломать в нескольких местах, особенно — в части управления зависимостями. То есть, luarocks не работает, и я так и не нашел способа установки чего-то без расширения *.lua, тех же сокетов, например.
Python
Вот тут-то по идее должен выйти на белом коне Python, как язык с бездонным количеством библиотек, при этом не требующий танцев с бубном вокруг CMake. Однако, с его использованием в рипере просто проблема на проблеме.
- надо устанавливать отдельно, еще и искать динамическую библиотеку ручками в настройках, что сильно повышает порог входа для конечных пользователей (а мы, типа, музыканты). В Linux python3.so приходится устанавливать отдельно как dev-пакет, что вообще не очевидно.
- импортируются расширения тоже немножко через жопу, вследствие чего два скрипта, использующие один и тот же, скажем,
numpy
нельзя запускать в одной сессии. - нет биндингов к GUI. Если в lua и eel есть дополнительные функции
gfx*
, которые оборачивают LICE (часть библиотеки Cokos WDL), C++ может использовать WDL напрямую; то Python sucks. В то же время, всякиеtkinter
,pyqt
и иже с ними запустить достаточно сложно, т.к. нет такого понятия как main loop, а есть просто возможность повесить функцию на отложенное выполнение (defer). - Вообще это характерно для всех расширений, но при использовании Python, почему-то от этого больнее: надо написать скрипт, сохранить файл, запустить его через Reaper, посмотреть на всплывающее окно с ошибкой, а то и словить креш, опять переписать в редакторе, опять запустить. Естественно, линтинг API функций не работает и т.п. Короче, кошмар поколения Z :)
Собственно, в разное время эти проблемы пытались решить разными способами, которые так или иначе крутились вокруг идеи того, чтобы Python в рипере жил своей жизнью, общался по TCP с Python снаружи рипера и все были счастливы. Однако, как-то все не складывалось: возможно отчасти от человеческого фактора (в смысле неудачной реализации), возможно, просто время не пришло.
история стала легендой, легнда — мифом
reapy
Если верить GitHub, в феврале 2019 (как раз где-то в это время я очередной раз пытался на коленке сварганить костыль на сокетах) Romeo Despres из Парижа выкатил первую редакцию reapy, который надежно и элегантно оборачивает ReaScript API. Более того, запускается он как изнутри, так и снаружи рипера. Библиотека старается обернуть все вызовы к API в нормальную ORM (если это так можно назвать), но, если какие-то функции еще не обернуты — можно вызвать оригинальные функции «напрямую». Устанавливается он через pip, и, пока что, еще отдельно его надо проинициализировать из нужной копии Reaper скриптом, который добавит веб-интерфейс для первоначальных рукопожатий внешней и внутренней части пакета.
Когда мы запускаемся «снаружи» обертка работает так: все, что не должно исполняться внутри рипера исполняется в нормальном режиме. Классы же из reapy.core
сериализуются и восстанавливаются на том конце, отрабатывают свой запрос и возвращают результат. Это не только решает проблему удобства запуска, но и нивелирует проблему падений от лишних импортов. По этому, за исключением ситуаций критичных к скорости реакции на события я бы рекомендовал все запускать снаружи: таким образом весь Python код, который исполняется внутри рипера, получает одну точку входа. Как раз в новом релизе мы добавили возможность наследования core
классов, что попутно привело к импортированию пользовательских модулей изнутри.
Багфиксы и сторонние расширения
Мы не очень хотим полагаться на сторонние расширения. Дело не в отсутствие доверия стороннему коду, а просто в удобстве конечного пользователя, которому придется устанавливать все отдельно: самые частые вопросы по скриптам сообщества относятся к части установки доп расширений, вроде тех же JS_ReaScript или SWS. Ситуация еще больше обостряется в Linux. В то же время, подавляющая часть SWS API — это точно такая же «более удобная» обертка ванильного реаскрипта. По этому мы стараемся писать все недостающее ручками. Тем не менее, при установленном SWS — он доступен точно также как и основное «raw» API как объекты модуля reapy.peascript_api
.
Кроме того, оказалось, что сами C-биндинги к Python (peaper_python.py
) тоже не без греха, и неизвестно, соберется ли Джастин это фиксить, или так и оставит висеть в баг-трекере. По этому я начал пилить свои C-биндинги в качестве надежного багфикса. Сейчас поставил вопрос о допустимости использования JS_ReaScript внутри reapy, т.к. там действительно оборачивается не сам реаскрипт, а Cokos WDL, которым можно рисовать корсс-платформенный нативный GUI. Конечно, можно попробовать доставлять свою копию WDL со своей оберткой, но это вопрос, с которым надо плотно разбираться.
Где стоит использовать уже сейчас
Благодаря последним нововведениям, а именно функции connect(host)
, reapy стал первым кандидатом для реализации микросервисов. Это звучит немного странно в контексте Digital Audio Workstation, но, серьезно, с возможностью подключения к любой инстанции Reaper в сети варианты использования вырастают многократно. Самый очевидный — сложный GUI, который может работать, допустим, с iOS или Android, почему нет? Reaper и так старался дать какие-то опции по дистанционному управлению, кроме OSC (а де-факто, Liine Lemur
за многие тыщщи), но, прямо скажем, веб-интерфейс не идет ни в какое сравнение с прямым доступом к API без необходимости мараковать с подключением.
Также заманчиво выглядят кейсы с большим количеством инстанций рипера, которые «что-то делают». Возможно, можно на базе рипера организовывать какой-нибудь суровый облачный аудио-процессинг, и мы, так получается, тоже сейчас смотрим куда-то в ту же сторону, хоть и немного из других соображений.
Там, где нужно писать тесты. Я понятия не имею, как можно протестировать расширение на eel иди lua. Да, с тестированием пакета, завязанного на рипер все еще есть трудности, но это в принципе реализуемо.
Роадмап
В настоящее время стоят следующие задачи:
- обернуть весь оригинальный API
- завести в проект тесты. Да, задача оказалась нетривиальная, особенно потому что пока что в проект не приходил ни один ops… На сайд-проекте у меня тесты крутятся локально. Запустить же рипер в облаке и научиться его использовать в цепочке CI — было бы здорово.
- научиться устанавливаться «в один клик». Собственно, приблизительный план действий есть, но дело стоит за реализацией. Скорее, все-таки, установка
reapy
и расширений на нем основанных будет организована на уровне setuptools и pip, а не через ReaPack. - сделать нативный GUI
contributing
Вы можете установить пакет, привязать его к своей инстанции рипера, начать кодить и уткнуться в место, которое «неудобно». Да, проект молодой, и вероятность такого развития событий велика. Так вот я в октябре понял, что не смотря на довольно человеко-читаемую обертку, у меня все вызовы reapy
светятся цветами ошибок mypy. В общем, два вечера, и у меня на руках были стабы, которые отправились в пул-реквест. Потом я заметил, что в моем сайд-проекте также противно (а главное — нефальсифицируемо) светятся вызовы к «raw-API», и, недолго думая, я решил, что не буду использовать «raw-API» в сайд-проекте, а буду все оборачивать на уровне reapy
. Собственно, теперь, получается, что в процессе кодинга на пользу себе-любимому половину работы я выполняю на стороне библиотеки, которая так похорошела за последние полгода. Мне кажется, в этом и есть смысл open-source: пользуйтесь им, и пушьте туда все, что отозвалось вам неудобством.
Автор: Тимофей Казанцев