Simple triggered random lines Q

Hi, I am to translate this basic Max/MSP patch into a Supercollider synthdef:

Screen Shot 2022-08-06 at 4.35.17 PM

This is my (broken) attempt so far:

(
SynthDef(\speakerbass, { |out = 0, amp = 1|
	var source, trig, freqRandStart, freqRandEnd, lineFreq;
	//trig clock
	trig = Metro.kr(100, 2, mul: 1, add: 0);
	//random line freqs
	freqRandStart = Drand([75, 66, 50, 90, 100, 60], inf);
	freqRandEnd = Drand([27, 26, 21, 21, 20, 24], inf);
	//demand bass freq, on trig
	lineFreq = Demand.kr(trig, 0, Line.ar(start: freqRandStart, end: freqRandEnd, dur: 3, mul: 1, add: 0););
	//sine output
	source = SinOsc.ar(freq: lineFreq, phase: 0.0, mul: amp, add: 0.0);
	Out.ar(out, source) / 2;
}
).play;
);

I expect it to differ from my Max version in that both the Start and End line frequencies are randomized rather than paired, the line duration is always 3 seconds, and the clock is in BPM vs ms. But at the moment I’m not getting any audio output. Can anyone offer any suggestions, or an ideal approach to this problem? Little things like this just seem so much simpler in Max&PD… Thank you:)))
Peter

hi

(
SynthDef(\sin, {
	var trig, pairs, line, sig;
	trig = Impulse.ar(1/5); // generates a trigger each 5 secs
	pairs = Demand.ar(trig, 0.0, Drand([
		[75, 27, 3.0],
		[66, 26, 3.5],
		[50, 21, 3.0],
		[90, 21, 4.0]
	], inf)); // randomly choosing pre-paired values
	line = Sweep.ar(trig, 1/pairs[2]).linlin(0, 1, pairs[0], pairs[1], \minmax); // linear sweep 
	sig = SinOsc.ar(line)!2 * -10.dbamp;
	Out.ar(0, sig);
}).add;
)

Synth(\sin);
1 Like

@wehrk’s solution is correct, but you could make the Impulse, Demand, and Sweep control rate to more accurately reflect the performance of the max patch - but it will probably sound the same.

However, this line…

Out.ar(out, source) / 2;

… is a big error. You are dividing the output of the Out.ar Ugen, rather than the sound that you want to output. It should look like this

Out.ar(out, source / 2);

See, Out takes its second argument and outputs that to the bus. It does not care what you do to it after.

Sweep, however, is a little tricky, it draws a line starting at 0 that forever increases until reset, that is why the clipping linlin is so important. This is a clever use of clipping, but might not be obvious on first glance.
Alternatively, you could use an envelope.

(
SynthDef(\sin, {
	var trig = Impulse.kr(1/5); 
	var pairs = Demand.kr(trig, 0.0, Drand([
		[75, 27, 3.0],
		[66, 26, 3.5],
		[50, 21, 3.0],
		[90, 21, 4.0]
	], inf));

    // important to repeat the first value so that we can jump to it,
    // rather than interpolate slowly from the previous value.
    // 0 is the intial jump to the new value, between pairs[0] & pairs[0], whereas, 
    // pairs[2] is the time between pairs[0] & pairs[1]

	var envShape = [pairs[0], pairs[0], pairs[1]]; 
	var envTime = [0, pairs[2]]; 

	var env = EnvGen.kr(Env(envShape, envTime), trig);
	var sig = SinOsc.ar(env)!2 * -10.dbamp;
	Out.ar(0, sig);
}).add;
)


Synth(\sin);
1 Like

Hi,
Thank you both @wehrk and @jordan for your perfect solutions!
A quick Q, what does it mean, the “!2” in “sig = SinOsc.ar(line)!2 * -10.dbamp;”?
For fun, i’m attaching a small video of a first test with two of these oscillators modulating movement of some floppy latex on a membrane.


Peter

3 Likes

.dup(2)

I.e…

2!3 == [2,2,2]

1 Like

thank you @jordan for the explanation !