Where to put gui code when there is a long initialisation routine?

I have this:

Task({
 Synthdefs
0.9.wait;
Other init
}).play;

GUI code

I need some variables to be initialised during the task other init phase to finish. Should the gui code be within Task but deferred for sequential execution to take place? Or is there another way to be sure everything is intialised before the gui is created?

Can you be a bit more specific about what you’re trying to accomplish?

I have my GUI high on the execution about 90% of the time, but it depends on the context. I like to do some variation of this for essentially every project:

( 
s = Server.local; //if s was accidentally overwritten

s.waitForBoot{
     //load SynthDefs
     SynthDef(...);

     s.sync; //server waits until the task is done before proceeding

     //Other Init

     s.sync;

     //GUI Code
};
)

You can forward declare variables either at the top or in s.waitForBoot, but at the top is probably more sensible. You can also use environmental variables instead of forward declaration.

ServerQuit, ServerBoot, ServerTree, CmdPeriod all have a .add method to add functions to evaluate when these actions are performed.

One useful design strategy is to have a strong separation between creating the UI and connecting the UI e.g. to server parameters, backing variables, or whatever data it’s displaying. This strategy is helpful for many reasons, but in particular I think it might help you with your organizational problem.

Here’s an example of what I mean:

(
/////////////////////////////////////////////////////////////////
// DESCRIBE THE MODEL
~controls = (
	amp: 0.8,
	cutoff: 700,
	res: 0.7
);



/////////////////////////////////////////////////////////////////
// DESCRIBE THE UI
// Note that we're not connecting to anything, only creating our controls and layout.
~views = ();
~views.use {
	~parent = View().layout_(
		VLayout(
			~ampView =     NumberBox().scroll_step_(0.05).clipLo_(0).clipHi_(1).fixedWidth_(80),
			~cutoffView =  NumberBox().scroll_step_(50).clipLo_(80).clipHi_(10000).fixedWidth_(80),
			~resView =     NumberBox().scroll_step_(0.1).clipLo_(0.1).clipHi_(1.0).fixedWidth_(80),
		)
	).front;
};


/////////////////////////////////////////////////////////////////
// MAKE A SYNTH
~synth = {
	var freq, trig, sig;
	
	trig = Impulse.ar(8);
	
	freq = Demand.ar(
		trig,
		0,
		32 + Dseq([0, 2, 3, 6, 5, 7, 3], inf) + Drand([0, 0, 0, 0, 12], inf)
	);
	
	sig = LFSaw.ar(freq * [0, 0.01, -0.0342, 0.042].midiratio).sum.clip2(1);
	sig = BLowPass.ar(
		sig,
		\cutoff.ar(100),
		\res.ar(1.0),
	);
	
	sig = (
		\amp.ar(1.0) 
		* sig
		* Env.perc(0.01, TExpRand.ar(0.1, 0.8, trig)).ar(gate:trig)
	);
}.play(args: ~controls.asPairs);


/////////////////////////////////////////////////////////////////
// CONNECT THE UI
~updateViews = {
	~views[\ampView].value      = ~controls[\amp];
	~views[\cutoffView].value   = ~controls[\cutoff];
	~views[\resView].value      = ~controls[\res];
};

~updateControls = {
	~controls[\amp] 			= ~views[\ampView].value;
	~controls[\cutoff] 			= ~views[\cutoffView].value;
	~controls[\res] 			= ~views[\resView].value;

	~controls.changed(\value);
};

~updateSynth = {
	~synth.set(*~controls.asPairs);
};

~controls.addDependant(~updateViews);
~controls.addDependant(~updateSynth);

~views[\parent].onClose({ ~views[\parent].removeDependant(~controls) });
~views[\ampView].action = ~updateControls;
~views[\cutoffView].action = ~updateControls;
~views[\resView].action = ~updateControls;

// Now that everything is connected, fire this to update.
~controls.changed(\value);
)

Think of each of these as almost fully independent parts… Your controls might have parameters for multiple synths, patterns, etc. You might have multiple views for a given set of controls. You might connect the same controls to different synths over time. You don’t want to have to worry about initialization order of any of these things (e.g. you can connect your UI to your controls BEFORE you have a synth, or your controls to your synth BEFORE you have a UI), and you want to be able to add/remove pieces without breaking other pieces.

The only part that’s order dependent is the connection piece at the end, which (as it’s written) requires all the pieces to be ready - this is the part that would go at the end for you.

2 Likes

Incredibly helpful ideas here. I noticed from browsing other projects, the tutorials didn’t cover all the instances on how to do this, and so wanted to know how everyone else did it.