Offset for filter

The first lines of code have a xline controlling the frequency , since the xLine is a unipolar signal , I can just addd a value of 5000 going to 10 added to the initial frequency value , all good

Using a LFNoise , which is a bipolar signal , I have to use a rather high multiply value for filter modulation , however going to high ( over 5000 ) clips the filter becasue the filter frequency is going negative )
So I wan’t to use an upper clipping limit for the lfo multiply amount.
Here I used a save value of 500 , everyhting over it will clip te filter

(
{
	  var sig;
	sig=Saw.ar([110,111])*0.2;
	sig=RLPF.ar(in:sig,freq:500+LFNoise1.kr(freq:5,add:1,mul:500),rq:0.1);//using lfo 
}.play)

Replacing the lfo with saw , I can get the multiply amount as high without clipping the filter , unless initial filter frequency is also set to low value

(
{
	  var sig;
	sig=Saw.ar([110,111])*0.2;
	sig=RLPF.ar(in:sig,freq:500+Saw.ar(1,5000),rq:0.1);//using lfo 
}.play)

Hey, there’s a shortcut to do this mapping: UGen::range

 // 5hz LFNoise, output between 100 and 10000 Hz
LFNoise1.kr(5).range(100,10000)

// alternative using an exponential scale since we are dealing with freqs
LFNoise1.kr(5).exprange(100,10000)

By the way, it is now not recommended to use UGens add and mul arguments. MulAdd (used internally by .range, and also when you do sums and multiplications) is a more efficient alternative.

If you want to scale LFNoise manually (by adding and multiplying), I suggest to first make it positive and then to scale it to the range:
LFNoise1.kr(5) + 1 * 10000 / 2
I don’t have a precise insight for why this works as expected (no negatives) while your example with mul: and add: doesn’t. Sounds like something in the order of operations (maybe add is performed after mul by LFNoise1?)… but if you write them explicitly like here you can control that :slight_smile:

Extra: there is also a .clip function, for all your clipping needs:

// 5hz LFNoise, output between 100 and 10000 Hz, clipped between 500 and 1000 Hz
LFNoise1.kr(5).range(100,10000).clip(500,1000) 

It’s true that there’s some movement away from using mul and add, but the reason is to move UGen range scaling away from positional arguments toward concrete math operations. In fact, gentleclockdivider already got burned by putting a mul in the wrong argument slot.

My preference for range mapping is range or exprange first, then linlin or linexp, lastly falling back to math operators if nothing else fits. I hardly use mul and add arguments at all anymore.

The SynthDef builder is pretty good at finding a * b + c constructions and optimizing them to MulAdd(a, b, c). Efficiency shouldn’t be a concern (unless we find cases that the optimizer doesn’t handle – if the optimizer is working correctly, there should be no need to invoke MulAdd directly).

hjh

Actually the reason for the OP’s unexpected result is order of operations.
All the alternatives presented here, from mul and add slots to .range call MulAdd behind the scenes. Now:

  • MulAdd executes (input * mul) + add. Notice the order of operations.
  • For bipolar signals, since multiplication is performed before addition, you need to compute mul and add like:
mul =  (hi - lo) / 2;
add = mul + lo;

So, in this example case, to map from LFNoise1’s range [-1,1] to [0,5000], it would be:

LFNoise1.kr(freq:5, mul: 2500, add: 2500)
// where:
// mul = (hi - lo) / 2 = (5000 - 0) / 2 = 2500
// add = mul + lo = 2500 + 0 = 2500

Note that .range (and other “range functions” like .exprange) performs the necessary adjustments automatically, and results in code that is cleaner to read and understand, and less prone to mistakes

Oh, yes, I see it now.

LFNoise1.kr(freq:5,add:1,mul:500)

mul and add always apply as ugen * mul + add.

Just because OP used keyword arguments to write add first does not mean that add is done first.

This is another reason why we should discourage mul and add. If you use math operators directly, then you control the order, not the underlying implementation.

mul and add are legacy from a time when math operations on UGens were not well optimized. We don’t need them anymore.

(LFNoise1.kr(freq:5) + 1) * 500

That would have done what was wanted, from the beginning.

And also:

SynthDef(\muladd, { Out.ar(0, SinOsc.ar(440) * 0.5 + 0.5) }).dumpUGens;

[ 0_SinOsc, audio, [ 440, 0.0 ] ]
[ 1_MulAdd, audio, [ 0_SinOsc, 0.5, 0.5 ] ]
[ 2_Out, audio, [ 0, 1_MulAdd ] ]

Wrote * and + – got the equivalent MulAdd.

hah