latency of send~ and receive~ objects

Charly Beck's icon

Hi,

my dynamic signal matrix works with latency compensation :)

There is only one issue i was not aware of: The send~ and receive~ seem to have a latency. I could not find any comment in the doc about this but there seems to be a small latency causing phasing if in between a channel path is a different number of sends and receives.

(I'm pretty sure that it is not my problem becaues the latency of the plugins i used to test are much higher and audible by delayed signal, where a phasing remains in certain graphs and this should be caused by the latency of the mc objects.)

I'm not sure if i know where this comes from but i guess it is caused by the mixers which use a buffer and have latency (?)

Anyway i wanna get rid of this and i wonder if the latency of the mc.send~ mc.receive~ mc.pack~ and mc.unpack~ are defined values and if they are documented.

Luigi Castelli's icon

The latency of an [mc.send~] [mc.receive~] pair is one signal vector size.
[mc.pack~] and [mc.unpack~] do not introduce any latency.

Jean-Francois Charles's icon

send~ and receive~ do not introduce any latency when not necessary. For instance:

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

When necessary - for instance when send~ and receive~ are in a network with feedback - they introduce a signal vector of delay, because it's the only way to compute the audio.
Charly, maybe you are using a matrix~ to route signals in/out of different plugins with send~ and receive~ objects at the output/input of your matrix~. This architecture includes feedback, thus you will get latency.

Charly Beck's icon

@jean yes this is exactly what i do. so i get 1 sample per send/receive pair?

(1 signal vector size = 1 sample?)

Charly Beck's icon

ok that worked. i guess it should have though i imagine i still hear phasing but it is a minimum with the calculated 10 samples delay.... i mean it should be perfect and maybe what i hear is just the volume change?!

still it's a bit ugly because i just copied the code from the latency calculation because i wanted to have this value seperately... justin case ;)

thanks for the help, you two!

Roman Thilenius's icon


the vector size is user definable (audio prefs), can only be changed when using poly~, and is never only 1 sec.

i recommend the highest possible value when delay is no problem and you dont need overdrive (for example in a nonrealtime context), if you want to use audio together with a stable clock you should be fine with 32 samples.

Charly Beck's icon

hm.. are you sure we're talking about the same? i dont mean the global audio buffer but the buffer in the mc send~, receive... if it is in setup its the question if i can query it in the max device.. because if this differs the mixer will sound shitty...

and i'm talking about samples not secs. i had 10x send~ an receive and a bypass and i could hear it phasing. with 10 samples latency compensation its the best result i could get but still sounds strange a bit..

i'm having a look at the prevs ... i think you are aiming the performance and scheduler settings in the audio status settings? the io vector size seems tobe the buffer for ad/da card.. but the signal vector size... i gonna try what happens when i use this value.

Iain Duncan's icon

I think Roman mean to type samples not sec. This is probably all known to you, apologies if so and I'm misunderstanding the questions, but in case I'm not....

My understanding is that there are are two vector sizes at play, the one for your card, and the vector that is how many samples are calculated in one audio calculation pass of Max. The second is the one that matters for feedback and latency within Max (before hitting the OS). In most audio platforms like Max (Csound, SuperCollider, etc), your control rate is the sample rate divided by this vector size, so calculations can be done at either audio rate (of a whole vector) or control rate (of a scalar). Each control rate pass pre-calculates your vector of audio samples, which is then handed off to the audio subsystem in use. So if you want true feedback in a delay, the shortest possible delay time is determined by the size of this vector, and I'm thinking this is what happens when you send to the top of the graph: it gets rendered on the next vector calculation, introducing one vector of delay.

This page is decent if you haven't read it.
https://docs.cycling74.com/max8/vignettes/audio_status

Jean-Francois Charles's icon

Charly, with the structure of a matrix~ and things going in and out, you will always have delay introduced by your send~ and receive~. The delay "in the send~ / receive~" as you say will be one audio vector ("Signal vector size" in Audio Status).
To avoid that side effect, ideas include adding some compensating delay to your direct sound, or rebuilding your patch with a different architecture.

Iain Duncan's icon

I guess the other option is to move everything into C. (I say this because Charlie is posting elsewhere about C dev). Then you can do everything you want in one vector calculation pass. At the obvious cost of some C hacking... :-)

Roman Thilenius's icon


yeah, max is a program where you can set that "internal" vector size, too, where most programs just use 32 and dont even tell you about it. ;)

"because if this differs the mixer will sound shitty..."

i dont know offhand, but in max4live it will be as big as live tells it to be, probably 32 or 64 samples.

so instead of setting constants manually you have to measure the current vectorsize using dspstate~ and use that to sum up the overall latency of channel #7 in order to build a final latency compensator.

do the same with the samplingrate, and your DSP will then work in any max runtime no matter the settings. (for delay~ the samplingrate wont matter, but you might need it in places where you have to convert samples to ms)

Charly Beck's icon

@iain: thanks for the detailed description. that is some deep insight.

@jean: no i dont want to avoid it. it is the way i wanna process the data (see pic below). i allready have latency compensation running - for the vst plugins and for the send~ / receive~ latency. it works just my problem is now performance.

@iain: that is where 2nd post of iain hits the road.... maybe i'll try to calculate the buffers on my own. should be much faster than using send~ receive... it will be just a horror to connect all the cables XD because i dont feel like writing a vst host... at least not in the first stept.... but that will be a lot of work... mabe it's better not to do this in .net... although... when you dont use the garbage collector it performs quite good... i'm just quite sure the runtime of the animtion dll affects the other dll doing the buffer calculation. frequently the garbage collector might cause dropouts.....

Roman Thilenius's icon


& i somehow suspect that it might be avoidable to use 123 connections to patch 8 plug-ins.

Charly Beck's icon

well "everything" can be connected with "everything"... using 11 channels you need 11 input buffers per channel to allow latency compensation...

ok... there are feedback loops which are not conected but which buffers become unused depends on the actual graph. and since max does not allow dynamic allocation all buffers are allocated, though they are not used...

i think this is correct as it is...

Charly Beck's icon

11 channels * 11 input buffers + 2 (for main in and main out) = 123 send/receive pairs...

Charly Beck's icon

When i implement the buffers/mixer on my own i can
- use a buffer pool and allocate only buffers actually used
- allocate only as much buffer size as for latency compensation, grow dynamically and require "no" max buffer size

i wouldn't need 123 buffers thats true, but i need buffers on 123 different locatiions... thaats why i need to allocate all in max...

Charly Beck's icon

thanks for the tip with dspstate - i i allredy use non hardcoded sample rate.

still the empirical confirmed latency of 1 sample per send/receive does not match the 64 samples for signal vector size i dont know where to place them for calculation... still i need to test again but <10 was phasing heavy, >10 also... tested with this graph:

Iain Duncan's icon

Hi Charly, if you don't already have it, the Eric Lyon book is really good. You have to look up a few minor changes in the SDK, but I found it very helpful overall.

Charly Beck's icon

@roman: you wrote:
the vector size is user definable (audio prefs), can only be changed when using poly~, and is never only 1 sec.

well i dont use poly... i only use send~/receive~ does the signal vector size affect me at all then?!

Luigi Castelli's icon

I don't understand exactly what Roman was referring to, but the global vector size can be changed at any time through the Audio Status window or with the help of the [adstatus] external.

Also as Jean already commented, as long as you don't have a recursive structure a send/receive pair will introduce no latency. So what you are observing is kind of correct. (actually it should not even be 1 sample, it should be the same as if you were to use a patch cord)

Luigi Castelli's icon

Charly, I love your project. A graph structure where each node is a VST plugin allows for the most flexible and creative routings one can think of. However it cannot be properly done in Max. In the screenshots you posted I can see all nodes are always fed forward. As soon as you introduce recursion your patch will break or it won't work as expected mainly for latency reasons. Also as you add layers of recursion the combined latencies of multiple send/receive pairs will add. You are gonna end up with phasing issues all over the place. They are gonna be difficult to track and they will interact with the way you connect your nodes. Unfortunately Max is not great at dealing with recursive structures. It is not a matter of architecture.

I think you need to set up your project differently and probably as Iain suggested the only way to achieve flexibility and efficiency is by coding, not patching. I can see how a C custom external could get you there. But obviously it's a lot more work. Fortunately there are great graph libraries that could significantly help you and you could still instantiate a [vst~] object internally in C code. That way you don't need to implement a VST host from scratch.

Roman Thilenius's icon


not sure if it helps or if it is required... but...

only with send~ you can get a vector delay. or should i write it in one word? "vectordelay"?

[delay~ 32] wont allow you to make feedback loops at all, it will only delay the values but output the "same" samples. :)

regarding 11*11... well, you might have a reason for your current design, but i bet you can do it with less if you keep it dynamic.

to track the overall latency of what you built already you will probably have to actually measure it using a click~ and recording it.
however, as soon as you allow loops to be built in your plug-in matrix, it will be questionable how that affects the overall accumulated latency. :) no latency inside the routing would be more interesting here than the final output, but impossible. you can only compensate a delay between parallel branches (2 plug-ins on channel 6, 3 plug-ins on channel 7)

Charly Beck's icon

well regarding the feedback loops: i forbit this. these are the greyed out dots in the matrix on the bottom right of the screnshot. it was one of the first things i wrote for this project: feedback loop detection.

i guess feedback loops cant work with feed forward only, but it was never my goal to support feedback loops (in digital world they just sound ugly i guess) so they are not allowed.

as i calculated the latency for the plugins i calculate the latency for the send~ receive pairs i use. it is the same algorithm, just the values dont come from the vst~ object but are hardcoded. and as i wrote it is much better with the send/receive latency recognized. though it still sounds strange but maybe this is the level change. in theory there should be no phasing..

btw. would be great if someone downloaded the source from github, review it and give feedback... everything is online.

but yes, luigie because of performance issues i'm thinking about digging deeper into audio processing and write everything in one seperate max external. still its not to clear to me how to control the vst~ ojbect from c code but i guess it can be done with no limitations.

and yes, i think this is gonna be a nice creative tool for sound design. i was thinking of adding a randimize function for the graph and maybe even for the plugin parameters. and a sample to allow play one sample per key and a midi pattern player. all might be randimized. but before i can do this i must get rid of these performance bottle neck. i'm not sure where it comes from.

Charly Beck's icon

roman: you wrote.

regarding 11*11... well, you might have a reason for your current design, but i bet you can do it with less if you keep it dynamic.

agree. but i think it is not really possible in max, is it? When i write it in c/c++ i will do it dynamically of course. but as luigi said, this wil be some effort but also a oppertunity to learn how to implement audio. i never did this.

Charly Beck's icon

ah ja, of course it might be possible in max for example by creating the patch cords and the objects dynamically. but i stumpled over several problems doing this so i did not even start with it instead i build the whole structure staticallly by copying the objects.

Charly Beck's icon

ps: here is a little video of the graph animations:

https://youtu.be/YO-V9K6z8XU

you see verythime you add or remove a node the matrix bottom right is updated so no feedback loops are possible.

Roman Thilenius's icon


ok, i was under the impression that you wanted to allow the user to make feedback loops. because then you will end up with the following situation: for the output there is no need for more than compensating the parallel stuff, and for the loop itself it is not possible. :)

yes, comapred to c++ making such things dynamic in max is a bit complicated. but there is a range of options, for example you coould move the plug-ins inclusing their settings to another slot silently, using poly or mute, using scripting..

how does the DSP/matrix part looks like? (i ask because i wonder why you use send~ when not for loops) sry if that was covered already in another thread^^

Roman Thilenius's icon


if you want to take it to the extreme, you could double the instances you are reverse-connecting to. this trick can be used to pretend to have a "feedback FM operator" among other things. (you use a copy, not the starting instances.)

Charly Beck's icon

oh actually it is possible to make feedback loops. it just sounds ugly ;) ok in the meantime it is no more possible since the latency calculation might step into a endless loop.... but for the max send and receive i caused feedback loops during development. and it just made "feeep"

ok this with the fm is not clear to me haha.i think for the first step i gonna leave it by avoiding feedback loops. yet i thought of make an option to switch the node function from "vst plugin" to "oscillator" or "adsr envelope" so it will become a modular synth.

you know i always wanted to have a synth where it is possible to insert vst plugins before the envelopes for example... maybe some day the project will be in sthis state.

yes i think too it is possible in max to implement some dynamic stuff... just it is very complicated, unhandy and performs bad. i think i will go the way write an extra external to deal with matrix mixing...

well the source is only... i just copied the channel objects together with the sends and receives and the delay buffer.... it just implements the "worst case" all the time and the architecture is asap (as static as possible) which is the prefered architecture for hard realtime embedded systems, because when implementing the worst case all the time you can be sure the system can handle it and determinateability of runtime behaviour is easy to keep.. of course not the ideal thing for this project...

https://github.com/DeepSeeBee/CbMaxObjects

Charly Beck's icon

@ian thanks for the book proposal. one should definittely read more books ;) (instead of messing around and finding the solutions on his own haha)

Roman Thilenius's icon


"you know i always wanted to have a synth where it is possible to insert vst plugins before the envelopes"

yes, that is probably the moment when you should start using max. :)

there is nothing better than layering two synth presets with already existent envelopes and then add a third one on the sum which is fully under your control, at audio or scheduler rate (something an envelope plug-in in protools can not do)

...

when i have an audio module in max, i am attaching the latency of the module to the signal connection, and in the next module it will be added to the latency of that module and later ends up in the mixer for compensation of channels. ;)

but then again, i am using max itself as patching GUI, so you cant prevent the user from making a feedback lopp where it is not allowed.

Charly Beck's icon

true yes... however, i like max. though it is more software development than patching cable fun like in a modular synth. of course possibilities are endles. and so the bugs are ;-)

this is why i can not integrate it in my creative process when making music... as said it is more a software development issue for me. i need some layer above max. this is why i build such devices...

Roman Thilenius's icon


i would be interested to continue - how´s your latency compensation system doing?

Charly Beck's icon

well, i allready wrote in the other thread. But more detailed again:

A week ago or so i stopped. I created the hardwired graph using the c# code (Just using mc.+~ and mc.delay~)

it was outputing signal. (I saw it on the signal meters. I also checked the connections and it correct.

Yet i didn't hear anything because i was working from remote desktop with no audio. So i could not hear if there is some phasing caused by latency of the mc.+~ objects. But it should be quite simple to add the required number of samples to the delay. I wanted to test this but i started working on a remix project. Yet i wanna think of some test-environ to make it really accurate.

Yet i'm not sure how to continue with the project. Basically it is finished as a dynamic signal plugin chain but i thought of adding sampler and synthesizer features to the nodes. But this is another big task, maybe i will continue some day. I think the strategy is good. I could create any graph dynamically so the virtual synth graph would be very fast as everthing is connected hard wired.

Also i might release the binaries for the current version on maxforlive.com when i finally tested and fixed the last few samples on the delay parameter. Currently all files are are available through github. (See link in my profile page)

Charly Beck's icon

Future Stuff:

So v2 of the system may extend the dynamic signal graph with vst plugins to become a modular synthesizer. i think it would be VERY interesting to place lfos and so on and to modulate single plugin parameters. yet the effects i did with different plugins without modulating parameters sounded quite freaky... hehe

But it will be a bit more complex. because i have audio signals and control signals then.

and since every node has several control message AND a audio stream this somehow breaks the logic of the signal chain where every node only has one input and output. although it extends the use case. this is why i'm not sure how to reflect this. though i didn't really think about it.

So basically it could also be understood as some higher level gui-builder for max patchers. Finally i would end up in rebuilding the max gui but that is not the goal. I need to find some simplified concept. Because it should be still the fun of a modular synthesizer and not the bug-searching software development stuff you do in max. This can be done by having a preselected number of basic objects like lfo, envelope etc.

Yet it would be very cool to build your custom modular modules using native max and then include them to that thing that still lacks a name.....

Basically my goal was just to play a bit with a signal graph, latency compensation and max using c#.... Let's see how it will develop.

Any ideas welcome.

Charly Beck's icon

btw... if someone wants to listen to some synthpop: here is the remix i did in the mean time.


Roman Thilenius's icon


"But it should be quite simple to add the required number of samples to the d
elay."

the thing is, i dont find it simple. "normal" PDC is easy, thats a no brainer. but the parallel stuff makes it quite complicated.

what is simple is to look at a custom created graph, knowing that two of the 7 plug-ins have 1024 ms delay, and then insert compensator modules respectively to the other nodes, manually via the GUI.

but to program an auto compensation feature for that thing is different. sure, i had in in my head after 2 minutes, but i dont find an easy way how to implement it, because every node must be checked against all other nodes for beeing connected to the same next node, too.
then sometimes you have to sum up values, sometimes you only have one plug-in with latency, and building a messaging system for this creates a mess.

so mine still without after all the years.

and to be honest i am also not too fond of having 20 active delay objects all the time, just in case i eventually need one or two of them.

about these modules.

well be careful not to recreate max in max. :) different number of outlets (or even variable number of outlets?) makes the system almost unmanagable.

but of course what you could do is always use a maximum number anywhere. for example 4 audio in and 4 audio out everywhere, even when not needed. you could send them again in the fashion of non-MSP forward/receive (like i do it with connection between bpatchers) and press the 4 signals and up to 100 messages/number into a single connection.

a module could output a master phase plus already-divided by 2,4,8 phases and the current speed in BPM as number all throught one cable that way. if it looks cool when you work with it? not sure.

however, if you want to create something which should serve as generic solution, make it as simple as possible in the beginning. t here are a lot of generators, filters, effects, where 1 in 1 out is sufficient.

Roman Thilenius's icon


what is cool about using non signal connection for signals is that you can make themn "incompatible" with each other.

i for example have 3 different sorts of 10-channel connections: generic ones, 10-channel vbap surround sound field, and partial dynamics signals. they have different names and if you connect wrong objects to each other, the signal will not be unpacked inside the receiving object.

that can become very important if you use audio, FFT, CV, and 32 bit parameter signals in your modules.

Charly Beck's icon

> must be checked against all other nodes for beeing connected to the same next node, too.

actually it is much simpler. The place where you need to do the delay compensation is the input of a node. here you have to mix several signals, each signal has a delay. so you must calc the max delay and subtract the delay of the individual signal. thats the delay for the compensation. then you forward the signal with the maximum delay, that will be the delay for that signal for the next node.

To make it more applicable it would be nice to have some list of meta info values for an audio stream. So you could comminicate the delay together with the stream requiring only one connection. Then it is easier to avoid some confusion in the patcher. however, since something like this does not exist in max you have to deal with it somehow differently. but doing so would allow max an automatic delay compensation without the user recognizes anything like delaycompensation actually exists.

i allready have this delay compensation running and this works. when i said, it's easy to add additional delay, i meant the delay which is created due to mixing the signals. i did not recognize this for now which should result in a phasing. but the "long delay" from the plugins is allready correctly compensated.

Charly Beck's icon

> what is cool about using non signal connection for signals is that you can make themn "incompatible" with each other.

hehe some kind of type save audio stream huh :D - makes sense.

Charly Beck's icon

however, to build that graph handling with the delay compensation in max is indeed complicated. that is a problem where i would definitely shift to some procedural programming language, because this kind of problem is not something what max is made for i think.

Roman Thilenius's icon


"The place where you need to do the delay compensation is the input of a node. here you have to mix several signals, each signal has a delay. so you must calc the max delay and subtract the delay of the individual signal."

that´s the "normal" way of PDC like in a summing mixer and is exactly what does not work as soon as parallel branches come in the way... or it would mean that you have 10 delay obejcts in every node, which is what i´d like to avoid.

imagine you would like to extend it to 50 plug-ins, then you need 2500 delay objects.

and calculating the delay times is also not becoming easier, it is always the same.

what would be cool would be an algorithm which 1. checks your graph for serial/parallel connections and 2. checks which plug-ins have a latency of 0 and not 0, and then enables delay compensation only for those nodes who need it.

Charly Beck's icon

Well what would be the alternative? I think there is none. Delay must be done before summing. If there is a delay you need to compensate it exactly at this posiition before summing.

Opt1: The optimiziation you are talking about can be done within the strategy i'm talking about: If there is no delay in none of the node's inputs you dont need a compensation. If there is a delay you need to compensate it. And you need to compensate it at this position in the graph. Once summed you can not correct a badly done compensation. This is why it is too late to do it later ;)

But you do not need to treat the graph at a whole. It is a thing that can be handled seeing only the inputs/delays of one node.... Stay local - stay at home :D

Opt2: An exception is when the delay is equal for all inputs. Then you could skip the compensation and forwared the delay to the next node's input.

Opt3: Another optimization you could do is use same delay~ for plugin groups with the same delay - as long as there is more than one group (Opt2)

Atm i did none of these optimizations. Because it is very likely that the delay 0 has the optimization built in. Because it is not possible to treat the 0-delay case without special state: if your buffersize is 0 and you run the same code for buffersize > 0 the delay would just output nothing. You need a special state: If the delay is 0 you just copy the frame buffer from input to output in the dsp routine.

I think this is just how it is. I can not see any other possibility for optimization...

Just one tip: Do not spend too much time in thinking about such optimization. Lot's of optimization that seem to be very powerfull will be obsolete with the next feature stage. You should do only optimization that can be integrated in a proper design. Any optimization breaking this is very short-dated and is likely to no more work, if you treat the problem in a more abstract way, which always happens if you extend functionality. That is just my experience.

imagine you would like to extend it to 50 plug-ins, then you need 2500 delay objects.

No that is not correct. It depends also on the number of connections between the plugins and how they are connected. Again calculation is a bit too high level mathematics for me but imagine you connect 50 plugins serial: You need no delay at all. If you connect 50 plugins paralell you need max. 49 delays.

You are dealing with 50*50 which apprarently happens if you connect every delay to every delay. (Actually i think it is factorial(50) )

I think i know what your misunderstanding is:

The static design of the model must support "connect any to any" but a instance for a specific graph never uses all these connections. You should distinguish between the static model and the instance of a model. That is quite similar to a static class design and a instance of an object: You can build a house with as many rooms you wish in any color. But one instance of the class "House" has a specific number of rooms and a specific color...

Just as the graph-model needs to be able to connect any node with each other. But one specific instance of such a graph never uses all possible connections.

Think of a house, which is painted in all existing colors and has the same time "No Rooms" and "infinite number of rooms". This is not possible. (At least not unless you build your quantum physics house in multiveres-cyberspace :D) But your model often supports such cases as for examle a house with no rooms at all. (For example your house class could have a list of room objects. this list may be empty although this may be defined as an invalid object state).

A good way to build a model is to look at the "worst case". System analytic beginners or people who never learn it tend to say things like "Yes but this case is only required very rarely". The answer is: For the system it does not matter how often the case happens. Either it is supported or not. That question is only of interest if you think about effectivity of certain optimiziations. But this is the last step in a development cycle and should never break the design (an early step in the dev cycle)

Roman Thilenius's icon


for the structure itself (each node has 10 inputs with delays) there is no alternative.

but i think it could be funny and eventually even useful to think of optimisation strategies.

if you would compare the times of 3 & 5 with each other here (picture) to "synchronize" them, the compensator in node 5 and node 7 could be turned off.

so it would be cool if a node knew if it has 1 or >1 inputs connected to it. because for only 1 input you could transport the time value to all outputs and turn its own compensator off. (and in your OPT2 situation - exactly)

yes, i was so far. :) but i havent found the perfect solution for myself.

latency nonsense

Charly Beck's icon

if you would compare the times of 3 & 5 with each other here (picture) to "synchronize" them, the compensator in node 5 and node 7 could be turned off.

yes i think our answers overlaped. This is Opt2 i described above i think. :)

Charly Beck's icon

your thoughts really help and are inspring for me. (y)

Charly Beck's icon

but think of the information flow:

Node4 forwards it's audio and latency to node 5.
node 5 decides not to compensate because only 1 input exists. so it forwards the audio and latency to node 7.
node 7 sees it has 2 inputs but both with the same latency. so it decides to skip compensation and forwards audio/latency to mainout.

all decisions can be done locally in the object without the need of a "mediator" object which looks at the graph as a whole.

Roman Thilenius's icon


"No that is not correct. It depends also on the number of connections between the plugins and how they are connected. "

well, when each node is an instance of the same max patcher and you dont turn off compensation when not needed, each node has these 50 delay objects running. (which in my special case might need more CPU than the VST hosted in the node ;) )


Roman Thilenius's icon


my main problem with all the latency stuff would be how to get the information that the delay has changed gets to the next node.

it is possible with max, but it is quite laborious. i normally use global variables for that, then the query works from a single loadbang.

Charly Beck's icon

well, when each node is an instance of the same max patcher and you dont turn off compensation when not needed, each node has these 50 delay objects running. (which in may special case might need more CPU than the VST hosted in the node ;) )

Ah ok i see. Yes true, with the old asap approach i had a lot of delays.... That was the aporoach i cancelled due to very high cpu load.

my main problem with all the latency stuff would be how to get the information that the delay has changed gets to the next node.

Indeed. I never thought to the end of how to do this in max only. I experimented with poly. Once every poly instance has it's unique id you can use send/receive to send messages from one node to another. But a also cancelled this apporach after a discussion where it was questioned, if mc.send~ / receive~ is officially supported by cycling or just works by good luck...

However i think going to c/c# was the right solution. You can even get c# to run on mac i think though i don't feel motiviated to figure out how this works. Neither my woman will be motivated to lent me her mc book i guess ;)