Locking output values

Hello all,

is there a way to have an UGen’s output locking or rounding only to pre-given values?
Lets say i have an Array = [-0.3,0.5,0.77,-0.9] and i’d want a UGen output to just lock to any of these values when it reaches them (until the next it passes, possibly with a margin to make the switch).

What do you call “reaches” and how do you foresee to have it moving to the next value ?
How is stored that array ? Is it static ? Can it be hardcoded ? Or not ?

Depending on how you’d like to work, there are several options.
You could you use the Demand UGen but you need one trigger to make it move to the next value.
You can also have the Stepper and PulseCount UGens to increment an index, but you also need a trigger.

If your array can be hardcoded, then you can use this trick : (val1-val2).sign.max(0) gives you 1 if val1 > val2 and 0 if val1 <= val2.

In this example, a random frequency is rounded to some hardcoded values:

	var debug=Impulse.ar(2);
	var ff=#[330,880];
	var ffasUgen;
	var f=LFNoise0.ar(2).linexp(-1,1,100,2000);
	var f2;
	var i=0;
	Poll.ar(debug,f,"freq: ");
	// computes the position of f within the hardocded array
	ff.do({|fi, iter|
		Poll.ar(debug,i,format("i at %: ",iter));
	// build an array of Ugens (necessary for the Select.ar
	ffasUgen=ff.collect({|fi, iter|
	// i = 0 if f<=330, 1 if 330<f<=880, 2 for 880<f
	// we need to get it back to [0,1]     

If your array cannot be hardcoded, better pass it through a buffer.

Like a quantizer in modular synthesis.

I think the trick is to get the signal into a linear scale. For arbitrary data points, I’d load them into a Buffer and use IndexInBetween.

Then round to an integer index, then get the buffer value using Index.

A modular pitch quantizer works in terms of volts-per-octave, which would look like this in SC. (Could simplify by having the LFO generate VPO instead of frequencies and then converting, but, well, demonstrate another math technique.) You don’t have to use VPO – the scale could be in terms of ratios. var quantized = ... would work for any arbitrary data.


var ratios = [1, 9/8, 5/4, 3/2, 27/16, 15/8, 2];
var voltsPerOctave = ratios.log2;
b = Buffer.sendCollection(s, voltsPerOctave, 1);

a = {
	// frequencies
	var lfo = LFDNoise3.kr(
		LFNoise2.kr(0.1).exprange(0.4, 5)
	).exprange(200, 800);
	// volts-per-octave with 0 = middle C
	// i.e. a linear scale with 1 octave = 1.0
	var volts = (lfo / 60.midicps).log2;
	var octave = volts.floor;
	var withinOctave = volts - octave;
	// quantize to the 'b' scale
	var quantized = Index.kr(b,
		IndexInBetween.kr(b, withinOctave).round(1)

	// (2**o) * (2**q) = 2 ** (o+q)
	var freq = (2 ** (octave + quantized)) * 60.midicps;
	LFTri.ar(Lag.kr(freq, 0.05), 0, 0.1).dup


Thank you both for your suggestions! One of the ideas was to have an e.g. Pitch Analyzer only returning fixed values of a scale (array) if it detects/crosses them, so indeed pretty much a quantizer!

IndexInBetween is a great discovery btw, even has pretty much such an example in helpfiles :smiley: