Computing negative predelay
I'm attempting to make a patch that will compute the amount of negative predelay that I need to add to a track for the different articulations of the orcestral instruments that I am composing with.
I have something that seems to work, though I am unable to truncate the output to only show the first result. Any insights would be much appreciated. (The instrument whose attack I am measuring is contained in the VST plugin SINE player).
Would this be a solution ? (You would have to find an event that re-initiates gswitch2 to position 0):

Edited
Thanks-you for taking a look.
I tried your suggestion and it resulted in the following:
Here is the initial output from my print:print: 85.076875
print: 92.989167
print: 102.466958
print: 106.551333
. . .
(I have been running the process a few times and then averaging the result of the first line)
With the change you suggested, it did remove the unnecessary additional print statements, but accumulated the results in a single print (It also looks like the values might have been front truncated - given that the time measurement is starting around 427ms.
(Is this related to your comment about deciding when to reinititate [onebang]?)
Here is the output after making the above tweak:
print: 243.77925 243.77925 243.77925 243.77925 243.77925 243.77925 243.77925 252.751459 253.837084 253.837084 253.837084 253.837084 253.837084 253.837084 253.837084 264.403417 265.502125 265.502125 265.502125 265.502125 265.502125 265.502125 265.502125 276.008417 277.065209 277.065209 277.065209 277.065209 277.065209 277.065209 277.065209 277.065209 287.650209 287.698292 287.698292 287.698292 288.805709 288.805709 288.805709 288.805709 299.248459 299.323959 300.391917 300.391917 300.391917 300.391917 300.391917 300.391917 310.858667 310.940084 315.19225 315.19225 315.19225 315.19225 315.19225 315.19225 322.474917 322.474917 322.663042 322.663042 323.858334 323.858334 323.858334 334.216209 335.306334 335.306334 335.306334 335.306334 335.306334 335.306334 335.306334 348.841917 348.841917 348.841917 348.841917 348.841917 348.841917 348.841917 348.841917 366.348959 366.348959 366.348959 366.348959 366.348959 366.348959 366.348959 366.348959 368.899084 370.974417 370.974417 370.974417 370.974417 370.974417 370.974417 381.502834 381.502834 381.502834 381.502834 381.502834 381.502834 381.502834 381.502834 392.140709 392.197625 394.239959 394.239959 394.239959 394.239959 394.239959 394.239959 403.688709 403.756792 404.800875 404.800875 404.800875 404.800875 404.800875 418.461375 418.461375 418.461375 418.461375 418.461375 418.461375 418.461375 418.461375 426.929834 426.994459 438.788459 440.025375 440.025375 440.025375 452.556459 452.556459 452.556459 461.759584 461.759584 461.802042 461.802042 461.802042 461.802042 462.887042 462.887042 462.887042 462.887042
. . .
I then added a zl.mth 0 to the patch to try and isolate the first entry, which seemed to do the trick, but the value looks dubious.
Here is the output:print: 453.737291
Here is a pic of the patch with those additions. . .
(I had also added a gain to make it easier for me to adjust the threshold)

Does anything wrong jump out at you? All suggestions welcome :)
Sorry I first posted the onebang option but removed it and edited with a better solution because I realized that zl.group keeps compiling new values that are all output at the same time when it receives the new bang. The edited post shows a solution that should solve this issue, I believe.
Here the first value is forwarded to print and switches the gswitch2 to its second outlet so that all other values are output through it. When you switch back to the first outlet, then it is ready to receive another value and the cycle goes on.

That did the trick - thank-you so much for your help!
This may be closer to your first attempt and avoids possibly useless iterative time laps calculations ?

Thanks! Yes, that does seem to produce more repeatable results.
I do have an additional question about the overall approach that I was taking, and am concerned that I might have a misunderstanding of the way that the thresh~ object interacts with the digital audio stream.
My assumption was as follows. . .
It should be ok to directly connect the left and the right audio channels to the thresh~ signal (input) port (there is some variation between how the left and the right channels oscillate - so I wanted to ensure that both were considered)
setting the low threshold number to something small should have the effect of clipping the beginning of the envelope off of the signal (in addition to clipping all subsequent valleys in the signal oscillations)
capturing the time differential between when the MIDI command was issued and when the instrument sound passes the first threshold should be a reasonable measure for the amount of time that will need to be added as a negative predelay so that when quantizing the instrument, it sounds in time.
When listening to the signal as it leaves the threshold, it is very loud and noisy - I assume that this is because all of the valleys in the signal are being clipped - and a digital audio signal in this respect wouldn't behave like it's analogue counterpart (where the overall volume shouldn't be louder - is this a correct and/or reasonable assumption?
The low threshold number is a fiddly one to set, as each articulation's overall average volume different.
Is there a better more rigorous way to approach this problem? If so, do any of you have any suggestions of what objects I should investigate? Or conversely, Is there a good way for me to estimate what the low threshold number should be per articulation?
This current patch has already been a huge help, but looking to further my understanding of Max.
Cheers!
This is a bit out of my competence zone, and I don't want to mislead you. A few comments, though :
- You add the L and R channels out of the VST (and increase the gain as well) ; so the signal arriving to the thresh~ object is quite loud .... may be wise to remove the gain and/or average it, so that you capture L and R variations, but avoid doubling it ?
- to be sure the 2 timer events are constently captured correctly, you may secure the sequence of bangs with something like this :

Hope this helps. I leave the floor to the experts now ...
Fantastic! Thank-you both!
I do have a noob question regarding the example directly above. . .
How do you sandwich a gate between the prints? (Or am I misunderstanding your dependency diagram?)
Cheers!
To answer your last question :
that was a nonsense alltogether, including advices about sampling rate, audio interface and print - gate 1ms suff.
I guess that is why it got removed by the poster ...
open gate when sending note, close it when first > nn mach arrives
is ok, but trigger timer and open gate using only note on.

but I would go different way...
as first which latency do you need to compensate ?
difference between sounds with differnt attack in same sample library ?
or that together with actual plugin latency - which can be eruated by sending get -10 message to plugin ? (in case it is programed correctly)
or is it real perception of attack needed from sent note till it sounds through Live track output, or when it gets recorded ?
measured elapsed time in max is not necessary same as what at the end gets output or recorded in Live.
only 1 approach can help 100% in that case,
send quantised notes at interval like 1/4 and record output
into a track in Live, measure distance from 1/4 point till attack
portion of recorded sample.
that could be done as well in max with buffer/waveform etc,
but again - it does not show what at the end happens in Live.
if on the other hand, you don't need to care about live and it's timing,
but only need to align different samples, you can do it in max alone.
let's have a simple example: if you measure threshod level of
foot hihat it will trigger earlier than what you would feel
as a hit.

or untrained guitar player that touches the string earlier than he pickes it,
causing transients before real sound ...
Thanks for your help! I am looking to compensate for the latency in attacks for different articulations of the same instrument in the same sample library.
For the time being I am just recording the amount of negative pre-delay that I will need to apply in a spreadsheet.
> "Does anything wrong jump out at you? All suggestions welcome :)"
VST instruments usually do not have any latency other than the vectorsize (of 32 samples in all major DAWs)
what jumps to my eye here is that you seem to underestimate how much (or that) [egde]~ is not continuous and introduces latency, too. and it also depends on the current vectorsize of the max runtime, just as converting a bang to an audio click or sending midi to vst~ would do.
so comparing the time an audio event arrives with the time a message arrives is kind of impossible.
Thanks Roman.
The issue I am trying to mitigate is the inconsistency between the attack times between different articulations of the same instrument in the VST by added a track negative pre-delay so that I could then use quantization.
In your experience, do functions like [edge]~ produce consistent latency? Or does it's computation time significantly vary between executions?
Is there a way to get a log or a heatmap or something of the compute time taken by each node?
i see, for different sample programs it can of course make sense (and then you also do not need the results and the compensation to be sampleexact like within the same DSP context, f.e. parallel processing)
and i am partially wrong about edge (see picture), because those 0.7 ms quatisation of time values are often not an issue, because it happens parallel in most setups. yet it is difficult to test...

maybe in your case you want to use visual control instead of ms or samples?
or wait for actual "attack" transients to appear? or use RT30 or something?
however, there is no need to compare sample program A vs the midi input. compare it directly vs sample program B´s audio output, so that you are comparing things on the same domain.
Thank-you for the advice Roman.
I'll make that change.
Going to look into ways to analyze the sample as a whole (still very new to Max, so my vocabulary with it is currently very limited)