How Long Before You Start to "Think in Max"?

ForestCat's icon

I had a small plane in the 90's. I was really considering trying to learn how to fly helicopters, but I remembered my instructor once telling me that it was way easier for someone who had never flown before, than it was for a fixed-wing pilot to make the transition to helicopters. He said everything was sort of the opposite of what you'd already worked so hard to make second-nature...

So I've been at Max in earnest about 3 weeks or so. I've got the obligatory "100 hours" in, I think.

I can say that it is really rich, and tremendous fun to learn. Having said that..

I'm not a talented c, java, basic, arduino, etc. programmer by any means. OTOH, I've been dabbling in computers/programming since 1980, & I can usually fight my way out of a wet paper bag, as they say, in most text/editor based languages if I need to get something done.

I'm learning that, in Max, for the most part, if there's a will, there's a way. The issue that I (and, I have to assume, countless others before me) am having is that I'm still trying to solve problems mentally in traditional programming constructs (if, then, for, next, do, while, case, switch, etc., etc.), and then finding a way to make Max emulate that functionality.

My instinct tells me that this is equivalent to walking out my back door & around the planet to get my mail across the street. IOW, not exactly efficient in either code or development time.

I'm a little frustrated with myself, and I guess I'm just wondering how long it was before some of you began to "Think in Max"?

Thanks for reading.

mzed's icon

I've tried to teach Max to people who had a text programming background and have seen the situation. There is a different mindset for dataflow programming, especially with regard to things like loops. How long does it take to get over that? I'm not sure.

Maybe post some patches, or problems, that are frustrating you? For loops, Uzi and various data registers (coll, float, int, zl.reg, message box) are important. Although, thinking of looping through a pile of data is much less "Maxlike" than operating on a continuous stream of data.

This would be a great area to expand in the wiki -- tactics for moving from other programming experience to Max.

Chris Muir's icon

If you can manage to think of it as a "math/algorithm based modular synthesizer" you might be better off than bringing a block-based programming mindset. Max's roughly data-flow paradigm does take some getting used to, but like traditional languages, once you start to collect solutions to put in your bag of tricks, things get much easier as you re-use things. Like any language, proper abstractions, and the occasional code-refactoring session will go a long way towards keeping things manageable. Encapsulate, abstract, and don't optimize too soon.

Max has some implicit "hidden" things that can bite the novice, e.g. the right-to-left, top-to-bottom evaluation order. This forum is fairly good at helping your figure out those sort of things (and the trigger object is your friend with controlling message order, BTW)

Scaling your problem size to match your ability size is important when starting out. You want projects that will challenge you, but not overwhelm you. I've seen a lot of novice Max users who embark on very ambitious projects, never to be heard from again.

You have to learn how to program things from scratch, but you don't have to build things from scratch while learning. Grab examples from people and modify them. Cycling has the Tools section of the site, there are examples posted on the forum, and many users host their Max stuff (my stuff is here and here.

ForestCat's icon

Chris, mzed,

Thanks for the detailed responses & great advice. I've been able(for the most part) to find analogous structures to the things I'm missing from the traditional syntax's, I've been reading, trolling, reverse-engineering, plagiarizing, etc, like crazy. As you correctly state, the resources are abundant. The problem is me, I feel as though I'm moving at a snail's pace. I am amassing my toolkit, making elaborate notes on my favorite objects & what problems they solve best, as well as checking out the "related" objects in the help, etc.

Since you asked, here's a small example. My goal at the outset, which I think is modest, is to use Max for Live to enable me to design a proper arduino-based foot controller for live clip-based (as opposed to the looper device) looping. By proper, I mean Live API event-driven feedback to LED's on the pedal board, including the blinking status of fired/cued clips . The looper is 4 Tracks x 4 Scenes (for now) w/ 4 track footswitches & four scene footswitches. Basic. I'd considered trying to learn some python & do this using the framework classes, etc., but I'm thinking (hopefully correctly) that the time invested in learning Max will pay off down the road as my ambitions (& skill level) increase, and allow me to do things beyond the scope of python/framework/remote MIDI scripts, etc.

Until now, I've been duct taping everything together with a combination of Bidule, Autohotkey, Clyph-X (all brilliant tools, btw...), but I'm seeking a more integrated (and hopefully bulletproof) approach.

Consider the following subpatcher, which is one small module of (of many) in my device. Although this is a Max for Live device, I'm posting about it in this subforum, since it's more about Max "zen" than Live, since I'm not asking for help w/ the device(it does actually work...), as much as a sanity check on my approach. To me it seems heavy-handed & clunky, and lacks the minimalistic elegance that always impresses me in well written code.

Functional description:
This subpatcher accepts two inputs: A two element list consisting of TriggeredStatus (generated in the parent patcher by a live.observer, and TrackIndex, corresponding to the track selected by one of the four track footswitches.

The second input is a metro-based "clock", essentially an 0 & 127 alternating on 1/8 notes.

Any data entering the subpatcher does two things: Fires all the if-then groups, resulting in an output of four consecutive cc's, for LED control. Because only one Track has been selected ($1), the other three cc commands will be 0, turning off the previously selected LED. A signal from the "LED Clock" is then injected (or not, if the switch is activated on an exact quantize boundary & the clipslot never flashes...) into the MIDI datasteam of the active track's LED, which is then disconnected when the router is cleared by the expected change of TriggeredStatus from 1 to 0 when the clip starts actually playing/recording.

Yeah, it's pretty basic, and there are potential unhandled exceptions (accidentally stepping on two switches, etc.)and I know the proper way to do this is with the arduino handling the flashing logic, and Max simply providing a third cc value for flashing, w/o the clock hack. But at the moment, my "footswitch" is a TouchOSC iPad mockup, and besides, I guess I wanted to see if I could pull it off.

The thing is that I could do this without using "IF" objects (which seems almost to be frowned upon among the Max elite...), but it would require more objects. From the standpoint of efficiency, I have no idea how these objects run under the hood, so I'm not able to say whether one IF object beats a Change, Select, two Messages & a Trigger ( I just made that up, not saying that's the alternative, but you get the idea)

So bigger picture ( parent patch, design objectives, etc,) aside, is this approach reasonable? Heavy-handed & clumsy?

I do appreciate any feedback. Thanks again, sorry for the length.

Max Patch
Copy patch and select New From Clipboard in Max.

5296.TrackLEDControl.jpg
jpg
Chris Muir's icon
Max Patch
Copy patch and select New From Clipboard in Max.

I have no way to test this so there might be bugs, but this is roughly equivalent, from a more data-flow POV:

Roman Thilenius's icon

more than a year (assuming an average use of 8 hours per week).

mzed's icon

Your example doesn't seem to work the way I would expect from your description. Do you want either one blinking light or no blinking lights, based on the input?

Max Patch
Copy patch and select New From Clipboard in Max.

Based on that idea, my response seems to be a joke about max programmers:

Chris's is nicer.

Chris Muir's icon
Max Patch
Copy patch and select New From Clipboard in Max.

I completely missed that part of the description. Here are all the methods so far:

ForestCat's icon

Wow. OK, where to start....

Roman: Would that equate to 6 months at 16hrs/Wk? :-)

mzed: Sorry if I explained that poorly. We're looking for three states, hence my comment about eventually implementing the flasher in firmware, and sending three different cc vals, i.e.
0= off
64= blink
127= On

We need to represent three possible conditions of a track (clip) at any given moment. If you've loaded Chris' latest combo patcher, the one thing that is CRITICAL to understand is that in vivo, any incoming data from the right column would, within one measure, ALWAYS be followed by an item from the left column, which stops all flashing, and leaves only one LED, lit steady. Normally, this would be the corresponding item, i.e. 0,2 for 1,2, etc., unless one had made a mistake and hit two different switches in rapid succession. In any case, the most recent TriggerStatus change will always result in a cancellation of all flashing, and the last man standing (solid LED) will be the last active(selected) track. Put another way, the last data packet into this patcher will always have a 0 as the first element.

That considerd, the logic on the TLM_ForestCat patcher is providing the desired result bearing in mind that:
A: One (and only one) track would always be "selected", as indicated by an LED
B: The status of a given track would either be:
1. Not selected (LED Off)
2. Selected, but "cued up", waiting for the next measure, etc. (Blinking)
3. Selected and playing/recording(Solid LED). For the future: Using tri-colored LED's for record play status w/ an add'l state/cc value. I'm just getting started w/ this :-)
Like I said, it does work, but it strikes me as a little brute force/amateurish, I guess...

Chris:
Jeez, I barely had time to think "I better roll together a test jig so these guys can see how this is supposed to work..." and you had already done it and posted it. Amazing.

It's really nice of you guys to throw your hats in the ring, I appreciate it, and it's fun to see how different minds approach it.

It's also sobering to see how far I have to go to "speak this language" natively. Chris/mzed, if your patches had replicated all of my functionality with that few components, I was ready to hang myself :-) But I'm keeping the rope handy...

Chris Muir's icon
Max Patch
Copy patch and select New From Clipboard in Max.

OK, I removed a couple objects from my solution, so it's now closer to your solution. The only thing my solution doesn't do that yours does, is allow for multiple blinking tracks.

Chris Muir's icon
Max Patch
Copy patch and select New From Clipboard in Max.

Here's a version with a slightly simpler TLM_CBM, getting rid of the bucket object:

mzed's icon

Good one. That looks like max to me. I think my solution might have converged on Chris's latest -- especially now that I've seen it -- so I haven't anything to add to the patching.

A few comments. First, I don't think there was anything "wrong" with the original logic. It might be less idiomatic, but I don't see a problem with it. Put your rope away. Second, I'm not entirely sure why max programmers avoid "if". It might have been inefficient, back in the day. Anyhow, if you want to program in this style, "gate" or "select" are more common. Third, we used to do these games at workshops I taught. Experienced programmers will solve problems differently. It's part of the charm of Max, although it makes it harder to learn.

ForestCat's icon

Amazing. Yeah, I guess that's what I was talking about when I used the term "elegant". I'm also guessing this was a trivial exercise for you. It took me about 20 minutes of concentration to figure out exactly how/why your patch works. It works as well as it does, with as few components as it does because it makes HEAVY use of the right to left execution order, correct? That's where my deficiency is right now, I'm not leveraging that concept much in my own code(except w/ triggers), because that concept is completely alien to any preconceived notions I have re: programming....

But back to the original question, how long were you working w/ Max before you reached the point where solutions like this started to become "obvious" to you? What was your prior background?

ForestCat's icon

mzed,

I posted the last reply before I refreshed & saw yours. Thanks for all your help/props, really does make me feel a little better. But your & Chris' approach is what I'm shooting for, and I guess what I suspected my code "could be".

One thing I'm noticing, and of course this could just as easily apply to function overloading, etc. in traditional languages, and it is this:

The more concisely a patcher is coded, or the higher the level of abstraction (if that's the right terminology...), the more difficult it is to look at it and figure out the entirety of what it's doing. Of course that could be a noob thing. But I find even with my own code, the earliest revisions are pretty easy for me to reverse-engineer without any comments. But as the code becomes revised, more streamlined, tight, & efficient, I find that without liberal comments, I have no idea what the hell I was thinking when I wrote it, even a few months ago. So now I comment the daylights out of everything. Minutes of extra work now to save hours later. What a drag it is getting old...

One of the coolest things about Max is the ability to attach comments (bubble tool tips) to your inlets/outlets, as well as the Clue window, which allows me to have extensive commenting on my structures/algorithms, without taking up precious patcher real estate. One of the things at this stage that slows me down is having to navigate into patchers/between windows to retrace program flow. In that regard, the tooltip help is a huge timesaver for those "what does that do again?" moments. And it took me two weeks before I even knew that functionality existed. Did I mention Max is deep? And I've barely knicked the peel...

mzed's icon

I got into Max in 1994, when all it could do was MIDI. (Chris was way ahead of me.) It was running on Mac LC's, and efficiency was a big deal. Floating point processing was software emulated. A metro interval of less than 20 ms crashed Max. It tended to focus problems.

My background was primarily as a musician. Although, I grew up programming in BASIC and Lisp. I think I was lucky: I had enough programming knowledge to get the concepts but not so much that it made Max more difficult.

My programming took a major step forward when I started working CNMAT (UC Berkeley) and wrote a lot of patches for other people. Something that was "good enough" in my individual work was inadequate for sharing. And, we thought a lot about best practices when generating the CNMAT MMJ Depot. (It's downloadable, at cnmat.berkeley.edu/downloads). So, I really needed a full-time job to get to where I'm at.

If anything, I would recommend *not* getting too involved in the aesthetics of programming. Max is great for artwork: one-off's and prototyping. When I started, I was a musician and made some cool patches that did what I wanted most of the time. Now, I'm a server-side software engineer and I don't make music at all. Programming can be a trap.

Chris Muir's icon

TLM_CBM make approximately zero use of R2L ordering; it's all explicit using the trigger objects. It does make use of a data-flow mindset though, I think. Instead of thinking "I need to perform these actions" I think more along the lines of "the data needs to perform these actions." It is a mindset shift, but not really different in magnitude to the shift required to go from a standard block-structured language like C, to an object oriented language like C++.

The [if] object is a late comer to Max, and did have some efficiency issues, but those are mostly historical. Any inefficiency in your use of [if] is in the fact that there are four of them chewing on data that they mostly ignored other than redundantly turning off leds. Your use of [router] requires a slightly more complicated control structure over something like [gate 4]. That said, I agree with mzed that there's nothing inherently wrong with your approach. If it seems clearer to you, it's probably better for you.

Where I disagree with mzed is in "'not' getting too involved in the aesthetics of programming." For a quick and dirty one-off he's right, but if you are making a patch that you think that you're going to have to live with for a while, it should be as clean & clear as possible. In C code, this usually involves shorter routines, with well thought out interfaces. In Max it's almost the same, good abstractions / encapsulations with well thought out data flow.

As far as my background goes,when getting into this world, I started out with modular synthesizers, and analog circuit design, quickly realized that I was way more comfortable with digital design, eventually including microprocessors (in assembly) as they got ubiquitous enough to use. I "graduated" to some "real" jobs, using Forth, Pascal, C, and eventually Object Pascal and C++. I started out with Max in the late 1980s, before it was a commercial product. In fact my learning C was because I wanted to write Max externals. Some of my early C efforts have been inflicted upon the Max community for decades, their quirks and foibles so cooked into the objects that they haven't been changed for fear of breaking compatibility. Sorry.

My favorite programming adage: "You can write bad Fortran in any language." I look back on some of my early C efforts, and my reaction is "It must have seemed like a good idea at the time." I predict that once you more fully wrap your head around Max you will look back on some of your earlier efforts and wonder what you were thinking at the time. I know I have. I had a bias against lists for the longest time. It just seemed inefficient to pack/unpack all over the place. What was I thinking?

Max Patch
Copy patch and select New From Clipboard in Max.

Here it is again. TLM_CBM has a few more comments.

Roman Thilenius's icon

"make approximately zero use of R2L ordering; it's all explicit using the trigger objects."

and why would you not call this r2l ordering?

Chris Muir's icon

When I think of R2L ordering in a Max patch, I think of the way Max evaluates thing based on where they are in the patch. Trigger allows you to control the order explicitly, not based on location.

ForestCat's icon

The forum doesn't let me quote in line, so I'll try to keep this coherent and respond to everybody...

re: right to left order, I should have been more specific, what I was mostly referring to was the "t i 127 b" and the total reliance of the patch on the "firing order" of the outputs, and that you had strategically placed the bang on the right, I usually see them on the left in many patches.

mzed: Like you, music was first for me, and led me computers as a means to that end. There's a lot of wisdom in your advice to to not get carried away with the coding, since as you correctly state, it can become a major time vampire/diversion, if the goal, after all, is to make music. It's a little sad to hear you've been swayed that far from music these days... Great work on the CNMAT stuff, I scarfed it all up and will peruse joyfully as time permits. I'm in the "kid in a candy store" phase with all the freely available Max stuff on the web.

Chris: Object Pascal, Forth, Lisp, Fortran... Them were some heady days, no? I still have a TRS80 Model III in the attic, and somewhere I have the 5 1/4" floppies w/ some of my Fortran college course assignments. The landscape has certainly changed. I do share your opinion that if code/module reuse is even a remote possibility, then it pays to keep maintainability/readability paramount as soon as practicable in the process. I have decades of spaghetti code & rat's nest wiring because I wasn't concerned about reusing things, believing my brain and my time were a bottomless resource. Like mzed says, though, there's a fine line between being a musician who programs and a programmer who plays music, but it's a line I want to stay on the musician side of. Of course that's just a personal choice, probably a dopey "identity" thing, not a value judgment in any way.

In any case, I feel pretty honored to have had my noobie code critiqued by a couple of the real "old dogs" :-) .You guys have my utmost respect and awe, having forged your signatures into the storied history of computers, music, & Max from nearly the beginning, when men were men & sheep were scared.. I don't know where music is heading aesthetically. Frankly, I'm pretty disappointed with 99% of the current, mainstream use of music technology. Hey look, I'm really not an "elitist", honest I'm not... While I cop to a penchant for 70's prog, I like most everything from the Monkees to the Eagles to Winger to Kraftwerk to Lawrence Welk to King Crimson, etc., etc.

But I gotta tell ya, if I hear one more vocals-quantized-to-half-step-auto-tuned-trance-gated-stacked-sawtooth-waved, well you get the picture. Just showing my age I guess.

I digress only to make the point that we have lived through some amazing times in the evolution of electronic music & computers. A close friend & mentor of mine worked at Bell Labs in Murray Hill, NJ in the 70's. (I'm in NJ myself) He told tales back then of Hal Alles & Max Matthews, countless others. But me, being a 17 year-old long haired rock & roll guitarist, although being pretty good w/ my dad's 100 watt Weller gun, a 5lb roll of acid-core plumbing solder, and a mad penchant for circuit bending, well I just didn't absorb the significance of what was going on a half-hour drive away in the Lab's expansive basement labyrinths at the time. Computers weren't on my radar yet. I deeply regret that I did not seize the opportunity to meet some of the Thomas Edison's of our day. The Labs was this incredible place back then where, brilliant, creative, eccentric people were given space, equipment, staff, & purchase order numbers, and their only obligation was essentially "to create", and that they did. I longed to work there someday, but by the time I was qualified, the gravy days were over. Like I said, heady times...
/flashback Off

Chris Muir's icon

I guess that I didn't think that "total reliance of the patch on the "firing order" " was much different than totally relying on the order of lines of C code.

ForestCat's icon

That's my point! It isn't any different at all. You might have taken that comment in a totally different way than I intended. In text based languages (most of them, anyway) the implicit execution order, at least the main loop, is top down, even the function calls are generally executed "in order". What I was trying to say, is that in Max, "t i 127 b" , for example, is a somewhat different animal than "t bi 127 i" from the standpoint of any devices downstream. That, I think, is probably not immediately obvious to a lot of Max noobs. Yeah, we read the tutorials & sort of grok the right to left, top to bottom, deep to shallow concept, but I think it may be one of the later concepts to actually be really grasped/put to use.

I looked at your code, and considered it "more advanced" because you know to take advantage of that fact and use it to reduce the need for all the extra conditional checking, etc. that I was doing. I'm just starting to get to the point where I'm really trying to think about execution order. Until now I've been brute forcing a lot w/ various types of conditional thinking/checking. Your way is much more elegant. I certainly did not mean to imply that it was anything less than that, sorry if it came across that way.

Chris Muir's icon

I took no offense. In fact, I think we're in violent agreement here. Procedural languages and data-flow languages are similar in many ways, just as procedural languages and object-oriented languages are similar in many ways. But… vive la différence.