diff --git a/.gitignore b/.gitignore index 5efdbfb4..0a77d636 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,8 @@ sysbuild log.txt externals support -build +build/ +build-*/ *.o *.dylib @@ -18,3 +19,4 @@ latex/ *.map *.md5 +script/verinfo.rc diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..23ef0cbb --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "source/max-sdk-base"] + path = source/max-sdk-base + url = https://github.com/Cycling74/max-sdk-base diff --git a/CMakeLists.txt b/CMakeLists.txt index 045635a8..87b516d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,10 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.19) string(REGEX REPLACE "(.*)/" "" THIS_FOLDER_NAME "${CMAKE_CURRENT_SOURCE_DIR}") project(${THIS_FOLDER_NAME}) +set(CMAKE_OSX_ARCHITECTURES x86_64;arm64) + MACRO(SUBDIRLIST result curdir) FILE(GLOB children RELATIVE ${curdir} ${curdir}/*) SET(dirlist "") diff --git a/MaxAPI.pdf b/MaxAPI.pdf index 41537d71..ef4a576d 100644 Binary files a/MaxAPI.pdf and b/MaxAPI.pdf differ diff --git a/help/jit.gl.simple.help b/help/jit.gl.simple.help deleted file mode 100644 index 895a4002..00000000 Binary files a/help/jit.gl.simple.help and /dev/null differ diff --git a/help/jit.gl.simple.maxhelp b/help/jit.gl.simple.maxhelp new file mode 100644 index 00000000..f6f73d54 --- /dev/null +++ b/help/jit.gl.simple.maxhelp @@ -0,0 +1,626 @@ +{ + "patcher" : { + "fileversion" : 1, + "appversion" : { + "major" : 8, + "minor" : 2, + "revision" : 0, + "architecture" : "x64", + "modernui" : 1 + } +, + "classnamespace" : "box", + "rect" : [ 35.0, 79.0, 816.0, 585.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" : "", + "helpsidebarclosed" : 1, + "assistshowspatchername" : 0, + "boxes" : [ { + "box" : { + "id" : "obj-58", + "maxclass" : "toggle", + "numinlets" : 1, + "numoutlets" : 1, + "outlettype" : [ "int" ], + "parameter_enable" : 0, + "patching_rect" : [ 51.0, 155.0, 24.0, 24.0 ] + } + + } +, { + "box" : { + "frozen_object_attributes" : { + "rect" : [ 886, 45, 1526, 525 ] + } +, + "id" : "obj-56", + "maxclass" : "newobj", + "numinlets" : 1, + "numoutlets" : 3, + "outlettype" : [ "jit_matrix", "bang", "" ], + "patching_rect" : [ 51.0, 181.0, 49.0, 22.0 ], + "text" : "jit.world" + } + + } +, { + "box" : { + "attr" : "scale", + "id" : "obj-55", + "maxclass" : "attrui", + "numinlets" : 1, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 423.0, 92.0, 338.0, 22.0 ] + } + + } +, { + "box" : { + "attr" : "position", + "id" : "obj-54", + "maxclass" : "attrui", + "numinlets" : 1, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 423.0, 59.0, 338.0, 22.0 ] + } + + } +, { + "box" : { + "id" : "obj-51", + "maxclass" : "newobj", + "numinlets" : 1, + "numoutlets" : 2, + "outlettype" : [ "jit_gl_texture", "" ], + "patching_rect" : [ 51.0, 252.0, 160.0, 22.0 ], + "text" : "jit.gl.camera @position 0 0 4" + } + + } +, { + "box" : { + "id" : "obj-4", + "maxclass" : "toggle", + "numinlets" : 1, + "numoutlets" : 1, + "outlettype" : [ "int" ], + "parameter_enable" : 0, + "patching_rect" : [ 247.0, 341.0, 25.0, 25.0 ], + "style" : "default" + } + + } +, { + "box" : { + "id" : "obj-5", + "maxclass" : "comment", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 274.0, 343.5, 83.0, 20.0 ], + "style" : "default", + "text" : "draw as mesh" + } + + } +, { + "box" : { + "id" : "obj-12", + "maxclass" : "newobj", + "numinlets" : 1, + "numoutlets" : 2, + "outlettype" : [ "", "" ], + "patching_rect" : [ 453.0, 128.0, 131.0, 22.0 ], + "style" : "default", + "text" : "jit.gl.handle @radius 2." + } + + } +, { + "box" : { + "id" : "obj-20", + "maxclass" : "newobj", + "numinlets" : 0, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 240.0, 134.0, 50.0, 22.0 ], + "style" : "default", + "text" : "r grimy" + } + + } +, { + "box" : { + "fontface" : 0, + "fontname" : "Geneva", + "fontsize" : 9.0, + "id" : "obj-21", + "maxclass" : "jit.fpsgui", + "numinlets" : 1, + "numoutlets" : 2, + "outlettype" : [ "", "" ], + "patching_rect" : [ 66.0, 207.0, 60.0, 34.0 ], + "style" : "default" + } + + } +, { + "box" : { + "id" : "obj-22", + "maxclass" : "newobj", + "numinlets" : 1, + "numoutlets" : 0, + "patching_rect" : [ 51.0, 466.0, 51.0, 22.0 ], + "style" : "default", + "text" : "s grimy" + } + + } +, { + "box" : { + "id" : "obj-26", + "maxclass" : "message", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 634.905882352941148, 410.0, 78.0, 22.0 ], + "style" : "default", + "text" : "shininess $1" + } + + } +, { + "box" : { + "format" : 6, + "id" : "obj-27", + "maxclass" : "flonum", + "numinlets" : 1, + "numoutlets" : 2, + "outlettype" : [ "", "bang" ], + "parameter_enable" : 0, + "patching_rect" : [ 634.905882352941148, 378.0, 38.0, 22.0 ], + "style" : "default", + "triscale" : 0.9 + } + + } +, { + "box" : { + "id" : "obj-28", + "maxclass" : "toggle", + "numinlets" : 1, + "numoutlets" : 1, + "outlettype" : [ "int" ], + "parameter_enable" : 0, + "patching_rect" : [ 415.800000000000011, 378.0, 25.0, 25.0 ], + "style" : "default" + } + + } +, { + "box" : { + "id" : "obj-29", + "maxclass" : "message", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 415.800000000000011, 410.0, 113.0, 22.0 ], + "style" : "default", + "text" : "smooth_shading $1" + } + + } +, { + "box" : { + "id" : "obj-30", + "maxclass" : "message", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 106.341176470588238, 410.0, 91.0, 22.0 ], + "style" : "default", + "text" : "color 0. 1. 0. 1." + } + + } +, { + "box" : { + "id" : "obj-31", + "maxclass" : "toggle", + "numinlets" : 1, + "numoutlets" : 1, + "outlettype" : [ "int" ], + "parameter_enable" : 0, + "patching_rect" : [ 527.611764705882365, 378.0, 25.0, 25.0 ], + "style" : "default" + } + + } +, { + "box" : { + "id" : "obj-32", + "maxclass" : "message", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 527.611764705882365, 410.0, 106.0, 22.0 ], + "style" : "default", + "text" : "lighting_enable $1" + } + + } +, { + "box" : { + "id" : "obj-34", + "maxclass" : "toggle", + "numinlets" : 1, + "numoutlets" : 1, + "outlettype" : [ "int" ], + "parameter_enable" : 0, + "patching_rect" : [ 51.0, 379.0, 25.0, 25.0 ], + "style" : "default" + } + + } +, { + "box" : { + "id" : "obj-35", + "maxclass" : "message", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 51.0, 410.0, 53.0, 22.0 ], + "style" : "default", + "text" : "axes $1" + } + + } +, { + "box" : { + "id" : "obj-36", + "maxclass" : "toggle", + "numinlets" : 1, + "numoutlets" : 1, + "outlettype" : [ "int" ], + "parameter_enable" : 0, + "patching_rect" : [ 267.0, 378.0, 25.0, 25.0 ], + "style" : "default" + } + + } +, { + "box" : { + "id" : "obj-37", + "maxclass" : "toggle", + "numinlets" : 1, + "numoutlets" : 1, + "outlettype" : [ "int" ], + "parameter_enable" : 0, + "patching_rect" : [ 226.0, 377.0, 25.0, 25.0 ], + "style" : "default" + } + + } +, { + "box" : { + "id" : "obj-38", + "maxclass" : "newobj", + "numinlets" : 3, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 202.341176470588238, 410.0, 110.0, 22.0 ], + "style" : "default", + "text" : "pak poly_mode 0 0" + } + + } +, { + "box" : { + "hidden" : 1, + "id" : "obj-39", + "maxclass" : "newobj", + "numinlets" : 1, + "numoutlets" : 1, + "outlettype" : [ "bang" ], + "patching_rect" : [ 411.5, 312.0, 61.0, 22.0 ], + "style" : "default", + "text" : "loadbang" + } + + } +, { + "box" : { + "id" : "obj-41", + "maxclass" : "toggle", + "numinlets" : 1, + "numoutlets" : 1, + "outlettype" : [ "int" ], + "parameter_enable" : 0, + "patching_rect" : [ 316.411764705882376, 378.0, 25.0, 25.0 ], + "style" : "default" + } + + } +, { + "box" : { + "id" : "obj-42", + "maxclass" : "message", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 316.411764705882376, 410.0, 98.0, 22.0 ], + "style" : "default", + "text" : "depth_enable $1" + } + + } +, { + "box" : { + "id" : "obj-46", + "maxclass" : "newobj", + "numinlets" : 1, + "numoutlets" : 2, + "outlettype" : [ "jit_matrix", "" ], + "patching_rect" : [ 240.0, 196.0, 68.0, 22.0 ], + "style" : "default", + "text" : "jit.gl.simple" + } + + } +, { + "box" : { + "hidden" : 1, + "id" : "obj-48", + "maxclass" : "message", + "numinlets" : 2, + "numoutlets" : 1, + "outlettype" : [ "" ], + "patching_rect" : [ 414.0, 336.0, 18.0, 22.0 ], + "style" : "default", + "text" : "1" + } + + } + ], + "lines" : [ { + "patchline" : { + "destination" : [ "obj-46", 0 ], + "midpoints" : [ 462.5, 180.0, 249.5, 180.0 ], + "source" : [ "obj-12", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-46", 0 ], + "midpoints" : [ 249.5, 174.0, 249.5, 174.0 ], + "source" : [ "obj-20", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-22", 0 ], + "midpoints" : [ 644.405882352941148, 445.0, 60.5, 445.0 ], + "source" : [ "obj-26", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-26", 0 ], + "source" : [ "obj-27", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-29", 0 ], + "source" : [ "obj-28", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-22", 0 ], + "midpoints" : [ 425.300000000000011, 445.0, 60.5, 445.0 ], + "source" : [ "obj-29", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-22", 0 ], + "midpoints" : [ 115.841176470588238, 447.0, 60.5, 447.0 ], + "source" : [ "obj-30", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-32", 0 ], + "source" : [ "obj-31", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-22", 0 ], + "midpoints" : [ 537.111764705882365, 445.0, 60.5, 445.0 ], + "source" : [ "obj-32", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-35", 0 ], + "source" : [ "obj-34", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-22", 0 ], + "midpoints" : [ 60.5, 447.0, 60.5, 447.0 ], + "source" : [ "obj-35", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-38", 2 ], + "source" : [ "obj-36", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-38", 1 ], + "source" : [ "obj-37", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-22", 0 ], + "midpoints" : [ 211.841176470588238, 447.0, 60.5, 447.0 ], + "source" : [ "obj-38", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-48", 0 ], + "hidden" : 1, + "source" : [ "obj-39", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-36", 0 ], + "midpoints" : [ 256.5, 377.0, 276.5, 377.0 ], + "order" : 0, + "source" : [ "obj-4", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-37", 0 ], + "midpoints" : [ 256.5, 376.0, 235.5, 376.0 ], + "order" : 1, + "source" : [ "obj-4", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-42", 0 ], + "source" : [ "obj-41", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-22", 0 ], + "midpoints" : [ 325.911764705882376, 445.0, 60.5, 445.0 ], + "source" : [ "obj-42", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-28", 0 ], + "hidden" : 1, + "midpoints" : [ 423.5, 362.0, 425.300000000000011, 362.0 ], + "order" : 1, + "source" : [ "obj-48", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-31", 0 ], + "hidden" : 1, + "midpoints" : [ 423.5, 364.0, 537.111764705882365, 364.0 ], + "order" : 0, + "source" : [ "obj-48", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-41", 0 ], + "hidden" : 1, + "midpoints" : [ 423.5, 366.0, 325.911764705882376, 366.0 ], + "order" : 2, + "source" : [ "obj-48", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-46", 0 ], + "midpoints" : [ 432.5, 180.0, 249.5, 180.0 ], + "source" : [ "obj-54", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-46", 0 ], + "midpoints" : [ 432.5, 179.5, 249.5, 179.5 ], + "source" : [ "obj-55", 0 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-21", 0 ], + "source" : [ "obj-56", 1 ] + } + + } +, { + "patchline" : { + "destination" : [ "obj-56", 0 ], + "source" : [ "obj-58", 0 ] + } + + } + ], + "dependency_cache" : [ { + "name" : "jit.gl.simple.mxo", + "type" : "iLaX" + } + ], + "autosave" : 0 + } + +} diff --git a/html/chapter_anatomy.html b/html/chapter_anatomy.html index a896ffbc..f43686ed 100644 --- a/html/chapter_anatomy.html +++ b/html/chapter_anatomy.html @@ -111,7 +111,7 @@
Your structure declaration will be used in the prototypes to functions you declare, so you'll need to place above these prototypes.
In order for Max to call the ext_main() function on your compiled external, that function must be "exported" or made public. This is accomplished by using the C74_EXPORT macro in the prototype of the ext_main() function, which is provided for you automatically in the "ext.h" header file.
class_new() creates a class with the new instance routine (see below), a free function (in this case there isn't one, so we pass NULL), the size of the structure, a no-longer used argument, and then a description of the arguments you type when creating an instance (in this case, there are no arguments, so we pass 0).
class_addmethod() binds a C function to a text symbol. The two methods defined here are int and bang.
@@ -180,8 +180,8 @@The post() function is similar to printf(), but puts the text in the Max window. post() is very helpful for debugging, particularly when you cannot stop user interaction or real-time computation to look at something in a debugger.
You can also add a float message, which is invoked when a floating-point number is sent to your object. Add the following to your initialization routine:
-Then write the method that receives the floating-point value as follows:
To illustrate the use of symbols and atoms, here is how you would send a message out an outlet. Let's say we want to send the message "green 43 crazy 8.34." This message consists of a selector "green" plus an array of three atoms.
First, we'll need to create a generic outlet with outlet_new in our new instance routine.
The second argument being NULL indicates that the outlet can be used to send any message. If the second argument had been a character string such as "int" or "set" only that specific message could be sent out the outlet. You'd be correct if you wondered whether intout() is actually just outlet_new(x, "int").
Now that we have our generic outlet, we'll call outlet_anything() on it in a method. The first step, however, is to assemble our message, with a selector "green" plus an array of atoms. Assigning ints and floats to an atom is relatively simple, but to assign a symbol, we need to transform a character string into a symbol using gensym(). The gensym() function returns a pointer to a symbol that is guaranteed to be unique for the string you supply. This means the string is compared with other symbols to ensure its uniqueness. If it already exists, gensym() will supply a pointer to the symbol. Otherwise it will create a new one and store it in a table so it can be found the next time someone asks for it.
In the call to outlet_anything() above, gensym("green") represents the message selector. The outlet_anything() function will try to find a message "green" in each of the objects connected to the outlet. If outlet_anything() finds such a message, it will execute it, passing it the array of atoms it received.
If it cannot find a match for the symbol green, it does one more thing, which allows objects to handle messages generically. Your object can define a special method bound to the symbol "anything" that will be invoked if no other match is found for a selector. We'll discuss the anything method in a moment, but first, we need to return to class_addmethod() and explain the final arguments it accepts.
To access atoms, you can use the functions atom_setlong(), atom_getlong() etc. or you can access the t_atom structure directly. We recommend using the accessor functions, as they lead to both cleaner code and will permit your source to work without modifications when changes to the t_atom structure occur over time.
In the simp example, you saw the int method defined as follows:
In the simp example, you saw the int method defined as follows:
The A_LONG, 0 arguments to class_addmethod() specify the type of arguments expected by the C function you have written. A_LONG means that the C function accepts a long integer argument. The 0 terminates the argument specifier list, so for the int message, there is a single long integer argument.
The other options are A_FLOAT for doubles, A_SYM for symbols, and A_GIMME, which passes the raw list of atoms that were originally used to send the Max message in the first place. These argument type specifiers define what are known as "typed" methods in Max. Typed methods are those where Max checks the type of each atom in a message to ensure it is consistent with what the receiving object has said it expects for a given selector.
If the atoms cannot be coerced into the format of the argument type specifier, a bad arguments error is printed in the Max window.
@@ -129,7 +129,7 @@A method that uses A_GIMME is declared as follows:
The symbol argument s is the message selector. Ordinarily this might seem redundant, but it is useful for the "anything" method as we'll discuss below.
argc is the number of atoms in the argv array. It could be 0 if the message was sent without arguments. argv is the array of atoms holding the arguments.
For typed messages, the atoms will be of type A_SYM, A_FLOAT, or A_LONG. Here is an example of a method that merely prints all of the arguments.
@@ -163,15 +163,15 @@You can interpret the arguments in whatever manner you wish. You cannot, however, modify the arguments as they may be about to be passed to another object.
As previously mentioned, your object can define a special method bound to the symbol "anything" that will be invoked if no other match is found for a selector. For example:
As previously mentioned, your object can define a special method bound to the symbol "anything" that will be invoked if no other match is found for a selector. For example:
Your function definition for an anything method follows the same pattern as for all other A_GIMME methods:
Your object can specify the file types accepted as well as a message that will be sent when the user releases the mouse button with the file on top of the object. UI and non-UI objects use the same interface to drag'n'drop.
Example UI object: pictmeter~. Example non-UI: TBD.
Messages to support:
Sent to an object during a drag when the mouse is over the object in an unlocked patcher.
Sent to an object during a drag when the mouse is over the object in a locked patcher.
Why two different scenarios? acceptsdrag_unlocked() can be thought of as an "editing" operation. For example, objects such as pictslider accept new image files for changing their appearance when the patcher is unlocked, but not when the patcher is locked. By contrast, sfplay~ can accept audio files for playback in either locked or unlocked patchers, since that is something you can do with a message (rather than an editing operation that changes the patcher).
Message handler definitions:
The handlers return true if the file(s) contained in the drag can be used in some way by the object. To test the filetypes, use jdrag_matchdragrole() passing in the drag object and a symbol for the file type. Here is list of pre-defined file types:
Presets are a simple state-saving mechanism. Your object receives a preset message when state is being saved. You respond by creating a message that will be sent back to your object when the preset is recalled.
For more powerful and general state-saving, use the pattr system described below.
To support saving a single integer in a preset, you can use the preset_int() convenience function. The preset_int() function records an int message with the value you pass it in the preset, to be sent back to your object at a later time.
-More generally, you can use preset_store(). Here is an example of storing two values (m_xvalue and m_yvalue) in a list.
To show descriptions of your object's inlets and outlets while editing a patcher, your object can respond to the assist message with a function that copies the text to a string.
-The function below has two inlets and one outlet. The io argument will be 1 for inlets, 2 for outlets. The index argument will be 0 for the leftmost inlet or outlet. You can copy a maximum of 512 characters to the output string s. You can use strncpy_zero() to copy the string, or if you want to format the assistance string based on a current value in the object, you could use snprintf_zero().
Objects such as operators (+, -, etc.) and the int object have inlets that merely store values rather than performing an operation and producing output. These inlets are labeled with a blue color to indicate they are "cold" rather than action-producing "hot" inlets. To implement this labeling, your object can respond to the inletinfo message.
-If all of your object's non-left inlets are "cold" you can use the function stdinletinfo() instead of writing your own, as shown below:
If all of your object's non-left inlets are "cold" you can use the function stdinletinfo() instead of writing your own, as shown below:
To write your own function, just look at the index argument (which is 0 for the left inlet). This example turns the third inlet cold. You don't need to do anything for "hot" inlets.
Objects such as coll and text display a text editor window when you double-click. Users can edit the contents of the objects and save the updated data (or not). Here's how to do the same thing in your object.
First, if you want to support double-clicking on a non-UI object, you can respond to the dblclick message.
-Initialize the m_editor field to NULL in your new instance routine. Then implement the dblclick method as follows:
The title attribute sets the window title of the text editor.
When the user closes the text window, your object (or the object you passed as an argument when creating the editor) will be sent the edclose message.
When the user closes the text window, your object (or the object you passed as an argument when creating the editor) will be sent the edclose message.
The edclose method is responsible for doing something with the text. It should also zero the reference to the editor stored in the object, because it will be freed. A pointer to the text pointer is passed, along with its size. The encoding of the text is always UTF-8.
Each time the user chooses Save, your object will receive an edsave message. If you return zero from your edsave method, the editor will proceed with saving the text in a file. If you return non-zero, the editor assumes you have taken care of saving the text. The general idea is that when the user wants to save the text, it is either updated inside your object, updated in a file, or both. As an example, the js object uses its edsave message to trigger a recompile of the Javascript code. But it also returns 0 from its edsave method so that the text editor will update the script file. Except for the return value, the prototype of the edsave method is identical to the edclose method.
-These routines permit you to search for files, show file open and save dialogs, as well as open, read, write, and close them. The file API is based around a "path identifier" – a number that describes the location of a file. When searching or reading a file, path identifiers can be either a folders or collectives. Path identifiers that are negative (or zero) describe actual folders in the computer's file system, while path identifiers that are positive refer to collectives.
A basic thing you might want to do make your object accept the read message in a manner similar to existing Max objects. If the word read is followed by no arguments, a file dialog appears for the user to choose a file. If read is followed by an argument, your object will search for the file. If a file is found (or chosen), your object will open it and read data from it.
First, make your object accept the read message. The simplest way to make the filename argument optional is to use the A_DEFSYM argument type specifier. When the symbol argument is not present, Max passes your method the empty symbol.
-The next requirement for any method that reads files is that it must defer execution to the low-priority thread, as shown in the following implementation, where the filename argument is passed as the symbol argument to defer.
The myobject_doread() function compares the filename argument with the empty symbol – if the argument was not supplied, the open_dialog() is used, otherwise, we call locatefile_extended() to search for the file. This object looks for text files, so we use a four-character code 'TEXT' as our file type to either open or locate. File type codes define a set of acceptable extensions. The file max-fileformats.txt permits contains standard definitions, and you can add your own by creating a similar text file and placing it in the init folder inside the Cycling '74 folder.
To open and read files, you can use the cross-platform sysfile API. Files can be opened using a filename plus path identifier. If successfully opened, the file can be accessed using a t_filehandle. Note that "files" inside collective files are treated identically to regular files, with the exception that they are read-only.
Some Max objects respond to the write message to save data into a file. If there is no argument present after the word write, a save file dialog is shown and the user specifies a file name and location. If an argument is present, it can either specify a complete path name or a filename. In the filename case, the file is written to the current "default" directory, which is the location where a patcher was last opened. In the full pathname case, the file is written to the location specified by the pathname.
Here's how to implement this behavior. We'll show how to handle the message arguments, then provide text and data file writing examples.
Message and argument handling is very similar to the way we implemented the read message above, including the use of deferred execution.
-The myobject_dowrite() function compares the filename argument with the empty symbol – if the argument was not supplied, saveasdialog_extended() is used to obtain the user's choice for filename and location. Our first example looks for text files, so we use a four-character code 'TEXT' as our file type for saving. File type codes define a set of acceptable extensions. The file max-fileformats.txt permits contains standard definitions, and you can add your own by creating a similar text file and placing it in the init folder inside the Cycling '74 folder.
Proper use of an inlet involves two steps: first, add a method that will respond to the message sent via the inlet in your initialization routine, and second, create the inlet in your new instance routine. (Creating inlets at any other time is not supported.)
There are three types of inlets: int, float, and custom. We'll only describe int and float inlets here because proxies are generally a better way to create an inlet that can respond to any message. For int inlets, you'll bind a function to a message "in1", "in2", "in3" etc. depending on the inlet number you assign. Here's how to create a single inlet using "in1"...
-In your initialization routine:
In your initialization routine:
In your new instance routine, after calling object_alloc() to create your instance:
The method that will be called when an int is received in the right inlet:
Creating a single inlet in this way gives your object two inlets (remember that it always has one by default). If you want to create multiple inlets, you'll need to create them in order from right to left, as shown below:
Inlets that send float messages to your object are created with floatin() and translate the float message into "ft1","ft2","ft3" etc. Example:
-In initialization routine:
In initialization routine:
In new instance routine:
Method:
Then we'll create the outlets in our new instance routine.
Next, in your ext_main() routine, you'll create attributes associated with the time object using the class_time_addattr() function.
Next, in your ext_main() routine, you'll create attributes associated with the time object using the class_time_addattr() function.
The second argument, "delaytime", is a string that names the attribute. Users of your object will be able to change the delay value by sending a delaytime message. "Delay Time" is the label users see for the attribute in the inspector. The flags argument permits you to customize the type of time object you'd like. TIME_FLAGS_TICKSONLY means that the object can only be specified in tempo-relative units. You would not use this flag if you want the object to use the regular Max scheduler if the user specifies an absolute time (such as milliseconds). TIME_FLAGS_USECLOCK means that it is a time object that will actually schedule events. If you do not use this flag, you can use the time object to hold and convert time values, which you use to schedule events manually. TIME_FLAGS_TRANSPORT means that an additional attribute for specifying the transport name is added to your object automatically (it's called "transport" and has the label "Transport Name"). The combination of flags above is appropriate for an object that will be scheduling events on a temporary basis that are only synchronized with the transport and specified in tempo-relative units.
+ +The second argument, "delaytime", is a string that names the attribute. Users of your object will be able to change the delay value by sending a delaytime message. "Delay Time" is the label users see for the attribute in the inspector. The flags argument permits you to customize the type of time object you'd like. TIME_FLAGS_TICKSONLY means that the object can only be specified in tempo-relative units. You would not use this flag if you want the object to use the regular Max scheduler if the user specifies an absolute time (such as milliseconds). TIME_FLAGS_USECLOCK means that it is a time object that will actually schedule events. If you do not use this flag, you can use the time object to hold and convert time values, which you use to schedule events manually. TIME_FLAGS_TRANSPORT means that an additional attribute for specifying the transport name is added to your object automatically (it's called "transport" and has the label "Transport Name"). The combination of flags above is appropriate for an object that will be scheduling events on a temporary basis that are only synchronized with the transport and specified in tempo-relative units.
The next step is to create a time object in your new instance routine using time_new. The time_new function is something like clock_new – you pass it a task function that will be executed when the scheduler reaches a certain time (in this case, delay2simple_tick, which will send out a bang). The first argument to time_new is a pointer to your object, the second is the name of the attribute created via class_time_addattr, the third is your task function, and the fourth are flags to control the behavior of the time object, as explained above for class_time_addattr.
Finally, we use time_setvalue to set the initial delay value to 0.
To make a delayed bang, we need a delay2simple_bang function that causes our time object to put its task function into the ITM scheduler. This is accomplished using time_schedule. Note that unlike the roughly equivalent clock_fdelay, where the delay time is an argument, the time value must already be stored inside the time object using time_setvalue. The second argument to time_schedule is another time object that can be used to control quantization of an event. Since we aren't using quantization in this simple version of delay2, we pass NULL.
In the initialization routine, we'll define a quantization time attribute to work in conjunction with the d_quantize time object we'll be creating. This attribute does not have its own clock to worry about. It just holds a time value, which we specify will only be in ticks (quantizing in milliseconds doesn't make sense in the ITM context). If you build delay2 and open the inspector, you will see time attributes for both Delay Time and Quantization.
In the initialization routine, we'll define a quantization time attribute to work in conjunction with the d_quantize time object we'll be creating. This attribute does not have its own clock to worry about. It just holds a time value, which we specify will only be in ticks (quantizing in milliseconds doesn't make sense in the ITM context). If you build delay2 and open the inspector, you will see time attributes for both Delay Time and Quantization.
Here is part of the revised delay2 new instance routine. It now creates two time objects, plus a regular clock object.
To use the quantization time object, we can pass it as the second argument to time_schedule. If the value of the quantization is 0, there is no effect. Otherwise, time_schedule will move the event time so it lies on a quantization boundary. For example, if the quantization value is 4n (480 ticks), the delay time is 8n (240 ticks) and current time is 650 ticks, the delay time will be adjusted so that the bang comes out of the delay2 object at 980 ticks instead of 890 ticks.
@@ -187,18 +187,18 @@A permanent event in ITM is one that has been scheduled to occur when the transport reaches a specific time. You can schedule a permanent event in terms of ticks or bars/beats/units. An event based in ticks will occur when the transport reaches the specified tick value, and it will not be affected by changes in time signature. An event specified for a time in bars/beats/units will be affected by the time signature. As an example, consider an event scheduled for bar 2, beat 1, unit 0. If the time signature of the ITM object on which the event has been scheduled is 3/4, the event will occur at 480 times 3 or 1440 ticks. But if the time signature is 4/4, the event will occur at 1920 ticks. If, as an alternative, you had scheduled the event to occur at 1920 ticks, setting the time signature to 3/4 would not have affected when it occurred.
You don't "schedule" a permanent event. Once it is created, it is always in an ITM object's list of permanent events. To specify when the event should occur, use time_setvalue.
-The high-level time object interface handles permanent events. Let's say we want to have a time value called "targettime." First, we declare an attribute using class_time_addattr. The flags used are TIME_FLAGS_TICKSONLY (required because you can't specify a permanent event in milliseconds), TIME_FLAGS_LOCATION (which interprets the bar/beat/unit times where 1 1 0 is zero ticks), TIME_FLAGS_PERMANENT (for a permanent event), and TIME_FLAGS_TRANSPORT (which adds a transport attribute permitting a user to choose a transport object as a destination for the event) and TIME_FLAGS_POSITIVE (constrains the event to happen only for positive tick and bar/beat/unit values).
-The TIME_FLAGS_TRANSPORT flag is particularly nice. Without any intervention on your part, it creates a transport attribute for your object, and takes care of scheduling the permanent event on the transport the user specifies, with a default value of the global ITM object. If you want to cause your event to be rescheduled dynamically when the user changes the transport, your object can respond to the reschedule message as follows.
-The high-level time object interface handles permanent events. Let's say we want to have a time value called "targettime." First, we declare an attribute using class_time_addattr. The flags used are TIME_FLAGS_TICKSONLY (required because you can't specify a permanent event in milliseconds), TIME_FLAGS_LOCATION (which interprets the bar/beat/unit times where 1 1 0 is zero ticks), TIME_FLAGS_PERMANENT (for a permanent event), and TIME_FLAGS_TRANSPORT (which adds a transport attribute permitting a user to choose a transport object as a destination for the event) and TIME_FLAGS_POSITIVE (constrains the event to happen only for positive tick and bar/beat/unit values).
+The TIME_FLAGS_TRANSPORT flag is particularly nice. Without any intervention on your part, it creates a transport attribute for your object, and takes care of scheduling the permanent event on the transport the user specifies, with a default value of the global ITM object. If you want to cause your event to be rescheduled dynamically when the user changes the transport, your object can respond to the reschedule message as follows.
+All you need to do in your reschedule method is just act as if the user has changed the time value, and use the current time value to call time_setvalue.
In your new instance routine, creating a permanent event with time_new uses the same flags as were passed to class_time_addattr:
-The task called by the permanent time object is identical to a clock task or an ITM temporary event task.
The first thing you must do is define your Max class object struct. As is typical, for standard Max objects the first entry of the object struct must be of type t_object; for UI objects, it must be of type t_jbox; for MSP objects, it must be of type t_pxobject; and for MSP UI objects, it must be of type t_pxjbox. For more information on these different Max object types, please consult the Max developer documentation. Jitter objects can be wrapped within any of these object types.
diff --git a/html/chapter_jit_mopdetails.html b/html/chapter_jit_mopdetails.html index a473e2d3..bb85d630 100644 --- a/html/chapter_jit_mopdetails.html +++ b/html/chapter_jit_mopdetails.html @@ -96,7 +96,7 @@You can specify variable input/output MOPs with a negative argument for input and/or outputs when constructing your jit_mop object. When the using variable inputs and/or outputs, there is not a jit_mop_io for each input and/or output within your class definition, and therefore the template type, dim, planecount, and linking attributes are not settable. If anything but the default behavior is required, you must accomplished it in another way — for example, either by overriding the jit_matrix method of the MOP Max wrapper class, or defining an mproc method to be called from within the standard jit_matrix method of the MOP Max wrapper class. The jit.pack, jit.unpack, jit.scissors, and jit.glue objects are a few SDK examples of MOPs with variable inputs and outputs. More information on overriding the jit_matrix, mproc, and other default methods of the MOP Max wrapper class is covered later in this chapter.
@@ -168,7 +168,7 @@The entry point of the MOP Jitter class is the matrix_calc method, which is passed a list of matrices for the input, and a list of matrices for the output. It is not the responsibility of the matrix_calc method to perform any copying and adaptation behavior, but rather simply ensure that the matrices are valid, compatible, and if so, process. Certain objects may modify the dim, type, or planecount of the output matrices — e.g. the SDK project, jit.thin. However, it is the calling party's responsibility to perform any copying and conformance to MOP I/O restrictions as defined by the jit_mop_io objects—i.e. either the Max wrapper class, or the C, Java, or Javascript code which calls the matrix_calc method.
@@ -214,12 +214,12 @@And here is an example of calling the getinfo method to fill out the t_jit_matrix_info struct:
Important Note: If you aren't sure if your object is a pointwise operator, or don't fully understand how to make your algorithm parallelizable, you shouldn't use the parallel utility functions in your object. You should simply call the function directly.
This function is at the heart of the logic you will add in your own custom object. Since there is no "right way" to process this data, we won't cover any more code listings for the recursive N-dimensional processing function. However, the SDK projects that are good examples include: jit.clip, which performs a planar independent, pointwise operation (limiting numbers to some specified range); jit.rgb2luma, which performs a planar dependent, pointwise operation (converting RGB color to luminance); and jit.transpose, which performs a planar independent, spatial operation (rows become columns). For more ideas about N-dimensional matrix processing, we would recommend reading one of the several books available on 2D signal processing and/or image processing. Most of these concepts are easily generalized to higher dimensions.
MOP Max wrapper classes typically have a large amount of default behavior, as setup through the max_jit_classex_mop_wrap function, based on the jit_mop Jitter class adornment, and user specified flags. You can either override all of the default behavior or just specific features. If you wish to override all of the default behavior, you can use the flag MAX_JIT_MOP_FLAGS_OWN_ALL, when calling the max_jit_classex_mop_wrap() function. If you need to make use of the jit_mop adornment(), the jit_mop can be looked up by calling the jit_class_adornment_get() method on the Jitter class. The jit_mop_io inputs and outputs can be queried and their attributes inspected, similar to how they were set in the MOP Jitter class definition, described earlier in this chapter. Here is an example of how to look up the jit_mop adornment of the jit.scalebias object:
+MOP Max wrapper classes typically have a large amount of default behavior, as setup through the max_jit_classex_mop_wrap function, based on the jit_mop Jitter class adornment, and user specified flags. You can either override all of the default behavior or just specific features. If you wish to override all of the default behavior, you can use the flag MAX_JIT_MOP_FLAGS_OWN_ALL, when calling the max_jit_classex_mop_wrap() function. If you need to make use of the jit_mop adornment(), the jit_mop can be looked up by calling the jit_class_adornment_get() method on the Jitter class. The jit_mop_io inputs and outputs can be queried and their attributes inspected, similar to how they were set in the MOP Jitter class definition, described earlier in this chapter. Here is an example of how to look up the jit_mop adornment of the jit.scalebias object:
By default, a jit_matrix method is added which automatically manages matrix copying and calculation based on the incoming data. Most typical MOPs simply use the default jit_matrix method. However there are instances where it is necessary to override the default MOP method to get special behavior, such as recording which matrix input data is being input to as is the case for the jit.op SDK example, or to do something other than standard copying and adaptation as is the case for the jit.pack or jit.str.op SDK examples, or to prevent any jit_matrix method at all, as is the case for the jit.noise SDK example. To prevent the default jit_matrix method from being defined, you can use the flag MAX_JIT_MOP_FLAGS_OWN_JIT_MATRIX, when calling the max_jit_classex_mop_wrap() function. To define your own jit_matrix method, you can add an A_GIMME method bound to the symbol jit_matrix, in your ext_main() function. Here's an example from jit.op:
+By default, a jit_matrix method is added which automatically manages matrix copying and calculation based on the incoming data. Most typical MOPs simply use the default jit_matrix method. However there are instances where it is necessary to override the default MOP method to get special behavior, such as recording which matrix input data is being input to as is the case for the jit.op SDK example, or to do something other than standard copying and adaptation as is the case for the jit.pack or jit.str.op SDK examples, or to prevent any jit_matrix method at all, as is the case for the jit.noise SDK example. To prevent the default jit_matrix method from being defined, you can use the flag MAX_JIT_MOP_FLAGS_OWN_JIT_MATRIX, when calling the max_jit_classex_mop_wrap() function. To define your own jit_matrix method, you can add an A_GIMME method bound to the symbol jit_matrix, in your ext_main() function. Here's an example from jit.op:
The jit.pack and jit.str.op examples are a bit more involved and also better illustrate the kinds of tasks the default jit_matrix method performs.
A MOP Max wrapper class typically has a bang and outputmatrix method. These two methods are typically equivalent, and by default, both send out the most recently calcuated matrix output. Certain objects that don't have a matrix output, like the jit.3m SDK example, typcially override these messages with their own bang and sometimes outputmatrix method. These methods can be overridden by using the MAX_JIT_MOP_FLAGS_OWN_BANG and MAX_JIT_MOP_FLAGS_OWN_OUTPUTMATRIX flags when calling the max_jit_classex_mop_wrap() function. These flags are typically both passed in together.
+A MOP Max wrapper class typically has a bang and outputmatrix method. These two methods are typically equivalent, and by default, both send out the most recently calcuated matrix output. Certain objects that don't have a matrix output, like the jit.3m SDK example, typcially override these messages with their own bang and sometimes outputmatrix method. These methods can be overridden by using the MAX_JIT_MOP_FLAGS_OWN_BANG and MAX_JIT_MOP_FLAGS_OWN_OUTPUTMATRIX flags when calling the max_jit_classex_mop_wrap() function. These flags are typically both passed in together.
For each input and output, other than the leftmost input, there is, by default, an attribute added to query and set that input or output's matrix attributes, including name, type, dim, and planecount. While overriding the default attribute behavior is conceivably necessary to perform very specialized behavior, it is not used by any of the SDK examples. To prevent the addition of the default attributes for name, type, dim, and planecount, you can use the MAX_JIT_MOP_FLAGS_OWN_NAME, MAX_JIT_MOP_FLAGS_OWN_TYPE, MAX_JIT_MOP_FLAGS_OWN_DIM, and MAX_JIT_MOP_FLAGS_OWN_PLANECOUNT flags when calling the max_jit_classex_mop_wrap() function. To define your own attributes, you would follow the same means of defining any attributes for a Max wrapper class with the appropriate attribute name you wish to override.
+For each input and output, other than the leftmost input, there is, by default, an attribute added to query and set that input or output's matrix attributes, including name, type, dim, and planecount. While overriding the default attribute behavior is conceivably necessary to perform very specialized behavior, it is not used by any of the SDK examples. To prevent the addition of the default attributes for name, type, dim, and planecount, you can use the MAX_JIT_MOP_FLAGS_OWN_NAME, MAX_JIT_MOP_FLAGS_OWN_TYPE, MAX_JIT_MOP_FLAGS_OWN_DIM, and MAX_JIT_MOP_FLAGS_OWN_PLANECOUNT flags when calling the max_jit_classex_mop_wrap() function. To define your own attributes, you would follow the same means of defining any attributes for a Max wrapper class with the appropriate attribute name you wish to override.
By default, a clear and a notify method are added. The default clear method clears each of the input and output matrices. The default notify method, max_jit_mop_notify(), is called whenever any of the matrices maintained by the MOP are changed. If it is necessary to respond to additional notifications, it is important to call the max_jit_mop_notify function so that the MOP can perform any necessary maintenance with respect to input and output matrices, as demonstrated by the jit.notify SDK example. These methods can be overridden using the MAX_JIT_MOP_FLAGS_OWN_CLEAR and MAX_JIT_MOP_FLAGS_OWN_NOTIFY flags, respectively, when calling the max_jit_classex_mop_wrap() function. Object registration and notification is covered in detail in a future chapter, but the jit.notify notify method is provided as an example.
+By default, a clear and a notify method are added. The default clear method clears each of the input and output matrices. The default notify method, max_jit_mop_notify(), is called whenever any of the matrices maintained by the MOP are changed. If it is necessary to respond to additional notifications, it is important to call the max_jit_mop_notify function so that the MOP can perform any necessary maintenance with respect to input and output matrices, as demonstrated by the jit.notify SDK example. These methods can be overridden using the MAX_JIT_MOP_FLAGS_OWN_CLEAR and MAX_JIT_MOP_FLAGS_OWN_NOTIFY flags, respectively, when calling the max_jit_classex_mop_wrap() function. Object registration and notification is covered in detail in a future chapter, but the jit.notify notify method is provided as an example.
By default, adapt and outputmode attributes are added to the MOP Max Wrapper. These attributes determine whether or not to adapt to incoming matrix attributes, and whether or not the output should calculate a new output matrix, output the last calculated matrix (freeze), pass on the input matrix (bypass). To prevent the addition of the default attributes for adapt and outputmode, you can use the MAX_JIT_MOP_FLAGS_OWN_ADAPT, and MAX_JIT_MOP_FLAGS_OWN_OUTPUTMODE flags when calling the max_jit_classex_mop_wrap() function. To define your own attributes, you would follow the same means of defining any attributes for a Max wrapper class with the appropriate attribute name you wish to override.
+By default, adapt and outputmode attributes are added to the MOP Max Wrapper. These attributes determine whether or not to adapt to incoming matrix attributes, and whether or not the output should calculate a new output matrix, output the last calculated matrix (freeze), pass on the input matrix (bypass). To prevent the addition of the default attributes for adapt and outputmode, you can use the MAX_JIT_MOP_FLAGS_OWN_ADAPT, and MAX_JIT_MOP_FLAGS_OWN_OUTPUTMODE flags when calling the max_jit_classex_mop_wrap() function. To define your own attributes, you would follow the same means of defining any attributes for a Max wrapper class with the appropriate attribute name you wish to override.
For many types of operations, it's not required to fully override the default jit_matrix method and any adaptation. If your object simply needs to override the way in which the Jitter class' matrix_calc method and outlet functions are called, you can do so by defining an mproc method, which will be called instead of the default behavior. The jit.3m SDK project is an example where after it calls the Jitter class' matrix_calc method, it queries the Jitter class' attributes and outputs max messages rather than the default jit_matrix message output.
@@ -353,8 +353,8 @@As we discussed in the Matrix Operator Quick Start, inside your Max class' constructor you need to allocate the matrices necessary for the MOP inputs and outputs, the corresponding matrix inlets and outlets, process matrix arguments and other MOP setup. And in your destructor, you need to free oup MOP resources. Typically you would accomplish this all with the standard max_jit_mop_setup_simple() and max_jit_mop_free() functions, however there are some instances where you may need to introduce custom behavior.
@@ -434,10 +434,10 @@The max_jit_mop_setup_simple() function calls max_jit_mop_matrix_args() to read any matrix arguments, and if present send them to any linked inputs/outputs and disable the adapt attribute. The listing is provided below to illustrate the default behavior.
@@ -524,11 +524,11 @@Once you have created your jit_mop instance, and configured it according to the needs of your object, you add it as an adornment to your Jitter class with the jit_class_add_adornment() function. Adornments are one way for Jitter objects to have additional information, and in some instances behavior, tacked onto an existing class. Adornments will be discussed in detail in a later chapter.
You also want to define your matrix calculation method, where most of the work of a Matrix Operator occurs, with the jit_class_addmethod() function as a private, untyped method bound to the symbol matrix_calc.
You don't need to add anything special to your Matrix Operator's constructor or destructor, aside from the standard initialization and cleanup any Jitter object would need to do. Any internal matrices for input and outputs are maintained, and only required, by the Max wrapper's asynchronous interface. The Jitter MOP contains no matrices for inputs and outputs, but rather expects that the matrix calculation method is called with all inputs and outputs synchronously. When used from languages like C, Java, and JavaScript, it is up to the programmer to maintain and provide any matrices which are being passed into the matrix calculation method.
@@ -129,7 +129,7 @@Since Jitter supports the processing of N-dimensional matrices where N can be any number from 1 to 32, most Matrix Operators are designed with a recursive function that will process the data in some lower dimensional slice, most often 2 dimensional. The recursive function that does this is typically named myobject_calculate_ndim(), and is called by your matrix_calc method either directly or via one of the parallel processing utility functions, which are discussed in a future chapter.
@@ -313,7 +313,7 @@Rather than using multidimensional arrays, Jitter matrix data is packed in a single dimensional array, with defined byte strides for each dimension for greatest flexibility. This permits matrices to reference subregions of larger matrices, as well as support data that is not tightly packed. Therefore, rather than using multidimensional array syntax, this code uses pointer arithmetic to access each plane of each cell of the matrix, adding the corresponding byte strides to the base pointer for each dimension across which it is iterating. These byte strides are stored in the dimstride entry of the t_jit_matrix_info struct. Note that Jitter requires that planes within a cell, and cells across the first dimension (dim[0]) are tightly packed. The above code assumes that this is the case, using a simple pointer increment for each plane and cell, rather than looking up byte strides for dim[0].
Below is the listing of the max_jit_mop_setup_simple() function, demonstrating the smaller pieces, it manages for you. If your object has special requirements, you can use whatever subset of the following function as necessary.
In your Max class' destructor, you need to free the resources allocated for your MOP. This is accomplished with the max_jit_mop_free() function, which should be called before you free your internal Jitter instance, and your Max class' obex data. As an example, the jit.scalebias destructor is listed below.
Following this header the next data received will be the matrix data, the size of which was passed in the above header. When using the data, please note the dimstrides transmitted in the header.
The time field in the above header will be set to the time of transmission from the sending computer. jit.net.send expects the server to respond by sending back timing data of its own – it uses this data to estimate the transmission latency. The exact data in the latency chunk that jit.net.send expects to receive is the following:
An atom is a typed datum. More... | |
-Macros | |
-#define | ATOM_MAX_STRLEN |
Defines the largest possible string size for an atom. | |