Better idiom for setting a synth control to an LFO?

Ndef(\t, \default).play
Ndef(\t).set(\freq, 220) // fine
Ndef(\lo, {,440)})
Ndef(\t).set(\freq, Ndef(\lo)) // fine

or in one line:

Ndef(\t).set(\freq, Ndef(\lo, {,440)}))

but why can’t I do this?

Ndef(\lo, {}) // no .range here
Ndef(\t).set(\freq, Ndef(\lo).range(220,440)) // message 'range' not understood

Second question, why do I need the Ndef(\lo) at all? Is not possible to do something like:

Ndef(\t).set(\freq, // 'can't set a control to a UGen'


Ndef(\t).set(\freq, {}) // either silence or crashes the server

this is ok though, in one line:

Ndef(\t).set(\freq, Ndef(\lo, {,440)}))

or one of these:

Ndef(\t).set(\freq, {,440)} ))

Ndef(\t).set(\freq,{,440)} ))

So I guess, answering own question, that’s just the way it works?

If you’re up to exploring new things, you can do all the things you are trying to achieve (and much more) with my library, Alga.

For your specific case, you would do something like:

//Boot into alga

//A simple sine
~sine = AlgaNode({\ }, interpTime: 2).play(chans: 2);

//A simple stepped noise
~lfo = AlgaNode({ });

//Patch ~lfo into ~sine's freq control. Alga allows to specify a custom "scale"
~sine.from(~lfo, \freq, scale: [220, 440])

//Among many other things, you can also define Functions on the fly
~sine <<.freq {, 880) }

//Or, using the same syntax as before
~sine.from({ }, \freq, scale: [330, 660])

Hmm! I’ve just started playing with Alga, and this interpolation idea is very interesting indeed! I’m preparing for a gig next month, I might just use this :slight_smile:


Because the range method needs to add more math UGens, and this needs to happen in a synthesis function, and JITLib currently manages synthesis in node proxies. Also the new range mapping would require a new bus.

In theory it would be possible to extend NodeProxy with additional range mappings, but there would be some design considerations (e.g., if you wrote a free-standing range as above, how would you access it to remove it later? Are the resources managed in Ndef(\lo) or Ndef(\t)? … all solvable problems! Just saying that it isn’t problem-free to implement).

A UGen is not a signal. It’s totally inert until it’s added to a SynthDef and played on the server.

The key for all of these cases is that control mapping requires at minimum a synth and a bus, and something needs to manage those. You were trying a few ways hoping that the synth/bus requirement could be bypassed, but it can’t be. It could be automated with a feature request (or by Alga :wink: ), but not eliminated.


And while it opens up a different can of worms, CtkNotes (from the Ctk quark) can use CtkControl or (if the control is Audio rate - CtkAudio) as values for CtkSynthDef arguments.


// create the CtkSynthDef
a = CtkSynthDef(\test, {arg freq;,, 0, 0.2))

// create a note based on it
b = a.note;
// set the freq to 440
// play it!;
// update to use a CtkControl with an LFTri - freq 1, low val of 440, high of 880
b.freq_(CtkControl.lfo(LFTri, 1, 440, 880));
// change it back to 440
// free it;
1 Like

Useful reply, thanks James.