How to parse multiple blocks of code

I am only just starting with SuperCollider and getting stuck understanding how to actually parse various blocks of code in one go. So here’s the scenario (below); I have three SynthDefs, which I can parse/execute in one go (no probs there) - but I can’t figure out how to include the routine that follows (i.e. starting with var g=group) … any advice on how to do this?

SynthDef(

}).add;

SynthDef(

}).add;

SynthDef(

}).add;

	var g=Group.basicNew(s,1);	
	var stereoBuffer1L = Buffer.alloc(s, s.sampleRate*2, 1);
	var stereoBuffer1R = Buffer.alloc(s, s.sampleRate*2, 1);

        var monoBuffer1 = Buffer.alloc(s, s.sampleRate*2, 1);
   		
	var rootPitch=36; // Start on a C

	var stopTranspose = 0;									
	var transposeCount = 10;//Wait a while after transposing to minimise semitone clashes
	
	//Non-ET pentatonic ratios
	var thisRatio = [0.25, 0.5, 0.75, 1, 1.125, 1.333333, 1.5, 1.6875, 2, 2.25, 2.6666666, 3, 3.375, 4, 5];
	var thisPitch;

	var svf = Synth.tail(g, \Filters);
	var d = Synth.tail(g, \StereoModDelay);

	stereoBuffer1L.zero;
	stereoBuffer1R.zero;
	d.set(\bufferL, stereoBuffer1L);
	d.set(\bufferR, stereoBuffer1R);
	
	thisPitch = thisRatio.choose*rootPitch.midicps;
	
	//First few notes have a slow attack and longer interval
	{4.do
		{ 
		Synth.head(g, \ChMach,
			[\f, thisPitch,
			\width, rrand(0,1),
			\pan, rrand(-1,1),
			\aTime, rrand(5,15),
			\rTime, rrand(7,20),
			\filter, rrand(4,10),
			\filterQ, rrand(0,3.7), 
			\modFreq, rrand(0.7,1.5)]);
		  rrand(1,3).wait;
		}
	};
	  
	//Pick a note from the pentatonic scale with somewhat random settings
	//and slowly noodle around the circle of fifths
	{inf.do 
		{
		thisPitch = thisRatio.choose*rootPitch.midicps;
		stopTranspose = stopTranspose + 1;
		Synth.head(g, \ChMach,
			[\f, thisPitch,
			\width, rrand(0,1),
			\pan, rrand(-1,1),
			\aTime, rrand(0.01,15),
			\rTime, rrand(7,20),
			\filter, rrand(4,10),
			\filterQ, rrand(0,3.7), 
			\modFreq, rrand(0.7,1.5)]);
		if ((0.04.coin) && (stopTranspose > transposeCount)) {
			stopTranspose = 0;
			rootPitch = rootPitch + 7;
			if (rootPitch > 47) {rootPitch = rootPitch - 12};
			rootPitch.postln;
			};	
		   rrand(0.1,2).wait;
		   }
	}.fork;

Hey,
so you actually will have some problems doing this due to synchronisation.
You need to sync the server after adding synthdefs, creating groups (or any Node), allocating buffers…etc.

The best way to do this (in my opinion) is to use s.waitForBoot and multiple files to manage a larger code base. Then split the function into two phases, make the data and sync, then use the data. This has the disadvantage you won’t be able to call the function directly, but must always wrap in in a fork.

You can take this a step further by move ~my_routine_data and ~launch_my_routine to a separate file.

in file synthDefs.scd

SynthDef(\a, {}).add;
	
SynthDef(\b, {}).add;
	
SynthDef(\c, {}).add;

in file main.scd


~my_routine_data = { // makes data
	var out = (
		\group: Group.basicNew(s,1),
		\stereoBuffer1L: Buffer.alloc(s, s.sampleRate*2, 1),
		\stereoBuffer1R: Buffer.alloc(s, s.sampleRate*2, 1),
		\monoBuffer1: Buffer.alloc(s, s.sampleRate*2, 1)
	);
	s.sync;
	out
};

~launch_my_routine = { // uses data
	|data|
	var rootPitch=36; // Start on a C
	var stopTranspose = 0;									
	var transposeCount = 10;
	var thisRatio = [...];
	var thisPitch = thisRatio.choose * rootPitch.midicps;
		
	var svf = Synth.tail(data[\group], \Filters);
	var d = Synth.tail(data[\group], \StereoModDelay);
	s.sync;                                     // this line is important and must be called before you use svf or d!!
	data[\stereoBuffer1L].zero;
	data[\stereoBuffer1R].zero;
	
	... etc ...
};


(
s.waitForBoot {
	"../../../synthDefs.scd".load;
	s.sync; 
	
	~launch_my_routine.(~my_routine_data.());
)

One thing to be a little weary of is when you have global buses (like an effects send) or any shared resource, I’d recommend you have a separate file called globals.scd to store all of these and save them to global variables.

1 Like

If you don’t like the Event accessing inside of ~launch_my_routine you can do this…

~launch_my_routine = {
	|group, stereoBuffer1L, stereoBuffer1R, monoBuffer1|
     ...
};

// then to call it

~launch_my_routine.performWithEnvir(\value, ~my_routine_data.());

This relies on having the keys of the Event match the argument names in the function exactly.

Cheers Jordan! I went with your first -very elegant and clever- solution. Thanks!

To add detail to a small point: sync is required for asynchronous server commands (including SynthDefs, and buffer operations). Node commands are synchronous on the server side and do not require sync (unless something went terribly wrong). Also, bus allocation involves no commands at all, so there is nothing to sync.

Getting data from the server is typically asynchronous in the language, but those server commands are usually synchronous in the server.

hjh