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.