Help refining live coding piece from a semantics and syntax perspective

How to better write this piece of code?
thanks

/*

 author: tmm88
 date: 09-22

*/

(
~myPiece=Task({

	/////////////////////////////////////////////////////////

	~sec1={Pan2.ar(SinGrain.ar(Impulse.kr(100*Line.kr(0.1,10,15,1,0,2)),0.05,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,15,1,0,2))),0.3))}.play;
	15.wait; Server.freeAll;
	~sec2={Pan2.ar(GrainSin.ar(2,Impulse.kr(100*Line.kr(0.1,10,15,1,0,2)),1,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,15,1,0,2))),WhiteNoise.ar(0.6),-1,2048,1/64))}.play;
	15.wait; Server.freeAll;
	~sec3={Pan2.ar(GrainFM.ar(2,Impulse.kr(100*Line.kr(0.1,10,30,1,0,2)),10,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,30,1,0,2))),TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,30,1,0,2))),1,WhiteNoise.ar(0.6),-1,128,1/64))}.play;
	30.wait; Server.freeAll;
	~sec4={Pan2.ar(GrainFM.ar(2,Impulse.kr(100*Line.kr(0.1,10,60,1,0,2)),10,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,60,1,0,2))),TRand.kr(322,3222,Impulse.kr(100)),1,WhiteNoise.ar(0.6),-1,128,1/64))}.play;
	60.wait; Server.freeAll;
	~sec5={Pan2.ar(GrainFM.ar(2,Impulse.kr(1*Line.kr(0.1,10,90,1,0,2)),0.1,TRand.kr(322,3222,Impulse.kr(1*Line.kr(0.1,10,90,1,0,2))),TRand.kr(322,3222,Impulse.kr(1*Line.kr(0.1,10,90,1,0,2))),1,WhiteNoise.ar(0.6),-1,128,1/64))}.play;
	90.wait; Server.freeAll;

	/////////////////////////////////////////////////////////

	~sec1={Pan2.ar(Saw.ar(2000)*EnvGen.kr(Env.perc,Impulse.kr(5*2)))*(1/5)}.play;
	~sec2={Pan2.ar(Saw.ar(200)*EnvGen.kr(Env.perc,Impulse.kr(5*0.5)))*(1/5)}.play;
	~sec3={Pan2.ar(Saw.ar(1000)*EnvGen.kr(Env.perc,Impulse.kr(5*0.25)))*(1/5)}.play;
	~sec4={Pan2.ar(Saw.ar(4000)*EnvGen.kr(Env.perc,Impulse.kr(5*0.125)))*(1/5)}.play;
	~sec5={Pan2.ar(SinOsc.ar(128)*EnvGen.kr(Env.perc,Impulse.kr(5*0.125)))*(1/5)}.play;
	60.wait; Server.freeAll;

	/////////////////////////////////////////////////////////

	~sec1={Pan2.ar(SinGrain.ar(Impulse.kr(100*Line.kr(0.1,10,15,1,0,2)),0.05,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,15,1,0,2))),0.3))}.play;
	15.wait; Server.freeAll;
	~sec2={Pan2.ar(GrainSin.ar(2,Impulse.kr(100*Line.kr(0.1,10,15,1,0,2)),1,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,15,1,0,2))),WhiteNoise.ar(0.6),-1,2048,1/64))}.play;
	15.wait; Server.freeAll;
	~sec3={Pan2.ar(GrainFM.ar(2,Impulse.kr(100*Line.kr(0.1,10,30,1,0,2)),10,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,30,1,0,2))),TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,30,1,0,2))),1,WhiteNoise.ar(0.6),-1,128,1/64))}.play;
	30.wait; Server.freeAll;
	~sec4={Pan2.ar(GrainFM.ar(2,Impulse.kr(100*Line.kr(0.1,10,60,1,0,2)),10,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,60,1,0,2))),TRand.kr(322,3222,Impulse.kr(100)),1,WhiteNoise.ar(0.6),-1,128,1/64))}.play;
	60.wait; Server.freeAll;

	/////////////////////////////////////////////////////////

	~sec1_1={Pan2.ar(Mix.fill(128, {SinOsc.ar(rrand(20,20000)*Line.kr(0.125,8,60),mul:1/128)}))}.play;
	~sec1_2={Pan2.ar(Mix.fill(128, {SinOsc.ar(rrand(20,20000)*Line.kr(8,0.125,60),mul:1/128)}))}.play;
	15.wait; Server.freeAll;
	~sec2_1={Pan2.ar(Mix.fill(128, {SinOsc.ar(rrand(20,200)*Line.kr(0.125,8,60),mul:1/128)}))}.play;
	~sec2_2={Pan2.ar(Mix.fill(128, {SinOsc.ar(rrand(20,200)*Line.kr(8,0.125,60),mul:1/128)}))}.play;
	15.wait; Server.freeAll;
	~sec3_1={Pan2.ar(Mix.fill(128, {SinOsc.ar(rrand(200,2000)*Line.kr(0.125,8,60),mul:1/128)}))}.play;
	~sec3_2={Pan2.ar(Mix.fill(128, {SinOsc.ar(rrand(200,2000)*Line.kr(8,0.125,60),mul:1/128)}))}.play;
	30.wait; Server.freeAll;
	~sec4_1={Pan2.ar(Mix.fill(128, {SinOsc.ar(rrand(2000,20000)*Line.kr(0.125,8,60),mul:1/128)}))}.play;
	~sec4_2={Pan2.ar(Mix.fill(128, {SinOsc.ar(rrand(2000,20000)*Line.kr(8,0.125,60),mul:1/128)}))}.play;
	60.wait; Server.freeAll;

	/////////////////////////////////////////////////////////

	~sec1_1={Pan2.ar(Mix.fill(128, {SinOsc.ar(TRand.kr(20,20000,Impulse.kr(10))*Line.kr(0.5,2,60),mul:1/128)}))}.play;
	~sec1_2={Pan2.ar(Mix.fill(128, {SinOsc.ar(TRand.kr(20,20000,Impulse.kr(10))*Line.kr(2,0.5,60),mul:1/128)}))}.play;
	30.wait; Server.freeAll;
	~sec2_1={Pan2.ar(Mix.fill(128, {SinOsc.ar(TRand.kr(20,2000,Impulse.kr(10))*Line.kr(0.5,2,60),mul:1/128)}))}.play;
	~sec2_2={Pan2.ar(Mix.fill(128, {SinOsc.ar(TRand.kr(20,2000,Impulse.kr(10))*Line.kr(2,0.5,60),mul:1/128)}))}.play;
	30.wait; Server.freeAll;
	~sec3_1={Pan2.ar(Mix.fill(128, {SinOsc.ar(TRand.kr(200,2000,Impulse.kr(10))*Line.kr(0.5,2,60),mul:1/128)}))}.play;
	~sec3_2={Pan2.ar(Mix.fill(128, {SinOsc.ar(TRand.kr(200,2000,Impulse.kr(10))*Line.kr(2,0.5,60),mul:1/128)}))}.play;
	30.wait; Server.freeAll;
	~sec4_1={Pan2.ar(Mix.fill(128, {SinOsc.ar(TRand.kr(20,200,Impulse.kr(10))*Line.kr(0.5,2,60),mul:1/128)}))}.play;
	~sec4_2={Pan2.ar(Mix.fill(128, {SinOsc.ar(TRand.kr(20,200,Impulse.kr(10))*Line.kr(2,0.5,60),mul:1/128)}))}.play;
	30.wait; Server.freeAll;

	/////////////////////////////////////////////////////////

	~sec1={Pan2.ar(SinGrain.ar(Impulse.kr(100*Line.kr(0.1,10,15,1,0,2)),0.05,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,15,1,0,2))),0.3))}.play;
	15.wait; Server.freeAll;
	~sec2={Pan2.ar(GrainSin.ar(2,Impulse.kr(100*Line.kr(0.1,10,15,1,0,2)),1,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,15,1,0,2))),WhiteNoise.ar(0.6),-1,2048,1/64))}.play;
	15.wait; Server.freeAll;
	~sec3={Pan2.ar(GrainFM.ar(2,Impulse.kr(100*Line.kr(0.1,10,30,1,0,2)),10,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,30,1,0,2))),TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,30,1,0,2))),1,WhiteNoise.ar(0.6),-1,128,1/64))}.play;
	30.wait; Server.freeAll;
	~sec1_1={Pan2.ar(Mix.fill(128, {SinOsc.ar(TRand.kr(20,20000,Impulse.kr(10))*Line.kr(0.5,2,60),mul:1/128)}))}.play;
	~sec1_2={Pan2.ar(Mix.fill(128, {SinOsc.ar(TRand.kr(20,20000,Impulse.kr(10))*Line.kr(2,0.5,60),mul:1/128)}))}.play;
	30.wait; Server.freeAll;
	~sec2_1={Pan2.ar(Mix.fill(128, {SinOsc.ar(TRand.kr(20,2000,Impulse.kr(10))*Line.kr(0.5,2,60),mul:1/128)}))}.play;
	~sec2_2={Pan2.ar(Mix.fill(128, {SinOsc.ar(TRand.kr(20,2000,Impulse.kr(10))*Line.kr(2,0.5,60),mul:1/128)}))}.play;
	30.wait; Server.freeAll;
	~sec3_1={Pan2.ar(Mix.fill(128, {SinOsc.ar(TRand.kr(200,2000,Impulse.kr(10))*Line.kr(0.5,2,60),mul:1/128)}))}.play;
	~sec3_2={Pan2.ar(Mix.fill(128, {SinOsc.ar(TRand.kr(200,2000,Impulse.kr(10))*Line.kr(2,0.5,60),mul:1/128)}))}.play;
	30.wait; Server.freeAll;
	~sec4_1={Pan2.ar(Mix.fill(128, {SinOsc.ar(TRand.kr(20,200,Impulse.kr(10))*Line.kr(0.5,2,60),mul:1/128)}))}.play;
	~sec4_2={Pan2.ar(Mix.fill(128, {SinOsc.ar(TRand.kr(20,200,Impulse.kr(10))*Line.kr(2,0.5,60),mul:1/128)}))}.play;
	30.wait; Server.freeAll;

	/////////////////////////////////////////////////////////

	~sec1={Pan2.ar(SinGrain.ar(Impulse.kr(100*Line.kr(0.1,10,15,1,0,2)),0.05,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,15,1,0,2))),0.3))}.play;
	15.wait; Server.freeAll;
	~sec2={Pan2.ar(GrainSin.ar(2,Impulse.kr(100*Line.kr(0.1,10,15,1,0,2)),1,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,15,1,0,2))),WhiteNoise.ar(0.6),-1,2048,1/64))}.play;
	15.wait; Server.freeAll;
	~sec1_1={Pan2.ar(Mix.fill(16, {SinOsc.ar(rrand(20,200)*(Line.kr(0.5,2,60)))*0.01}))}.play;
	~sec1_2={Pan2.ar(Mix.fill(16, {SinOsc.ar(rrand(20,200)*(Line.kr(2,0.5,60)))*0.01}))}.play;
	60.wait; Server.freeAll;
	~sec2_1={Pan2.ar(Mix.fill(16, {SinOsc.ar(rrand(200,2000)*(Line.kr(0.5,2,60)))*0.01}))}.play;
	~sec2_2={Pan2.ar(Mix.fill(16, {SinOsc.ar(rrand(200,2000)*(Line.kr(2,0.5,60)))*0.01}))}.play;
	60.wait; Server.freeAll;
	~sec3_1={Pan2.ar(Mix.fill(16, {SinOsc.ar(rrand(2000,20000)*(Line.kr(0.5,2,60)))*0.01}))}.play;
	~sec3_2={Pan2.ar(Mix.fill(16, {SinOsc.ar(rrand(2000,20000)*(Line.kr(2,0.5,60)))*0.01}))}.play;
	60.wait; Server.freeAll;

	/////////////////////////////////////////////////////////

	~sec1={Pan2.ar(SinGrain.ar(Impulse.kr(100*Line.kr(0.1,10,15,1,0,2)),0.05,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,15,1,0,2))),0.3))}.play;
	15.wait; Server.freeAll;
});
)

~myPiece.play;

There doesn’t seem to be any scope for live coding here. You can’t interact with any of this in real time. What exactly do you mean by live coding?

I don’t quite know what you mean by semantics and syntax perspective, those are both technical terms which are either correct or not, since your code runs, it correct in that sense. I assume you mean how can you mean the code more maintainable, easier to read and understand (i.e., how can you make this code follow standard practice so that other people may understand your work).

That means this is going to be somewhat personal, based on what I can read, but I will try and justify my recommendation - please argue back if you think I’m wrong!

The most obvious thing would be to ask why you aren’t using a Synthdef for the similar functions? Personally, I quite like the raw function calls though, it will make it easier to move suddenly change something, so I’ll keep them in my examples.

Also, I don’t think you are using Pan2 correctly, normally you give it a pan position. If you just want to make a stereo sound do SinOsc.ar()!2 or a quad sound SinOsc.ar()!4.

1

Writing long lines is hard to read and makes it difficult to quickly understand what is wrong. I’ve changed a line in this code segment, can you tell what that change was at a glance?

~sec1={Pan2.ar(SinGrain.ar(Impulse.kr(100*Line.kr(0.1,10,15,1,0,2)),0.05,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,15,1,0,2))),0.3,0.1))}.play;

2

Use language features to simplify the signal flow inside of your functions.

~pipe = { |input ...fns|  fns.inject(input, {|v, f| f.(v) }) };

inject is incredibly powerful for this! The following function allows ~sec1 to be rewritten like such…

{
	~pipe.(
		Line.kr(start: 0.1, end: 10, dur: 15, doneAction: 2),
		{|ln| Impulse.kr(100 * ln) }, 
		{|im| SinGrain.ar(trigger: im, dur: 0.05, freq: TRand.kr(322, 3222, im)) * 0.3 },
		{|sngr| Pan2.ar(sngr) }
	)
}.play;

Here the result of the previous function becomes the input to the next, you can clearly see how the signal flows down the page.

All of this is in order to avoid repeating code.

~sec1={Pan2.ar(SinGrain.ar(Impulse.kr(100*Line.kr(0.1,10,15,1,0,2)),0.05,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,15,1,0,2))),0.3))}.play;

In the above you have 2 lines with all the parameters duplicated, what happens you want to change it and forget to alter the other accordingly?

If you find my ~pipe to be weird, the standard way is to use varibles.

{
	var line = Line.kr(0.1, 10, 15, 1, 0, 2);
	var impulse = Impulse.kr(100 * line)
	var freqs = TRand.kr(322, 3222, impulse);
	var sine = SinGrain.ar(impulse, 0.05, freqs, 0.3);
	sine!2
}.play;

3

Use named arguments where there might be ambiguity .

{Pan2.ar(SinGrain.ar(Impulse.kr(100*Line.kr(0.1,10,15,1,0,2)),0.05,TRand.kr(322,3222,Impulse.kr(100*Line.kr(0.1,10,15,1,0,2))),0.3))}.play;

What does the number 0.05 mean just before TRand in the above code? I have no idea without look at the documentation. Here it is obvious

{|im| SinGrain.ar(trigger: im, dur: 0.05, freq: TRand.kr(322, 3222, im)) * 0.3 },

4

After every event you are coping the duration of the line to then wait for said number of seconds. Try this…

~pipe = { |input ...fns|  fns.inject(input, {|v, f| f.(v) }) };

~syncAndFreeOnDone = {
	|src|
	var done = Done.kr(src);
	SendReply.kr(done, '/my/sync', 1);
	FreeSelf.kr(done);
	src
};

~sync = CondVar();
OSCdef(\syncer, { ~sync.signalOne }, '/my/sync');

~m = Task({
	{~pipe.(
		Line.kr(start: 0.1, end: 10, dur: 15),
		~syncAndFreeOnDone,
		{|ln| Impulse.kr(100 * ln) }, 
		{|im| SinGrain.ar(trigger: im, dur: 0.05, freq: TRand.kr(322, 3222, im)) * 0.3 },
		{|sngr| Pan2.ar(sngr) }
	)}.play;
	
	~sync.wait; // this is the important line
	
	{~pipe.(
		Line.kr(start: 0.1, end: 10, dur: 15),
		~syncAndFreeOnDone,
		{|ln| Impulse.kr(100 * ln) }, 
		{|im| SinGrain.ar(trigger: im, dur: 0.05, freq: TRand.kr(322, 3222, im)) * 0.3 },
		{|sngr| Pan2.ar(sngr) }
	)}.play
	
	...
	
	
});

~m.play

Above a CondVar is used to wait. Then, when the Line is done it will send an OSC message that, in turn, is used to signal the CondVar to continue.

5

Break similar code into functions. Functions are the bread and butter of programming. They let you avoid repeating code (see a theme here).

This line is repeated with a small change, the difference should be completely obvious.

~sec3_2={Pan2.ar(Mix.fill(16, {SinOsc.ar(rrand(2000,20000)*(Line.kr(2,0.5,60)))*0.01}))}.play;

Below, aside from the function name, only the differences are visible at the call site.

~cross = {
	|from, to|
	~pipe.(
		Line.kr(from, to, 60),
		~syncAndFreeOnDone,
		{|l| 16.collect{ rrand(2000, 20000) * l } },
		{|freqs| SinOsc.ar(freqs) * 0.01 },
		{|sines| Mix.ar(sines) },
		{|sine| sine!2 }
	)
};
...

{ ~cross.(from: 0.5, to: 2) }.play;
{ ~cross.(from: 2, to: 0.5) }.play;

6

Since the functions you are playing free themselves and you don’t alter them, there is no need for a varible, all the ~secN can be removed. Likewise, Server.freeAll shouldn’t be run in the middle of a piece, if you had effects buses they too would be destroyed at each call.

4 Likes

thank you so much for the attention driven on reviewing my piece of code.
vielen danke