При решении задач визуализации информации, очевидно, возникает вопрос о практической реализации задуманного. Данный цикл статей посвящен процессу проектирования приложений с помощью визуального программирования в Max 7 от компании Cycling '74. Начнем изучение вопроса, от простого к сложному. Пройдем путь от любительской заготовки до полноценного «однофайлового» приложения (standalone .exe).
Уровень: легкий
?
Наверняка кто-нибудь использует какую-либо программу и не догадывается, что она сделана с помощью Max. Такой программой, например, является DAW Ableton Live. Догадаться можно по интерфейсу объектов, а так же из-за Max for Live. Конечно, в таких серьезных проектах используется не только Max, но и дополнительные библиотеки, файлы, скрипты и т.д. Так же и некоторые современные программы на iPad для работы со звуком. В данном цикле статей мы попытаемся разобраться в процессе проектирования подобных сложных приложений, начиная с самых простых примеров.
1. Визуальное программирование
> 1.1. Понятия
2. Интерфейс Max 7
3. Принцип построения программ в Max
> 3.1. Модульные объекты со входами и выходами
> 3.2. Инкапсуляция [p patchname]
> 3.3. Самостоятельный патч
> 3.4. [bpatcher]
4. Структура программы
> 4.1. Проектное пространство
> 4.2. Первые шаги
> 4.3. Bang, Metro, Counter, Number
> 4.4. Избавляемся от лишнего
> 4.5. Другие объекты
> 4.6. Логические операторы, регулярные выражения
> 4.7. Логические выражения
5. Полезные модули
Прилагаемые файлы
Полезные ссылки
?
Данный способ программирования позволит читателю отойти от написания строк кода на языках типа C/C++, всё представляется в виде модулей и связями между ними. Процесс разработки становится более наглядным и быстрым, при этом программа выполняется в реальном времени, пока вы ее делаете, т.е. для любых изменений в структуре результат отображается сразу же, без компиляции. По сути, вы строите логическую блок-схему работы программы.
Для решения задач визуализации будем использовать визуальный язык программирования — Max (MSP). В основном, Max предназначен для создания интерактивных объектов искусства, но так же может быть применим при решении огромного пласта задач. Во многих областях можно найти что-то, что можно визуализировать красиво. Вспомните эти шпионские фильмы, в которых на компьютерах героев изображены различные красивые интерфейсы неведомых программ. Да и в реальности — например, визуализация информации, поступающей с датчиков. К тому же он позволяет вести разработку как на машинах с ОС Windows, так и на компьютерах с Mac OS.
Для описания процесса осуществления задуманного, введем следующие понятия:
В терминах телекоммуникаций модуль выступает в роли коммутатора, который в свою очередь выполняет заданные функции и отправляет результат в выходные порты.
Примечание. В данной статье я постараюсь не углубляться в очевидные вещи. Очень много дополнительной информации о работе объектов и модулей доступно в справке — правой кнопкой мыши по объетку, затем «Open имя_объекта help». Поэтому при описании элементов программы предпочтение будет отдаваться в основном их предназначению, а не синтаксису или структуре.
В основном мы будем пользоваться режимами: конструктора и презентации, а так же заглядывать в окна консоли (Max console), инспектора объектов (Inspector, CTRL+I) и инспектора патча (Inspector Window, CTRL+SHIFT+I).
Процесс создания программ сводится к построению блок-схемы из модулей и связей между ними. Можно использовать встроенные в Max модули, но интересней всего писать их самому. Это очень удобно как при вызове их изнутри программы, так и при использовании в процессе программирования в целом. В обычном программировании данная модель представлена в виде ООП, ну или можно представить один модуль как одну функцию, параметры которой — входы [inlets] модуля, а return — выход, причем выходов может быть много [outlets].
Примечание: Для добавления объекта в патч кликните 2 раза мышкой по пустому месту, либо нажмите клавишу N, вводите название, нажимаете Enter и дальше можно уже соединять его с другими.
Есть несколько способов добавления самописных модулей:
— инкапсуляция [p patchname],
— самостоятельный патч,
— [bpatcher].
Данное понятие аналогично понятию инкапсуляции в обычных языках программирования. В данном случае всё элементарно — выделяем объекты, которые хотим объединить в группу/модуль/файл и нажимаем CTRL+E (Edit — Encapsulation). При завершении инкапсуляции таким образом на выходе получаем модуль — [p patchname], где patchname — любая фраза на английском. Так же доступны и цифровые символы.
Объединяемые объекты после завершения данного процесса исчезают из окна патча Max. На самом деле Max внутри оригинально патча создает субпатч, куда и переносит все выделенные объекты и их связи. В зависимости от наличия внешних связей, инкапсулятор может автоматически вставить объекты входы [inlet] и выходы [outlet], а так же соединить их с внешними объектами. Это очень удобно, когда разрабатываешь программу не задумываясь о нагромождении объектов, ведь при достижении определенного количества, структура программы перестанет быть читаемой и вы сами в ней запутаетесь. Для решения проблем организации структуры программы и предназначена инкапсуляция в Max.
Особенности: это инструмент группировки/разграничения объектов по различным признакам. Данный способ создания модулей предполагает уникальность каждого модуля. Т.е. если вы создадите один модуль таким образом и продублируете его — это будут уже два разных модуля. Внесение изменений в один из них не повлечет за собой изменение остальных дубликатов.
Применение: уникальные группы объектов, простые или сложные вычисления, которые используются в программе только в одном месте.
Для создания модулей можно воспользоваться и другим способом. Достаточно создать новый файл, добавить и соединить нужные блоки, расположить объекты, отвечающие за принятие входа [inlet], и за отправку на выход — [outlet] и сохранить файл в любой папке (например создать отдельную папку «tools» и кидать туда патчи со своим префиксом). Далее уже в рабочем патче добавляем объект с именем [название_файла] (без разрешения файла .maxpat) и всё готово.
Данный способ можно использовать в комбинации с инкапсуляцией, описанной выше. Это даже еще удобнее. Можно инкапсулировать нужные объекты, открыть получившийся модуль (в заблокированном режиме два раза левой кнопкой мыши по блоку) и сохранить его как самостоятельный патч.
Особенности: удобная вещь для создания модулей по типу «черный ящик» — один раз написав модуль, мы уже не возвращаемся без надобности к его структуре и т.д. Мы видим только 3 вида информации: входы, название модуля и выходы. Важно, что при дублировании таких модулей, их содержание не становится уникальными, т.е. изменения в одном из дубликатов повлечет за собой изменения во всех них. Если точнее, то в таком случае редактируется не дубликаты модулей, а сам оригинальный файл модуля.
Применение: удобно применять этот способ при создании модулей для каких либо отдельных вычислений. Особенности позволяют многократно дублировать модули.
Наглядный пример — модуль перевода сообщения из десятичной системы исчисления в двоичную. Простой копипаст объектов абстракции переводчика внутри главного патча в этом случае будет признаком плохого тона, т.к. может оказаться, что модуль требует изменений. Легче изменить 1 файл, чем весь этот копипаст перебирать.
Если в предыдущих способах мы создавали «черные ящики», то бывают случаи, когда нужно отобразить какую-либо часть модуля в открытом виде без редактирования структуры. Это могут быть различные числовые визуализации, отображения служебных сообщений и т.д. Еще более интересным является процесс добавления презентационной части модуля.
Данный способ расширяет границы применения самостоятельных патчей. Для того, чтобы добавить модуль в рабочий патч, просто создаем объект [bpatcher], а в инспекторе его свойств указываем путь к файлу модуля.
Особенности: можем видеть структуру и связи модуля, а так же презентационную часть.
Применение: подходит для организации верхнего уровня структуры программы, т.е. по сути отображаем сгруппированные модули одним объектом, что очень удобно — избавляемся от лишних нагромождений проводов и вспомогательных объектов. Видим только те части, которые нужны.
Весь процесс написания программы будет сводится к многочисленным инкапсуляциям и добавлением субпатчей в рабочий патч. Это довольно логично и удобно, особенно при проектировании сложных программ, где может быть много модулей различных применений. По сути, мы просто закапываем «рабочие лошадки» программы по группам все глубже и глубже, а на выходе будем иметь элементарную структуру всего из нескольких объектов. Если потребуется внести изменения в какой-то из модулей, то мы просто откроем его файл и без проблем это сделаем.
Лично для меня данный способ проектирования программ является более наглядным. К тому же не нужно дублировать кучу символьного кода, а в некоторых случаях это очень утомляет.
Наглядные примеры модулей, созданных перечисленными способами, представлены в файле tut01_subpatches.maxpat
Для создания сложных программ с большим количеством библиотек в Max 7 предусмотрена возможность создания проектов. Данная функция представлена в виде списка патчей и других файлов, использующихся в проекте. Справа в миниатюре показано содержание уже готового крупного проекта по визуализации цифровой обработки сигналов. Как можно наблюдать, в списке видим не только сами файлы патчей .maxpat, но и файлы других типов.
В дальнейшем можно будет использовать такой список «для себя», просто чтобы знать где что лежит и какие модули использованы в данном проекте. Хотя, компилировать программу можно хоть из главного патча, хоть из меню с проектом. Отличие в том, что при открытии проекта можно задать открываемый патч по-умолчанию — открываешь проект и вместе со списком файлов открывается и назначенный патч.
Начнем наш тернистый путь познания о программировании в среде Max 7 созданием нового проекта. File > New Project. Рекомендуется сохранять все проекты в отдельную папку, например — C:\Max Projects\… Расположение папки с проектами в корне диска крайне удобно, особенно когда меняешь версию программы или ОС.
После сохранения проекта откроется окно со списком патчей проекта, но пока он пуст. Добавим новый файл в проект:
+ > Add New File… Сохраняем файл в каталог, где лежит файл проекта, а дальше Max сам создаст там папку patchers, в которую на самом деле и сохраняется файл патча.
Начнем с чего-нибудь простого, например, счетчик с тактовым генератором. Счетчик будет прибавлять значение на единицу только тогда, когда на вход счетчика приходит bang-сообщение от тактового генератора (метроном).
Bang — это специальное сообщение, запускающее работу модуля/объекта. Завершение любой операции в модуле на выходе дает какое-то значение, при этом сам факт отправки данных из модуля на его выход сопровождается bang-сообщением. Bang используем когда нужно отправить какое-либо сообщение куда-либо. Он как бы «толкает» сообщение дальше по тракту от объекта к объекту.
Добавим в наш патч объекты и связи как на изображении справа.
Примечание. Max хорош еще и тем, что патчами можно спокойно обмениваться в текстовом формате. Происходит это следующим образом — вы выделяете все элементы и связи в патче нажатием клавиш CTRL+A, копируете CTRL+C, а затем открываете любой текстовый редактор или форму на сайте и вставляете текст из буфера. По сути, описание состояния программы (структуру, данные) представляется в виде JSON-текста, который вы только что скопировали. Чтение такой информации происходит не намного сложнее: в главном меню Max 7 Files > New From Clipboard… А вообще, не обязательно даже создавать новый файл, можно вставить скопированное прямо в уже открытый патч. Таким образом, Max считывает текст из буфера обмена и интерпретирует его в модули и связи между ними.
Попробуйте скопировать приведенный ниже код программы и вставить в Max.
Разберем всё по порядку. Основными объектами в данном патче являются:
3 тактовых генератора ([toggle] + [metro]) и 3 счетчика [counter].
[toggle]
Переключатель. Имеет два режима работы: кнопка (Button) и переключатель (Toggle). При нажатии на [toggle], объект посылает на выход значение своего состояния [0, 1]. При нажатии на «кнопку», он посылает на выход короткую единицу. Переключатель отличается тем, что запоминает свое предыдущее состояние. Нажал один раз, на выходе 0, еще раз — 1, еще раз — 0 и т.д.
[metro 500]
Данный объект представляет из себя метроном, отправляющий на свой выход каждые N-миллисекунд bang-сообщение. В данном случае bang будет генерироваться каждые 500 мс.
[counter направление мин.значение макс.значение]
Счетчик, прибавляющий свое значение на одну единицу при каждом поступающем на вход bang-сообщении. По направлению отсчета счетчики разделяются на восходящий (а), нисходящий (б) и туда-обратно (в).
Таким образом, название объекта [counter 1 0 3] говорит нам о нисходящем счетчике с границами от 0 до 3. При достижении максимального значения счетчик сбрасывается и отсчет начинается с мин. значения. У объекта есть два инлета, позволяющие сбросить счетчик. Один из них сбрасывает значение мгновенно, а другой — сбросить, начиная со следующего отсчета.
[number]
Объект ввода данных, позволяющих хранить введенную информацию. Используется только для целых чисел, для чисел с плавающей точкой есть объект [flonum].
Немаловажным умением при создании программ является умение избавляться от лишних деталей. Любознательный читатель заметил бы, что в вышепреведенном патче мы используем 3 тактовых генератора и 3 выключателя, хотя можно использовать всего по одному экземпляру. Забегая вперед скажу, что чем больше объектов типа [metro] вы будете добавлять, тем медленнее может стать обработка остальной информации программой. Чтобы получить программу с хорошим быстродействием, следует задумываться об этом еще на начальной стадии.
Избавимся от лишнего, удалим избыточные вычисления из программы:
Заменяем три тактовых генератора одним, входы счетчиков соединяем с выходом объекта [metro 500]. Так-то лучше. Вы можете сказать, что так надо было поступать с самого начала, но на практике координаты блоков генератора и других объектов могут сильно отличатся, не задумываясь добавляешь еще один метроном, а потом еще и еще. В итоге связи объектов превращаются в запутанную паутину, в которой довольно сложно ориентироваться. При правильном подходе данных проблем можно избежать множественными инкапсуляциями объектов в модули, группируя их по назначению. Этого мы и будем добиваться на протяжении всего цикла статей.
[print]
Если по каким-то причинам вы хотите выводить информацию в консоли, то для этого используется объект [print имя_строки]. Название объекта [print b] говорит нам о том, что при поступлении сообщения на вход [print], в консоли отобразится сообщение вида b: текст.
Элементарные операции
[+ i/f] — сложение двух значений входных параметров объекта с возвратом результата в аутлет.
Прим.: [+ 1], [+ 1.06]. i -int — целые числа, f — float — числа с плавающей точкой.
[* i/f] — умножение
[/ i/f] — деление
[+~ 1.] — сложение двух сигналов
[*~ 1.] — умножение двух сигналов
[if условие then 1 else 0] — при выполнении условия послать на выход единицу, в ином случае — ноль.
[if условие then 1 else out2 0] — при выполнении условия послать на первый выход единицу, в ином случае — во второй выход послать ноль.
[regexp] — работа с регулярными выражениями
[expr $i1 + $i2*$i3] — выполнить выражение между значениями, поступающими на вход модуль.
Остальные операции доступны в документации, всё аналогично обычному программированию.
Сохраняем приведенный код как два разных файла с именами tobit.maxpat и frombit.maxpat в папке патчей проекта. В дальнейшем, для осуществления операций кодирования/декодирования чисел мы будем пользоваться именно этими модулями.
С помощью объекта [vexpr] можно задавать различные логические операции.
tut_bitlogic.maxpat — в данном примере рассматриваются побитовые логические операции многоразрядных сообщений.
Здесь я перечислю модули, которые используются мной чаще всего. Некоторые писал сам с нуля, некоторые нашел на форуме тех.поддержки. Там можно найти решение почти любого вопроса, редко когда я чего-то не находил.
[toBit]
Данный модуль переводит переводит числа от 0 до 255 в восьмиразрядный битовый код.
[fromBit]
Принимает на вход восьмиразрядный битовый код, а на выходе выдает числа от 0 до 255.
[tut_dynamicInlets N]
Пример организации входов и выходов с заданным количеством N.
tut_lesson01.maxpat
tut_bitlogic.maxpat
tut_encapsulation.maxpat
tut_dynamicInlets.maxpat
toBit.maxpat
fromBit.maxpat
Все сразу (.zip) + каталог tutorial_all.maxpat
Обзорная статья на хабре про Max MSP
Cycling '74 — сайт производителя/страница Max 7.
Max Objects Database — хранилище разнообразных объектов. Зачем изобретать велосипед, когда он уже есть.
Cycling '74 — Projects — проекты других разработчиков.
Cycling '74 — Forums — форум, где можно найти решения для многих задач, достаточно лишь правильно спросить.
Pattr.ru — здесь можно найти полезные плюшки и статьи для Max MSP.
maxmsp vk — группа русскоязычных пользователей.
Список может изменится, примерный вариант:
?
Автор: Валерий Зимнев
Уровень: легкий
?
Конечный результат
Наверняка кто-нибудь использует какую-либо программу и не догадывается, что она сделана с помощью Max. Такой программой, например, является DAW Ableton Live. Догадаться можно по интерфейсу объектов, а так же из-за Max for Live. Конечно, в таких серьезных проектах используется не только Max, но и дополнительные библиотеки, файлы, скрипты и т.д. Так же и некоторые современные программы на iPad для работы со звуком. В данном цикле статей мы попытаемся разобраться в процессе проектирования подобных сложных приложений, начиная с самых простых примеров.
Оглавление
1. Визуальное программирование
> 1.1. Понятия
2. Интерфейс Max 7
3. Принцип построения программ в Max
> 3.1. Модульные объекты со входами и выходами
> 3.2. Инкапсуляция [p patchname]
> 3.3. Самостоятельный патч
> 3.4. [bpatcher]
4. Структура программы
> 4.1. Проектное пространство
> 4.2. Первые шаги
> 4.3. Bang, Metro, Counter, Number
> 4.4. Избавляемся от лишнего
> 4.5. Другие объекты
> 4.6. Логические операторы, регулярные выражения
> 4.7. Логические выражения
5. Полезные модули
Прилагаемые файлы
Полезные ссылки
?
§ 1. Визуальное программирование
Визуальное программирование — способ создания программы для ЭВМ путём манипулирования графическими объектами вместо написания её текста. [1]
Данный способ программирования позволит читателю отойти от написания строк кода на языках типа C/C++, всё представляется в виде модулей и связями между ними. Процесс разработки становится более наглядным и быстрым, при этом программа выполняется в реальном времени, пока вы ее делаете, т.е. для любых изменений в структуре результат отображается сразу же, без компиляции. По сути, вы строите логическую блок-схему работы программы.
Для решения задач визуализации будем использовать визуальный язык программирования — Max (MSP). В основном, Max предназначен для создания интерактивных объектов искусства, но так же может быть применим при решении огромного пласта задач. Во многих областях можно найти что-то, что можно визуализировать красиво. Вспомните эти шпионские фильмы, в которых на компьютерах героев изображены различные красивые интерфейсы неведомых программ. Да и в реальности — например, визуализация информации, поступающей с датчиков. К тому же он позволяет вести разработку как на машинах с ОС Windows, так и на компьютерах с Mac OS.
§ 1.1. Понятия
Для описания процесса осуществления задуманного, введем следующие понятия:
- Объект — элемент программы, выполняющий какие либо операции, либо отображающие информацию. Может иметь или не иметь входные и выходные значения.
- Модуль — группа объектов, объединенных в субпатч с целью выполнения заданной работы.
- Патч, субпатч — в Max 7 исходные файлы проектируемой программы сохраняются в формате .maxpatch, .maxhelp или .json и называются патчами. Субпатч в свою очередь — это дочерний элемент патча, может хранится как в отдельном файле, так и внутри патча.
- Инлет [inlet] — объект, создающий коммутацию модулей друг с другом. Является входным параметром модуля.
- Аутлет [outlet] — объект, создающий коммутацию модулей друг с другом. Является выходным значением модуля.
- Вход модуля — место соединения внешних объектов с модулем, входные параметры.
- Выход модуля — место соединения внешних объектов с модулем, посылает на выход результат работы модуля.
В терминах телекоммуникаций модуль выступает в роли коммутатора, который в свою очередь выполняет заданные функции и отправляет результат в выходные порты.
§ 2. Интерфейс Max 7
Примечание. В данной статье я постараюсь не углубляться в очевидные вещи. Очень много дополнительной информации о работе объектов и модулей доступно в справке — правой кнопкой мыши по объетку, затем «Open имя_объекта help». Поэтому при описании элементов программы предпочтение будет отдаваться в основном их предназначению, а не синтаксису или структуре.
В основном мы будем пользоваться режимами: конструктора и презентации, а так же заглядывать в окна консоли (Max console), инспектора объектов (Inspector, CTRL+I) и инспектора патча (Inspector Window, CTRL+SHIFT+I).
- Главное меню программы
- [inlet]-объекты. Входы модуля.
- Компоненты (объекты) модуля, исполняемая часть.
- [outlet]-объекты. Выходы модуля.
- Inspector. Параметры выделенного объекта.
- Заблокировать/разблокировать редактирование патча.
- Переключатель между режимами конструктора и презентации.
- Вкл/выкл сетку.
- Область программы.
§ 3. Принцип построения программ в Max
§ 3.1. Модульные объекты со входами и выходами
Процесс создания программ сводится к построению блок-схемы из модулей и связей между ними. Можно использовать встроенные в Max модули, но интересней всего писать их самому. Это очень удобно как при вызове их изнутри программы, так и при использовании в процессе программирования в целом. В обычном программировании данная модель представлена в виде ООП, ну или можно представить один модуль как одну функцию, параметры которой — входы [inlets] модуля, а return — выход, причем выходов может быть много [outlets].
Примечание: Для добавления объекта в патч кликните 2 раза мышкой по пустому месту, либо нажмите клавишу N, вводите название, нажимаете Enter и дальше можно уже соединять его с другими.
Есть несколько способов добавления самописных модулей:
— инкапсуляция [p patchname],
— самостоятельный патч,
— [bpatcher].
§ 3.2. Инкапсуляция [p patchname]
Данное понятие аналогично понятию инкапсуляции в обычных языках программирования. В данном случае всё элементарно — выделяем объекты, которые хотим объединить в группу/модуль/файл и нажимаем CTRL+E (Edit — Encapsulation). При завершении инкапсуляции таким образом на выходе получаем модуль — [p patchname], где patchname — любая фраза на английском. Так же доступны и цифровые символы.
Объединяемые объекты после завершения данного процесса исчезают из окна патча Max. На самом деле Max внутри оригинально патча создает субпатч, куда и переносит все выделенные объекты и их связи. В зависимости от наличия внешних связей, инкапсулятор может автоматически вставить объекты входы [inlet] и выходы [outlet], а так же соединить их с внешними объектами. Это очень удобно, когда разрабатываешь программу не задумываясь о нагромождении объектов, ведь при достижении определенного количества, структура программы перестанет быть читаемой и вы сами в ней запутаетесь. Для решения проблем организации структуры программы и предназначена инкапсуляция в Max.
Особенности: это инструмент группировки/разграничения объектов по различным признакам. Данный способ создания модулей предполагает уникальность каждого модуля. Т.е. если вы создадите один модуль таким образом и продублируете его — это будут уже два разных модуля. Внесение изменений в один из них не повлечет за собой изменение остальных дубликатов.
Применение: уникальные группы объектов, простые или сложные вычисления, которые используются в программе только в одном месте.
§ 3.3. Самостоятельный патч
Для создания модулей можно воспользоваться и другим способом. Достаточно создать новый файл, добавить и соединить нужные блоки, расположить объекты, отвечающие за принятие входа [inlet], и за отправку на выход — [outlet] и сохранить файл в любой папке (например создать отдельную папку «tools» и кидать туда патчи со своим префиксом). Далее уже в рабочем патче добавляем объект с именем [название_файла] (без разрешения файла .maxpat) и всё готово.
Данный способ можно использовать в комбинации с инкапсуляцией, описанной выше. Это даже еще удобнее. Можно инкапсулировать нужные объекты, открыть получившийся модуль (в заблокированном режиме два раза левой кнопкой мыши по блоку) и сохранить его как самостоятельный патч.
Особенности: удобная вещь для создания модулей по типу «черный ящик» — один раз написав модуль, мы уже не возвращаемся без надобности к его структуре и т.д. Мы видим только 3 вида информации: входы, название модуля и выходы. Важно, что при дублировании таких модулей, их содержание не становится уникальными, т.е. изменения в одном из дубликатов повлечет за собой изменения во всех них. Если точнее, то в таком случае редактируется не дубликаты модулей, а сам оригинальный файл модуля.
Применение: удобно применять этот способ при создании модулей для каких либо отдельных вычислений. Особенности позволяют многократно дублировать модули.
Наглядный пример — модуль перевода сообщения из десятичной системы исчисления в двоичную. Простой копипаст объектов абстракции переводчика внутри главного патча в этом случае будет признаком плохого тона, т.к. может оказаться, что модуль требует изменений. Легче изменить 1 файл, чем весь этот копипаст перебирать.
§ 3.4. [bpatcher]
Если в предыдущих способах мы создавали «черные ящики», то бывают случаи, когда нужно отобразить какую-либо часть модуля в открытом виде без редактирования структуры. Это могут быть различные числовые визуализации, отображения служебных сообщений и т.д. Еще более интересным является процесс добавления презентационной части модуля.
Данный способ расширяет границы применения самостоятельных патчей. Для того, чтобы добавить модуль в рабочий патч, просто создаем объект [bpatcher], а в инспекторе его свойств указываем путь к файлу модуля.
Особенности: можем видеть структуру и связи модуля, а так же презентационную часть.
Применение: подходит для организации верхнего уровня структуры программы, т.е. по сути отображаем сгруппированные модули одним объектом, что очень удобно — избавляемся от лишних нагромождений проводов и вспомогательных объектов. Видим только те части, которые нужны.
§ 4. Структура программы
Весь процесс написания программы будет сводится к многочисленным инкапсуляциям и добавлением субпатчей в рабочий патч. Это довольно логично и удобно, особенно при проектировании сложных программ, где может быть много модулей различных применений. По сути, мы просто закапываем «рабочие лошадки» программы по группам все глубже и глубже, а на выходе будем иметь элементарную структуру всего из нескольких объектов. Если потребуется внести изменения в какой-то из модулей, то мы просто откроем его файл и без проблем это сделаем.
Лично для меня данный способ проектирования программ является более наглядным. К тому же не нужно дублировать кучу символьного кода, а в некоторых случаях это очень утомляет.
Наглядные примеры модулей, созданных перечисленными способами, представлены в файле tut01_subpatches.maxpat
§ 4.1. Проектное пространство
Для создания сложных программ с большим количеством библиотек в Max 7 предусмотрена возможность создания проектов. Данная функция представлена в виде списка патчей и других файлов, использующихся в проекте. Справа в миниатюре показано содержание уже готового крупного проекта по визуализации цифровой обработки сигналов. Как можно наблюдать, в списке видим не только сами файлы патчей .maxpat, но и файлы других типов.
В дальнейшем можно будет использовать такой список «для себя», просто чтобы знать где что лежит и какие модули использованы в данном проекте. Хотя, компилировать программу можно хоть из главного патча, хоть из меню с проектом. Отличие в том, что при открытии проекта можно задать открываемый патч по-умолчанию — открываешь проект и вместе со списком файлов открывается и назначенный патч.
§ 4.2. Первые шаги
Начнем наш тернистый путь познания о программировании в среде Max 7 созданием нового проекта. File > New Project. Рекомендуется сохранять все проекты в отдельную папку, например — C:\Max Projects\… Расположение папки с проектами в корне диска крайне удобно, особенно когда меняешь версию программы или ОС.
После сохранения проекта откроется окно со списком патчей проекта, но пока он пуст. Добавим новый файл в проект:
+ > Add New File… Сохраняем файл в каталог, где лежит файл проекта, а дальше Max сам создаст там папку patchers, в которую на самом деле и сохраняется файл патча.
§ 4.3. Bang, Metro, Counter, Number
Начнем с чего-нибудь простого, например, счетчик с тактовым генератором. Счетчик будет прибавлять значение на единицу только тогда, когда на вход счетчика приходит bang-сообщение от тактового генератора (метроном).
Bang — это специальное сообщение, запускающее работу модуля/объекта. Завершение любой операции в модуле на выходе дает какое-то значение, при этом сам факт отправки данных из модуля на его выход сопровождается bang-сообщением. Bang используем когда нужно отправить какое-либо сообщение куда-либо. Он как бы «толкает» сообщение дальше по тракту от объекта к объекту.
Добавим в наш патч объекты и связи как на изображении справа.
Примечание. Max хорош еще и тем, что патчами можно спокойно обмениваться в текстовом формате. Происходит это следующим образом — вы выделяете все элементы и связи в патче нажатием клавиш CTRL+A, копируете CTRL+C, а затем открываете любой текстовый редактор или форму на сайте и вставляете текст из буфера. По сути, описание состояния программы (структуру, данные) представляется в виде JSON-текста, который вы только что скопировали. Чтение такой информации происходит не намного сложнее: в главном меню Max 7 Files > New From Clipboard… А вообще, не обязательно даже создавать новый файл, можно вставить скопированное прямо в уже открытый патч. Таким образом, Max считывает текст из буфера обмена и интерпретирует его в модули и связи между ними.
Попробуйте скопировать приведенный ниже код программы и вставить в Max.
Код модуля tut_lesson01
{
"patcher" : {
"fileversion" : 1,
"appversion" : {
"major" : 7,
"minor" : 0,
"revision" : 2,
"architecture" : "x64",
"modernui" : 1
}
,
"rect" : [ 458.0, 167.0, 574.0, 390.0 ],
"bglocked" : 0,
"openinpresentation" : 0,
"default_fontsize" : 12.0,
"default_fontface" : 0,
"default_fontname" : "Arial",
"gridonopen" : 1,
"gridsize" : [ 15.0, 15.0 ],
"gridsnaponopen" : 1,
"objectsnaponopen" : 1,
"statusbarvisible" : 2,
"toolbarvisible" : 1,
"lefttoolbarpinned" : 0,
"toptoolbarpinned" : 0,
"righttoolbarpinned" : 0,
"bottomtoolbarpinned" : 0,
"toolbars_unpinned_last_save" : 0,
"tallnewobj" : 0,
"boxanimatetime" : 200,
"enablehscroll" : 1,
"enablevscroll" : 1,
"devicewidth" : 0.0,
"description" : "",
"digest" : "",
"tags" : "",
"style" : "",
"subpatcher_template" : "",
"boxes" : [ {
"box" : {
"id" : "obj-21",
"maxclass" : "comment",
"numinlets" : 1,
"numoutlets" : 0,
"patching_rect" : [ 265.0, 165.0, 30.0, 20.0 ],
"style" : "",
"text" : "в)"
}
}
, {
"box" : {
"id" : "obj-20",
"maxclass" : "comment",
"numinlets" : 1,
"numoutlets" : 0,
"patching_rect" : [ 145.0, 165.0, 30.0, 20.0 ],
"style" : "",
"text" : "б)"
}
}
, {
"box" : {
"id" : "obj-19",
"maxclass" : "comment",
"numinlets" : 1,
"numoutlets" : 0,
"patching_rect" : [ 25.0, 165.0, 30.0, 20.0 ],
"style" : "",
"text" : "a)"
}
}
, {
"box" : {
"id" : "obj-13",
"maxclass" : "number",
"numinlets" : 1,
"numoutlets" : 2,
"outlettype" : [ "", "bang" ],
"parameter_enable" : 0,
"patching_rect" : [ 255.0, 135.0, 50.0, 22.0 ],
"style" : ""
}
}
, {
"box" : {
"id" : "obj-14",
"maxclass" : "newobj",
"numinlets" : 5,
"numoutlets" : 4,
"outlettype" : [ "int", "", "", "int" ],
"patching_rect" : [ 255.0, 90.0, 81.0, 22.0 ],
"style" : "",
"text" : "counter 2 0 3"
}
}
, {
"box" : {
"id" : "obj-15",
"maxclass" : "number",
"numinlets" : 1,
"numoutlets" : 2,
"outlettype" : [ "", "bang" ],
"parameter_enable" : 0,
"patching_rect" : [ 300.0, 15.0, 50.0, 22.0 ],
"style" : ""
}
}
, {
"box" : {
"id" : "obj-16",
"maxclass" : "newobj",
"numinlets" : 2,
"numoutlets" : 1,
"outlettype" : [ "bang" ],
"patching_rect" : [ 255.0, 60.0, 65.0, 22.0 ],
"style" : "",
"text" : "metro 500"
}
}
, {
"box" : {
"id" : "obj-17",
"maxclass" : "toggle",
"numinlets" : 1,
"numoutlets" : 1,
"outlettype" : [ "int" ],
"parameter_enable" : 0,
"patching_rect" : [ 255.0, 15.0, 24.0, 24.0 ],
"style" : ""
}
}
, {
"box" : {
"id" : "obj-8",
"maxclass" : "number",
"numinlets" : 1,
"numoutlets" : 2,
"outlettype" : [ "", "bang" ],
"parameter_enable" : 0,
"patching_rect" : [ 135.0, 135.0, 50.0, 22.0 ],
"style" : ""
}
}
, {
"box" : {
"id" : "obj-9",
"maxclass" : "newobj",
"numinlets" : 5,
"numoutlets" : 4,
"outlettype" : [ "int", "", "", "int" ],
"patching_rect" : [ 135.0, 90.0, 81.0, 22.0 ],
"style" : "",
"text" : "counter 1 0 3"
}
}
, {
"box" : {
"id" : "obj-10",
"maxclass" : "number",
"numinlets" : 1,
"numoutlets" : 2,
"outlettype" : [ "", "bang" ],
"parameter_enable" : 0,
"patching_rect" : [ 180.0, 15.0, 50.0, 22.0 ],
"style" : ""
}
}
, {
"box" : {
"id" : "obj-11",
"maxclass" : "newobj",
"numinlets" : 2,
"numoutlets" : 1,
"outlettype" : [ "bang" ],
"patching_rect" : [ 135.0, 60.0, 65.0, 22.0 ],
"style" : "",
"text" : "metro 500"
}
}
, {
"box" : {
"id" : "obj-12",
"maxclass" : "toggle",
"numinlets" : 1,
"numoutlets" : 1,
"outlettype" : [ "int" ],
"parameter_enable" : 0,
"patching_rect" : [ 135.0, 15.0, 24.0, 24.0 ],
"style" : ""
}
}
, {
"box" : {
"id" : "obj-7",
"maxclass" : "number",
"numinlets" : 1,
"numoutlets" : 2,
"outlettype" : [ "", "bang" ],
"parameter_enable" : 0,
"patching_rect" : [ 15.0, 135.0, 50.0, 22.0 ],
"style" : ""
}
}
, {
"box" : {
"id" : "obj-6",
"maxclass" : "newobj",
"numinlets" : 5,
"numoutlets" : 4,
"outlettype" : [ "int", "", "", "int" ],
"patching_rect" : [ 15.0, 90.0, 71.0, 22.0 ],
"style" : "",
"text" : "counter 0 3"
}
}
, {
"box" : {
"id" : "obj-5",
"maxclass" : "number",
"numinlets" : 1,
"numoutlets" : 2,
"outlettype" : [ "", "bang" ],
"parameter_enable" : 0,
"patching_rect" : [ 60.0, 15.0, 50.0, 22.0 ],
"style" : ""
}
}
, {
"box" : {
"id" : "obj-4",
"maxclass" : "newobj",
"numinlets" : 2,
"numoutlets" : 1,
"outlettype" : [ "bang" ],
"patching_rect" : [ 15.0, 60.0, 65.0, 22.0 ],
"style" : "",
"text" : "metro 500"
}
}
, {
"box" : {
"id" : "obj-3",
"maxclass" : "toggle",
"numinlets" : 1,
"numoutlets" : 1,
"outlettype" : [ "int" ],
"parameter_enable" : 0,
"patching_rect" : [ 15.0, 15.0, 24.0, 24.0 ],
"style" : ""
}
}
],
"lines" : [ {
"patchline" : {
"destination" : [ "obj-11", 1 ],
"disabled" : 0,
"hidden" : 0,
"midpoints" : [ 189.5, 58.0 ],
"source" : [ "obj-10", 0 ]
}
}
, {
"patchline" : {
"destination" : [ "obj-9", 0 ],
"disabled" : 0,
"hidden" : 0,
"source" : [ "obj-11", 0 ]
}
}
, {
"patchline" : {
"destination" : [ "obj-11", 0 ],
"disabled" : 0,
"hidden" : 0,
"source" : [ "obj-12", 0 ]
}
}
, {
"patchline" : {
"destination" : [ "obj-13", 0 ],
"disabled" : 0,
"hidden" : 0,
"midpoints" : [ 264.5, 134.0 ],
"source" : [ "obj-14", 0 ]
}
}
, {
"patchline" : {
"destination" : [ "obj-16", 1 ],
"disabled" : 0,
"hidden" : 0,
"midpoints" : [ 309.5, 58.0 ],
"source" : [ "obj-15", 0 ]
}
}
, {
"patchline" : {
"destination" : [ "obj-14", 0 ],
"disabled" : 0,
"hidden" : 0,
"source" : [ "obj-16", 0 ]
}
}
, {
"patchline" : {
"destination" : [ "obj-16", 0 ],
"disabled" : 0,
"hidden" : 0,
"source" : [ "obj-17", 0 ]
}
}
, {
"patchline" : {
"destination" : [ "obj-4", 0 ],
"disabled" : 0,
"hidden" : 0,
"source" : [ "obj-3", 0 ]
}
}
, {
"patchline" : {
"destination" : [ "obj-6", 0 ],
"disabled" : 0,
"hidden" : 0,
"source" : [ "obj-4", 0 ]
}
}
, {
"patchline" : {
"destination" : [ "obj-4", 1 ],
"disabled" : 0,
"hidden" : 0,
"midpoints" : [ 69.5, 58.0 ],
"source" : [ "obj-5", 0 ]
}
}
, {
"patchline" : {
"destination" : [ "obj-7", 0 ],
"disabled" : 0,
"hidden" : 0,
"midpoints" : [ 24.5, 134.0 ],
"source" : [ "obj-6", 0 ]
}
}
, {
"patchline" : {
"destination" : [ "obj-8", 0 ],
"disabled" : 0,
"hidden" : 0,
"midpoints" : [ 144.5, 134.0 ],
"source" : [ "obj-9", 0 ]
}
}
],
"dependency_cache" : [ ],
"embedsnapshot" : 0
}
}
Разберем всё по порядку. Основными объектами в данном патче являются:
3 тактовых генератора ([toggle] + [metro]) и 3 счетчика [counter].
[toggle]
Переключатель. Имеет два режима работы: кнопка (Button) и переключатель (Toggle). При нажатии на [toggle], объект посылает на выход значение своего состояния [0, 1]. При нажатии на «кнопку», он посылает на выход короткую единицу. Переключатель отличается тем, что запоминает свое предыдущее состояние. Нажал один раз, на выходе 0, еще раз — 1, еще раз — 0 и т.д.
[metro 500]
Данный объект представляет из себя метроном, отправляющий на свой выход каждые N-миллисекунд bang-сообщение. В данном случае bang будет генерироваться каждые 500 мс.
[counter направление мин.значение макс.значение]
Счетчик, прибавляющий свое значение на одну единицу при каждом поступающем на вход bang-сообщении. По направлению отсчета счетчики разделяются на восходящий (а), нисходящий (б) и туда-обратно (в).
Таким образом, название объекта [counter 1 0 3] говорит нам о нисходящем счетчике с границами от 0 до 3. При достижении максимального значения счетчик сбрасывается и отсчет начинается с мин. значения. У объекта есть два инлета, позволяющие сбросить счетчик. Один из них сбрасывает значение мгновенно, а другой — сбросить, начиная со следующего отсчета.
[number]
Объект ввода данных, позволяющих хранить введенную информацию. Используется только для целых чисел, для чисел с плавающей точкой есть объект [flonum].
§ 4.4. Избавляемся от лишнего
Немаловажным умением при создании программ является умение избавляться от лишних деталей. Любознательный читатель заметил бы, что в вышепреведенном патче мы используем 3 тактовых генератора и 3 выключателя, хотя можно использовать всего по одному экземпляру. Забегая вперед скажу, что чем больше объектов типа [metro] вы будете добавлять, тем медленнее может стать обработка остальной информации программой. Чтобы получить программу с хорошим быстродействием, следует задумываться об этом еще на начальной стадии.
Избавимся от лишнего, удалим избыточные вычисления из программы:
Заменяем три тактовых генератора одним, входы счетчиков соединяем с выходом объекта [metro 500]. Так-то лучше. Вы можете сказать, что так надо было поступать с самого начала, но на практике координаты блоков генератора и других объектов могут сильно отличатся, не задумываясь добавляешь еще один метроном, а потом еще и еще. В итоге связи объектов превращаются в запутанную паутину, в которой довольно сложно ориентироваться. При правильном подходе данных проблем можно избежать множественными инкапсуляциями объектов в модули, группируя их по назначению. Этого мы и будем добиваться на протяжении всего цикла статей.
§ 4.5. Другие объекты
[print]
Если по каким-то причинам вы хотите выводить информацию в консоли, то для этого используется объект [print имя_строки]. Название объекта [print b] говорит нам о том, что при поступлении сообщения на вход [print], в консоли отобразится сообщение вида b: текст.
§ 4.6. Логические операторы, регулярные выражения
Элементарные операции
[+ i/f] — сложение двух значений входных параметров объекта с возвратом результата в аутлет.
Прим.: [+ 1], [+ 1.06]. i -int — целые числа, f — float — числа с плавающей точкой.
[* i/f] — умножение
[/ i/f] — деление
[+~ 1.] — сложение двух сигналов
[*~ 1.] — умножение двух сигналов
[if условие then 1 else 0] — при выполнении условия послать на выход единицу, в ином случае — ноль.
[if условие then 1 else out2 0] — при выполнении условия послать на первый выход единицу, в ином случае — во второй выход послать ноль.
[regexp] — работа с регулярными выражениями
[expr $i1 + $i2*$i3] — выполнить выражение между значениями, поступающими на вход модуль.
Остальные операции доступны в документации, всё аналогично обычному программированию.
Сохраняем приведенный код как два разных файла с именами tobit.maxpat и frombit.maxpat в папке патчей проекта. В дальнейшем, для осуществления операций кодирования/декодирования чисел мы будем пользоваться именно этими модулями.
§ 4.7. Логические выражения
С помощью объекта [vexpr] можно задавать различные логические операции.
tut_bitlogic.maxpat — в данном примере рассматриваются побитовые логические операции многоразрядных сообщений.
§ 5. Полезные модули
Здесь я перечислю модули, которые используются мной чаще всего. Некоторые писал сам с нуля, некоторые нашел на форуме тех.поддержки. Там можно найти решение почти любого вопроса, редко когда я чего-то не находил.
[toBit]
Данный модуль переводит переводит числа от 0 до 255 в восьмиразрядный битовый код.
[fromBit]
Принимает на вход восьмиразрядный битовый код, а на выходе выдает числа от 0 до 255.
[tut_dynamicInlets N]
Пример организации входов и выходов с заданным количеством N.
Прилагаемые файлы
tut_lesson01.maxpat
tut_bitlogic.maxpat
tut_encapsulation.maxpat
tut_dynamicInlets.maxpat
toBit.maxpat
fromBit.maxpat
Все сразу (.zip) + каталог tutorial_all.maxpat
Полезные ссылки
Обзорная статья на хабре про Max MSP
Cycling '74 — сайт производителя/страница Max 7.
Max Objects Database — хранилище разнообразных объектов. Зачем изобретать велосипед, когда он уже есть.
Cycling '74 — Projects — проекты других разработчиков.
Cycling '74 — Forums — форум, где можно найти решения для многих задач, достаточно лишь правильно спросить.
Pattr.ru — здесь можно найти полезные плюшки и статьи для Max MSP.
maxmsp vk — группа русскоязычных пользователей.
В следующем номере
Список может изменится, примерный вариант:
- § Формы и способы ввода информации
- § Графики, GUI
- § Открытие HTML-страниц
- § Создание внешних ссылок
- § Пути. PATH=?
- § Вызов внешнего модуля (окна) с помощью кнопки
?
Автор: Валерий Зимнев