Abstractsthe contents of a patcher or subpatcher for use in other patchers, displaying only those visual elements which are desired. The number of inlets and outlets in a bpatcher object is determined by the number of inlet and outlet objects contained in its subpatch window.
The inlet to the bpatcher object is used for sending messages to its subpatcher, not for sending messages or setting attributes to the bpatcher object itself. Setting an attribute or sending a message to the bpatcher object must be done via scripting messages to the thispatcher object. Please see the replace message for an example of how to do this.
The way I usually add serialosc is by making a new object, typing serialosc, then converting to a bptacher (green triangle at left on the object > transform > convert to bpatcher). looks like this for me:
The serialosc patches inside monome app folders seem a likely culprit. You really should be able to function entirely off of the one serialosc housed in the max app, but if you were leery you could just backup the current state of your monome app folders to an external drive before removing the stray serialosc (and ejecting the drive to be safe)
The bpatcher object allows a patch to show a window onto the contents of a subpatch. The portion of the subpatch that will be shown in the bpatcher window can be changed by an offset message to the bpatcher or to a thispatcher object inside the subpatch.
You can change the visible portion of the bpatcher with an offset message to a thispatcher object in the bpatcher's subpatch, and you can change the size of your bpatcher with a script size message to a thispatcher object in the parent patch.
I didn't always enjoy working in Max. As a beginner, it all seems so opaque, and others' patches just seem to magically work. But I've worked with computers for long enough to know that when anything feels like magic, I just need to learn more about it.
This post exists to help others get over the hump of feeling comfortable and confident working with Max. It's not a beginner's tutorial (there are several out there), but rather a collection of bits and pieces that were either important to my learning journey, or a feature implemented in a non-intuitive way.
A good example of this is the [+] (addition) object. It has two inlets, one for each number on either side of the + operator. Sending a number to the 2nd inlet just gets it ready to add that number to whatever shows up on the 1st inlet. Sending a number (or bang) to the 1st inlet, actually does the addition and sends the result to the [+] outlet. This is a hugely important thing to understand when working with Max.
This is actually documented quite a lot, but I didn't really appreciate its importance until I really got into Max. Use this to control what happens when. The [trigger] object is a good example of this. If you send an integer to a [trigger bang float] (you can also write [t b f]), then the float will be sent out the right outlet, then the bang will be sent out the left outlet.
When the first number changes, it's sent to the [+] and the addition happens. When the second number changes, its value is sent to the [t] (trigger) object. The trigger first sends the new number to the second inlet of the [+], THEN a bang is sent to the first inlet of the [+], which triggers the calculation.
I had been using [print FOO] to output debugging messages to the Max console (Cmd-Shift-M to show/hide), but it's not always the best or most convenient option. In some cases, just seeing a value in-place in the patcher is best. So for those cases, hooking up some source of anything to the second inlet of a [message] (shortcut key "m") will show whatever is being sent out.
This one is super non-obvious from reading docs or looking at other peoples' patcher work. Presentation Mode is an alternative view of your patcher that only includes things you want to show in the UI. This lets you have a "development view" of your work and a "UI view". This lets you make a readable, sensible development view without a lot of spaghetti connecting to UI elements.
Just like a good programmer, commenting your work helps others (and yourself!) understand what is going on. I feel like this is much more important in Max than in regular programming languages, because of the visual nature of the environment (this is counter-intuitive, I know).
Unlike every other device in Max, pressing Enter when editing a message just moves the cursor to the next line of the message, which is something I've wanted to actually do exactly zero times. To actually finish editing the message when using the keyboard, use Shift-Enter. Dear Cycling 74: consistency is important for user experience.
If you open a frozen device from Ableton Live, a copy will be made of the frozen device and saved into the Live User Library. So if you're a device developer and you're testing your frozen device, be aware that if you make any changes you should make sure the changes are being saved where you think they are being saved.
This is equivalent to setting the @autowatch flag on the device itself. Not sure why Cycling 74 felt the need to do the same thing in two very different ways. This flag will make it so the device is reloaded if the javascript file changes. Very handy for development.
This drove me crazy for so long. Many methods that handle receiving messages or interacting with the Live API (e.g. callbacks) appear to receive an array in the first argument, but it's definitely not an array. You have to use the built-in function arrayfromargs() to properly do some nonstandard voodoo to convert whatever object Max has decided to pass as an arg into an array. No idea why Cycling 74 chose this path and then barely documented it.
The built-in console logging function post() is a pain in the ass. Not only is the name bad, but you have to include a newline character at the end of your logging payload if you want it to be its own log message. There is no built-in control over logging levels, so debugging code tends to leave a lot of commented-out post() calls everywhere, or unnecessarily verbose debug logging left in because commenting and uncommenting is a pain.
I wrote this small method "debug()" that I use in my patches that pays attention to a variable called debugLog to decide whether or not to log anything and gives context about what function it's called from.
If you have set up your [udpsend] and [udpreceive] objects with mDNS hostnames for sources/destinations, and the device is not powered on, then it can cause a spinning beach ball on a Mac while the system times out waiting for the DNS resolution.
Using the [send NAME] and [receive NAME] (abbreviated [s NAME] and [r NAME]) objects can really help clean up your patchers and make them more understandable. The NAME helps to document the purpose or nature of messages traveling through the connection. I like to color-code the borders of the groups of [send] and [receive] objects (i.e. those sharing the same name) to make them easier to see and work with.
If you have a [buffer Foo] object inside of a patcher, that buffer named "Foo" is shared between all instances of your patcher (which includes all copies of a Max For Live device in a Live Set). To make sure that the buffer stays local to its home patcher, prefix the name with three dashes, like [buffer ---Foo]. Same goes for [send], [receive], and other objects that take a name argument.
This has come up for me a couple of times. If you have several instances of a [bpatcher] or [patcher] and each instance needs to know their particular instance number, then there are a couple of ways to do it.
The other way is with [bpatcher] arguments. Those arguments are set in the inspector, and accessed in the bpatcher with the special values #1, #2, #3, etc. Many objects will automatically do string substitution of #N with the value of argument N, but I also have found that the string substitution is inconsistently applied -- with some objects and properties replacing it anywhere, and some only recognizing it at the beginning of the string.
Cycling 74 has published some good tips (though good luck finding them!) for making sure your devices you make and distribute are good. One thing that seems to be ignored is how to apply good programming discipline to Max work.
Each device I work on in any significant manner lives in a dedicated directory, which is also a git repo. Using git in my workflow allows me to branch to try different things, or revert to a known good state if an experiment goes in a bad direction.
Good software has enough documentation to help a new user get up to speed, or decide if it's something they want to install. By putting a README.md file in your project root gives you a place to document installation, usage, include screenshots, etc.
When you get your project to a point where you feel like other people should use it, lock and freeze it, then do File .. Save As and save it in a frozen/ or similar directory in your project. Give it a name that includes a version number, like myCoolDevice-0.0.1.amxd, then git add and git commit this frozen device. This gives you a clear path to make improvements, and lets your users upgrade with you, since it's possible to totally change a device from one version to the next, and you don't want to break peoples' old projects if they upgrade your device.
If you'd like to see some devices I've put together, have a look at the Music Tools page. There are links from there to the various GitHub repos where you can see working examples of everything in this post.
3a8082e126