UGens output value to function?

Dear List,

how can one use the output values of an Ugen at a given time to be evaluated by a function?
Lets say i have function

( f = { |x|
				{
					x / x.nextPowerOfTwo 
			};
})

and id like to apply .collect(f) on the 8 values of LFDNoise0.kr(1!8).range(1,100) at a given moment (e.g. on a trigger)?

Thanks,
Jan

1 Like

Here is a quick and dirty version, the first part could be packed into a synthdef which would allow modulation of the frequency of both trigger and noise generator:

(
{
	var freq1 = 0.5, freq2 = 2;
	var noisegenerators = {LFDNoise0.kr(freq1)}!8;
	SendReply.kr(Impulse.kr(freq2), '/noise', noisegenerators);
}.play(s);

~x = 1000; // can be dynamically changed while the functions are running
o = OSCFunc({ |msg|
	// msg.postln; 
	// msg[3..] holds all the noise values
	(~x/msg[3..]).postln;
}, '/noise');
)

Hi @Thor_Madsen,
thanks for the example! How would i be able to get the resulting array of the OscFunc back into the SynthDef though?
So generally SendReply/OscFunc are the way to go to allow this kind of communication between language and server?
Thanks,
Jan

It seems a little unnecessary to send the values back to the SynthDef as all these calculations could be done inside the SynthDef. I am a little unsure about what you are trying to accomplish, maybe explain?

Of course. I was aiming to evaluate an array of values by the function below using .collect(f) and using the resulting array within the same synth. I wouldn’t know how to do that because of the language operation involved e.g. with .nextPowerOfTwo…
It’d be great to know if there is another way beside OSCFunc which you proposed!

( f = { |x|
				{
					x / x.nextPowerOfTwo 
			};
})

Yes I see the problem, you can’t use NextPowerOfTwo inside a synthdef, I tried other solutions but could not come with anything that works. So maybe you do have to send the values generated back to (another) synthdef. Maybe someone else has a solution for this?

I think I found a solution. This could easily be expanded to 8 ugens, or you could simply instantiate and trigger 8 different synths in a .collect

SynthDef(\test, {|t_trig = 0|
	var sig = LFDNoise0.kr;
	var val = Latch.kr(sig, t_trig).unipolar ; // avoid negative values by using unipolar	
	(val/2.pow(ceil(log(val)/log(2)))).poll; // == val/val.nextPowerOfTwo
}).add

x = Synth(\test); // instantiate synth first

x.set(\t_trig, 1); // trigger to change values

Thanks @Thor_Madsen! I see, so the idea would be to find a way to circumscribe .nextPowerOfTwo.
The original function i intended to use though is a bit longer, i had just extracted that specific part because thats where it was returning error. So i do wonder if it is actually impossible to be able to use the results of such a function at all dynamically (for direct usage in a Synth), which would avoid the difficulty of translating the “illegal” language operations. Otherwise at least its beyond me how to circumscribe this specific function…

( f = { |x|
				if(x.log2.frac == 0.0) {
					1.0
				} {
					x / x.nextPowerOfTwo * 2
				};
			};
)

What do you intent to do with the result of the calculation? Is the SynthDef necessary or can you just keep it a language side function?

In your above example x.log2.frac == 0 will only return true for whole number values like 1.0, 2.0, 3.0 etc. If x comes from a noisegenerator like LFDNoise you will never in reality get a value of like that unless you use some kind of rounding.

Yes it really is necessary, as the function would take the values of a running frequency analysis as inputs, scaling these incoming intervals to within an octave…

Ah yes, its supposed to be using frequency analysis ugens, rounded to 1!

This SynthDef yields the value 1 approx. every other time but you can make any boolean-turned-into-trigger statement.

(
SynthDef(\test, {|t_trig = 0|
	var sig, val, trig, vals, selected;
	sig = LFDNoise0.kr;
	val = Latch.kr(sig, t_trig).unipolar ; // avoid negative values by using unipolar
	// trig = ((val.log2.frac == 0).asInteger).poll;
	trig = val.round(1);
	vals = [(val/2.pow(ceil(log(val)/log(2)))), 1]; // == val/val.nextPowerOfTwo]
	selected = Select.kr(trig, vals);
	selected.poll;
}).add;
)

x = Synth(\test); // instantiate synth first

x.set(\t_trig, 1); // trigger to change values

Ok, im trying to follow up on the language operation that is included in the SynthDef and while it seems close, it doesn’t give exactly the same results as the function:

( f = { |x|
				if(x.log2.frac == 0.0) {
					1.0
				} {
					x / x.nextPowerOfTwo * 2
				};
			};
)

z=Array.geom(8,1,3/2)
z.collect(f)
z.collect{ |i| (i/2.pow(ceil(log(i)/(log(2)*1))))}
z.collect{ |i| (i/2.pow(ceil(log(i)/(log(2)*2))))} //bit closer even

If this alternative approach can be furher modified to give the same results, the SynthDef you propose should do the trick!

I don’t really understand how you can compare the two. In my example the value is derived from a noisegenerator and in you example from a math function, maybe I am missing something. Try changing everything to .ar in my SynthDef, this should yield more accurate results in any case.

Sorry if im being confusing. The reason i do the array/math function is just to exemplify how an ideal input (from the frequency analysis) to the function could look like e.g. perfect ratios: 9000Hz/4000Hz=9/4 etc. The resulting array after the function should contain values between 1 and 2 ([9/4].collect(f)=1.125), as they represent the intervals reduced to within one octave (so its seems to me not to be a rounding accuracy error):

(
SynthDef(\test, {|t_trig = 0|
	var sig, val, trig, vals, selected;
        sig = LFDNoise3.ar(0.1!8).range(1,10000);
	//sig = Array.geom(8,1,3/2);
	val = Latch.ar(sig, 1).unipolar ; // avoid negative values by using unipolar
	trig = ((val.log2.frac == 0.0).asInteger);
	//trig = val.round(1);
	vals = [(val/2.pow(ceil(log(val)/(log(2)*1)))), 1]; // == val/val.nextPowerOfTwo]
	selected = Select.ar(trig, K2A.ar(vals));
	selected.poll;
}).play;
)

Edited

.kr will sample the signal for every control cycle, usually 64. So for 64 samples the value will remain the same whereas .ar will output a new value for every sample. So if you want high precision use .ar. EDIT I see you already changed to .ar except LFDNoise…

trig = ((val.log2.frac == 0.0).asInteger);

Problem here: == cannot be used this way with UGens. So this will never trigger.

You should use |==|. See lazy equality.

Was going to demonstrate this with a simple poll synth but poll is broken for initial triggers other than Impulse, so, no demo (spent 15 minutes trying to work around the bug, and failing).

vals = [(val/2.pow(ceil(log(val)/(log(2)*1)))), 1]; // == val/val.nextPowerOfTwo]

log(val)/(log(2)*1… log(val)/(log(2) == log2(val), and * 1 is a no-op, so, simplify:

vals = [(val / 2.pow(ceil(log2(val)))), 1]; // == val/val.nextPowerOfTwo]

hjh

1 Like

Thanks @jamshark70 for pointing out!
Still, im not getting the same results as with this function though:

( f = { |x|
	if(x.log2.frac == 0.0) {
		1.0
	} {
		x / x.nextPowerOfTwo * 2
	};
};
) 

z=Array.geom(8,1,3/2)
z.collect(f) //the aim would be to get the same results as here
z.collect{ |i| (i/2.pow(ceil(log(i)/(log(2)*1))))} //but values are still beyond an octave

ideally in the running synth vals would return values between 1 and 2 (values reduced to one octave):

(
SynthDef(\test, {|t_trig = 0|
	var sig, val, trig, vals, selected;
	sig = LFDNoise3.ar(0.1!8).range(1,10).round(1); //
	val = Latch.ar(sig, 1).unipolar ; // avoid negative values by using unipolar
	trig = ((val.log2.frac |==| 0.0).asInteger);
	//trig = val.round(1);
	vals = [(val/(2.pow(ceil(log2(val))))*1), 1]; // == val/val.nextPowerOfTwo]
	selected = Select.ar(trig, K2A.ar(vals));
	selected.poll;
}).play;
)

One thing is, asInteger should not be needed for Boolean expressions on UGens. If it is needed, it’s a sign that something is wrong. In your earlier example, you might have needed asInteger to handle the true or false value being returned from ==. But true and false are meaningless in the server – the server really does not know what to do with these. So if you are getting these in context of a SynthDef, this is a hint that == is the wrong operator.

If a or b is a UGen, then a (some boolean) b should be a BinaryOpUGen, and the server-side result of this will be either 0.0 or 1.0. Applying asInteger to either of these values is a no op = unneeded.

Also, I’m puzzled by sig. LFDNoise3.kr(...).range(1, 10) must be positive, but then you apply unipolar to avoid negative values, on a signal that cannot possibly be negative. Latch.kr(sig, 1) – is it really supposed to hold one value for the entire duration?

Oh, and if you do val / next power of two, then the values would be between 0.5 and 1.0, not 1.0 and 2.0. So let’s do floor instead.

TBH I think you might be overcomplicating it.

	var sig = LFDNoise3.ar(1).range(1, 10);
	var lower2Pow = 2 ** floor(log2(sig));
	Poll.ar(5, [sig, sig / lower2Pow]);

This will, AFAIK, guarantee values 1.0 <= x < 2.0. E.g., a couple of results from my test run:

UGen(MulAdd): 3.91262
UGen(BinaryOpUGen): 1.95631
UGen(MulAdd): 4.02559
UGen(BinaryOpUGen): 1.0064  <<-- oh! crossed an octave, ratio dropped

The |==| 0 logic is unnecessary because, e.g., log2(2.0) == 1.0 and floor(1.0) == 1.0, so there is no need to force it by Select at all. Unnecessary components = more chances for confusion and bugs.

hjh