Info Guide
#13
01 апреля 2021 |
|
Music - Bintracker: в поисках идеального трекера для создания биперной музыки
Bintracker Bintracker: в поисках идеального трекера utz В начале был бипер Вы, наверно, уже знаете,что я много лет занимаюсь музыкальными движками и написал их довольно много, в основном для однобит─ ного бипера ZX Spectrum, а также для Atari 2600, Dragon/CoCo и др. Понятно, что одно─ го музыкального движка недостаточно - ну─ жен ещё инструмент для написания музыки под него. Изначально я решал эту проблему с помощью конверсии нотного текста из фор─ мата XM и писал такие конверторы. Но чем мощнее становились мои движки, тем они бы─ ли сложнее, и конвертировать из XM стало неудобно. Я писал движок за движком, и не─ используемые движки просто копились на мо─ ём жёстком диске. К счастью, Shiru начал добавлять некоторые из них в 1tracker, но скоро мы столкнулись и с ограничениями 1tracker'а. Но я всё-таки хотел использовать эти движки, так что в конце 2016 года родился план. План заключался в том, что я сам на─ пишу свой трекер. Мне всё равно следовало научиться программировать "по-настоящему" (я, так сказать, вляпался в ассемблер со─ вершенно случайно несколько лет назад, а до этого вообще не учился программирова─ нию), а тут я ещё получил бы возможность реализовать своё видение "идеального" тре─ кера. Но в своём энтузиазме и наивности я не учёл две вещи. Во-первых, моё видение идеального трекера оказалось весьма туман─ ным, если не сказать больше. Во-вторых, я явно недооценил объём работ. Впрочем, это и неплохо, иначе бы я даже и не начал! Моя идея "идеального" трекера выглядела примерно так: 1. Трекер должен без проблем поддержи─ вать все виды музыкальных движков, через единый пользовательский интерфейс. 2. Пользовательский интерфейс должен быть простым и эффективным. 3. Редактор должен быть мощным. 4. Редактор не должен сам по себе огра─ ничивать пользователя,можно даже разрешать "прострелить себе ногу". Нет нужды говорить, что я был очарован вышеупомянутым 1tracker'ом, который впер─ вые реализовал поддержку множества движков через механизм плагинов.Я хотел продолжить эту концепцию, но вылечить некоторые проб─ лемы самого 1tracker'а. - Плагин к 1tracker'у - по сути, голая реализация кода конверсии из стандартного формата .1tm в бинарник. Этот подход, ко─ нечно, гибкий, но с такой системой новые плагины пишутся трудно и нудно. Это затру─ дняет освоение со стороны авторов движков. - Как я и говорил, некоторые мои движки требуют фич, которых нет в 1tracker'е. Особенно ограничивает редактор инструмен─ тов и отсутствие таблиц. - Многие экзотические платформы, с кото─ рыми я имею дело, не могут быть поддержаны в 1tracker'е, потому что он работает через Game_Music_Emu. - Я, безусловно, люблю минимализм, и в отказе от паттернов в 1tracker'е что-то есть, но нельзя отрицать, что такой необы─ чный подход может отпугнуть множество лю─ дей, как и отсутствие поддержки мыши. Для решения первой проблемы я придумал MDAL (Music Data Abstraction Language - язык абстракции музыкальных данных). MDAL определяет два стандарта под названиями MMOD и MDEF . MMOD - стандартизированный формат музыкального модуля. Это, по сути, текстовое представление трекерного модуля, наподобие .vt2 в Vortex Tracker'е или .1tm в 1tracker'е, но с некоторыми идеями из музыкальных нотаций типа MML. Что же каса─ ется MDEF, то это формат описания конфигу─ рации для конкретного музыкального движка. Там указано, какие элементы MMOD использу─ ются и как будут строиться выходные дан─ ные. Обратите внимание на пару важных от─ личий. Во-первых, файлы MDEF описывают конфигурацию, а не код, как .1te плагины для 1tracker'а. Во-вторых, файлы MDEF даже не описывают алгоритм конверсии в бинар─ ник. Алгоритм должен полностью содержаться в универсальном компиляторе MDAL. Значит, следующим шагом надо было напи─ сать этот волшебный компилятор. И я так и сделал, и мне действительно удалось в зна─ чительной степени осуществить задуманное. Дальше я написал к нему графический интер─ фейс, который и стал прототипом Bintracker (сейчас он, конечно, устарел). Этот прото─ тип был на C++, содержал кастомный эмуля─ тор ZX Spectrum и настраиваемый набор вид─ жетов пользовательского интерфейса, реали─ зованный с помощью Allegro. Некоторые даже написали в нём отличные 1-битные треки! Однако очень скоро стало очевидно, что в его дизайне были серьёзные недостатки. Просто нереально было написать кастом─ ные эмуляторы для всех платформ, которые я хотел поддержать. Пользовательский интер─ фейс отнимал слишком много времени на раз─ работку и при этом глючил. Но самое глав─ ное, стало ясно, что компилятор MDAL в том виде не мог поддержать даже половину моих собственных движков,не говоря уж о чужих.И вообще, к тому времени я уже был недоволен той базовой реализацией трекера, моё виде─ ние "идеального трекера" сильно измени─ лось, а текущая реализация даже близко не подошла бы к этому. Вторая попытка Я отложил проект на время, а потом при─ готовился к неизбежному:переписыванию все─ го с нуля. Меня уже засасывало в кроличью нору (современного) программирования, и я начал поглядывать на другие языки, помимо C/C++. И вот, зимой 2017/2018 я сидел в какой-то глуши без доступа к интернету и возился с Nyquist, загадочным потомком Лиспа, который используется как скриптовый язык в аудиоредакторе Audacity. Почти не имея документации,я не мог ни─ чего понять, но был очарован синтаксисом, который сильно отличался от всего, что я видел раньше. Мне стоило остановиться на этом. Но я вместо этого сосредоточился на идее, что компилятору MDAL нужен механизм сценариев для компенсации того, что не удаётся решить файлом конфигурации. Излиш─ не говорить, что языком написания сценари─ ев должен был стать Лисп. В конце концов я остановился на диалекте Лиспа, который лу─ чше всего подходит мне как минималисту: Scheme. Когда я повозился с переписыванием ком─ пилятора MDAL примерно полгода,я понял две вещи: 1. интерпретатор Scheme, который я планировал внедрить, плохо совмещался с объектной моделью C++, и, что более важно, 2. написание компилятора и трекера на C++ просто займет слишком много времени. Так что я глубоко вздохнул, выбросил эти шесть месяцев кодинга и начал заново, переписав весь проект на Scheme. Вернувшись к нача─ лу, через месяц с небольшим и на 1/10 объ─ ема кода я был уверен, что я на правильном пути. Как упоминалось раньше, моё видение "идеального" трекера эволюционировало,а по мере моего продвижения оно должно было развиваться дальше. Оно меняется до сих пор. Прототип Bintracker доказал,что общая идея поддержки различных звуковых движков в едином интерфейсе была жизнеспособной, так что эта часть концепции сохранилась. Развивались идеи о дизайне интерфейса и определении того, что такое "мощный" реда─ ктор. Что касается интерфейса, мне никогда особо не нравилось, что нельзя писать без паттернов, но я всё-таки хотел сделать Bintracker доступным для композиторов, привыкших к традиционному подходу с патте─ рнами и ордером. Следовательно, Bintracker должен под─ держивать оба подхода.Я также пришел к вы─ воду, что в конечном итоге ни один из этих полностью не решет проблему,а именно:ордер раздражает, потому что заставляет музыкан─ та прерывать творческий поток, чтобы разо─ браться с техническими деталями. С другой стороны, при отсутствии ордера труднее ви─ деть общую структуры произведения. Пока эти проблемы не решены, думаю, стоит про─ должить эксперименты с трекерным интерфей─ сом. И Bintracker должен предоставлять во─ зможность для таких экспериментов. Что касается "мощности",основным камнем преткновения стали плагины. Не плагины в смысле плагинов звукового движка, а плаги─ ны для самого трекера. Интерфейс плагинов должен позволить сохранить ядро Bintracker достаточно компактным, но неограниченно расширять его новыми и навороченными функ─ циями. Хотите графическое редактирование с пианороллом и огибающими? Хотите библиоте─ ку для хранения фрагментов мелодий и наст─ роек инструментов? Как насчёт управления проектами с контролем версий, чтобы можно было откатиться к предыдущей версии, не копаясь в куче файлов резервных копий? Или как насчёт живого онлайн-сотрудничества? Я думаю, что мощный редактор чиптюнов должен поддерживать такие вещи. Ещё одна вещь, на которой я часто оста─ навливался.Мы в основном воспринимаем тре─ кер как своего рода редактор электронных таблиц для музыки, поэтому мы думаем о но─ тах, паттернах и ордере как о данных. Од─ нако мы можем думать о них как о скриптах: список инструкций, которые выполняются му─ зыкальным движком (например, "играть ноту X в течение Y тиков" ). В этом смысле тре─ керы становятся средой аудиовизуального программирования. Написание музыки в тре─ кере - это программирование. Многие мощные языки программирования имеют возможность, которая называется метапрограммированием. Вместо того, чтобы писать сам код, в мета─ программировании вы пишете код, который пишет код. Вопрос, как это относится к на─ писанию музыки? Я думаю, что пока это открытый вопрос, одним из вариантов ответа на него является алгоритмическая композиция. Алгоритмичес─ кая композиция не особо популярна среди восьмибитных композиторов, но она станови─ тся всё популярнее в более широком сообще─ стве хакеров. Так что поддержка алгоритми─ ческой композиции в Bintracker может внес─ ти свежую струю в сцену, и это должно быть хорошо. Bintracker также может выиграть от этого, потому что люди, занимающиеся алго─ ритмической композицией, обычно умеют про─ граммировать и могут заинтересоваться про─ граммированием самого Bintracker, если пе─ реход будет достаточно плавный. Так что, по сути, Bintracker должен быть "взламыва─ емым". Звучит здорово, но как? Поговорим коротко о технологиях и стеке технологий, используемых в Bintracker. Самая интересная технология - это,коне─ чно же, компилятор MDAL, который генериру─ ет результат из описания движка в формате MDEF и модуля в формате MMOD. Поскольку структуры данных существующих и будущих звуковых движков сильно различаются, осно─ вная задача заключалась в создании общей абстракции, которая могла бы представлять все разновидности. Пока детали ещё уточня─ ются, но общая структура уже стала стаби─ льной. MDEF состоит из трёх основных эле─ ментов: узлы ввода, узлы вывода и команды. Узлы ввода определяют шаблон входного фор─ мата MMOD. Есть 3 типа входных узлов: группы, поля и блоки. Поле - основная еди─ ница, представляющая одиночное значение какого-либо типа. Блоки - двухмерные мас─ сивы полей. Блок может представлять такие структуры, как паттерны, ордеры, таблицы или сэмплы. Группа - рекурсивная структу─ ра, она может содержать поля, блоки и дру─ гие группы. Это позволяет создавать вло─ женные структуры, например, Module -> Song -> Patterns. По умолчанию узлы могут иметь неограниченное количество экземпляров,кро─ ме групп,которые всегда уникальны.Все вхо─ дные узлы MDEF являются частями неявного глобального узла (группы), образуя дерево. Типы полей определяются командами. Есть несколько типов команд: String (используе─ тся для представления тектов типа названия и автора композиции), Trigger (использует─ ся для событий типа барабана щелчком в движке Huby или запуска огибающей в SID ), Reference (числовая ссылка на другую груп─ пу, например, на паттерн в ордере или инс─ трумент в паттерне), Label (используется для указания места в экземпляре блока, на─ пример, точка зацикливания), Int и Uint (знаковое и беззнаковое целое произвольно─ го размера, также могут хранить логические значения), а также Key и Ukey, представля─ ющие знаковую и беззнаковую пары ключ, значение. Команды Key и Ukey могут иметь связанные с ними команды Modifier, которые содержат арифметический оператор и целочи─ сленное значение, соответствующее диапазо─ ну значений команды Key / Ukey. Обратите внимание, что нет типа "нота": ноты описы─ ваются через тип Ukey, возможно с модифи─ каторами для реализации расстройки. MDAL обычно не заботится о функции команды или узла ввода, он заботится только о его ти─ пе. Однако команды и узлы ввода могут иметь произвольное количество флагов, и этот механизм, например, может сообщать Bintracker'у, следует ли рассматривать по─ ле с командой Ukey как ноту или что-то ещё. Всё дерево входных узлов содержится в неявном глобальном узле. Проницательный читатель, возможно,заме─ тил, что я мало говорил об ордере, когда упоминал о входных узлах. Входной ордер задан неявно, его определение создаётся автоматически компилятором MDAL (его можно настроить, чтобы включить дополнительные поля для транспонирования и тому подоб─ ное). Входной ордер всегда является матри─ цей: каждый канал / дорожка в блоке имеет отдельную дорожку в ордере, и номера пат─ тернов в разных каналах не пересекаются. Поскольку входные блоки по определению имеют неограниченную длину, ордер также содержит дорожку длины, которая устанавли─ вает длину каждого шага. Помимо этого, ор─ дер - ещё и обычный входной узел типа "блок". Входные узлы разрежены, то есть экземп─ ляры узлов не создаются, пока они не пона─ добятся, и экземпляры узлов,значение кото─ рых не установлено пользователем, имеют значение NULL. Что происходит при обраще─ нии к такому несуществующему узлу, зависит от запрашивающей стороны, типа узла и, в случае полей,от типа команды.Для узлов ти─ па "группа" и "блок", если запрошенный эк─ земпляр узла не существует, он создаётся и инициализируется как пустой. Таким обра─ зом, когда пользователь вводит номер пат─ терна, которого нет в ордере, то этот пат─ терн автоматически создаётся как новый пу─ стой узел. Когда пользователь затем вво─ дит, скажем, ноту в восьмой строке, экзем─ пляры полей до строки 8 включительно соз─ даются и устанавливаются в NULL, а в поле, которое только что отредактировал пользо─ ватель, записывается то, что он ввёл. Ког─ да компилятор запрашивает поле блока, ко─ торое имеет значение NULL, то зависит от команды поля: либо будет выполнена обрат─ ная трассировка, чтобы найти последнее ус─ тановленное значение, либо будет значение по умолчанию. Узлы вывода несколько сложнее. В насто─ ящее время есть 6 типов узлов вывода. Osymbols работают как метки в ассемблере, то есть либо принимают значение текущего адреса, либо явно присваиваются. Ofields - одиночные целые значения произвольного размера. Oblocks - массивы данных блока, эквивалентные входным блокам. Они состоят из Ofields. Ogroups содержат Oblocks и Ofields. Они могут генерировать ордеры, которые, в свою очередь, используются уз─ лами Oorder для создания фактического ор─ дера. Наконец, узлы Oasm представляют со─ бой голый ассемблерный код. Например, в таком узле может содержаться код плейера. Компилятор MDAL интегрируется с ассембле─ ром, то есть ассемблер может видеть опре─ деления символов уровня MDAL, и MDAL может отслеживать текущий адрес в ассемблере. Узлы Oasm могут быть полностью предвари─ тельно сассемблированы (то есть выходной код создаётся заранее) или могут использо─ вать частично вычисленные выражения, если ассемблерный код зависит от символов, оп─ ределённых на уровне MDAL. Из этих определений компилятор MDAL со─ здает синтаксический анализатор, который считывает модули .mmod во внутреннее пред─ ставление и генерирует компилятор,специфи─ чный для этой конфигурации MDEF. Поэтому компилятор MDAL - это на самом деле не ко─ мпилятор, а компилятор компилятора. Испо─ льзование кастомного компилятора ускоряет компиляцию MMOD. По общей концепции, стру─ ктура ввода полностью виртуальна и может быть разработана как логическое представ─ ление элементов модуля, а не в соответст─ вии со структурой вывода. Таким образом, обычно назначаются отдельные блоки на каж─ дый канал, даже если в выходной структуре используются комбинированные паттерны из всех каналов, и не будет устанавливаться фиксированная длина для входных блоков, даже если выходные узлы должны быть фикси─ рованной длины - кроме случаев, когда это важно (на ум приходят волновые таблицы). Что касается технического стека, многие из пунктов в списке фантастических желаний из предыдущего раздела стали возможны бла─ годаря языку Scheme и его используемой ре─ ализации: Chicken Scheme . Chicken Scheme может работать и как интерпретатор, и как компилятор (с мощным интерфейсом внешних функций для взаимодействия с кодом на C ). Это значит, что важные по времени части ядра можно ускорить путем компиляции, а расширения интерпретируются, и для их за─ пуска не требуется повторная компиляция. Встроив интерпретатор в Bintracker, мы по─ лучили возможность метапрограммирования практически бесплатно. И это относится не только к созданию музыки: весь Bintracker можно программировать во время выполнения. На самом деле Bintracker - это не трекер. Это интерпретатор языка Scheme, который запускает приложение-трекер. Поскольку было понятно, что написание эмуляторов для каждой поддерживаемой плат─ формы - это не дело, планировалось исполь─ зовать MAME как серверную часть эмуляции, с возможностью поддержки других эмуляторов позже. Пока у МАМЕ довольно плохая репута─ ция относительно точности эмуляции, но как по мне, ситуация значительно улучшилась за последние годы, и эмуляция многих распрос─ транённых платформ теперь не уступает спе─ циализированным эмуляторам. Любой программист, который когда-либо имел дело с нативной интерфейсной разрабо─ ткой, знает, что все библиотеки GUI - от─ стой. В конце концов я остановился на той, которая для меня - отстой меньше всего. Она почти такая же старая,как наши любимые 8-битные машины, написана на языке,который ещё страннее,чем Scheme, но работает почти везде, и называется она "Tk". Да, я знаю: сейчас как минимум половина из вас, уважа─ ющих себя программистов, вероятно, бегает с воплями. Остальным, наверное, не терпит─ ся посмотреть исходный код. Текущее состояние и будущее Bintracker Последние два года были для меня серьё─ зным испытанием, поскольку я постоянно ра─ ботаю над этим проектом,не имея возможнос─ ти ничего показать. К счастью, сейчас дело пошло на поправку. Я выпустил исходный код в конце мая,поэтому вы можете рискнуть по─ пробовать Bintracker в его текущей, ранней альфа-стадии. Пока вам придётся самим его компилировать, и вам, вероятно, лучше иметь для этого Linux (но WSL2 тоже должен работать). Хотя к тому времени, когда вый─ дет этот номер Info Guide, я, возможно, дойду до создания сборок под Windows. Это один из первых пунктов в моём плане. В лю─ бом случае, на данный момент Bintracker поставляется с несколькими разными движка─ ми под спектрумовский бипер, а также рядом движков для более экзотических платформ. В настоящее время я работаю над поддержкой Atari 2600, а значит, потом смогу перейти к множеству других платформ на базе 6502. Метапрограммирование реализовано полнос─ тью, хотя и несколько неудобно, в термина─ ле интерпретатора Scheme. Уже доступно не─ сколько примеров простых плагинов. Пока отсутствует несколько важных дета─ лей. Компилятор MDAL ещё не оптимизирует выходные данные,то есть создаваемые им би─ нарные файлы длинее, чем могли бы быть. Кое-что отсутствует и в реализации MDAL, что ограничивает набор звуковых движков, которые на данном этапе может поддерживать Bintracker. Ещё одна деталь, которой не хватает, касается ордера: сейчас поддержи─ вается только ордер в виде матрицы (по паттерну на каждый канал, а на на все сра─ зу,как в XM или Vortex Tracker ). Планиру─ ется реализовать два уровня абстракции сверху: режим с единым ордером и режим без ордера (как у 1tracker ). Кроме того, как упоминалось выше, пока нет исполняемых сборок. И, конечно, в программе ещё много глюков. Как только я с ними разберусь, вы─ йдет официальная бета-версия. По моим рас─ чётам, это, вероятно, случится поздней осенью. А пока ознакомьтесь с проектом на https://bintracker.org. Там вы также може─ те подписаться на рассылку новостей о Bintracker, если хотите получать последние новости о проекте.
Другие статьи номера:
Похожие статьи:
В этот день... 21 ноября