OK tested everything and it's working. Here's the latest version of the code block to add to startup.scd:
(
~minCv = -1;
~maxCv = 9;
~triggerHold = 0.05;
~latencyCorrection = -0.035;
~dirt.orbits.do({ | orbit, i |
var chans, cvtag, gatetag;
chans = ~dirt.numChannels;
// CV synths
cvtag=('cv_np'++i).asSymbol;
Ndef(cvtag, { | freq = 440, lag=0.01, level |
var sig = if (
level > 0,
level,
LinLin.kr( log2(freq/440), ~minCv, ~maxCv, 0, 1)
);
Lag.ar(K2A.ar(sig), lagTime: lag);
});
~dirt.soundLibrary.addSynth(('cv'++i).asSymbol, (play: {
var latency = (~latency ? 0) + (~latencyCorrection ? 0);
var n = ~n;
Ndef(cvtag).wakeUp; // make sure the Ndef runs
thisThread.clock.sched(latency, {
Ndef(cvtag).set(\level, n);
});
}));
~dirt.soundLibrary.addSynth(('cv'++i++'_pitch').asSymbol, (play: {
var latency = (~latency ? 0) + (~latencyCorrection ? 0);
var freq = ~freq;
Ndef(cvtag).wakeUp; // make sure the Ndef runs
thisThread.clock.sched(latency, {
Ndef(cvtag).set(\level, 0, \freq, freq);
});
}));
~dirt.soundLibrary.addSynth(('cv'++i++'_lag').asSymbol, (play: {
var latency = (~latency ? 0) + (~latencyCorrection ? 0);
var n = ~n;
Ndef(cvtag).wakeUp; // make sure the Ndef runs
thisThread.clock.sched(latency, {
Ndef(cvtag).set(\lag, n);
});
}));
~dirt.soundLibrary.addSynth(('cv'++i++'_on').asSymbol, (play: {
('cv'++i++'_on').postln;
Ndef(cvtag).play(i*chans);
}));
~dirt.soundLibrary.addSynth(('cv'++i++'_off').asSymbol, (play: {
('cv'++i++'_off').postln;
Ndef(cvtag).stop;
}));
// gate synths
gatetag = ('gate_np' ++ i).asSymbol;
Ndef(gatetag, { | level = 0, lag=0.01 |
Lag.ar(K2A.ar(level), lagTime: lag);
});
~dirt.soundLibrary.addSynth(('gate'++i).asSymbol, (play: {
var latency = (~latency ? 0) + (~latencyCorrection ? 0);
var n = ~n;
Ndef(gatetag).wakeUp; // make sure the Ndef runs
thisThread.clock.sched(latency, {
Ndef(gatetag).set(\level, n);
});
}));
~dirt.soundLibrary.addSynth(('gate'++i++'_lag').asSymbol, (play: {
var latency = (~latency ? 0) + (~latencyCorrection ? 0);
var n = ~n;
Ndef(gatetag).wakeUp; // make sure the Ndef runs
thisThread.clock.sched(latency, {
Ndef(gatetag).set(\lag, n);
});
}));
~dirt.soundLibrary.addSynth(('gate'++i++'_trig').asSymbol, (play: {
var latency = (~latency ? 0) + (~latencyCorrection ? 0);
var triggerTime = latency + (~triggerHold ? 0.05);
Ndef(gatetag).wakeUp; // make sure the Ndef runs
thisThread.clock.sched(latency, {
Ndef(gatetag).set(\level, 1.0);
});
thisThread.clock.sched(triggerTime, {
Ndef(gatetag).set(\level, -0.01);
});
}));
~dirt.soundLibrary.addSynth(('gate'++i++'_on').asSymbol, (play: {
('gate'++i++'_on').postln;
Ndef(gatetag).play(i*chans+1);
}));
~dirt.soundLibrary.addSynth(('gate'++i++'_off').asSymbol, (play: {
('gate'++i++'_off').postln;
Ndef(gatetag).stop;
}));
});
)
The ~minCv
and ~maxCv
values are used to tune the cv_pitch
values for 1v/oct output.
There's a trigger synth for the gate signal that will send a short cv pulse (of ~triggerHold
seconds).
Finally there's a ~latencyCorrection
value that should be experimentally set to ensure the timing is correct for all these synth events.
Here's a demo of how it's used:
-- turn on cv1, this starts a continuous DC output on channel 1
once $ s "cv1_on"
-- turn on gate1
once $ s "gate1_on"
d1 $ stack [
n "a6 a5 a6 a5" # s "cv1_pitch",
n "0.01 0.4" # s "cv1_lag",
n "1 1 1 1" # s "gate1_trig"
]
And here's the output:

I'll chuck all this into a github repo and post to the opening thread soon.