order of DSP chain in Max5...

Pierre Alexandre Tremblay's icon

Dear all

I use to be able to predict the order of the DSP chain in Max/MSP4.6

Now in Max5, I have problems, like the one I sent in this thread. Is it due to new features like thread-safe access to buffers?

If anyone can read the following thread and confirm/clarify, that would be appreciated

thanks

pa

AlexHarker's icon

Hi pa,

In my experience it is not wise to make ANY assumptions about the order in which MSP objects will proces based on right-left ordering etc.. However, there is one very safe assumption you can make - an object will not process until all its inputs are processed. So - if one object's (let's call in X) output is connected to another objects (let's call it Y) input you know which order they will process in (X then Y).

Therefore, you can use this to force order of execution by doing things like this:

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

You've might notice that this patch does exactly the opposite of what you want - the index~ will proces first (this has nothing to do with the position on the screen - the connection of the *~ between the index~ and poke~ is forcing this order.

The problem is that poke~ has no output so you can't easily do the same the other way round. However - if you put the poke~ in a poly~ eith a dummy out~ you can connect the output of the poly~ to the index~. You can connect a [sig~ 0] to the dummy out~.

This is a little annoying, but 100% reliable. If poke~ had a dummy output this would be a lot easier (feature request c74 ?) This is pretty much the only scenario in which order of execution has ever been an issue for me, but it is is very convenient to be able to control this - I don't want to have to have a vector of delay so I would use the poly~ trick if I needed to do this.

There is a thread in which I posted a patch using this trick. The message is:

Download the zip file and look at the Transpose2.pfft patch - you'll see some poly~ objects throughout the patch that are being used to force the order of execution.

Hope this helps,

Alex

Pierre Alexandre Tremblay's icon

Thanks Alex

your safe assumption, with the *~ 0. is working, though I hate to do this kind of dummy thing.

It also explain why my previous patch, which was doing something like in the following, was working and is still working in Max5.

you are, as usual, a wise man. I'll use your trick to trick my dsp chain.

cheers

pa

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

Zachary Seldess's icon

Alex, great post.

I've been implementing optimized panner objects designed to work inside poly~ @parallel 1, and order of the DSP chain is a critical issue if I want to design a lockless multi-threaded approach (although I have a locking approach that works extremely well, lockless would just be prettier...). I read this post a year or so ago, and decided to just go with a smart and minimally locking approach, but now I'm at the point where I'd like to at least try lockless, and see if it improves performance.

In your example, you're dealing with MSP outlets/inlets. I'm wondering if I can use Max outlets and inlets (like with tapin~/tapout~.. tapin~ sends the message "tapconnect" to tap out when dac is toggled). Do you or anyone at Cycling have some insights into how tapin~/tapout~ and this passed message work to ensure dsp chain ordering?

I'm wondering if the tapconnect message triggers an overridden send_dspstate-like method that calls the tapout~ perform routine (not even sure if that is possible)...

best,
Zachary

Zachary Seldess's icon

Interestingly, I'm find that the DSP chain in less predictable in Max6 than in Max5, when poly~ @parallel 1 is used. For example, I have some multi-threaded send~/receive~-like objects (currently performing ca. 2x better than vanilla send~/receive~ when using many of them inside a poly~). But when I only have a few instances of "senders" inside the poly~, I can clearly see through some debugging information that the dsp chain is often getting reordered on consecutive passes, for example:

sender 1, sender 2, receiver
sender 2, sender 1, receiver
receiver, sender 1, sender 2

The first two don't bother me, can't control the order of perform routine completion for instances running in different threads (that's the point of it). But the third result is bad (the receiver is outside of the poly~, btw). It means that the receiver doesn't have anything new to read for that pass through the DSP chain.

I don't get this behavior in Max5, FWIW.

This makes me think some sort of absolute ordering guarantee is really needed, if I want to work in Max6 with my objects. Or I can revert to a more cumbersome locking approach that seems to guarantee order...

Hope someone has some thoughts on a tapin~/tapout~-like setup. I don't want to use MSP outlets and inlets, for efficiency reasons.

best,
Zachary

Zachary Seldess's icon

Sorry, that last post was probably noise. I can seemingly cause the above behavior in one of my objects, but it must be due to something idiosyncratic to my code. I spent some time today and came up with a barebones implementation to see if I could reduce the behavior in a way that I could report here (and share the code), but dsp chain is behaving fine there. As a result of that exercise, I've basically arrived at a lockless approach (within the perform routines, that is).

I'd still be interested in the idea of using a Max connection between objects to ensure dsp chain order, or more realistically, to ensure the "receiving" object is behind by one signal vector (which is the way tapin~/tapout~ works?).

best,
Zachary