Questions about basic workflow with abstractions...

    Jun 12 2009 | 5:09 pm
    Here's something getting in the way and I'm not I'm dealing with it the best way:
    What I'm coming up against here is how to manage subpatchers & abstractions. In my case, a need large numbers of instances of the same abstraction in the main patch. But I also need a few elements at various levels in each instance to be 'customized' or specific to that instance. Seemed like the way to do this was to open the instance, make the specific change to it, and say *don't save* to leave the original intact. So then I have this customized instance that's now 'disconnected' from any further changes made to the original.
    So in trying to develop the patch, when I make a change to the basic content of the original abstraction, I end up having to go back and re-doing all this work...Putting in new instances of the updated abstraction, going into each and changing the various things that need to be customized, re-patching, etc.
    Is there a better way to handle this, or is it just what we have to live with in this case? I've done things like use as few as I can when developing, and trying to do an "initialize" routine where I send as many customizations as I can to the instances as variables. This works OK some of the time, but has it's own side-effects.
    Just wondering if this is the best track, or there is a better one that hasn't dawned on me yet?
    Many thanks, Brian

    • Jun 12 2009 | 5:59 pm
      It depends on what you want to do. Subpatches are unique so can be modified; instances of another patch aren't unique, but using arguments you can modify things per-instance. If you have a lot of things that need to be different, save another master file with the changes and use that for the instances, or you can always open the master file, select all, then paste into a subpatch and change the few (or many) things you need to. I wouldn't get into "not-saving" on a per-instance basis, that sounds confusing to me, but maybe it's OK for your work style. I'd always use a subpatch instead, so it's a new unique entity that's unaffected by the master, and saved in the main patch.
      With arguments to the instance, and some setup, you can have each instance be quite different... presets are an obvious tool here, but you could even hide or show different objects, colorize them, make them bigger or smaller... since all their objects can all be sent messages to that effect, using an argument which triggers a series of these messages (that's unique to that instance, versus a different series of messages to another instance) would let you customize all kinds of things, without making an actual unique new subpatch. Just use the #1 #2 #3 etc. wherever you need the arguments, though if you're using them in a symbol (like send or receive) they must be at the start of the symbol, like [send #1_command]. Or you can use the #0, which is a per-patch unique number which guarantees it won't interact with others. I stick to the #1 #2 #3, and usually only need #1.
      Also if you're using bpatchers, a fun trick is to have a bunch of the same patch laying on top of each other, then use "bringtofront" messages as you need each to be usable. Saves a ton of space if that's needed, and in some designs (like a channel strip or something) it makes sense... so as the user highlights a track, that track's settings pop up in the strip.
      They have their pros and cons, but if you're making a ton of very similar patches, use an instance method. That way you can tweak the original and everything updates. Nothing worse than having to make a similar (even small) change to dozens of subpatches that you thought for sure were done when you copied them... but then... you just had to have that extra feature, didn't you??
    • Jun 12 2009 | 9:12 pm
      what you are looking for is way of converting the content of a patcher object [p foo] into a patcher on disk and the other way round.
      an understandable request, but i believe it is part of the inner logic of max patching that maxmsp does not offer a function which allow this that easy.
      what could help around this is that you do not make too big abstractions. i.e. dont make multimode sterei filters, make highpasses and lowpasses and separate mono and sterei versions.
      if you feel that an abstraction must be changed all the time, you did you not find the perfect interface for it yet.
      there are two options IMO, making 5 copies of the patcher in disk, rename them to foo2, foo3, foo4 ... then you can change them on the fly when needed. or, of course, you have to copy the content of the abstraction and make it a [p ] in yoour current project.
    • Jun 13 2009 | 12:34 am
      You can also do Save As... from within a subpatch, this will save the current state as an abstraction, but still keep the subpatch. Right after saving, you can actually just rename the "p sample" (or whatever it's called) with the name of the abstraction, and it'll now be an instance. even the connections will stay. In the opposite direction, you can select all from an abstraction (the original) and paste into a subpatch, as mentioned, then make changes as needed. With bpatchers you can do a bit of a hybrid style with "Embed Patcher In Parent" too.
      My only problem is that I forget what additional files I need to bring along with the main patch... then it's trouble! But if I want it all in one patch, I can use the copy-paste technique (once the patch is done) and replace the abstractions with subpatches. As long as there aren't too many arguments to track down and change... which makes me think about "find-replace". Looks like in Max you can find but not replace, even if you open as text. But of course any text editor can do that. So you could replace all the "#1" entries with whatever number you want.
    • Jun 16 2009 | 5:55 am
      This is great, thank you all very much! Yes seejayjames, the problem I have is pretty much as you describe as a drag at the end of your first post. Unfortunately with this project (and where I'm at in the learning process) it's not so feasible to completely develop an abstraction, have it be finished, and plant 50 of them inside another patch without ever having to make a change or correction So that is the very problem I'm encountering.
      I was unaware of the [patcherargs] object Is this what you're referring to for using arguments in abstractions above? I'll look into this more, and if it can do what it seems like it can, it could be exactly the ticket.
      Thanks again, all!
      Best, brian
    • Jun 17 2009 | 12:37 am
      Brian H. wrote on Tue, 16 June 2009 00:55
      I was unaware of the [patcherargs] object Is this what you're referring to for using arguments in abstractions above? I'll look into this more, and if it can do what it seems like it can, it could be exactly the ticket.
      I was actually talking about just using #1 #2 etc. in abstractions. So if the channel strip for your mixer (as an example) is an abstraction/patch named mixer_channel, if you instantiate it by typing "mixer_channel 5 88" (without quotes) into the object box, then it'll create the abstraction with those two arguments. Then, inside the abstraction, everywhere you have a #1 it'll be replaced by 5, and everywhere you have a #2 it'll be replaced by 88. This works up to #9, but there are ways to get more. 9 is a lot though.
      Generally I just use one argument, so I'd have 8 instances for my 8-channel mixer, each one named "mixer_channel 1" "mixer_channel 2" etc., coming from the same abstraction. So they each have a unique argument, and inside, there might be a "loadmess #1" going to a preset, which would load that numbered preset (1, 2, 3, 4...) in each.
      Another very useful thing is if you want to send commands to your abstractions from a master patch (like setting params in all the instances at once), you compile the list of commands (or even a single one like volume level), then run it through [prepend 1], [prepend 2], etc., depending on where you want to send it, then into [send vol_level]. Inside the abstraction, [receive vol_level] --> [route #1], and the #1 corresponds to the number of the abstraction (channel 1, 2, 3...), which has been set in the object box: "mixer_channel 1". This will ensure that only the commands going to that instance will actually get in. The same could be done with [send 1vol_level], and inside the abstraction it's [receive #1vol_level]. The #1 gets replaced, the regular 1 is in the main patch, and the abstraction has the appropriate number at the start of its argument list. again, in this case, it must be at the front of the symbol, so I stick to [route] for better readability.
      Lots of fancier things you can do, like [gate #1] would make a gate with a variable number of outlets, or a "size #1" message to a multislider would set the number of sliders based on the argument. I generally stick to using presets, route, and send/receive, this gets me pretty much all the flexibility I need.
      Everything mentioned here works the same way for bpatchers, just enter the argument list in the Inspector.
      I haven't used [patcherargs], it looks related, but more for monitoring and getting attributes rather than setting, but I'm not sure everything it could be used for. Also, when using [pattr] with a bunch of bpatchers or abstractions, often the #1 will be handy, though in other places you won't need it. Takes some experimentation, but the ability to instantiate abstractions with arguments is very powerful.
    • Jun 17 2009 | 4:47 am
      Ah, now I see! I had seen that referenced in a number of other posts from way-back when I searched the forums, but never just simply explained, now I get it! Very slick, and it even works inside trigger. I didn't know you could use arguments in patchers and abstractions, and when it looked like it was possible I spent some time searching the documentation and didn't really find it (it was late, maybe I missed it), but I did find the rather basic use of [patcherargs] which worked the same way, and should allow more than 9 arguments if needed. One way or the other, it's a huge piece of functionality that I just got my head around, and effectively solves my workflow problem in this situation. Thanks again everyone!
    • Jun 18 2009 | 5:10 pm
      Brian H. wrote on Fri, 12 June 2009 19:09 Just wondering if this is the best track, or there is a better one that hasn't dawned on me yet?
      The standard way is using arguments. Just create the abstraction in a way that allows to customise with arguments. That might lead to mor complicated than necessary patchers, but you do it once, and you're set. (look at my St.ools, its full of hopefully universal abhaXions...) A technique I am using as well, is loading the abstraction the normal way, connect it as necessary, then de-encapsulate, followed by an immediate encapsulate, which creates a customizable subpatcher. You can tweak that then, but if the original patcher is changed, that won't change with it of course...
      hope that give some ideas...
    • Jun 18 2009 | 7:57 pm
      Hey, I like that de-encapsulate / encapsulate trick. Kind of like select all--copy--paste, but quicker and more controlled, plus you can do the connections like you said. I didn't think you could do this with abstractions, only subpatches. Cool!
    • Jun 19 2009 | 4:39 pm
      Yes, thanks Stefan, great idea! It also helps clarify for me the exact point at which an abstraction becomes an independent subpatch. Glad I know about the ability to add your own arguments now Makes the whole thing quite powerful and elegant.