What's new

Logic Scripter—multiport track delay compensation?

Dewdman42

Senior Member
I will also point out another benefit of using channelizing for your articulations is that can easily level-balance all your articulations
 

lettucehat

Senior Member
Thank you both again for all of the insight. I think I'm fighting the inevitable necessity of redoing many of my VEPro instances as channelized, at least where I care about negative track delay. I'll see how it goes.
 

Bear Market

I am Legion
To get around this, in VEPro, I set up multiple outputs in each Kontakt instrument. So in CB Solo Tpt, I have legatos going to st.1 (1/2), longs going to st.2 (3/4), and shorts going to st.3 (5/6). In VEPro, I hit the plus button to create two additional audio outputs that correspond to the Kontakt outs. These 3 audio outs are where I put the Expert Sleeper Latency plugin. All audio in each VEPro instance still sums to a single stereo return that feeds back into Logic. You could obviously use multi-out if you wanted.

Still not sure this is all going to work... I just finished setting up all the instruments today and need to do some stress testing with an actual track. If it doesn't work, I'm seriously thinking of switching to Cubase.

Did you get a chance to try this out? I'm curious how you find it working since I'm also in the planning stages of setting up a VEP template for Logic.
 

marclawsonmusic

Senior Member
Did you get a chance to try this out? I'm curious how you find it working since I'm also in the planning stages of setting up a VEP template for Logic.
Sadly, no. I am still in the process of building everything out with the Latency Fixer plugin. Figuring out by ear all the values for shorts, longs, legatos is very time-consuming.

Performance-wise, things are good so far. I had to experiment with VEPro's 'cores per instance' and put that at 1 core per instance (I have an 8-core iMac and 9 instances in VEP). Things are hovering around 20% in VEP and Logic during playback (w/ audio track selected) and it gets as high as 40-50% when I arm a track for recording during playback.

One plus is the Logic project's file size is only like 2Mb. :emoji_astonished: This with about 285 tracks and full stem routing. My last template was only 80 tracks and about 80Mb in size. I think it's because I'm doing everything in the Environment so there is less bloat behind the scenes.

Anyway, it's a long process and I'm mainly doing it on the weekends so it might take me some time. I want to get a baseline setup with Latency Fixer and then take those values and put them into @Dewdman42's plugin as a second A/B test. My biggest concern is how to handle recording / live input - I know there is a 'play thru port / channel' that can be defined, but I'd have to figure out how to automate that. I'm also not sure about the 'delay Note On but leave everything else as-is'. I know there is a reason for that decision, but it seems counter-intuitive to how Logic handles region delay (the entire region is delayed). So I'm not sure how that will work in practice.

If you get a chance to test, I'd be curious to hear your findings, so please do share. Cheers.
 

Bear Market

I am Legion
If you get a chance to test, I'd be curious to hear your findings, so please do share. Cheers.
Thanks for your reply! It seems we're pretty much in the same place currently. I'm thinking of using an approach with more VEP instances though. The "live input" issue you describe is on my radar as well. I'll let you know if I stumble upon something useful.
 

Dewdman42

Senior Member
I want to get a baseline setup with Latency Fixer and then take those values and put them into @Dewdman42's plugin as a second A/B test.

I can tell you for certain that the script approach will use slightly more CPU then latency fixer. But probably not much more. LatencyFixer literally uses nearly no extra cpu at all. I mean any plugin is using a bit more CPU just by being inserted. But Latency Fixer is not doing any DSP whatsoever, it just reports a fixed value to the host and that's it. So in terms of the lightest CPU approach...I think latency fixer is probably going to be less CPU...though not much less because my script doesn't really use that much CPU either compared to many other plugins.

There is also some concern that you have all that elaborate VePro audio routing in order to seperate the shorts, longs, etc. That could effect CPU a bit also, and hundreds of latency fixer instances.

But with Scripter you have javascript to execute. So its hard to say which way would be better or worse, but I don't expect to see a significant difference either.


My biggest concern is how to handle recording / live input - I know there is a 'play thru port / channel' that can be defined, but I'd have to figure out how to automate that.

With the pure latency fixer approach you are doing now, you don't have to change or automate anything for live channel. LatencyFixer doesn't actually delay anything. It passes through the audio untouched. All it does is report the latency to the host. LogicPro can't really do anything to correct PDC on your live channel. Whatever you play is what you will hear...if the sample library has a lot of latency, then you will hear that latency as you play the part. PDC takes effect when you leave live mode and then LogicPro executes PDC by sending midi data from the regions ahead of schedule to the instrument channel...in much the same way as if you were using a negative track delay. it just does it automatically according to whatever latency value is being reported from latencyFixer.

So what I'm trying to say is, with the approach you are now trying, you should just be able to record your parts and when you playback they will be adjusted and you shouldn't have to change in and out of live mode per say.

The same goes for if you were instead using negative track delay (if it actually worked right)...when you record the part, the negative track delay would have no effect at all, but during playback it would.

With the Scripter approach its a little different because the script imposes a 500ms lookahead delay in order to do what it needs to do. There is no way around that. And that lookahead delay would be pretty much impossible to have on while recording a particular part. So in the Scripter approach there is a GUI control to select one track to ignore the lookahead..that way you can record your part without hearing the lookahead delay (though you will still hear whatever delay is built into the instrument sample). Then you have to make sure to turn that off for playback. So yea that is a little bit of a headache honestly.


I'm also not sure about the 'delay Note On but leave everything else as-is'. I know there is a reason for that decision, but it seems counter-intuitive to how Logic handles region delay (the entire region is delayed). So I'm not sure how that will work in practice.
To be clear I am still undecided on which way I like working better also. I see pros and cons both ways. Having to setup elaborate VePro routings and hundreds of latency fixer instances is not necessarily that easy to setup. On the other hand...less Scripter mumbo-jumbo is always a good thing when it can be avoided. Also, there is the point about having to disable lookahead for the track while you are recording part and then re-enabling it for playback...it can become second nature after a while, but still it is a step that has to be taken.

To explain about leaving "everything else as-is"...let me try to explain that better...

when you use negative track delay or the latency-fixer approach, the entire midi stream is basically being played ahead of time, by a fixed amount. That is kind of a brute force adjustment. That means NoteOn's, NoteOff's, CC events, etc..will all be sent to the instrument early, ahead of the grid.

That makes sense for NoteOn events because if an articulation sample has a slow attack, then the attack needs to start ahead of the grid in order to sound like its on the grid. However, the release of the note needs to happen on the grid, otherwise it might sound like it was terminated early. Same with CC events. If you are trying to draw an expression curve for CC11, the peak needs to be heard when you visually see it on your CC automation lane...at the proper time on the grid. There is no slow attack built into how CC expression works, the effects of CC expression are going to be immediate. So CC events should not be sent to the instrument early..they should be sent exactly on the time you are expecting them to be sent...as they appear on the grid.

Same goes for PitchBend and Aftertouch.

When you use simple negative track delay or latency fixer...then all those events are being sent to the instrument early...at whatever amount you have specified needs to be there for the NoteOn slow attack to sound on time...but expression events, and probably NoteOff...will end up sounding too early because their effects are immediate.

With a scripter approach we can control which events need to be early..and really its just NoteOn that needs to be early due to slow sample attack. Everything else needs to be exactly on the grid.

When NoteOff's are early, the result will be that it will sound like the note is ending too early. But this is an area we haven't spent that much time thinking or talking about. Its quite possible that. many sample libraries have some slow release rates also that perhaps could be taken into consideration separately from slow attack rates. Its quite possible that in some samples, the Noteoff also needs to be early..but not necessarily by the same amount as NoteOn's! Short samples typically just end anyway..they don't sustain while you hold a key down..so the NoteOff time doesn't even matter. Where it matters more is sustains, tremolos, trills, things where the sample has a hold time until the actual NoteOff comes. Even legatos don't really matter, because in a legato transition, the next note will be coming early (good thing) and that will cause the previous note to end early..regardless of when its NoteOff comes...the NoteOff just needs to be overlapped, but whether it overlaps by a lot or a little, won't matter, either way the first note will be terminated early to start the legato transition...early..which is generally what you want...so that the next note reaches its transient peak on the grid.

So... at least in terms of NoteOff...it may be totally ok for them to be early..because really there are more situations where an early NoteOff termination won't matter after all; and only a few situations where it might sound like its ending earlier then it should be. I think the Scripter approach eliminates that rare situation, but it may not be often enough to justify the script approach.

On the other hand...CC expression events need to not be early!! So what about that? that's where Scripter can provide solution.

Now that being said, it gets further complicated.. If you are using CC instrument switches. (not as expression, but as instrument switches), those CC events do need to be early in order to make sure they are in front of the NoteOn's which are being sent early. See what I mean?

And sometimes there are certain kinds of expression that actually needs to be a bit of both, for example, Velocity CrossFade, as some like to call its where a certain CC will determine which sample layer is played, rather then using key velocity. Well in that case there will need to be at least one CC event of that CC# that needs to be before the early NoteOn, in order to start the initial attack with the desired sample layer...but while the note is being held...then that particular CC would need to be on the grid, in order to effect the sound properly in time... This last scenario I do not have scripted yet...and especially the simple script with faders that you like..will not be able to handle that because it requires specifying which CC's would need to be handled that way.

So while the script can help a lot fo put CC expression on the grid while sending NoteOn's early..it doesn't really account for that above scenario...and I don't know right now, I think it would be better to have the VelocityXF on the grid, even if the initial attack starts with the wrong sample..but maybe not..maybe that is just too complicated.

The truth is the only way to really handle it properly in Scripter is to have a way to specify every instrument and articulation for how it should be handled...and that becomes monumentally more complex.

so what I'ms saying is...there is really just no perfect solution. There are pros and cons all the way... I could yet make a version of the script that doesn't do the tricky different timing for different types of events, but just slides all midi forward..which would make it essentially do exactly the same result as the latencyFixer trick...but without having to create a super big elaborate VePro routing setup..and maybe that will turn out the best, but for the moment I'm still committed to trying to work out all the timing issues so that the script really does the right thing in a more effective manner then brute force sliding the region early, or using brute force negative track delay, etc..
 
Last edited:

marclawsonmusic

Senior Member
There is also some concern that you have all that elaborate VePro audio routing in order to seperate the shorts, longs, etc. That could effect CPU a bit also, and hundreds of latency fixer instances.
Great post, as always @Dewdman42. I definitely understand the Note On issue better now. Crap, it's probably yet another problem to solve down the line! Sigh.

FWIW - I am not using hundreds of Latency Fixer instances... maybe a few dozen. So it's not having much of an impact. Something like CSB is using basically 7x3 (each patch separated into legato, long and short). Anyway, I'll keep you all posted.
 

marclawsonmusic

Senior Member
With the Scripter approach its a little different because the script imposes a 500ms lookahead delay in order to do what it needs to do. There is no way around that. And that lookahead delay would be pretty much impossible to have on while recording a particular part. So in the Scripter approach there is a GUI control to select one track to ignore the lookahead..that way you can record your part without hearing the lookahead delay (though you will still hear whatever delay is built into the instrument sample). Then you have to make sure to turn that off for playback. So yea that is a little bit of a headache honestly.
One thought came to mind... would it be possible to ignore lookahead by looking at the transport 'is playing' instead of a specific MIDI port/channel?

So, if transport is playing, enforce all the delays, but if not... basically operate in 'ignore lookahead' mode?

I saw this code snippet and it got me thinking:

function ProcessMIDI() {
var info = GetTimingInfo(); /* get the timing info from the host */
if (info.playing) /* if the transport is playing */ Trace(info.blockStartBeat) /* print the beat position */
}
 

Dewdman42

Senior Member
I am also adding right now as we speak an option to my ArtAlignGui.js script where you can put it into super simple mode where it just does the same thing Negative track delay does...the brute force method...but with a fader for every channel.

that would enable you to put all the articulations in VePro on seperate midi channels, but not necessarily have to split out all the extra audio outs, and get exactly the same timing correction, and only a single instance of Latency Fixer for the whole VePro instance.

You can give that a try too. There will be pros and cons, the cc expression will just all seem early when it plays back unless you intentionally record it late so that the correction puts it in the right place hehe.

Great post, as always @Dewdman42. I definitely understand the Note On issue better now. Crap, it's probably yet another problem to solve down the line! Sigh.

Well let's just say...there are issues...and sometimes we may end up getting some particular library with some complications and can't figure out WTF is going on.

CSS is probably the worst culprit with its extreme legato latencies...but honestly I think the only solution for CSS is a dedicated custom script, which I am 80% done with also. The more I think about it I'm inclined to just write a custom script for each of the libraries I own and be done with it. I literally wrote that 80% done version of that one in one evening...not a big deal. I am still kind of committed to finding a general solution in order to help everyone else out too.

CSB and CSW will be very similar, I don't own them, but I should be able to copy my CSS script into versions for them pretty easy.

They are way more complicated because they use different latency for the first note of a legato phrase then for the secondary notes of the phrase. They also use different amounts of latency depending on the velocity of the secondary note. They also have some interesting things related to crossfade and stuff that I mentioned before.

I'll respond to your latest question in next post..


FWIW - I am not using hundreds of Latency Fixer instances... maybe a few dozen. So it's not having much of an impact. Something like CSB is using basically 7x3 (each patch separated into legato, long and short). Anyway, I'll keep you all posted.
Even hundreds, it doesn't use that much CPU. you're using more CPU by splitting the audio probably.
 

Dewdman42

Senior Member
One thought came to mind... would it be possible to ignore lookahead by looking at the transport 'is playing' instead of a specific MIDI port/channel?

So, if transport is playing, enforce all the delays, but if not... basically operate in 'ignore lookahead' mode?

I saw this code snippet and it got me thinking:

function ProcessMIDI() {
var info = GetTimingInfo(); /* get the timing info from the host */
if (info.playing) /* if the transport is playing */ Trace(info.blockStartBeat) /* print the beat position */
}

A couple comments about that. first, the ProcessMIDI callback is quite a bit harder on the CPU. So I'm trying to avoid it.

secondly, what we really need to do here is to detect when the midi channels is in live mode where you are trying to play your keyboard and hear sound,...generally while recording. During normal playback you always need the correction and lookahead happening. But while recording...you want it off for at least the channel you are recording to.

There is not a built in way to distinguish between playing and recording, in Scripter. Plus you actually want the lookahead turned off even when the transport isn't moving and you're just tinkering on the keyboard some ideas. or clicking on the piano roll and hearing the notes you had already placed there, its really annoying with the lookahead on to do that. you turn off the lookahead so you click a note and hear it immediately, etc..

So anyway, no I haven't figured out a way to automatically detect when to cancel the lookahead, but if I figure out a way, and I'm open to suggestions..keep them coming..I would definitely add that.
 

marclawsonmusic

Senior Member
A couple comments about that. first, the ProcessMIDI callback is quite a bit harder on the CPU. So I'm trying to avoid it.

secondly, what we really need to do here is to detect when the midi channels is in live mode where you are trying to play your keyboard and hear sound,...generally while recording. During normal playback you always need the correction and lookahead happening. But while recording...you want it off for at least the channel you are recording to.

There is not a built in way to distinguish between playing and recording, in Scripter. Plus you actually want the lookahead turned off even when the transport isn't moving and you're just tinkering on the keyboard some ideas. or clicking on the piano roll and hearing the notes you had already placed there, its really annoying with the lookahead on to do that. you turn off the lookahead so you click a note and hear it immediately, etc..

So anyway, no I haven't figured out a way to automatically detect when to cancel the lookahead, but if I figure out a way, and I'm open to suggestions..keep them coming..I would definitely add that.
Good point! I didn't think about the scenario of playing along during playback... duh. And I understand the CPU concern as well.

I really appreciate you throwing your brainpower at this to help the community. It really is a very tricky niche problem and you understand it very well.

Thanks again!
 

Dewdman42

Senior Member
I've updated the ArtAlignGui.js script, it now has a checkbox to enable a simple correction mode which will more exactly mimic the behavior of simple negative track delay. The main advantage of this would be you just need one instance of this script, one instance of LatencyFixer and no extra audio splits in Vepro, etc.. actually you can put both latencyFixer and Scripter into LogicPro and keep the Vepro mixer clean from anything at all. Perhaps easier to setup IMHO. You can even drag the faders while the project is playing and dynamically hear the latency adjustment change.

check it here: https://gitlab.com/dewdman42/artalign/-/wikis/ArtAlignGui.js


Screen Shot 2021-04-24 at 4.30.26 PM.jpg
 

Dewdman42

Senior Member
I don't know, I think I found a strange LogicPro bug in Scripter, but the script basically works..the bug is related to the GUI not updating itself properly...so basically you might have to press the Run Script button two times when you first load it..

its very strange. I explained the bug in the troubleshooting section of my gitlab page..so.. there it is..let me know if you get anywhere with that.

But the actual script itself is working fine...
 

robh

Senior Member
I don't know, I think I found a strange LogicPro bug in Scripter, but the script basically works..the bug is related to the GUI not updating itself properly...so basically you might have to press the Run Script button two times when you first load it..

its very strange. I explained the bug in the troubleshooting section of my gitlab page..so.. there it is..let me know if you get anywhere with that.

But the actual script itself is working fine...
I've encountered that bug myself - if I think I understand what you are describing. How I got around it is to "get" the parameter, set it to the opposite and then set it back again under the Reset() function. Here's an example from a script I'm working on.
function Reset(){ if(GetParameter("Responsiveness") == 0){ SetParameter("Responsiveness",1); SetParameter("Responsiveness",0); }else{ SetParameter("Responsiveness",0); SetParameter("Responsiveness",1); } Responsiveness(GetParameter("Responsiveness"));
 

Dewdman42

Senior Member
I'll try that later to see if it works. The funny thing is this bug only comes up when dragging the right edge of the GUI window to make it bigger to show all the controls. When you do that, one of the controls isn't showing until I hit the Run Script button again. But once its showing, then I can change the size of the GUI small to big to small to big as much as I want and its always there.

Conversely, if instead of initially dragging the GUI window bigger, I simply scroll down, the control is where it is supposed to be from the get go.

I can't find any explicable reason for this other then Scripter must have a bug in the code it uses internally when you resize the window and it needs to repaint the screen, but only the first time.

I actually don't really want to have code like that above that will run during Reset every time, so probably I'd rather just say, hit the Run Script button twice,....but I will try it out later to see if that somehow magically gets Scripter to paint its resized window correctly.
 

robh

Senior Member
I'll try that later to see if it works. The funny thing is this bug only comes up when dragging the right edge of the GUI window to make it bigger to show all the controls. When you do that, one of the controls isn't showing until I hit the Run Script button again. But once its showing, then I can change the size of the GUI small to big to small to big as much as I want and its always there.

Conversely, if instead of initially dragging the GUI window bigger, I simply scroll down, the control is where it is supposed to be from the get go.

I can't find any explicable reason for this other then Scripter must have a bug in the code it uses internally when you resize the window and it needs to repaint the screen, but only the first time.

I actually don't really want to have code like that above that will run during Reset every time, so probably I'd rather just say, hit the Run Script button twice,....but I will try it out later to see if that somehow magically gets Scripter to paint its resized window correctly.
I read your gitlab note after posting and it might be a different thing after all. But who knows, maybe it's related.
 

marclawsonmusic

Senior Member
Hey @Dewdman42, I finally had some time to test the script...

I did a baseline comparison to my Latency Fixer template using the same MIDI. I put the same delay values in the script as I did in Latency Fixer. I rendered audio and listened back. Observations:
  • Using 'normal' mode (simple correction not checked), there were some strange transitions and jumps / hiccups in the MIDI. I assume this is because only NoteOn is being delayed? Not sure, but I didn't really like how it sounded.
  • I changed to 'simple correction' and that render was much closer to my original Latency Fixer version. In fact, it sounded identical, so I decided to put it to a null test. For some reason the audio didn't null out - I had a staccato passage and could hear those notes clearly - so something must have been out of time in one of the renders. I think the issue is with Latency Fixer and the cumulative effect of adding so many instances of that plugin. When I was setting it up, there were times when tracks I brought into alignment seemed to 'drift' time-wise and get slow again. I thought it was all in my head but maybe there is something in PDC that adds more headroom as more plugins are deployed? As an example, CSS shorts are normally 60ms delay (works fine in your script), but with Latency Fixer, I have them at 80ms.
So I decided that Latency Fixer was problematic and started ripping it out and replacing with your script. I even made the script leaner by removing all but 'simple correction' logic (all I need), and also put in some new code to toggle 'Record thru' using a CC. I set it up on my strings and it worked great. Woo hoo!

Then I moved on to woodwinds and that's when everything fell to shit. With the second Scripter instance, I started getting audio engine overloads. All cores firing at 75-100%. This was during a dense passage in the MIDI. I have a total of 9 VEP instances, so if I can't even get 2 to work, I am toast.

I am running a Late 2013 iMac i7 w/ 32GB of RAM. Maybe it's because of my older machine? With Latency Fixer, my VEP instances barely hit 20-25% during playback. Logic does hover around 40-50% during a dense passage, but that's totally OK for me.

So I'm back to using Latency Fixer on everything. I just want to get back to writing anyway... all this workaround stuff really breaks my balls. I'd be happy if Apple fixed the negative delay thing so I didn't have this MacGyver setup over here, but I guess that's life. It does seem like Cubase is better-suited to large templates these days, but I'm not sure I have the patience to learn a new DAW after all this recent pain.

Anyway, I REALLY appreciate your efforts to help solve this. Your script is great - it's a work of genius. But I think Scripter can be heavy, especially on an older computer. Unfortunately, it doesn't look like it will work for me.

Best,
Marc
 

Dewdman42

Senior Member
Latency fixer doesnt touch midi, it just reports the latency and logicpro should send the midi early so I can’t think of any reason why that would cause the shorts to sound funny. If you have an example of that you can share it would be good to know. Well it could be that using a lot of latency fixer instances confused vepro in some way but I’d need to see what you did to make sure the problem wasn’t something you did ;) only one latency fixer per channel strip, right?

Scripter doesn’t actually take much cpu so I’d like to see the project that you said ran into a bottleneck with dense passages. Dense passages in general do take a lot of cpu. Since I see you modified the script then I have no idea what you did or the impact of that either. Are you sure you didn’t have the track selected in live mode?

Also would like to understand what you said sounds funny like hiccups and why? That could be resolvable script bugs if I find out more info about how you were trying to use it and the modifications you made to the script.

Well anyway this issue is definitely a PITA. I am leaning towards using kontakt script for any kontakt libraries with this problem such as CSS. I’d rather use scripts custom written for each library and in many cases I don’t want to channelize anything for anything except PLAY which remains brain dead that way
 
Last edited:

Dewdman42

Senior Member
By the way you can also just go back to using one vepro instance per inst track with negative track delay. That would be a lot of instances though ;)
 
Top Bottom