Clicking on a MIDI Clip [live.path] sends out an id which goes to [live.object] to get the path of the Clip. I don't understand why it works only with [deferlow] right after [live.path]. Can anyone explain this?
Note: changes to a Live Set and its contents are not possible from a notification. The error message in the Max Window is 'Changes cannot be triggered by notifications'. In many cases putting a deferlow between the notification outlet and the actual change helps to resolve the issue.
yeah maybe. But then I wonder why it's not set to low priority in the first place? Oh, it seems to behave differently depending on where the live.object points at. It works with getting the path of a track f.e.
I think the whole deferlow thing is unclear - for me it's a bit like if it doesn't work, stick a couple of deferlows in.. If we understood why they are required, the mechanics behind it, it would be easier to program efficiently. Apologies if it actually is explained somewhere, I searched but couldn't find it.
[deferlow] puts the message it receives at the end of the low priority thread (there is high priority and low priority) unlike [defer] which puts it at the front of the low priority thing (This of course also affects the objects downstream of [deferlow] but they are not "deferlowed" per se). So [deferlow] is the strongest slow-down you can do.
In this case it's the sending out the id of the MIDI Clip I clicked on that is put to the very end of the queue. Why it has to do this to give back the path of a Clip but f.e. not of the track this Clip is on I don't understand.
I'd think that if I can deferlow it than it could also be built into the object so it "just works".
the thing about max is, whilst most of it is easy to learn and use, it wants to give you so much flexibility, individuality and level of control like no other app, that sometimes you run into these sort of issues (the perennial "scheduler" issue) that you wish "it just took care of it for me", but it cannot be that way, as then you would not have all that amazing control and uniqueness.
however, i do not think that this issue is particularly complex at all, and once you have been patching with max a while it starts to make more and more sense and becomes natural to take into account. even though the helpfiles explain it very well, here is my potentially nightmare explanation, i hope it helps a bit:
"I'd think that if I can deferlow it than it could also be built into the object so it "just works"."
... usually, the reason one thing has to be deferlow-ed when another does not and you are not expecting it, is simply to do with bad patching practices and not following your execution orders exactly correctly. the problem with deferlow is that it 'makes stuff work' so it gets way overused, whereas ones use of it should not be frivilous but very exacting. if you need 10 in a simple patch then something is usually very wrong with the patch, not the execution / thread calling of Live/Max.
Pid - I completely agree, I'd want my patches to be defer low-free if possible. I build all my abstractions to Max object conventions like get triggered by the left-most input, and try to build my patches so that they execute in the right order just by design (left-to-right arrangement of objects etc), and use a lot of triggers to add control of execution order.
Which makes it so much more interesting to understand why m4l needs deferlows to operate! And not knowing why/when to use them and when not means you end up using many more than required, because at the end of the day you want it to work....
I'll look at your patch when I get home, I hope it will clear things up for me!
I think your second example shows normal behavior because the path calculation is simple and thus can easily be performed in real time (high priority). In contrast, the path calculation of 'detail_clip' is much more complicated since the clip may reside anywhere in the set depending on user interaction (selection).
ok, broc is the expert, listen to her/him. however, i'll add...
no, sorry, i did not look at your .amxd originally - i was just answering to the 'deferlow' issues. however, i have looked at your example device now and the answer is very simple and is a tricky part of the API that has always been there (and documented), namely: you cannot trigger processes based on observed notifications from the API.
this is one of the API 'rules'. it is for this very reason that use of the [deferlow] object was envisioned, and its use here is perfect and correct and exactly as you had it. there was possibly confusion because [live.path] is not strictly an 'observer' object but, give it the argument you gave it to track straight to "detail_clip" and it becomes one.
therefore we ascertain that SOME parts of the API are slow, in terms of callbacks. as broc said, "the path calculation of 'detailed_clip' is much more complicated as the clip may reside anywhere in the set depending on user interaction".
i attach a .amxd device based on yours demonstrating this, although i am sure you get it... disclaimer: i am no API expert - i delve into it now and then.
easy vs. complicated path calculation: OK, if this is what it is I can live with that. Is anywhere documented which calls are what?
so sending an id is an "observed notification from the API" and doesn't work, but deferring the same to the end of the low priority thread works. uh...
I just tried one more thing which really baffles me: even when I [zl reg] the id for a second before passing it on to [live.object] it won't work. So it's somehow not about the time but the thread it is attached to. or so...
"So it's somehow not about the time but the thread it is attached to. or so..."
- well done, you got it! as i demonstrated in my initial crazy patch-description of [defer] and [deferlow], with [defer] (or [delay], or [pipe], or [s]/[r] etc) it is not possible to pinpoint the exact execution order, but with [deferlow] it is. this is why it works to [deferlow] on an 'observer notification' from the live api because it is saying 'do everything you need to do, when you have done that, let me know and i will execute'. it is nothing to do with 'id's in general - you can happily trigger as many as you want so long as not directly from a notification. with the tricky business of 'you cannot start another process based on a live api observer,' think of it like a digital audio feedback loop - it cannot be done like in the analog domain - you need a sample of delay (in gen~!) or a vector of delay (in msp).
re: "easy vs. complicated path calculation" - sort of; it is more about 'possible to predict time scale of callback' and 'NOT possible to predict...'; but in general, yes. "No, unfortunately. But I'm not sure if a strict classification would be possible" - exactly. it is not about a 'list' of objects or api calls that always need [deferlow], it is more about realising general situations in which these things are needed. so no list. however, i do agree that the documentation on this could be better. e.g. there should be a discussion-chapter somewhere in the docs about 'strategies for programming with the live api and fitting everything else in and around it'. or a snappier title of course. preferably not written by such a hacky douchebag as myself that does not really know what the hell he is talking about.
api calls in both live and max (and most other media software on the planet) happen in a slower, deferred, not as high priority and to some degree unpredictable thread, because the important stuff - audio (and video, but that is different again) - must never get interrupted or glitch. in fact live and max are two of the most amazing and robust softwares in this regard and it is one of the things that makes them special. letting 'us' in there, right in the guts of the software, to mess around and hack and create our own things, is very tricky and sometimes difficult. but that is what max is. i am constantly amazed that max even works at all with me abusing it all the time.
and yes, obviously, i had a pretty slow day today (!!), but i just got an email with some c++ stuff in it so i'll be locked away this weekend doing that as i really suck at it.
p.s. broc is still the man! i learn loads from him!
Seriously helpful thread! I finally understand the language they're using in the manuals.
the below may be obvious? let me know...
I think you can interpret the message "live.object: Setting the Id cannot be triggered by notifications" as follows:
1) because you clicked a different clip in Live, the live.path object had to send out a new path ID. this is a notification
2) the notification (the new path ID) was going to set the ID for the live.object BUT.. "Setting the Id cannot be triggered by notifications"
But not everything you do (even some possibly complicated stuff) is triggered by a 'notification' from live: eg if you trigger the path directly by clicking a message box with the path name. Maybe the attached device illustrates it. I changed from 'getpath' to 'get name' because it illustrates my point a bit better. It definitely means I can get rid of a lot of deferlows in my patches.
I think I learned more in 3 months on this forum than I have in the year before :-)
Awesome. I just was able to cut out 32 deferlows (see clipping) : not understanding when they were required, I had a deferlow after every live.path that was feeding an id into a live.object. Now I know when I need them and when I don't!
agreed. I've actually learned so much in the past weeks I may gather it up and put it in a thread. (my dayjob is running a small technical training team in a big company, I don't really want to take my work home!)
and yes my patch was a bit sloppy - (late night work) I like the [t getpath l] method, so much cleaner that using a message box: unfortunately it doesn't work with 'get name'!
@ broc: understood: this was just because I made the patch from editing other ones, a slip-up. From a trying-explain-the-issue point of view, superconfusing, I agree
@pure: i did try that too. I think I did a test using pedant to compare a [trigger object with symbol + fromsymbol] patch with [triggering a message box], but in terms of performance it doesn't matter much: the API calls are slow and their time varies a lot, compared to the rest of the objects. So I guess that's a good argument to just make the patch the way it is most readable.. I vote for triggering a message box
Update - in case anyone reads this thread and finds it useful: I've been wrecking my head over the above: i think I figured out when and where I should use [deferlow] to prevent the "Setting the Id cannot be triggered by notifications" error messages, but M4L behaviour has been very inconsistent.
Turns out there is a BIG difference whether you have your device open for editing in Max (many more errors, and inconsistently: sometimes execution is fine, sometimes I get the error, and it seems to depend on how busy the system is whether or not you get the error). But when you have the M4L device running inside Live (ie _not_ open for editing) it behaves a lot more predictable
In my experience, serious testing in edit mode is not possible. So in general I save (or even reload) the device and then look at the Max window in performance mode (open with right-click on the device title bar). Strictly speaking, interactive editing in M4L doesn't really work as advertised.
The example in this thread fails to take into account that the terminal node of a chain is executed first-- for example, a chain of bang objects will execute from end to beginning, like this (second attachment)
Here is a revision of the first part of example that you can play with to get a better understanding.
The reverse chain of execution is odd, but once you know that , it's easier to see what these objects are doing.
it's been a while since this thread so I would need to reread to exactly recall what it was about. however, I ran your example patcher here and you will probably spot the interesting difference in behavior when you look at 2 and 3. shouldn't be like that, no?
Actually, based on the idea that the end of the chain executes first, yours makes more sense. I thought it would end up that way, and that's how I numbered it, but then it reversed the 3 and 2 so I reversed mine.. Strange!
I believe it's supposed to work the way it did for you. Maybe some sort of glitch.. What version of max are you using? That test was done in 5.19