Is there a more concise/elegant way of making an LFO than this

Hello all,

Due to the order of mul and add I am guessing this is the most concise way to make an LFO with a range from 0 to 1?

(
{
var lfo = SinOsc.ar(0.2,add:1)/2;
WhiteNoise.ar * lfo;
}.play;
)

Thanks, Dom

I realise there is this as well…

var lfo = SinOsc.ar(0.2).linlin(-1,1,0,1);

there’s SinOsc.ar(0.2).unipolar

its a shortcut for .range(0, mul=1) - you can also specify a different mul

In your first example I’d also write it as SinOsc.ar(0.2) + 1 / 2 !

1 Like

Thanks. That definitely qualifies!

I think * 0.5 + 0.5 is slightly more concise, and also a bit more efficient because signal * factor + offset gets folded into a single MulAdd UGen whereas doing the addition first requires two separate UGens.

hjh

Interesting comparison! I guess the term ‘concise’ could mean different things depending on the context—either brevity in code or computational efficiency.

We have three variations of the same result:

(
{
	[
		SinOsc.ar(1),
		SinOsc.ar(1).unipolar,
		SinOsc.ar(1) * 0.5 + 0.5,
		SinOsc.ar(1).range(0, 1)
	]
}.plot(1)
)

While SinOsc.ar(1).range(0, 1) is very expressive and readable, approaches like SinOsc.ar(1).unipolar or even SinOsc.ar(1) * 0.5 + 0.5 might involve slightly simpler computations.

It’s hard to say definitively which one is most CPU-efficient without profiling, but each method seems to have its own trade-offs depending on the situation. Is the following code is correct to compare CPU-efficiency?

(
{
	{ { SinOsc.ar(1) }.plot(1) }.bench;
	0.1.wait;
	{ { SinOsc.ar(1).unipolar }.plot(1) }.bench;
	0.1.wait;
	{ { SinOsc.ar(1) * 0.5 + 0.5 }.plot(1) }.bench;
	0.1.wait;
	{ { SinOsc.ar(1).range(0, 1) }.plot(1) }.bench;
}.fork(AppClock)
)

EDIT:

The last code in this post cannot benchmark the server-side CPU efficiency. Sorry for the wrong code…

Exactly! That’s why, in teaching, I switched to range and mostly dropped using mul and add args.

1 Like

SinOsc.ar(1).range(0, 1) is SinOsc.ar(1) * 0.5 + 0.5 :wink:

		if (this.signalRange == \bipolar, {
			mul = (hi - lo) * 0.5;
			add = mul + lo;
		}

mul = (1-0) * 0.5 = 0.5 and add = 0.5 + 0 = 0.5. Then it writes directly into MulAdd(this, mul, add).

If the arguments to range are constant numbers, then it will always produce only a single MulAdd (as in this case). If one or both args are UGens, then the mul and add calculations will produce other UGens.

hjh

1 Like