# Just wanted to share

Yoyu can so much with just4 operators and a pbind

(
SynthDef(\dex11algo3,//op3-->op2-->op1<--op4
{
|
pitch=48,
op1att=0.001,op1dec=0.4,op1sus=0.01,op1rel=0.1,op1amt=1,op1tune=0,gate=1,
op2att=0.001,op2dec=3.250,op2sus=0.1,op2rel=0.1,op2amt=4,op2tune=12,
op3att=0.001,op3dec=0.150,op3sus=0.1,op3rel=0.1,op3amt=2,op3tune=0,
op4att=0.001,op4dec=1.250,op4sus=0.1,op4rel=0.1,op4amt=2,op4tune=0,feedbackamt=0.5,vol=0.5,out|
var env1,env2,env3,env4,op1,op2,op3,op4,pan=(0.0),signal;

op4=SinOscFB.ar((pitch+op4tune).midicps,env4*feedbackamt);
op4=op4*env4;
op4=op4*op4amt;
op4;
op3=SinOsc.ar((pitch+op3tune).midicps);
op3=op3*env3;
op3=op3*op3amt;
op2=SinOsc.ar((pitch+op2tune).midicps,op3);
op2=op2*env2;
op2=op2*op2amt;
op1=SinOsc.ar((pitch+op1tune).midicps,op2+op4);
op1=op1*env1;
signal=op1*op1amt;
signal=signal*vol;
signal=Pan2.ar(signal,pos:pan);
Out.ar(out,signal);
)

/////
q=Synth(\dex11algo3)
q.free

(
Pdef(\jj,
Pbind(\instrument,
\dex11algo3,
\dur,Pseq([
Pseq([1/4,1/8,1/4,1/8],6),
Pxrand([1/16,1/32,1/8],8),
],inf),
\pitch,Pxrand([
Pseq([48,49,53,60,67,43],8),
Pseq([46,49,51,63,65],8),
Pseq([50,46,60,58,41],8),
],inf),
\op1att,Pwhite (0,0.04,inf),
\op1dec,0.61,
\op1sus,0.2,
\op1rel,Pwhite(0.1,1,inf),
\op1amt,1,\op1tune,0,
\op2att,0.001,\op2dec,0.150,\op2sus,0.2,\op2rel,0.1,\op2amt,1,\op2tune,Pxrand ([24,12,0,12,24,36],inf),
\op3att,0.001,\op3dec,0.050,\op3sus,0.1,\op3rel,0.1,\op3amt,Pwhite(8,12,inf),\op3tune,12.1,
\op4att,0,\op4dec,Pwhite(0.050,1,inf),\op4sus,0.1,\op4rel,0.1,\op4amt,Pwhite(1,4,inf),\op4tune,Pwhite(-0.3,0.3,inf),\feedbackamt,Pwhite(0.1,0.7,inf),
\vol,Pseq([1,0,0.3,1,0,0.5,0.2,0.8,0.1,0,0.9,0.1,0.2,0.3,0.4,0.5,0.4,0.3,0.2,0.1]*0.5,inf),
\out,0,

)
)
)

Pdef(\jj).play
Pdef(\jj).stop

3 Likes

Sounds great!

Your synthdef is very hard to read though! Particularly if you want other people to be able to understand what it does.

Perhaps this is easier to read?

(
SynthDef(\dex11algo3,//v3-->v2-->v1<--v4
{
var allArguments = (
\atk:  [\op1att.kr(0.001), \op2att.kr(0.001), \op3att.kr(0.001), \op4att.kr(0.001)],  // \atk.kr(0.00!4), or like this?
\dec:  [\op1dec.kr(0.4),   \op2dec.kr(3.25),  \op3dec.kr(0.15),  \op4dec.kr(1.25)],   // \dec.kr(0.1!4),
\sus:  [\op1sus.kr(0.01),  \op2sus.kr(0.1),   \op3sus.kr(0.1),   \op4sus.kr(0.1)],    // \sus.kr(0.1!4)
\rel:  [\op1rel.kr(0.1),   \op2rel.kr(0.1),   \op3rel.kr(0.1),   \op4rel.kr(0.1)],    // \rel.kr(0.1!4),
\amt:  [\op1amt.kr(1),     \op2amt.kr(4),     \op3amt.kr(2),     \op4amt.kr(2)],      // \amt.kr(2!4),
\tune: [\op1tune.kr(0),    \op2tune.kr(12),   \op3tune.kr(0),    \op4tune.kr(0)]      // \tune.kr(2!4)
);
var voice1 = allArguments.collect{|a| a[0] };
var voice2 = allArguments.collect{|a| a[1] };
var voice3 = allArguments.collect{|a| a[2] };
var voice4 = allArguments.collect{|a| a[3] };

var mk_env = {
|voice, gate, done_action|
EnvGen.ar(Env.adsr(voice[\atk], voice[\dec], voice[\sus], voice[\rel]), gate: gate ? 1,  doneAction: done_action ? 0)
};
var mk_freq = {
|voice|
(\pitch.kr(48) + voice[\tune]).midicps
};

var env3 = mk_env.(voice3);
var v3 = SinOsc.ar(mk_freq.(voice3)) * env3 * voice3[\amt];

var env2 = mk_env.(voice2);
var v2 = SinOsc.ar(mk_freq.(voice2), v3) * env2 * voice2[\amt];

var env4 = mk_env.(voice4);
var v4 = SinOscFB.ar(mk_freq.(voice4), env4 * \feedback.kr(0.5) ) * env4 * voice4[\amt];

var env1 = mk_env.(voice1, \gate.kr(1), 2);
var v1 = SinOsc.ar(mk_freq.(voice1), v2 + v4) * env1 * voice1[\amt];

var final = Pan2.ar(v1 * \vol.kr(0.5), pos: \pan.kr(0));
Out.ar(\out.kr(0), final);
)

3 Likes

changed it a bit , this is why I love supercolldier so much

3 Likes

Could you explain some bit of your cleaned up code ., to me it looks intimitading but thatw proabl
Why ar the all arguments .kr ?
There are not kontrol rate or are they ?
And the \out.kr ??

Same reason as the last time you asked about \name.kr notation

If you write your control inputs as arguments, the SynthDef builder makes them control rate for you.

If you write the input names as symbols, then you have to give the rate (otherwise they’re just symbols).

hjh

But why are they turned into control rate ?
These are envelope stages and imho an envelope should always be .ar since its muliplying an ac signal ( ~SinOc ) with a dc signal ( unipolar envelope )
We don’t wan’t loose any resolution of the envelopes do we ?

Go look for the EnvGen, and see if it’s ar or kr.

It’s extremely common to have audio rate units with control rate inputs. Basically, control rate would be useless if this were not possible. It is true that the output rate of a filter should match the rate of its signal input, but EnvGen is not a filter.

Btw your envelope control inputs were always control rate… and there wasn’t a problem with that.

hjh

Ah yes indeed , the envelope arguments were control rate

All it does is put each control into an Event as an array, which is a key->value collection, based on the control type (atk, dec). Here each index is a voice.

Then it turns them into an Event for each voice.

This way when you use it you can write the voice number and the argument you want, voice1[\atk].

Since you are only using one voice at a time, I think this is easier to read. If you started to mix and match, it wouldn’t.

The code I wrote has only 6 real lines of code that the reader has to understand (starting at var env3...), the rest is just bookkeeping, whereas you have to read all of your code in quite some detail to unpick it; essentially remembering what each variable was at the previous state and running a little simulation in your head as you read down the page. Whereas because I’ve used inline ‘var’ and the variables never change, once something is set, that’s it. Also by using functions you can just see that the thing returns an Env and sort of skip over it, worrying about the details later.

I’m not suggesting this is a perfectly readable solution, that’s quite a personal thing, but using more complex language features, like Events, functions, and functional methods like .collect can sometimes reduce complexity and makes things quicker to read. Which is really useful for maintaining the code and particularly if you want someone to understand how it works.

Jamshark70 has already mentioned the named controls. But \out.kr is control rate because this isn’t the audio output, but the input for the output’s bus number. It should probably be called \outBusNumber.kr(0).
Personally I dislike arguments in synths (unless being used to inject a value into SynthDef.wrap…) and prefer using NamedControl, or the shorthand (which I know some others dislike), specifically because you don’t explicitly state the rate.

For an example of audio rate control look at Synth.map…

~d = SynthDef(\t, {Out.ar(0, \in.ar(0)) }).play;
~b = Bus.audio(s,1); // put some sound in here some how
~d.map(\in, ~b) // now ~b is mapped to \in.ar