# Fractions in SynthDef Argument

I made a synth with the low pass filter controlled by a triangle LFO. It took me a solid 20 minutes to figure out that what I thought was a problem with using `.exprange` on the output of the LFO was actually a problem with the SynthDef arguments.

I was thinking in CPM, so naturally
440 cycles / 60 seconds → |modFreq = 440/60|
but this doesn’t work and I’m wondering why using a fraction wouldn’t work as a SynthDef argument on compile. It works with `Synth.(\key).set` but not as a default.

``````(

//LFO freq == 440 cpm; modFreq arg is unused in function

SynthDef(\filtSaw, {
arg amp=1, pan=0, freq=60, detun=0.5, fFreqLo=20, fFreqHi=1500, modFreq=440/60, dur=5;
var sig, env, filtMod;

filtMod = LFTri.ar(22/3, 0.0).exprange(fFreqLo,fFreqHi);

env = EnvGen.ar(Env.triangle(dur), doneAction:2);
sig = 7.collect({
LFSaw.ar(freq*rand2(detun).midiratio, 4.0.rand)/7;
});
sig = Mix.ar(sig);
sig = RLPF.ar(sig, filtMod, 0.3);
sig = Pan2.ar(sig, pan);
sig = sig * amp * env;
Out.ar(0, sig);

//LFO freq == \modFreq == 440/60

SynthDef(\filtSaw1, {
arg amp=1, pan=0, freq=60, detun=0.5, fFreqLo=20, fFreqHi=1500, modFreq=440/60, dur=5;
var sig, env, filtMod;
filtMod = LFTri.ar(modFreq, 0.0).exprange(fFreqLo,fFreqHi);
env = EnvGen.ar(Env.triangle(dur), doneAction:2);
sig = 7.collect({
LFSaw.ar(freq*rand2(detun).midiratio, 4.0.rand)/7;
});
sig = Mix.ar(sig);
sig = RLPF.ar(sig, filtMod, 0.3);
sig = Pan2.ar(sig, pan);
sig = sig * amp * env;
Out.ar(0, sig);

//modFreq as a variable

SynthDef(\filtSaw2, {
arg amp=1, pan=0, freq=60, detun=0.5, fFreqLo=20, fFreqHi=1500, dur=5;
var sig, env, filtMod, modFreq=440/60;

filtMod = LFTri.ar(22/3, 0.0).exprange(fFreqLo,fFreqHi);

env = EnvGen.ar(Env.triangle(dur), doneAction:2);
sig = 7.collect({
LFSaw.ar(freq*rand2(detun).midiratio, 4.0.rand)/7;
});
sig = Mix.ar(sig);
sig = RLPF.ar(sig, filtMod, 0.3);
sig = Pan2.ar(sig, pan);
sig = sig * amp * env;
Out.ar(0, sig);

//impractical for this purpose but works

SynthDef(\filtSaw3, {
arg amp=1, pan=0, freq=60, detun=0.5, fFreqLo=20, fFreqHi=1500, dur=5, modFreq=7.333333;
var sig, env, filtMod;

filtMod = LFTri.ar(modFreq, 0.0).exprange(fFreqLo,fFreqHi);

env = EnvGen.ar(Env.triangle(dur), doneAction:2);
sig = 7.collect({
LFSaw.ar(freq*rand2(detun).midiratio, 4.0.rand)/7;
});
sig = Mix.ar(sig);
sig = RLPF.ar(sig, filtMod, 0.3);
sig = Pan2.ar(sig, pan);
sig = sig * amp * env;
Out.ar(0, sig);

//best solution
SynthDef(\filtSaw4, {
arg amp=1, pan=0, freq=60, detun=0.5, fFreqLo=20, fFreqHi=1500, dur=5, modCpm=440;
var sig, env, filtMod, modFreq;

modFreq = modCpm/60;

filtMod = LFTri.ar(modFreq, 0.0).exprange(fFreqLo,fFreqHi);

env = EnvGen.ar(Env.triangle(dur), doneAction:2);
sig = 7.collect({
LFSaw.ar(freq*rand2(detun).midiratio, 4.0.rand)/7;
});
sig = Mix.ar(sig);
sig = RLPF.ar(sig, filtMod, 0.3);
sig = Pan2.ar(sig, pan);
sig = sig * amp * env;
Out.ar(0, sig);
)

Synth(\filtSaw); //expected result
Synth(\filtSaw1); //|modFreq = 440/60| doesn't work...
Synth(\filtSaw1).set(\modFreq, 440/60); //magic! now it works
Synth(\filtSaw3); //annoying but works
Synth(\filtSaw4); //best solution probably?``````

Hi,

A quirk of sclang - if you use pipe syntax, you have to surround an expression with parenthases, so `|modFreq = (440/60)|`

Edit: OH but actually this might still not work in a synthdef function (not at computer now, sorry) – in any case check out NamedControls for fewer surprises overall: NamedControl: a better way to write SynthDef arguments

1 Like

I did try the parenthesis. Fails both in pipe syntax and in whatever my typical

``````arg ...
var...
``````

would be called. I’ve seen a few folks posting using the named control. I did look over the documentation for Control which touches on the link you posted. I tried that with the fraction and it does work.

However, perhaps I misunderstood this, it did say “Generally you do not create controls yourself,” which I somehow interpreted as “named control isn’t something you normally would want to do or is deprecated.”

I assume now it’s saying something more along the lines of “Generally you do not use Control.kr/Control.ar directly” and I’ve just misunderstood this.

So now if I’m correct, 7.333333 is now an instance of control and not a float here and a fraction cannot be a control?

It needs to be a literal, i.e.

{ arg x = 1/2, y = 0.5; }.def.prototypeFrame == [nil, 0.5]

1 Like

Actually Control is not the same thing as NamedControl, they’re two different classes. NamedControl is a more recent addition.

`Control` is tricky to use by yourself – I’d avoid it.

NamedControl is a user-friendly factory for Control UGens. It’s definitely supported/recommended.

If you need to create a synth input where the default is not a literal, the best formula is one of the following:

``````var x = NamedControl.kr(\x, default_expression);

// or, equivalent:
var x = \x.kr(default_expression);
``````

Note however that the default expression cannot use any UGens – the default must be a fixed number or array.

hjh

1 Like

Thanks for clarifying! I thought what I was seeing with NamedControl was just a syntax for Control. That makes a lot of sense