4 samples delay?
-
Can I get some extra eyes on this script? Incorporating delay compensation with Engine.setMinimumSampleRate() has become a challenge.
I've created a simple project that has the following:
- simple gain module (set to induce 1ms latency)
- scriptFX to set Engine.setLatencySamples()
- button to enable oversampling
- simple gain module to compensate for oversampling latency
- label to display simple gain compensation delay
What I'm expecting to see is 4 samples delay when OS is off and 0 samples delay with OS on. Instead, I'm getting double the amount of delay at init, and then the expected values when I toggle the button. Does anyone see what I'm doing wrong?
HiseSnippet 1374.3oc6X0saaaCElJwZKVctncnO.B4JEzrT40zlNTLrz3jTDr7iQbV15tXAzRz1DQlTPhtsdEEXOE6UY21WjArGg8FrcHo9gNVIywXKXAn5FCRd946b3geGR2NgGPRS4IHq5mLNlfr9L6NiYhAsFfoLzdairtq8A3TAIwUO0ViiwoojPjk0huTNgU8ZH02e9Magivr.R4THzobZ.Ye5Ppnb11a9sznncwgjSnCMjd8M2KfyZwi3i.7rnsOJFGbNtO4PrTrErQVexNgTAOoi.KHofLawCG2Y.+MLs7mRSociHxAMQc.ComF0Z.MJrcdrlhPV0ZWF4Kpi7GXe.MjVLeYF3dpEbK0vLGXsvjPp1DPp4kAoc4QgRCbIvyx.d0zv691cBRnwhxUjX6N16wfMmdXHsaBKsrnEVslcKNHASr1P74jcSfAEZ38Te+Ucehu+JOugSCGH4mJbeMNw8nNms0Hgfyb+Z2b06SDs3Ci4LXf2xERrrT2RM2lDgGe1o3nQjKUWCYtf1gpUB.QIrTrfp.fpdTZhc50iD.5OsTKqCfFNTVDkQb6MhEnzlyJ.pDKI7HufbfrJ3R.Bqzv4cMbbguG8H9qIIo3gwfQ5C51voNsmWtTRQxjT9sCqO3p0RIhCnL5vQC6HUjbLTF30zW9IAUtzSiYoluPHRncGAZTw5pzzptEl485epFq85ommDkRtVPc8+8PZla5KcSTDsCA1XCS2ENanbWp25qbIwB3.2HbWRjqf6FRSiA641gJ0xURxnAkq.nKzpXTBIQm2xBxaEKupqt1BJZV18gUEI8mkHo.kMbdupt5e7HfDCY0WsvQQcAZKuoK8xKRMpNOjKHGw7Vw4cN0cdui6EWpWuJWKyfQjjJWVRqlbUJ5wFMrKII+DPtf.WyjjY2Y1HyBz4GCA4r8XTwQPhUOtjtStlNpyW0GUDrlSUFilypBsJDqbpoYSQY6eRd0LwAQEJV06lwpp2nPzPIkZwFGRkeL6mg9t81FKv4lBrJ3oXRhfJyWVaSdMzuSSXW2daR54BdLz5pnrAYYOSN9sEN8O5t43hA+xwaJqzkpejw4eYHV3fkzNnQlC1WdtRY+F1FGaL7vG9QCO7geZy2PCEC.fZ86v3ADZ+ARXasALh.kGXUE.j.U.4ysyOx4ul+W4ug+igF7S07BZgxCGEgES1WUd1NaAn9bhFXxlTrTpXr4ELtFMa8qrYaypJOlQ3de61TQvfpw6BUfWnH4+Z7lc0kF15dikfsl8t+vMv8TVb5bmSVcG3e8lrpHkbFjyHrfwUdKke0xtjkJNgDiSHmvaC0UdoEspV0saDO37NzeVxXk25H.GEH2OHtYNPufwcJRiy8MTjV1iJqoDzdxrakWyUdtoUCk5eYllmZX40ufdSzedBCX10SvE3HWydPN0KaWuuVs79mlgxCM79JOWxdazXIVuqrkLe4EL.yXv8Blm9A1SVz7oyVQyjr+v03M2QKaJXhxqKk9w7QBHwd.FZlCLY1GBWpAd1P.oUVzJYrVPx8qG6KGKOR1gvBUC9K3KawlxwVYK1Lewx1FyEou4wzkJdak5NMur7sU8vidakGMl7sUHjhk0jK760rz4S7pMq30W6wfhPQadDN4hLSKdUujp4b9Rpql9TcStaMz84M1tEAXUIwsG.eO613+OTPbSvkLCjAOvd5GjL6TBG8re6iTBejR3iTB2ZnDtI7wPbPB+r.8EpjGMWRMCD2L0+oZc6Cjical+RWaa3YbngzP5YAAxmQ7EP9oZc9x4PmGOG5r9bnySlCcd5bnyFygNO6J0Q9WM+hQB9Pc8OLQ6cTW9zxZGl7o2piBn+FvbCV5y
-
@dustbro said in 4 samples delay?:
Can I get some extra eyes on this script? Incorporating delay compensation with Engine.setMinimumSampleRate() has become a challenge.
I've created a simple project that has the following:
- simple gain module (set to induce 1ms latency)
- scriptFX to set Engine.setLatencySamples()
- button to enable oversampling
- simple gain module to compensate for oversampling latency
- label to display simple gain compensation delay
What I'm expecting to see is 4 samples delay when OS is off and 0 samples delay with OS on. Instead, I'm getting double the amount of delay at init, and then the expected values when I toggle the button. Does anyone see what I'm doing wrong?
Why do you use
Engine.setMinimumSampleRate()
for that instead ofEngine.getMilliSecondsForSamples()
?Let's say the (ampirically) calculated latency value is 4 samples. The latency compensation value is declared with a script fx at the very end of the fx chain.
In this example I've used ShapeFX for oversampling. So when oversampling is on (for example 16x), the simple gain delay must be 0 (because we've already declared a latency compensation value with script fx).
If the oversampling is 1x (since there is no oversampling delay anymore), then we need to give an artificial simple gain delay because of the previously declared latency compensation value, in order to keep the delay balanced.
We need to calculate the delay amount (in ms unit) with the current sample rate and previously declared sample delay using
Engine.getMilliSecondsForSamples()
HiseSnippet 1363.3oc0X80aaTDDeujrnDCopklG5KHsxOcQDhrg1BhJDWicbwh3Vq3RoUBonM2s1dUta2S2smaMUUpOxGIdfG3QdmuH7M.l89iu8puFRMsUDGIqLyN+42N6ryLqGFIcYwwxHj0VObdHCY8Q3QyEpoclR4BT+tHqqfGPiUrHRFqClGRiiYdHKq0umlg0VafR+7We6ATepvkUxBgdjj6xNhGvUkbG578be+dTO1C4AFReSm9tRQGouLAvy53VnPp6YzIr6S0hsFFY8AG5wUxnQJphEir13.o27QSkOUjI+i3w7S8YZh1nQfgxX2S56oQrlKpyTtu2vh8cLBrxvxnv5YQgcvC3d7E7KiFWMcARoFlwCq0NO301Ddst3vyx.dajAuqgG4FwCUkqnw1Gh6KfCpwT3HvDVYxhV6O1.2QBRHT6GPOi0KBHVng8sa0ZOxsZ0Z26rcisa.GDwJxLZD4AiNoKymN+j7S2Hx2PRSQ1eBSc33wLWkcykDpo1LkFYzTZHq2iqS07kZl42JddFKZDMHzmKl.lm5p3yRcewt.rRGYPnT.D.FpU7WAHYnbF0OgcxQzSY9uVysjjKPn9Ot.bCiLNQ.tQJHRQ8tWa6Housagg2ijZxc2twy2tAA9vGamwgjQSHOW+UAAoH1seLScWkJheZhhYWvT603butGosFhE5szYRUKr7xoj6QrOTLA1a5vw.3dJeDCBedw8jYaOVr8M2cWSGsTjR6H6lJ1yTM2ijEHI2MPlHTeMoI4SIW.G.R0jDD2zvOuvHnj8OL+X1agn1sdaD0Zseq+SwjV0ui0aoWjl28lkzqcWdtWGpu+oPgT6+kbzxz67HoQx88kJ1CD16134M1pwKVdowiqcsbK6yhpcYc4+nySQaQRvornh6LEBB0AqVnE+5KzZ1GvMKDZHnTzWvUOHjIdckeQ4wccg3bTAhpRKCek7xvGjnTRAhCUauAt9XLJcGX1YD8C86RUzB6Bt.baHKRw06HqtrYPmyrx8ag6xhOSICglfKN6glgu4n3YKPvKejy7EDN+jyS4dpokLF6LkwmL0nq8cbPF9dyLeuctuSSwSc80wKk5a30e2zquzyQeeP2ss58gVv8.TNfvV+FHZAXvVeBPQ84SDAYw.rOaLb3rbORnSszKwmpp19VOyR9BPpVk9j5dghXtZt4LMuS5oeQg60vC4J2o0i20pAuP1z6Z7lOgz13rF3kfcCbuGupiCU+zZspabn0Mvx0JtejWXOEGkTlfoSRzrrgHOfSiORmzXD0z7NtZ9NB8c.igfALmP8H4Sqv5W9SG3zw3h8MbLatTxeGmpSKuiywLujpCKe2DkbxqLSc5ry8EgIJStci3ypn5.9ypLId5NuG2GltKtR5w5UOSV+hclTSJwwxDErCGPgVifuw2OIXDL3tKCxHDBn0r915Z5BnYzszzZHLhI7RI9a3S9hs0zV4K1tXQyztMW77DtdFg6Uj0cc7RsjOm2nfPohZx3GqT3C8DmZdESeAblpFJ8oQu50s2VwyJuB47qIn2RjKM0v937h6Wh.bZJwkG.eU7P5+GRHdOWQHOjzHeDj7p+MvcO5ImzYvv108FXqeEWNlYXDKjFwdnbHjeZmVxlcLDP1ibpuz8rQ7e13YZYem+xEX35i.AEtyKewRwX6v75lyNGlA3CzVz1MOPrJi7huLU3dwPyqzTtuWfX.0MRdhaVTWW.dyTNPXSj9yMsEdfllztXzcLFdiGJf6wOw0UOtymA2BpWmOeEz4KVAct4JnysVAct8JnyWtB57UmqN5e4M8HRAYU4.FCOLM4xx5PAER9Suoi9G4sdruC
-
@orange The only problem with ShapeFX is the oversampling is restricted to that individual module. Engine.setMinimumSampleRate() must be called to encapsulate the entire plugin in oversampling... and that's where I'm getting the issue.
-
@Christoph-Hart said in Scriptnode "oversample" node starting at 4x crashes in PC:
You could add a HQ button somewhere and do
if(value) Engine.setMinimumSampleRate(100000); else Engine.setMinimumSampleRate(40000);
in its callback. 100.000 will ensure that it's always 4x oversampled for lower samplerates and 40000 that it's never oversampled in non-HQ mode (using 48000 would cause oversampling in 44,1kHz).
It will fade out all voices and then reinitiliase the processing chain with the new samplerate .
-
@dustbro said in 4 samples delay?:
@orange The only problem with ShapeFX is the oversampling is restricted to that individual module. Engine.setMinimumSampleRate() must be called to encapsulate the entire plugin in oversampling... and that's where I'm getting the issue.
function prepareToPlay(sampleRate, blockSize) { //calculate latency const var dsp_latency = Engine.getSamplesForMilliSeconds(1); //calculated dsp latency const var os_latency = 4; //calculated oversampling latency //set total compensation Engine.setLatencySamples(dsp_latency + os_latency); }
I see that in above delay compensation code you used a dynamic value (dsp_latency + os_latency).
AFAIK
Engine.setLatencySamples
should be set to a static value. This value is declared to the DAW while the initialization and it can't be changed after the plugin is loaded.Since
dsp_latency
will change with sample rate, that might cause the crash.If setting the delay compensation to a static value doesn't solve the issue, maybe the new scriptnode version will be better for this.
-
@orange said in 4 samples delay?:
I see that in above delay compensation code you used a dynamic value (dsp_latency + os_latency).
This is actually the described method for the Latency Test sample in HISE tutorial.
// This converts the 200ms delay to samples using the samplerate local numSamples = Engine.getSamplesForMilliSeconds(200.0); // Propagate the latency to the host. This callback is as late as possible to // catch any misbehaving hosts... Engine.setLatencySamples(numSamples);
Engine.setLatencySamples should be set to a static value.
In the real world I'm calculating absolute latency values for each module (in samples), adding them up, and then using Engine.setLatencySamples() to compensate for the largest possible latency. I'm just using Engine.getSamplesForMilliSeconds() as a quick way to compensate for the faux latency I set in the example.
I'm not seeing any crashes on this end (that I know of :grinning_cat_face_with_smiling_eyes: ) Just the wrong calculation of Engine.getMilliSecondsForSamples() at init.
-
After a little digging, I'm getting closer to the calculation issue.
Calling Console.print(Engine.getSampleRate()); from inside the ScriptFX I'm using for Engine.setLatencySamples() shows this in the console:
Oversampling off:
Interface: 88200 ScriptFX: 44100
Oversampling on:
Interface: 44100 ScriptFX: 88200
@Christoph-Hart is this because Engine.getSampleRate() is reporting the oversampled rate, and not the sample rate of the DAW?
-
I'm going to try to grab the sample rate at init and store it in a variable. Then calculate the delay with that as my baseline.
const var HostSampleRate = Engine.getSampleRate();
-
@dustbro I know this thread is a bit old but did you end up figuring out the sample delay issue? I'm running into the same problem of double the amount of delay at init until I toggle the os button. Then it works as expected.
-
@pluginboi I ended up making a script that toggles the oversampling back and forth.
Not the most elegant way to do it, but it works