Pattr

Создаем Менеджер Пресетов в PureData

Да, Max более совершенен, чем Pd. Да, в нем очень много сделано для продуктивной работы. Наконец, его интерфейс не тормозит при большом количестве объектов в патче. Однако, есть в Pd что-то магическое, что заставляет снова и снова открывать этот простой интерфейс, написанный на tcl/tk, с его простенькими боксами и радующей ночью глаза цветовой гаммой. Может это и есть та самая магия open source, которая заставляет людей использовать gentoo на десктопе?

В данной статье мы будем пытаться решить одну наболевшую тему всех пользователей этой замечательной программы, а именно, сохранение параметров числовых боксов, слайдеров, кнобов и т.д. Казалось бы, очень важная функция, почему до сих пор нет специального объекта, аналогичного preset в Max? Все дело в том, что с помощью PureData API нельзя получить доступ к данным других объектов, поэтому приходится выкручиваться.

Разумеется, были попытки реализации пресетов. Я посмотрел некоторые из них, но их реализация мне показалась сложной (да, я в них до конца не разобрался) — мне же хотелось чего-то легковесного и гибкого. В результате, после дня раздумий и вечера патчинга родилась такая система.

Постановка задачи

Для сохранения пресетов нам нужно:

  1. постоянно следить за сохраняемыми параметрами;
  2. иметь возможность в любой момент извлечь и сохранить их;
  3. иметь возможность восстановить состояние объектов из сохраненных данных.

Мне сразу вспомнились объекты pattr и pattrstorage из макса. Первый соединяется с gui объектом, значение которого надо сохранить, а второй является хранилищем данных. Было решено сделать что-то похожее.

Представляем, как все будет

Итак, наш «аналог» объекта pattr будет следить за параметром, а аналог pattrstorage’а будет при необходимости опрашивать его и сохранять значения. Назовем эти объекты store и storage соответственно. Все это должно выглядеть примерно так:

Presets PureData. Представляем, как все будет

Объект store будет хранить текущее, а также присваивать новое значение числовому боксу. Именно поэтому эти объекты соединены между собой таким образом. С помощью сообщения «save» storage будет посылать команду в store, чтобы тот отправил ему текущее значение числового бокса. «recall» же будет делать обратное — задавать боксу новое значение.

Итак, общий вид понятен, двигаемся дальше.

Делаем основу

В максе информация пресетов хранится в json формате. Теоретически, Pd может работать с ним через библиотеку PuREST, но он заточен под онлайн сервисы, да и зачем все усложнять? Легче всего записать данные в .txt файл с помощью объекта textfile. Он может сохранять любое количество сообщений и последовательно выводить их. Подробнее о том, как он работает, можно прочитать в хелпе, а сейчас приступим к созданию объектов.

Presets PureData. Первые наброски.

На картинке выше сделан скрин с комментариями трех открытых патчей. root — это тестовый патч, в котором мы будем проверять работу store и storage. В прицнипе, на данном этапе уже можно сохранять и восстанавливать значение одного числового бокса. Порядок действий таков:

  1. меняем значение числового бокса;
  2. нажимаем «get» — это очистит textfile, после чего будет послан bang в [s presetTrigger]. Получателем этого bang будет объект float, находящийся в store, который выведет сохраненное число через свой аутлет и направит его снова в store, где оно выведется через [r presetGetAll]], превратится в сообщение вида «add %число_из_бокса%» и отправится в textfile;
  3. нажимаем «store» — это пошлет в textfile сообщение «write» и создаст в папке с патчем root текстовый файл, в котором будут содежраться все параметры пресета;
  4. изменим значение числового бокса, после чего жмем «recall» — сохраненное ранее значение должно восстановиться.

Наращиваем функциональность

Итак, мы сделали основную часть и успешно проверили работоспособность. Однако, в данный момент можно сохранять только один параметр, что не есть гуд. Как мы помним, объекту pattr в аргументе необходимо задать имя параметра, значение которого будет запоминаться. Таким образом макс индексирует параметры в json файле, чтобы впоследствии можно было восстановить данные в нужные gui объекты. Мы сделаем что-то похожее и сделаем это хитро. Взглянем на скриншот:

Presets PureData. Работаем с несколькими числовыми боксами.

Как видно, в патче root.pd теперь три числовых бокса соединены с тремя объектами [store one], [store two] и [store three]. Каждому сохраняемому параметру дано имя. Теперь если покрутить парамтеры боксов и открыть preset.txt, то можно увидеть примерно следующее:

three 67; two 43; one 165;

То есть каждому параметру в файле добавился индекс, соответствующий введенному в объект store аргументу. Это делается благодаря так называемым аргументам абстракций (abstraction creation arguments). Если внимательно посмотреть в патч store.pd, то можно заметить, что объекты route и prepend имеют аргумент «$1» — это и есть эти аргументы. Суть в том, что при использовании патча, как абстрации, внутри не $1, $2, $3 и т.д. заменяются соответственно первым, вторым и третьим аргументами. То есть в [store one] на самом деле находятся объекты [route one] и [prepend one].

Теперь о том, для чего, собственно, были добавлены prepend и route. Первый из них перед отправкой параметра в textfile создает тот самый индекс, который мы видели в текстовом файле, а route в отсеивает параметры с не чужими индексами, когда все содержание текстового файла транслируются через [p presetSetParam].

Как уже говорилось ранее, при помощи bang можно выводить по одному сообщения из textfile. При достижении последнего сообщения, объект выводит bang через правый аутлет.

Для того, чтобы извлечь все сообщения, надо пройтись по всему файлу, что делает объект until. Это очень интересный объект, если ему в инлет подать какое-либо число, скажем, десять, то он один за другим выведет 10 bang-сообщений. Если же на вход подать просто bang, то until будет генерировать бэнги до тех пор, пока в его правый инлет не поступит bang.

Теперь можно разобраться, что происходит, при нажатии на сообщение «recall». Сначала bang поступает в сообщение «read preset.txt, rewind», которое загружает в textfile файл preset.txt, после чего посылает сообщение «rewind», которое делает так, что следующий bang, полученный объектом, выведет первый параметр из файла. Дальше bang идет в объект until, который генерирует бэнги один за другим и направляет их в textfile до тех пор, пока последний не выведет bang из своего правого аутлета и не остановит until.

На данный момент в пресете можно сохранять только числа, но чтобы можно было использовать в реальных патчах необходимо добавить, как минимум сохранение списков. Сделаем это, создав аналоги патча store для остальных типов данных. Переименуем наш store в sfloat и создадим объект slist.

Presets PureData. Сохраняем списки (lists)

Это немного измененный патч sfloat.pd, адаптированный для списков.

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

Итак, для того, чтобы можно было управлять несколькими пресетами, нам надо менять содержимое месседж боксов «read preset.txt» и «write preset.txt», а именно менять имена файлов для соответствующих пресетов. Взглянем на модифицированный storage.pd:

Presets PureData. Реализуем работу с несколькими пресетами.

Здесь появился новый объект: makefilename. Он как раз создан для генерации имен файлов и идеально нам подходит. При поступлении числа в инлет, объект выводит сообщение вида «preset-%i.txt», заменяя %i числом. Также пришлось вывести «rewind» в отдельный месседж бокс, так как я не нашел легкого способа сгенерировать сложное сообщение с запятой.

Все, теперь система работает. Теперь добавим удобства, сделав небольшой gui. Откроем storage.pd, нажмем правой кнопкой на пустом месте и выберем Properties. В появившемся окне отметим галочку «Graph on parent», после этого появится красный прямоугольник в патче. Все объекты интерфейса, как кнопки, числовые боксы и комментарии, находящиеся внутри этого прямоугольника будут отображаться в патча а-ля максовский bpatcher. Размеры и позиция прямоугольника задаются в окне canvas с помощью параметров size и margin.

Presets PureData. Делаем GUI.

Вот так теперь выглядит объект storage в патче:

Presets PureData. Окончательный вариант объекта [storage].

Теперь Pd стала немного удобнее :)

Напоследок

Данной системе пресетов не хватает сохранения таблиц, но это я отложу на будущее, так как скорее всего, чтобы было удобно, потребуется какое-то хитрое колдунство. Также я неплохо было бы добавить в storage инлет для внешнего управления пресетами, чтобы можно было подрубить какой-нибудь MIDI контроллер.

Скачать файлы можно из раздела «Патчи», enjoy!

26 Jan 2012  OSCII