КПДВ


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


Однако, сегодня нежданно вырвался из локдауна ментейнер героя сегодняшнего обзора, и, буквально несколько часов назад в 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: пользуйтесь им, и пушьте туда все, что отозвалось вам неудобством.