Project structure - timing random events

Hi all!
I am quite new to SuperCollider and programming, so really sorry in advance if I am doing something wrong or saying something stupid.
I want to build a project with a live interaction between some things been played, and some things kind of auto-generated.
So, what I am thinking about is to do a 10 minutes long performance.
In this 10 minutes, there are some points of accumulation, I am talking about 4 or 5 (maybe less), these are auto generated when the project starts.
Events are kind of attracted by this points and so they will accumulate in that area: i am not afraid about silence.
Then, at the climax of each point, they could all fade irregularly into silence or stop everything at the same time.
The events could be different kind of things, I was thinking about some textures and some irregular accumulation of glitchy rythms.
I already have code for generating the textures which satisfies me enough (for the moment), and I think I can work something for the rhytmic things too.
The problem for me is about structuring these time events, in order to reach this kind of global structure.
I was having a look on patterns, but one of the main problems, is that it seems so much linked to traditional timing events, I am about to code something which would be more kind of experimental, and not linked to the traditional timing, or at least in the global structure.
I am attaching also a very very bad photo (sorry, my phone sucks) which I hope will make it clear, I hope i will provide a better one before this evening!


So thanks in advance for your help,
I hope I have explained myself in a decent way.
Thanks

There are lots of different ways to do what you want in SC (too many maybe!)
…but have a look at scheduling on a clock:

SystemClock.sched( deltaInSeconds, myTextureFunction)

will schedule execution of myTextureFunction in deltaInSeconds - there’s also a version that schedules in absolute time: SystemClock.schedAbs

then you can build arrays of start times for example :

~times=10.collect{exprand(10,20)}

will give an array of 10 start times between 10 and 20 seconds distributed exponentially (more near 10 than 20) then

~times.do(SystemClock.sched(_,anArrayofMyCoolTextures.choose)

will schedule 10 textures chosen at random at those times.

you can set the target of one clump of sounds in a Group which is one way for you to do the group cutoffs that your score indicates (check out the help for Group)

you can also schedule on a TempoClock which lets you warp the whole composition as it runs…

I don’t agree with this. Patterns are very flexible and can certainly be handled in the way you describe. E.g. have a look at Pspawner, which is a powerful tool for sprouting sub-patterns, sequential and parallel as you prefer, with an arbitrary level of pattern nesting. The problem is rather that it definitely needs time to dive into its specific syntax (in addition to SC syntax in general !) and to handle it fluently so that you have fun and benefit of working with it. I’d say it’s worth giving it a try, but be patient.

I agree with semiquaver that the best way to do this is to generate all the onset times in advance.

You can use patterns to stream out these onset times by .differentiate (or see also Pdiff) – that will turn an array of time points into an array of time deltas (suitable for patterns’ \dur key or a wait time in a Routine or Task). This can handle more events than scheduling them all in advance. (The schedAbs approach is limited to the size of clock’s queue; converting to deltas allows them to be scheduled incrementally, which you can do indefinitely.)

hjh

Thanks a lot for those answers you all,
I am kind of more confused than before maybe!

Thanks a lot for, this, so far it kinda seems the easiest way for me, or at least the most comprehensible.

The only problem I have with this method is that I do not really get how to schedule properly my texture’s SynthDef, I’ll try to explain myself better:
In this moment I am not getting that idea of construction and destruction i kind of explained, it is more of a massive drone that starts and ends togheter(and somethimes it merges too!), what am I doing wrong?

Plus: how can I get any ~stops different from each other, without creating a 2D Array? (or how can I go from 2D array to a normal one?)
AND:
How can I get more widespread random values between each synths? thanks a lot!

//server config
(
s = Server.local;
s.newBusAllocators;
ServerBoot.removeAll;
ServerTree.removeAll;
ServerQuit.removeAll;
/*o = Server.default.options;
o.inDevice_("ASIO : Focusrite USB");
o.outDevice_("ASIO : Focusrite USB")
*/
);

//global var
(
~times = [rrand(3, 30)] ++ [rrand(100, 200)] ++ [rrand(210, 300)] ++ [rrand(300, 400)] ++ [rrand(500, 600)];
~stops = ~times + [rrand(30, 100)];
);

//global func
(
~cleanUp = {
   s.newBusAllocators;
   ServerBoot.removeAll;
   ServerTree.removeAll;
   ServerQuit.removeAll;
};

~makeBuffers={
   var arrSqu, arrSaw;
   var sigSqu, sigSaw, sigSin, sigRand;

   arrSqu = {
   	var arr;
   	arr = Array.fill(2.pow(14).asInteger, {|i|
   		i = i+1;
   		if(i.odd){
   			i = 1/i;
   		}
   		{i = 1}
   	}
   	);
   	arr = arr * 0.75
   };

   sigSqu = Signal.sineFill(
   	2.pow(14).asInteger,
   	arrSqu.value,
   	0!8192);

   arrSaw = {
   	var arr;
   	arr = Array.fill(2.pow(14).asInteger, {|i|
   		i = i+1;
   		i = 1/i;
   	});
   	arr = arr * 0.5
   };

   sigSaw = Signal.sineFill(
   	2.pow(14).asInteger,
   	arrSaw.value,
   	0!8192);

   sigSin = Signal.sineFill(
   	2.pow(14).asInteger,
   	[1],
   	[0]);

   sigRand = Env(
   	{rrand(-1, 1)}!14,
   	{exprand(1, 20)}!15,
   	{rrand(1, 20)}!15
   ).asSignal(2.pow(14).asInteger);

   ~bufSin = Buffer.alloc(s, (2.pow(15)).asInteger);
   ~bufSqu = Buffer.alloc(s, (2.pow(15)).asInteger);
   ~bufSaw = Buffer.alloc(s, (2.pow(15)).asInteger);
   ~bufRand = Buffer.alloc(s, (2.pow(15)).asInteger);

   ~bufSin.loadCollection(sigSin.asWavetable);
   ~bufSqu.loadCollection(sigSqu.asWavetable);
   ~bufSaw.loadCollection(sigSaw.asWavetable);
   ~bufRand.loadCollection(sigRand.asWavetable);
};

~makeBusses = s.makeBundle(s.latency, {
   ~masterBus = Bus.audio(s,2);
});

~makeNodes = {
   s.bind(
   	~srcGrp = Group.new;
   	~msGrp = Group.after(~srcGrp);
   	s.sync;
   	~masterSynth = Synth.new(\master, [\in, ~masterBus], ~msGrp, \addToTail);
   )
}
);

//reg
(
ServerBoot.add(~makeBuffers);
ServerBoot.add(~makeBusses);
ServerQuit.add(~cleanUp);
);

//boot
(
s.waitForBoot({

   SynthDef.new(\vOsc, {
   	arg buf=0, numBufs=4, pos= 1.0,
   	freq = 300, amp = 0.9, detuneAmount = 0.001, jitAmount=0.001, cutoff = 12000,
   	atk=0.001, dcy=0.2, sus = 1.0, rel = 0.4, dly=0.0, crv=1,
   	gate = 1, out=0;
   	var sig, env, bufpos, jitter, detuneSig;
   	jitter = LFNoise2.kr(jitAmount, jitAmount, jitAmount);
   	bufpos = buf + pos + jitter;
   	detuneSig = LFNoise1.kr(0.02!8).bipolar(detuneAmount).midiratio.lag(0.5);
   	env = EnvGen.kr(Env.dadsr(dly, atk, dcy, sus, rel, curve:crv), gate, doneAction:2);
   	sig = VOsc.ar(bufpos.clip(buf+0.1, numBufs-0.1), freq*(detuneSig).lag(0.3));
   	sig = Splay.ar(sig) * env * amp;
   	//sig = OnePole.ar(sig, exp(-2pi * (freq * SampleDur.ir * 9))); //http://tiny.cc/4462gz || 4 is arbitrary
   	sig = LPF.ar(sig, cutoff);
   	Out.ar(out, sig);
   }).add;

   SynthDef(\master, {
   	arg in = 0, out = 0;
   	var sig;
   	sig = In.ar(in, 2);
   	//sig = FreeVerb2.ar(sig, sig, 0.3, 0.5, 0.8);
   	sig = LeakDC.ar(sig);
   	sig = Limiter.ar(sig);
   	Out.ar(out, sig);
   }).add;

   s.sync;
   ServerTree.add(~makeNodes);
   s.sync;
   s.freeAll;

   ~myCoolTextures = {
   	~times.postln;
   	~stops.postln;

   	~scale = [
   		Scale.dorian, Scale.hexPhrygian, Scale.hungarianMinor, Scale.ionian,
   		Scale.partch_o1, Scale.mixolydian, Scale.superLocrian, Scale.marva,
   		Scale.partch_o5, Scale.yakahDesc, Scale.suznak, Scale.romanianMinor,
   	].choose;
   	~chords = 16.collect{
   		[~scale.degreeToFreq(~scale.degrees.choose,
   			(if(rrand(0,1).coin,
   				rrand(5.1, 20),
   				rrand(96, 127))
   			),
   			(if(rrand(0,1).coin,
   				(rrand(0.01, 1)),
   				(rrand(4, 5)))
   			)
   		)].clip(0.1, 18000)
   	};
   	
   	~synthi = 16.collect{
   		Synth.new(\vOsc, [
   			\freq, ~chords.choose,
   			\amp, rrand(0.1, 0.25),
   			\atk, rrand(6.5, 17),
   			\dly, rrand(0, 9.8),
   			\dcy, rrand(3.1, 8),
   			\sus, rrand(0.2, 0.6),
   			\rel, rrand(0.000001, 11),
   			\crv, if(1.coin,rrand(-6,-0.001),rrand(0.0001,6)),
   			\pos, rrand(0.5, 2.7),
   			\cutoff, rrand(6000, 14000),
   			\detuneAmount, rrand(0.01, 0.55),
   			\jitAmount, rrand(0.001, 0.2),
   			\buf, ~bufSin.bufnum,
   			\out, ~masterBus
   		], ~srcGrp
   	)};

   };


   ~times.do(SystemClock.sched(_, ~myCoolTextures));
   ~stops.do(SystemClock.sched(_,
   	~srcGrp.set(\gate, 0)
   	/*{
   	if(rrand(0,1).coin,
   	(~srcGrp.set(\gate, 0)),
   	(~srcGrp.freeAll))
   	}
   	*/
   ));

   ~times.postln;
   ~stops.postln;
})//s.waitForBoot ends
)

Same thing applies to patterns!

//server config
(
s = Server.local;
s.newBusAllocators;
ServerBoot.removeAll;
ServerTree.removeAll;
ServerQuit.removeAll;
o = Server.default.options;
o.inDevice_("ASIO : Focusrite USB");
o.outDevice_("ASIO : Focusrite USB")
);

//global var
(
~tempo = TempoClock.new;
~tempo = (80/60);
~times_buf = [rrand(3, 15)] ++ [rrand(90, 180)] ++ [rrand(190, 285)] ++ [rrand(295, 380)] ++ [rrand(480, 540)];
~stops_buf = ~times_buf + [rrand(10, 40)];
~path = PathName(thisProcess.nowExecutingPath).parentPath++"buf/";
);

//global func
(
~cleanUp = {
   s.newBusAllocators;
   ServerBoot.removeAll;
   ServerTree.removeAll;
   ServerQuit.removeAll;
};

~makeRhythmBuffers={
   b = Dictionary.new;
   PathName(~path.asString).entries.do{
   	arg subfolder;
   	b.add(
   		subfolder.folderName.asSymbol ->
   		Array.fill(
   			subfolder.entries.size,
   			{
   				arg i;
   				Buffer.read(s, subfolder.entries[i].fullPath);
   			}
   		)
   	);
   };

   ~kick = b[\kick][0];
   ~snare = b[\snare][(0..2).choose];
   ~hihat = b[\hihat][(0..3).choose];
   ~sines = b[\sines][(0..1).choose];

};

~makeBusses = {
   ~masterBus = Bus.audio(s,2);

};

~makeNodes = {
   s.bind(
   	~bufGrp = Group.new;
   	~msGrp = Group.after(~bufGrp);
   	s.sync;
   	~masterSynth = Synth.new(\master, [\in, ~masterBus], ~msGrp, \addToTail);
   )
}
);

//reg
(
ServerBoot.add(~makeRhythmBuffers);
ServerBoot.add(~makeBusses);
ServerQuit.add(~cleanUp);
);
//boot
(
s.waitForBoot({
   var kick, hihat, snare, sines;
   s.sync;

   SynthDef.new(\BUF, {
   	arg atk=0.00001, sus=1, rel=0.2,
   	buf=0, rate=1, lo=0,
   	amp=1, out=0;
   	var sig, env;
   	env = EnvGen.kr(Env([0,1,1,0],[atk,sus,rel],), doneAction:2);
   	sig = PlayBuf.ar(1, buf, rate*BufRateScale.ir(buf));
   	sig = sig*env*amp;
   	sig = Splay.ar(sig);
   	Out.ar(out,sig);
   }).add;

   SynthDef(\master, {
   	arg in = 0, out = 0;
   	var sig;
   	sig = In.ar(in, 2);
   	//sig = FreeVerb2.ar(sig, sig, 0.3, 0.5, 0.8);
   	sig = LeakDC.ar(sig);
   	sig = Limiter.ar(sig);
   	Out.ar(out, sig);
   }).add;

   s.sync;
   ServerTree.add(~makeNodes);
   s.freeAll;


   ~myCoolRhytms = {

   	~times_buf.postln;
   	~stops_buf.postln;

   	kick = Pbind (
   		\instrument, \BUF,
   		\tempo, Pwrand([62/60, 124/90], [6, 1].normalizeSum, inf),
   		\dur, Pwrand([1/2, 1/4], [10, 2].normalizeSum, inf),
   		\amp, Pwhite(0.5, 1.5, inf),
   		\buf, ~kick,
   		\rate, Pwhite(0.95, 1.05, inf),
   		\out, ~masterBus,
   	);
   	kick.play;

   	hihat = Pbind (
   		\instrument, \BUF,
   		\tempo, Prand([40/60, 60/60, 80,60, 100/60, 120/60], inf),
   		\dur, Pwrand([1/2, 1/7, 1/16, 1/3, 1/5, 1/4, 1/6], [10, 8, 2, 7, 6, 5, 4].normalizeSum, inf),
   		\amp, Pwhite(0.05, 0.9, inf),
   		\buf, ~hihat,
   		\rate, Pwhite(1.5, 16, inf),
   		\out, ~masterBus,
   		\node, ~bufGrp
   	);
   	hihat.play;

   	snare = Pbind (
   		\instrument, \BUF,
   		\tempo, Prand([40/60, 80/90], inf),
   		\dur, Pwrand([1/4, Rest(), 1/7, 1/3], [10, 4, 2, 0.5].normalizeSum, inf),
   		\amp, Pwhite(0.005, 0.3, inf),
   		\buf, ~snare,
   		\rate, Pwhite(1.3, 2, inf),
   		\out, ~masterBus,
   	);
   	snare.play;

   	sines = Pbind (
   		\instrument, \BUF,
   		\tempo, Prand([40/60, 80/90], inf),
   		\dur, Pwrand([1/7, Rest(), 1/4, Rest(), 1/5], [10, 10, 4, 8, 2].normalizeSum, inf),
   		\amp, Pwhite(0.005, 0.25, inf),
   		\buf, ~sines,
   		\rate, Pwhite(0.3, 13, inf),
   		\out, ~masterBus,
   	);
   	sines.play;

   };

   ~times_buf.do(SystemClock.sched(_, ~myCoolRhytms));

   ~stops_buf.do(SystemClock.sched(_,
   	{
   		kick.stop;
   		snare.stop;
   		hihat.stop;
   		sines.stop
   	}
   ));

   ~times_buf.postln;
   ~stops_buf.postln;

});//waitForBoot ends

);



In this moment I am not getting that idea of construction and destruction i kind of explained, it is more of a massive drone that starts and ends togheter(and somethimes it merges too!), what am I doing wrong?

It would be really helpful to simplify your code for the forum. If the concern is about scheduling, then we need a simple example that illustrates the scheduling problem.

This is also a valid troubleshooting technique that you can use on your own sometimes, not only for the forum. If you can reproduce the problem in a simpler context, then it may be clearer where the problem lies. But as it is, you’re lost in a thousand details.

(
// No, this is a clumsy way to write it
// ~times = [rrand(3, 30)] ++ [rrand(100, 200)] ++ [rrand(210, 300)] ++ [rrand(300, 400)] ++ [rrand(500, 600)];

// Better:
~times = [rrand(3, 30), rrand(100, 200), rrand(210, 300), rrand(300, 400), rrand(500, 600)];

// This is adding one and only one duration to all of the random times
// ~stops = ~times + [rrand(30, 100)];

// Do you want different durations?
~stops = ~times + Array.fill(~times.size { rrand(30, 100) });
)

// But, to reproduce in a simple case
// which won't force the people helping you to wait 10 minutes:
~times = [0, 3];
~stops = [5, 8];

// and a simpler version of the rest
//global var
(
~times = [0, 3];
~stops = [5, 8];

~srcGrp = Group.new;
);

(
~freq = 0;

~myCoolTextures = {
	SystemClock.seconds.debug("myCoolTextures at");
	~freq = ~freq + rrand(100, 400);
	~synthi = Synth(\default, [freq: ~freq], target: ~srcGrp);
};


~times.do(SystemClock.sched(_, ~myCoolTextures));
~stops.do(SystemClock.sched(_,
	~srcGrp.set(\gate, 0)
));

~times.postln;
~stops.postln;
)

With this version, I get the two synths at different times (okay), but they never stop.

~stops.do(SystemClock.sched(_,
	~srcGrp.set(\gate, 0)
));

^^ Here, you have actually not scheduled an action – it’s doing ~srcGrp.set immediately. You should schedule a function:

~stops.do(SystemClock.sched(_, { ~srcGrp.set(\gate, 0) }));

So if you replace that in the simpler code snippet, then you get two synths at different times (OK) but they stop at the same time… because they are both going into ~srcGrp, and there is no way for ~srcGrp to know which of its children should stop at which time. That is, your logic differentiates between onset times, but “stop” is currently defined as “stop everything.”

So the next thing you would probably try is to stop ~synthi… but this is also incorrect.

(
~freq = 0;

~myCoolTextures = {
	SystemClock.seconds.debug("myCoolTextures at");
	~freq = ~freq + rrand(100, 400);
	~synthi = Synth(\default, [freq: ~freq], target: ~srcGrp).debug("created synthi");
};

~times.do(SystemClock.sched(_, ~myCoolTextures));
~stops.do(SystemClock.sched(_, { ~synthi.debug("stopping").set(\gate, 0) }));
)

myCoolTextures at: 4968.12814079
created synthi: Synth('default' : 1018)
myCoolTextures at: 4971.12814079
created synthi: Synth('default' : 1019)
stopping: Synth('default' : 1019)
stopping: Synth('default' : 1019)
FAILURE IN SERVER /n_set Node 1019 not found

See the problem? The second myCoolTextures changed ~synthi – so the release function no longer has access to the earlier one.

If there is the possibility of overlapping, then it isn’t safe to use a variable to transmit from one function to the other.

In this case, you have to have the textures function do both the start and the stop, and use local variable scope to keep them separate.

(
~freq = 0;

~myCoolTextures = { |start, stop|
	var synthi;
	SystemClock.sched(start, {
		SystemClock.seconds.debug("myCoolTextures at");
		// note, this is not used separately for stop, so it doesn't have to be local
		~freq = ~freq + rrand(100, 400);
		synthi = Synth(\default, [freq: ~freq], target: ~srcGrp).debug("created synthi");
	});
	SystemClock.sched(stop, {
		synthi.debug("releasing").set(\gate, 0);
	});
	synthi  // return, just in case
};

[~times, ~stops].flop.do { |pair|
	~myCoolTextures.value(pair[0], pair[1]);
};
)

myCoolTextures at: 5271.389609383
created synthi: Synth('default' : 1025)
myCoolTextures at: 5274.389609383
created synthi: Synth('default' : 1026)
releasing: Synth('default' : 1025)
releasing: Synth('default' : 1026)

With the onset/release logic finally working, then, expand to multiple synths:

(
~myCoolTextures = { |start, stop|
	var synthi;
	SystemClock.sched(start, {
		SystemClock.seconds.debug("myCoolTextures at");
		// note, this is not used separately for stop, so it doesn't have to be local
		synthi = Array.fill(16, {
			Synth(\default, [freq: rrand(100, 800)], target: ~srcGrp)
		}).debug("created synthi");
	});
	SystemClock.sched(stop, {
		synthi.debug("releasing").do(_.set(\gate, 0));
	});
	synthi  // return, just in case
};

[~times, ~stops].flop.do { |pair|
	~myCoolTextures.value(pair[0], pair[1]);
};
)

Then you’re ready to plug this into all the other logic.

It definitely takes some time to wrap your head around scheduling – so I strongly suggest getting that part to work first, before expanding into the sounds and timing that you ultimately want to use.

hjh

1 Like

Hi jamshark70,
Thanks a lot for your advices, they were really helpful.
So now I have things working nice, but not exactly as I would like them to sound.
One of the main problems I have right now is that i would like to stop all of the synths running (if they are not overlapped), and maybe before the stop, make them reach a little climax (so maybe a little volume increase could work just fine).
Plus, i would like the rythms to be more pointillistic and be less dense, cause until now, they spawn every time with the code, every type of rhytm.
Also, a sort of a mixing problem: these rhytms are more or less sucked into the textures… what should I do maybe? Route the textures through a bus a lower it?
Thanks in advance, and sorry if I am gonna post all the code… I swear I will use you suggestion on synthetize the logic, in the future!

//server config
(
s = Server.local;
s.newBusAllocators;
ServerBoot.removeAll;
ServerTree.removeAll;
ServerQuit.removeAll;

/*
s.options.numAnalogInChannels = 2;
s.options.numAnalogOutChannels = 2;
s.options.numDigitalChannels = 2;

s.options.blockSize = (2.pow(8));
s.options.numInputBusChannels = 2;
s.options.numOutputBusChannels = 2;

s.options.headphoneLevel = -3;

s.options.postln;
*/
o = Server.default.options;
o.inDevice_("ASIO : Focusrite USB");
o.outDevice_("ASIO : Focusrite USB")
);

//global var
(
~scale = [
	Scale.dorian, Scale.hexPhrygian, Scale.hungarianMinor, Scale.ionian,
	Scale.partch_o1, Scale.mixolydian, Scale.superLocrian, Scale.marva,
	Scale.partch_o5, Scale.yakahDesc, Scale.suznak, Scale.romanianMinor,
];

~tempo = TempoClock.new;
~tempo.tempo_(80/60);
~path = PathName(thisProcess.nowExecutingPath).parentPath++"buf/";

~times_1 = [rrand(3, 20), rrand(80, 150), rrand(180, 260), rrand(290, 360), rrand(390, 560)];
~stops_1 = ~times_1 + Array.fill(~times_1.size, {rrand(30, 70)});

~times_2 = [exprand(~times_1[0], ~times_1[0]+15), exprand(~times_1[1], ~times_1[1]+15), exprand(~times_1[2], ~times_1[2]+15), exprand(~times_1[3], ~times_1[3]+15), exprand(~times_1[4], ~times_1[4]+15)];
~stops_2 = ~times_2 + Array.fill(~times_2.size, {rrand(40, 60)});

~times_3 = [exprand(~times_1[0], ~times_1[0]+10), exprand(~times_1[1], ~times_1[1]+10), exprand(~times_1[2], ~times_1[2]+10), exprand(~times_1[3], ~times_1[3]+10), exprand(~times_1[4], ~times_1[4]+10)];
~stops_3 = ~times_3 + Array.fill(~times_3.size, {rrand(40, 50)});

~times = [~times_1, ~times_2, ~times_3];
~stops = [~stops_1, ~stops_2, ~stops_3];

~btimes_1 = [exprand(~times_1[0], ~times_1[0]+15), exprand(~times_1[1], ~times_1[1]+15), exprand(~times_1[2], ~times_1[2]+15), exprand(~times_1[3], ~times_1[3]+15), exprand(~times_1[4], ~times_1[4]+15)];
~bstops_1 = ~btimes_1 + Array.fill(~btimes_1.size, {rrand(40, 50)});

[~btimes_1, ~bstops_1].flop.postln;
);

//global func
(
~cleanUp = {
	s.newBusAllocators;
	ServerBoot.removeAll;
	ServerTree.removeAll;
	ServerQuit.removeAll;
};

~makeBuffers={
	var arrSqu, arrSaw;
	var sigSqu, sigSaw, sigSin, sigRand;

	arrSqu = {
		var arr;
		arr = Array.fill(2.pow(14).asInteger, {|i|
			i = i+1;
			if(i.odd){
				i = 1/i;
			}
			{i = 1}
		}
		);
		arr = arr * 0.75
	};

	sigSqu = Signal.sineFill(
		2.pow(14).asInteger,
		arrSqu.value,
		0!8192);

	arrSaw = {
		var arr;
		arr = Array.fill(2.pow(14).asInteger, {|i|
			i = i+1;
			i = 1/i;
		});
		arr = arr * 0.5
	};

	sigSaw = Signal.sineFill(
		2.pow(14).asInteger,
		arrSaw.value,
		0!8192);

	sigSin = Signal.sineFill(
		2.pow(14).asInteger,
		[1],
		[0]);

	sigRand = Env(
		{rrand(-1, 1)}!14,
		{exprand(1, 20)}!15,
		{rrand(1, 20)}!15
	).asSignal(2.pow(14).asInteger);

	~bufSin = Buffer.alloc(s, (2.pow(15)).asInteger);
	~bufSqu = Buffer.alloc(s, (2.pow(15)).asInteger);
	~bufSaw = Buffer.alloc(s, (2.pow(15)).asInteger);
	~bufRand = Buffer.alloc(s, (2.pow(15)).asInteger);

	~bufSin.loadCollection(sigSin.asWavetable);
	~bufSqu.loadCollection(sigSqu.asWavetable);
	~bufSaw.loadCollection(sigSaw.asWavetable);
	~bufRand.loadCollection(sigRand.asWavetable);
};

~makeRhythmBuffers={
	b = Dictionary.new;
	PathName(~path.asString).entries.do{
		arg subfolder;
		b.add(
			subfolder.folderName.asSymbol ->
			Array.fill(
				subfolder.entries.size,
				{
					arg i;
					Buffer.read(s, subfolder.entries[i].fullPath);
				}
			)
		);
	};

	~kick = b[\kick][0];
	~snare = b[\snare][(0..2).choose];
	~hihat = b[\hihat][(0..3).choose];
	~sines = b[\sines][(0..1).choose];
};

~makeBusses = s.makeBundle(s.latency, {
	~masterBus = Bus.audio(s,2);
});

~makeNodes = {
	s.bind(
		~srcGrp = Group.new;
		~bufGrp = Group.after(~srcGrp);
		~msGrp = Group.after(~bufGrp);
		s.sync;
		~masterSynth = Synth.new(\master, [\in, ~masterBus], ~msGrp, \addToTail);
	)
}
);

//reg
(
ServerBoot.add(~makeBuffers);
ServerBoot.add(~makeRhythmBuffers);
ServerBoot.add(~makeBusses);
ServerQuit.add(~cleanUp);
);

//boot
(
s.waitForBoot({

	SystemClock.seconds.debug("myCoolTextures at");

	SynthDef.new(\vOsc, {
		arg buf=0, numBufs=4, pos= 1.0,
		freq = 300, amp = 0.9, detuneAmount = 0.001, jitAmount=0.001, cutoff = 12000,
		atk=0.001, dcy=0.2, sus = 1.0, rel = 0.4, dly=0.0, crv=1,
		gate = 1, out=0;
		var sig, env, bufpos, jitter, detuneSig;
		jitter = LFNoise0.kr(jitAmount, jitAmount, jitAmount);
		bufpos = buf + pos + jitter;
		detuneSig = LFNoise1.kr(0.02!8).bipolar(detuneAmount).midiratio.lag(0.5);
		env = EnvGen.kr(Env.dadsr(dly, atk, dcy, sus, rel, curve:crv), gate, doneAction:2);
		sig = VOsc.ar(bufpos.clip(buf+0.1, numBufs-0.1), freq*(detuneSig).lag(0.3));
		sig = Splay.ar(sig) * env * amp;
		//sig = OnePole.ar(sig, exp(-2pi * (freq * SampleDur.ir * 9))); //http://tiny.cc/4462gz
		sig = LPF.ar(sig, cutoff);
		Out.ar(out, sig);
	}).add;

	SynthDef.new(\BUF, {
		arg atk=0.00001, sus=1, rel=0.2,
		buf=0, rate=1, lo=0,
		amp=1, out=0;
		var sig, env;
		env = EnvGen.kr(Env([0,1,1,0],[atk,sus,rel],), doneAction:2);
		sig = PlayBuf.ar(1, buf, rate*BufRateScale.ir(buf));
		sig = sig*env*amp;
		sig = Splay.ar(sig);
		Out.ar(out,sig);
	}).add;

	SynthDef(\master, {
		arg in = 0, out = 0;
		var sig;
		sig = In.ar(in, 2);
		//sig = FreeVerb2.ar(sig, sig, 0.3, 0.5, 0.8);
		sig = LeakDC.ar(sig);
		sig = Limiter.ar(sig);
		Out.ar(out, sig);
	}).add;

	s.sync;
	ServerTree.add(~makeNodes);
	s.sync;
	s.freeAll;

	~myCoolTextures_1 = { |start, stop|
		var synthi, scale, chords;

		scale = ~scale.choose;

		chords = 16.collect{
			[scale.degreeToFreq(scale.degrees.choose,
				(if(rrand(0,1).coin,
					rrand(3.1, 18),
					rrand(98, 127))
				),
				(if(rrand(0,1).coin,
					(rrand(0.01, 0.6)),
					(rrand(4.5, 5)))
				)
			)].clip(0.1, 18000)
		};

		SystemClock.sched(start, {

			SystemClock.seconds.debug("myCoolTextures_1 at");

			synthi = Array.fill(4, {
				Synth.new(\vOsc, [
					\freq, chords.choose,
					\amp, rrand(0.01, 0.25),
					\atk, if(rrand(0, 0.1).coin, 0, rrand(6.1, 19)),
					\dcy, rrand(3.1, 8),
					\sus, rrand(0.2, 0.6),
					\rel, if(rrand(0, 0.1).coin, 0, rrand(4.3, 15)),
					\crv, if(rrand(0, 1).coin, rrand(-6,-0.001), rrand(0.0001,6)),
					\pos, rrand(0.5, 2.7),
					\cutoff, rrand(6000, 14000),
					\detuneAmount, rrand(0.01, 0.55),
					\jitAmount, rrand(0.001, 0.2),
					\buf, ~bufSin.bufnum,
					\out, ~masterBus
				], ~srcGrp
			)});

		});

		SystemClock.sched(stop, {
			synthi.do(_.set(\gate, 0));
		});

	};//myCoolTextures ends

	[~times_1, ~stops_1].flop.do { |pair|
		~myCoolTextures_1.value(pair[0], pair[1]);
	};

	~myCoolTextures_2 = { |start, stop|
		var synthi, scale, chords;

		scale = ~scale.choose;

		chords = 16.collect{
			[scale.degreeToFreq(scale.degrees.choose,
				(if(rrand(0,1).coin,
					rrand(3.1, 18),
					rrand(98, 127))
				),
				(if(rrand(0,1).coin,
					(rrand(0.01, 0.6)),
					(rrand(4.5, 5)))
				)
			)].clip(0.1, 18000)
		};

		SystemClock.sched(start, {

			SystemClock.seconds.debug("myCoolTexture_2 at");

			synthi = Array.fill(4, {
				Synth.new(\vOsc, [
					\freq, chords.choose,
					\amp, rrand(0.01, 0.2),
					\atk, if(rrand(0, 0.1).coin, 0, rrand(6.1, 21)),
					\dcy, rrand(3.1, 8),
					\sus, rrand(0.2, 0.6),
					\rel, if(rrand(0, 0.1).coin, 0, rrand(4.3, 15)),
					\crv, if(rrand(0, 1).coin, rrand(-6,-0.001), rrand(0.0001,6)),
					\pos, rrand(0.5, 2.7),
					\cutoff, rrand(6000, 14000),
					\detuneAmount, rrand(0.01, 0.55),
					\jitAmount, rrand(0.001, 0.2),
					\buf, ~bufSin.bufnum,
					\out, ~masterBus
				], ~srcGrp
			)});

		});

		SystemClock.sched(stop, {
			synthi.do(_.set(\gate, 0));
		});

	};//myCoolTextures ends

	[~times_2, ~stops_2].flop.do { |pair|
		~myCoolTextures_2.value(pair[0], pair[1]);
	};

	~myCoolTextures_3 = { |start, stop|
		var synthi, scale, chords;

		scale = ~scale.choose;

		chords = 16.collect{
			[scale.degreeToFreq(scale.degrees.choose,
				(if(rrand(0,1).coin,
					rrand(3.1, 18),
					rrand(98, 127))
				),
				(if(rrand(0,1).coin,
					(rrand(0.01, 0.6)),
					(rrand(4.5, 5)))
				)
			)].clip(0.1, 18000)
		};

		SystemClock.sched(start, {

			SystemClock.seconds.debug("myCoolTexture_3 at");

			synthi = Array.fill(4, {
				Synth.new(\vOsc, [
					\freq, chords.choose,
					\amp, rrand(0.01, 0.2),
					\atk, if(rrand(0, 0.1).coin, 0, rrand(6.1, 19)),
					\dcy, rrand(3.1, 8),
					\sus, rrand(0.2, 0.6),
					\rel, if(rrand(0, 0.1).coin, 0, rrand(5.1, 15)),
					\crv, if(rrand(0, 1).coin, rrand(-6,-0.001), rrand(0.0001,6)),
					\pos, rrand(0.5, 2.7),
					\cutoff, rrand(6000, 14000),
					\detuneAmount, rrand(0.01, 0.55),
					\jitAmount, rrand(0.001, 0.2),
					\buf, ~bufSin.bufnum,
					\out, ~masterBus
				], ~srcGrp
			)});

		});

		SystemClock.sched(stop, {
			synthi.do(_.set(\gate, 0));
		});

	};//myCoolTextures ends

	[~times_3, ~stops_3].flop.do { |pair|
		~myCoolTextures_3.value(pair[0], pair[1]);
	};

	[~times, ~stops].postln;

	~myCoolRhythm= { |start, stop|
		var kick, snare, hihat, sines;

		SystemClock.sched(start, {

			SystemClock.seconds.debug("myCoolRhythm at");

			//if(rrand(0.9,1).coin,
				kick = Pbind (
					\instrument, \BUF,
					\tempo, Pwrand([62/60, 124/90], [6, 1].normalizeSum, inf),
					\dur, Pwrand([1/2, 1/4], [10, 2].normalizeSum, inf),
					\amp, Pwhite(0.7, 1.5, inf),
					\buf, ~kick,
					\rate, Pwhite(0.95, 1.05, inf),
					\out, ~masterBus,
					\group, ~bufGrp
			).play;//);

			//if(rrand(0.7,1).coin,
				sines = Pbind (
					\instrument, \BUF,
					\tempo, Prand([40/60, 80/90], inf),
					\dur, Pwrand([1/7, Rest(), 1/4, Rest(), 1/5], [10, 10, 4, 8, 2].normalizeSum, inf),
					\amp, Pwhite(0.005, 0.25, inf),
					\buf, ~sines,
					\rate, Pwhite(0.3, 13, inf),
					\out, ~masterBus,
					\group, ~bufGrp
			).play;//);

			//if(rrand(0,0.6).coin,
				snare = Pbind (
					\instrument, \BUF,
					\tempo, Prand([40/60, 80/90], inf),
					\dur, Pwrand([1/4, Rest(), 1/7, 1/3], [10, 4, 2, 0.5].normalizeSum, inf),
					\amp, Pwhite(0.005, 0.3, inf),
					\buf, ~snare,
					\rate, Pwhite(1.3, 2, inf),
					\out, ~masterBus,
					\group, ~bufGrp
			).play;//);

			//if(rrand(0,0.6).coin,
				hihat = Pbind (
					\instrument, \BUF,
					\tempo, Prand([40/60, 60/60, 80,60, 100/60, 120/60], inf),
					\dur, Pwrand([1/2, 1/7, 1/16, 1/3, 1/5, 1/4, 1/6], [10, 8, 2, 7, 6, 5, 4].normalizeSum, inf),
					\amp, Pwhite(0.05, 0.9, inf),
					\buf, ~hihat,
					\rate, Pwhite(1.5, 16, inf),
					\out, ~masterBus,
					\group, ~bufGrp
			).play;//);

		});//start schedule ends

		SystemClock.sched(stop, {
			kick.stop;
			snare.stop;
			hihat.stop;
			sines.stop;
		});
	};//myCoolRhytm ends

	[~btimes_1, ~bstops_1].flop.do {|pair|
		~myCoolRhythm.value(pair[0], pair[1]);
	};

})//waitForBoot ends
)

Again, it’s a lot of code to absorb. I’ll make some suggestions but I don’t have enough free time to dig into the code in detail.

^^ These two are actually related.

If you create a mixing framework, then you can handle both:

  • “make them reach a little climax” = automate the mixer volume for that channel.

  • “these rhytms are more or less sucked into the textures” = set the volume lower for the texture channels. (If I’m understanding what you mean.)

Plain SC leaves it up to you to decide how to route and mix signals. You would need a separate bus for each channel, and a synth for level scaling and panning (and mind the order of nodes).

I find it tiresome to do that by hand, so a long time ago, I wrote the ddwMixerChannel quark. (See “Using Quarks” help to learn about installing them.) With that, you can do:

~master = MixerChannel(\master, s, 2, 2, level: 1);

~texture_1 = MixerChannel(\texture1, s, 2, 2,
	level: 0.2,  // bc you said you wanted it softer
	outbus: ~master
);

~kick = MixerChannel(\kick, s, 2, 2,
	level: 0.6,  // or whatever
	outbus: ~master
);

~snare = MixerChannel(\snare, s, 2, 2,
	level: 0.6,  // or whatever
	outbus: ~master
);

// etc.

// then...
synthi = Array.fill(4, {
	~texture_1.play(\vOsc, [... args...]);
});

^^ Now, each \vOsc synth is placed into a group ~texture_1.synthgroup.

For the rise at the end of a texture:

~texture_1.levelAuto { EnvGen.kr(Env([0.2, 0.6, 0], [20, 30], \sin), doneAction: 2) };

For rhythm:

kick = ~kick.play(Pbind(.....));

I really don’t know what you mean here :worried:

hjh

1 Like