Today I tried moving the Ndef synth logic into a SynthDef that outputs with OffsetOut. The thinking was that if the freq
parameter is control rate, then perhaps I need to use OffsetOut for sample-accurate timing. I wrapped the synth in an Ndef so I can still control the frequency through a dirtSynth from Tidal.
Unfortunately, the results were no different. Here's the new synth setup:
(
SynthDef(\test_cv, { | out, freq = 440, lag=0.01 |
var n = Lag.ar(log2(K2A.ar(freq)/440), lag);
var sig = LinLin.ar(n, -1, 9, 0, 1);
OffsetOut.ar(out, [sig]);
}).add;
Ndef(\cv_np).source = \test_cv;
Ndef(\cv_np).play(0);
~dirt.soundLibrary.addSynth(\cv, (play: {
Ndef(\cv_np).wakeUp; // make sure the Ndef runs
Ndef(\cv_np).set(\freq, ~freq);
}));
)
Running through Tidal with:
d1 $ "hh hh hh hh" # channel 1 # gain 1.5
d2 $ n "a6 a5 a6 a5" # s "cv"
Overlaying the two signals you can see they're not lining up. Listening to the output, you can hear it, the high hat drops before the change in tone, and it's jittery.
As a sanity check I also compared the cv signal to a clock on my modular synth that I manually set to the same frequency as tidal. Here also, the timing isn't consistent.
I'm running out of ideas. Just for fun I ran ~offset.postln
from the dirtSynth and the results are 0 every time. So if there is an issue with the sample offset, I don't know where it is or how to fix it.
The other thought I have is perhaps the Ndef.set message isn't precise or introduces latency. This one:
Ndef(\cv_np).set(\freq, ~freq);
However, I'm not aware of another way to change a parameter of a running synth.
If I don't come up with any ideas soon, I guess i'll go back to using the envelope method with a very tight legato value, and deal with the occasional click by applying a slew limiter inside my modular. It's a bit frustrating though, since I'll need one per cv, which could be quite expensive.