general tedium

Eric Sterling's icon

I apologize beforehand for this rant.

Does anyone else find Max/MSP (specifically Gen) programming totally tedious?

I get that the visual metaphor is easy for simple patches, but it does not seem to scale at all.

I'm working on a gen patch that uses several levels of abstraction - so there are patches within patches within patches, etc - about 5 or 6 levels deep).

In a strictly text based language (say C++) navigating function references is pretty easy.

But with Max it is a nightmare. I open up my patch. Change to edit mode. Open up object. Undo "modify read-only". Change to edit mode. open up next level object.

Repeat 4 or 5 times.

Make change to low level object. Since it is used several times and Max/MSP recompiling is not that consistent, I have to close Max, and reload my patch to force re-compilation.

Since debugging gen is pretty much non-existant, if there is a problem (and there usually is), I have to sort through my codeboxes, hope i fixed the issue.

Now I click save. But again, with multiple instances, Max doesn't re-compile consistantly.

So I need to close, reload, and hope that the bug has been fixed.


I get that the bugs are my fault, but this process of fixing them is @(*$@ agaonizing.

end rant.

And if you Max vets have tips on how to improve this workflow, it would be appreciated

slo ~|•'s icon

If you set up your use of abstractions well you can open up the low level abstractions directly and make the edit without any "modify read-only" through multiple levels. Then perhaps re-open your main patch.

TFL's icon

I feel you.

I think part of what you describe is inherent to he visual nature of Max and the facts that it auto-recompiles in real-time.

Big projects tend to get difficult to work with if you keep approaching them as "one big thing". They tend to have many intricated parts that can require a meticulous initialization stage. And recompiling things on the fly doesn't always work well with that.

Usually I end up editing abstractions by opening the file directly from the explorer (not by digging through the patcher levels in Max), before launching the main patch and see if my modification worked. Then I spend some time experimenting with the whole thing, checking what doesn't work or could be improved, maybe try a few things 'in real time', then close the main patch, open the related abstraction and do the actual work. Sometimes I even do small tester patchs for my abstractions so I can test them remotely from the main project in a much simpler setup.

It sounds tedious but in the end I found this approach giving more consistent results and more efficient than the usual "i dig through my patch while I'm using it" approach, which is a big part of the Max fun but which also becomes frustrating when the project grows.

Because text-based code files are easier to manage in that regard, and if big parts of your patch relies on gen, you could maybe flatten the patcher hierarchy and instead rely on .genexpr requires.

For that same reason many jitter-based projects are mostly js files.

This said, I agree that debugging gen patchers is no fun. And the poor (although getting better) built-in code editor doesn't help.

soundyi's icon

@Eric ... you are not alone - I had similar experiences.

As I have a background in textual software programming languages I have gone through the same pain - which already started with the Max project management and side effects that the Max search path can have (when you are not used to it), which gets even more worse when working with Max for Live devices, and diving into Gen, it approaches another level - for the good and the confusing ;-).

After some time of struggling, I realized that I have to get rid off my "textural software programming" project / code design techniques and project management approaches nearly at all - complete think different and try to embrace Max & Gen as it is.

Its time consuming to reinvent the process of software development for oneself and its disturbing if you have build solid experiences & strategies, good practices that had proven themselves with success in several projects, over years or decades ... but it's worth it, if you got a feel that Max can do a lot for you.

I found myself that Max (often with the help of NodeJS or JS) can make projects possible for me that would take me ages in other software stacks - or were nearly impossible or would lead to restrictions that were not acceptable.

Max is not only about that's it a visual programing language, but its about it's building blocks and how versatile these can be combined to achieve something.

For myself I found out, that I have to get a grasp how can I build "conductors" around these building blocks that fit into the nature of Max - and the same goes for Gen ... and I tried to train myself to not fall back into "old textual programming techniques", when it comes to abstractions and complex patch design, but to find new ways of constructing a vivid digital being ;-).

Which is not that easy, if you got e.g. Max dictionary and arrays at your disposal - might sound strange, but these goodies can seduce you to fall back into "old textural programming" code design thinking.

But on the other hand this "know structures" like dictionary can be very handy - I use them quite often to pass state around or through sub-patchers, instead of using several inlets and outlets, which reduces visual clutter - quite helpful when you want to read & understand a patch that you have not used for a while.

A similar structural approach can be invented for Gen patching using matrices or textures - depending of if you are on the CPU or GPU. Encoding data & state into matrices and textures and process them in GenExpr where you are in then roam of textual programming, can make your life easier (for & while loops e.g.).

The clou would be : let the matrix / texture design be a companion to your Gen abstraction design.

Don't think of Gen abstractions like nested functions each with their own parameter list, but use a clever matrix / texture design that is like a guide or projection of state or process that the overall Gen patcher should accomplish.

Or in other words : designing data or state transmitting matrix / textures as nested structures that each Gen abstraction can read from and write to as needed.

And talking about Gen - for me this is even another foreign world in the already "foreign world" of Max, even if the patching interface looks similar ... it deserves yet another kind of thinking and it's also worth it.

For myself Gen seems to be one of the most powerful crafting tools in Max - while still exploring its depth and details - but being able to e.g. implement GPU based shaders this way, blows my creative mind in exciting ways.

But again I have to reinvent the way I code in Gen ... and that's not easy, but its worth it ... and I'm still on my way doing so, as I haven't patched that much in Gen yet, but also get confused by bugs in the editor (and even the compile some time ago).

+++

But what is the different way of approaching code / patcher / gen patcher design in Max?

Currently I follow a "result driven" and modular approach - maybe something like a industrial machine manufacture would think about it. Its very different from the breaking down & designing features like I do it in textual software programming languages.

I cannot give you practical tips like @TFL did - which are great by the way ! - as my approaches are quite different from project to project ... or in other words : currently I have no golden rule or reusable recipes, but I see them emerging over time.

I am quite sure that there are some good practice and reusable recipes, but that's something for a Max & Gen veteran to explain and make their fitting use case comprehensible ... this would be something I would love to see as a C74 Article series with example patchers and a vivid discussion in the forum (underneath in its comments)

I don't know if the book Generating Sound and Organizing Time - thinking with gen~ would help you in this regard - I want to read this books since months but so far I did not find the time to do so.

soundyi's icon

@Cycling 74 ;-) ... a hierarchical patcher content navigator could be very helpful in a lot of scenarios.

Something in the line of a solution / project browser like in other IDE (code editors) - a hierarchical browser for an individual patcher (maxpat, genjit, gendsp), in which all embedded sub-patchers and all abstractions (inkl. Gen patcher hierarchies) are visualized accordingly to their level in the patcher hierarchy and can be opened for editing by double clicking.

This would resolve a lot of clicking for getting to a "piece of code" down in the patcher hierarchy (just) for editing, but it also great for a birds eye view on the patcher architecture - which helps a lot, when you come back to patcher after some time asking yourself "how did I approach this".

Also grabbing a piece of implementation which was done in an embedded sub-patcher would be way more easier.

A while ago I thought about implementing something like that as a stand alone application as Max patchers and also Gen abstraction (like .genjit) are stored in JSON, but this approach fails with embedded sub-patchers, which I use quite often - for abstractions one could generate a OS shell "message" that represents double clicking the file in the file browser and hence trigger the patcher being opened in Max.

Don't know if Max has an Editor API which could be called from another application - this could resolve the sub-patcher "show stopper" ;-).

Eric Sterling's icon

thanks for all the helpful responses, much appreciated.

A couple of folks have mentioned "opening my abstractions" directly. How do I do this? Since I am working in gen, all my little pieces are gendsp files and Max does not seem to want to open those directly, unless I am doing something wrong

TFL's icon

@Cycling 74 ;-) ... a hierarchical patcher content navigator could be very helpful in a lot of scenarios.

+1 for this, really. I bet there's already a feature request for this as to me it is the logical next step for the new Patcher List View.

A couple of folks have mentioned "opening my abstractions" directly. How do I do this? Since I am working in gen, all my little pieces are gendsp files and Max does not seem to want to open those directly, unless I am doing something wrong

Oh you're right, when writing this I forgot you were talking about gendsp files. A workaround would be to maintain a small index maxpat with all your gen abstractions in it, but it definitely add some friction compared to just open the file from the browser.

Or you could convert all your gendsp to genexpr and edit them in an IDE. You can easily get the code from each gendsp abstraction, but then it's a matter of dealing with the automatically generated code which isn't always the most human-readable possible...

Nodanoma's icon

Hi Eric,

You are easily forgiven for the rand. Things can get super annoying. Since you seem to work in gen alot, and you mentioned codeboxes, have you worked with gen-dependencies of the .genexpr-type at all? Since they are very simple C-code files easily set up and handled and as they can be used with their defined functions in gen.codebox or codebox within gen, this could be a decent alternative to working with nested .gendsp-patchers. Of course this won't help with your current setups at all, but could be worth looking into. They have some limitations I figured, like Parameters having to be passed or global declarations can be confusing but perhaps worth a shot. When editing and saving a .genexpr-file which is used in a codebox via the require()-function, its gen-container is automatically compiled in my experience. If this does not happen, for some reason, or when auto-compile is disabled, and in general, you can always call the gen compile method on the universal object (with argument 1 for subpatchers) to compile all instances of gen anywhere in any subpatch. This works for me, actually, without any restart of Max.

Here's an example of how this could be managed, given a file under the name ngc.sub3.genexpr is found in the search path with a containg function arbitrary_mult(), for example.

Visual Studio Code, as an external editor, can be set up as main editor for .genexpr-files with native C-language, for example, same as Atom, or Sublime Text, etc.

If you keep all your .genexpr-files for a project/patch in the same folderyou can easily access them using folder or chooser with respective prefix and filetype-filter.

Max Patch
Copy patch and select New From Clipboard in Max.
example of a simple setup to get things going…

I hope this is somewhat useful. Your pain is palpable.

Tim


Nodanoma's icon

…and while JS can naccess anything Maxobj or Patcher, gen is a closed circle with semi-permeable membranes regarding accessibility. Opening gen-patchers from within JS is likely to not be possible so if you suffer from navigating through nested patchers then you'll have to find a different strategy or begin to accept the rigmarole (tough one).

In a way, I hope you do not know about the keyboard shortcut cmd+option+M which changed how I felt about nested abstractions since it allows to modify read-only without the mouse… so, if ye don't know, now ye know ;)