A little time before breakfast…
x
as x
isn’t important. But, seti
is defined only for Synth, so you need some Synth object in order to reach this method at all. And, the command will fail in the server if that Synth node isn’t active. Those factors, I would say, are indeed important.
You could do defer { ~ratiotextboxes[0].valueAction_(msg[2]) }
.
But IMO it isn’t a good idea to use a GUI object as the central traffic control point for the data flow. (This is a fairly typical process: You have a value on the server. You want to update this value using a GUI, so you write this into the GUI action function. Then you find you want to update the server value and the GUI based on something else, and discover that “first thought wasn’t best thought.”)
A better design is to have an object that represents the value, and have it broadcast when its value changes. Then, any interested observers can take the relevant action.
I’ll demonstrate here with GenericGlobalControl in my ddwCommon quark, because it already implements “representing the value” and “broadcasting.” If you don’t want to use a quark, it’s possible to make Event-style object prototypes to do this, but that might be a lot for this thread.
s.boot;
(
SynthDef(\test1, { |out, tratios = #[1, 2, 3, 4]|
tratios.do { |ratio, i|
ratio.poll(Changed.kr(ratio), "ratio % changed".format(i))
};
Out.ar(out, DC.ar(0));
}).add;
)
(
// these objects hold the 4 ratios
~controls = Array.fill(4, { |i|
GenericGlobalControl("ratio" ++ i, nil, i+1, [1, 10])
});
// GUIs add a dependent upon the control
// the dependent receives \value updates
// the appropriate action for a GUI to take,
// when its "model" changes, is to update its display
~guis = ~controls.collect { |ctl|
var box = NumberBox();
var func = { |obj, what|
switch(what[\what])
{ \value } {
defer { box.value = ctl.value }
}
{ \modelWasFreed } {
ctl.removeDependant(func)
}
};
ctl.addDependant(func);
// relay to the value-holder
box.action = { |view| ctl.value = view.value };
box.value = ctl.value;
box // return, save to array
};
~synth = Synth(\test1);
// we also add dependents to send the values to the server
~setiWatchers = ~controls.collect { |ctl, i|
var func = { |obj, what|
switch(what[\what])
{ \value } {
defer { ~synth.seti(\tratios, i, ctl.value) }
}
{ \modelWasFreed } {
ctl.removeDependant(func)
}
};
OSCFunc({
// stop watching for values when this synth dies
ctl.removeDependant(func);
}, '/n_end', ~synth.server.addr, argTemplate: [~synth.nodeID]).oneShot;
ctl.addDependant(func);
func
};
w = Window("Test", Rect(800, 200, 400, 300)).front;
w.layout = HLayout(*~guis);
)
Now, when you change a number in the GUI, the data flow goes GUI → GenericGlobalControl → dependants → synth.
And, in your OSC receivers, you can simply set the global controls directly – this is the correct programmatic way to change the value – and it will flow down to all connected dependents (GUI and synth):
~controls[2].value = 4
You might think this is overkill. But, it’s an extensible design. If you wanted later, for instance, to send the values also to a visualizer running in, say, processing, you can easily add another dependent. (Whereas, if you put all of the logic into the GUI, that’s the easy way out at first, but it’s going to get ugly when the project gets bigger.)
(This is another one of those mid-level tutorials that we don’t have in the documentation…)
hjh