📜 ⬆️ ⬇️

Creating full-fledged applications on Max 7. Part 1 - Task setting, visual programming

When solving problems of information visualization, obviously, the question arises about the practical implementation of the plan. This series of articles is devoted to the process of designing applications using visual programming in Max 7 from Cycling '74. Let's start the study of the issue, from simple to complex. Let's go from amateur workpiece to a full-fledged "single-file" application (standalone .exe).

Level: easy

♞
')

Final result


Surely someone uses any program and does not realize that it was made using Max. Such a program, for example, is DAW Ableton Live. You can guess the interface objects, as well as because of Max for Live. Of course, in such serious projects not only Max is used, but also additional libraries, files, scripts, etc. Similarly, some modern programs on the iPad to work with sound. In this series of articles we will try to understand the process of designing such complex applications, starting with the most simple examples.

Table of contents


1. Visual programming
→ 1.1. Concepts
2. Max 7 interface
3. The principle of building programs in Max
→ 3.1. Modular objects with entrances and exits
→ 3.2. Encapsulation [p patchname]
→ 3.3. Self patch
→ 3.4. [bpatcher]
4. Program structure
→ 4.1. Project space
→ 4.2. The first steps
→ 4.3. Bang, Metro, Counter, Number
→ 4.4. Get rid of excess
→ 4.5. Other objects
→ 4.6. Logical operators, regular expressions
→ 4.7. Boolean expressions
5. Useful modules
Attached Files
useful links
♞

§ 1. Visual programming


Visual programming is a way to create a computer program by manipulating graphic objects instead of writing its text. [one]


This programming method will allow the reader to move away from writing lines of code in languages ​​like C / C ++, everything is represented as modules and the links between them. The development process becomes more visual and fast, while the program runs in real time while you are doing it, i.e. for any changes in the structure, the result is displayed immediately, without compilation. In fact, you build a logical flowchart of the program.

To solve the visualization problems we will use the visual programming language - Max (MSP). Basically, Max is designed to create interactive objects of art, but it can also be used in solving a huge layer of problems. In many areas, you can find something that can be visualized beautifully. Remember these spy movies in which various beautiful interfaces of unknown programs are depicted on the heroes' computers. And in reality - for example, the visualization of information from the sensors. In addition, it allows you to develop on machines running Windows, and on computers with Mac OS.

§ 1.1. Concepts


To describe the process of implementing our plans, we introduce the following concepts:



In terms of telecommunications, the module acts as a switch, which in turn performs the specified functions and sends the result to the output ports.

§ 2. Interface Max 7


Note. In this article, I will try not to go into obvious things. A lot of additional information about the work of objects and modules is available in the help - right-click on the obtetku, then "Open object_name help". Therefore, when describing program elements, preference will be given mainly to their purpose, and not to syntax or structure.

Basically, we will use the following modes: constructor and presentation, as well as look into console windows (Max console), object inspector (Inspector, CTRL + I) and patch inspector (Inspector Window, CTRL + SHIFT + I).



  1. Main menu of the program
  2. [inlet] -objects. The inputs of the module.
  3. Components (objects) of the module, the executable part.
  4. [outlet] objects. The outputs of the module.
  5. Inspector. Parameters of the selected object.
  6. Lock / unlock patch editing.
  7. Switch between designer and presentation modes.
  8. On / off grid.
  9. Program area


§ 3. The principle of building programs in Max


§ 3.1. Modular objects with entrances and exits


The process of creating programs is reduced to building a flowchart of the modules and the links between them. You can use the modules built into Max, but the most interesting thing is to write them yourself. This is very convenient when calling them from within the program, and when used in the programming process as a whole. In normal programming, this model is represented as an OOP, or one module can be represented as a single function, the parameters of which are the module [inlets] inputs, and the output is the output, and there can be many [outlets] outputs.

Note: To add an object to the patch, click 2 times with the mouse on an empty space, or press the N key, enter the name, press Enter and then you can already connect it to others.

There are several ways to add self-written modules:

- encapsulation [p patchname],
- independent patch,
- [bpatcher].

§ 3.2. Encapsulation [p patchname]


This concept is similar to the concept of encapsulation in ordinary programming languages. In this case, everything is elementary - select the objects that we want to combine into a group / module / file and press CTRL + E (Edit - Encapsulation). When encapsulation is completed in this way, the output is a module - [p patchname], where patchname is any phrase in English. Numeric characters are also available.

The merged objects after the completion of this process disappear from the Max. In fact, Max inside the original patch creates a subpatch where all the selected objects and their links are transferred. Depending on the presence of external connections, the encapsulator can automatically insert objects [inlet] inputs and outputs [outlet], as well as connect them with external objects. This is very convenient when you are developing a program without thinking about piling up objects, because when you reach a certain number, the structure of the program will no longer be readable and you yourself will become confused in it. To solve the problems of the organization of the structure of the program and designed encapsulation in Max.

Features : it is a tool for grouping / delimiting objects for various reasons. This method of creating modules assumes the uniqueness of each module. Those. if you create one module in this way and duplicate it - these will be already two different modules. Making changes to one of them will not entail a change in the remaining duplicates.

Application : unique groups of objects, simple or complex calculations, which are used in the program only in one place.

§ 3.3. Self patch


To create modules, you can use another way. It is enough to create a new file, add and connect the necessary blocks, place the objects responsible for accepting the [inlet] input, and sending it to exit - [outlet] and save the file in any folder (for example, create a separate “tools” folder and throw patches there). your prefix). Then, in the working patch, we add an object with the name [filename] (without the permission of the .maxpat file) and everything is ready.

This method can be used in combination with the encapsulation described above. It is even more convenient. You can encapsulate the necessary objects, open the resulting module (in locked mode, twice with the left mouse button on the block) and save it as an independent patch.

Features : a handy thing for creating modules of the “black box” type - once having written a module, we no longer return without the need for its structure, etc. We see only 3 types of information: inputs, module name and outputs. It is important that when duplicating such modules, their content does not become unique, i.e. changes in one of the duplicates will entail changes in all of them. More specifically, in this case, it is not the duplicate modules that are being edited, but the original module file itself.

Application : it is convenient to use this method when creating modules for any individual calculations. Features allow multiple duplicate modules.

A good example is the message translation module from the decimal system to the binary one. A simple copy-paste of the translator's abstraction objects inside the main patch in this case will be a sign of a bad tone, since it may turn out that the module requires changes. It is easier to change 1 file than to copy this whole copy-paste.

§ 3.4. [bpatcher]


If in the previous methods we created “black boxes”, then there are cases when you need to display any part of the module in open form without editing the structure. These can be various numerical visualizations, display of service messages, etc. Even more interesting is the process of adding the presentation part of the module.

This method expands the boundaries of the application of independent patches. In order to add a module to the working patch, simply create an object [bpatcher], and in the inspector of its properties specify the path to the module file.

Features : we can see the structure and communication of the module, as well as the presentation part.

Application : suitable for organizing the top level of the program structure, i.e. in essence, we display the grouped modules with one object, which is very convenient - we get rid of unnecessary clutter of wires and auxiliary objects. We see only those parts that are needed.

§ 4. Program structure


The whole process of writing a program will be reduced to numerous encapsulations and the addition of subpatches to the working patch. This is quite logical and convenient, especially when designing complex programs, where there may be many modules of various applications. In fact, we simply bury the workhorses of the program into groups deeper and deeper, and at the output we will have an elementary structure of just a few objects. If you need to make changes to any of the modules, then we simply open its file and do it without any problems.

For me personally, this way of designing programs is more intuitive. In addition, you do not need to duplicate a bunch of character code, and in some cases it is very tiring.

Illustrative examples of modules created by these methods are presented in the tut01_subpatches.maxpat file.

§ 4.1. Project space


To create complex programs with a large number of libraries, Max 7 provides for the possibility of creating projects. This function is presented in the form of a list of patches and other files used in the project. The right in miniature shows the content of the already completed large-scale project on the visualization of digital signal processing. As you can see, in the list we see not only the .maxpat patch files themselves, but also other file types.

In the future, it will be possible to use such a list “for yourself”, just to know where what lies and what modules are used in this project. Although you can compile the program even from the main patch, even from the menu with the project. The difference is that when you open a project, you can specify the patch to be opened by default - you open the project and together with the list of files, the assigned patch opens.

§ 4.2. The first steps


Let's start our thorny way of learning about programming in the Max 7 environment by creating a new project. File → New Project. It is recommended to save all projects in a separate folder, for example - C: \ Max Projects \ ... The location of the project folder in the root of the disk is extremely convenient, especially when you change the version of the program or OS.

After saving the project, a window with a list of project patches will open, but for now it is empty. Add a new file to the project:
+ → Add New File ... Save the file to the directory where the project file is located, and then Max will create the patchers folder there, in which the patch file is actually saved.

§ 4.3. Bang, Metro, Counter, Number


Let's start with something simple, for example, a counter with a clock generator. The counter will add a value to the unit only when a bang message from the clock generator (metronome) arrives at the counter input.

Bang is a special message that triggers a module / object. The completion of any operation in the module at the output gives some value, while the fact of sending data from the module to its output is accompanied by a bang message. Bang is used when you need to send any message somewhere. He as if "pushes" the message further along the path from the object to the object.

Add in our patch objects and links as in the image on the right.

Note. Max is also good that patches can be easily exchanged in text format. It happens as follows - you select all the elements and links in the patch by pressing CTRL + A, copy CTRL + C, and then open any text editor or form on the website and paste the text from the clipboard. In essence, the description of the program state (structure, data) is presented in the form of JSON text that you just copied. Reading this information is not much more difficult: in the main menu, Max 7 Files → New From Clipboard ... In general, you don’t even have to create a new file, you can paste it copied directly into an already opened patch. Thus, Max reads text from the clipboard and interprets it into modules and links between them.

Try copying the program code below and paste it into Max.
Module code 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 } } 



Let's sort everything in order. The main objects in this patch are:
3 clocks ([toggle] + [metro]) and 3 counters [counter].

[toggle]
Switch. It has two modes of operation: a button (Button) and a switch (Toggle). When you click on [toggle], the object sends the output value of its state [0, 1]. When you press the "button", it sends a short unit to the output. The switch is different in that it remembers its previous state. I pressed once, at output 0, once again - 1, again - 0, etc.

[metro 500]
This object is a metronome that sends a bang message to its output every N-millisecond. In this case, bang will be generated every 500 ms.

[counter direction min value max value]
A counter that adds its own value to one unit for each bang message received at the input. In the direction of reference, the counters are divided into ascending (a), descending (b) and back and forth (c).

Thus, the name of the object [counter 1 0 3] tells us about a downward counter with bounds from 0 to 3. When the maximum value is reached, the counter is reset and the countdown begins with a min. values. The object has two inlets that allow you to reset the counter. One of them resets the value instantly, and the other - reset, starting with the next countdown.

[number]
A data entry object that allows you to store the entered information. Used only for integers, for floating point numbers there is an object [flonum].

§ 4.4. Get rid of excess


An important skill in creating programs is the ability to get rid of unnecessary details. An inquisitive reader would have noticed that in the patch above, we use 3 clocks and 3 switches, although you can use only one copy at a time. Looking ahead, I will say that the more objects of the [metro] type you add, the slower the processing of other information by the program can become. To get a program with good speed, you should think about it at the initial stage.

We will get rid of excess, we will remove redundant calculations from the program:

We replace three clock oscillators with one, connect the meter inputs with the object output [metro 500]. That's better. You can say that it was necessary to do so from the very beginning, but in practice the coordinates of the generator blocks and other objects can be very different, without hesitation you add another metronome, and then another and more. As a result, the connection of objects turns into a tangled web, in which it is rather difficult to navigate. With the right approach, these problems can be avoided by multiple encapsulations of objects into modules, grouping them by purpose. This is what we will pursue throughout the cycle of articles.

§ 4.5. Other objects


[print]
If for some reason you want to display information in the console, then this is done using the [print string_name] object. The name of the object [print b] tells us that when a message arrives at the input [print], a message like b: text will be displayed in the console.

§ 4.6. Logical operators, regular expressions


Elementary operations

[+ i / f] - the addition of two values ​​of the input parameters of the object with the return of the result to the outlet.
Note: [+ 1], [+ 1.06]. i -int are integers, f - float are floating point numbers.

[* i / f] - multiplication
[/ i / f] - division

[+ ~ 1.] - addition of two signals
[* ~ 1.] - multiplication of two signals

[if condition then 1 else 0] - if the condition is met, send a one to the output, otherwise - zero.
[if condition then 1 else out2 0] - if the condition is met, send a unit to the first output, otherwise, send a zero to the second output.

[regexp] - working with regular expressions
[expr $ i1 + $ i2 * $ i3] - execute the expression between the values ​​input to the module.

The rest of the operations are available in the documentation, everything is the same as normal programming.

We save the above code as two different files with the names tobit.maxpat and frombit.maxpat in the project patch folder. In the future, to implement the operations of encoding / decoding numbers, we will use these modules.

§ 4.7. Boolean expressions


Using the [vexpr] object, you can specify various logical operations.

tut_bitlogic.maxpat - in this example bitwise logical operations of multi-bit messages are considered.

§ 5. Useful modules


Here I will list the modules that I use most often. Some wrote it from scratch, some found on the tech support forum. There you can find a solution to almost any issue, rarely when I did not find something.

[toBit]
This module translates converts numbers from 0 to 255 into an eight-bit bit code.

[fromBit]
It accepts an eight-bit bit code as input, and outputs from 0 to 255 at the output.

[tut_dynamicInlets N]
An example of the organization of inputs and outputs with a given number of N.

Attached Files


image tut_lesson01.maxpat
image tut_bitlogic.maxpat
image tut_encapsulation.maxpat
image tut_dynamicInlets.maxpat

image toBit.maxpat
image fromBit.maxpat

image All at once (.zip) + tutorial_all.maxpat directory

useful links


Review article on Habré about Max MSP

Cycling '74 - manufacturer's website / page Max 7.
Max Objects Database - storage of various objects. Why reinvent the wheel when it is already there.
Cycling '74 - Projects - projects of other developers.
Cycling '74 - Forums - a forum where you can find solutions for many tasks, just ask correctly.
Pattr.ru - here you can find useful buns and articles for Max MSP.
maxmsp vk is a group of Russian-speaking users.

In the next issue


The list may change, an approximate option:


♞
The author: Valery Zimnev

Source: https://habr.com/ru/post/253745/


All Articles