Error: OSC type tags

Oh, my mistake: should be ~ratiotextboxes[0].value = ....

The “node not found” is out of the scope of the OSC messages and GUIs problem.

You’re calling x.seti(...).

So it’s assumed that x is a currently active Synth node.

If x is a Synth node that has already terminated, then you would get this error message.

So… you have 4 channels for this control input (arrayed control). And you have 4 GUI widgets, and 4 encoders.

I’m assuming there is a one-to-one match for all of these:

  • Arrayed control index 0 ↔ GUI 0 ↔ encoder 0.
  • Arrayed control index 1 ↔ GUI 1 ↔ encoder 1.
  • etc.

So: If you’re testing encoder 0, then:

  • When updating the GUI, you will need index 0.
  • When updating the synth, you will need index 0 (in seti).

I really can’t see any way that it would make sense to omit the index in either of these places. In seti, you don’t e.g. want it to randomly choose which channel it’s setting, or, you don’t want it to use the default first channel once you start working with encoders 1, 2 and 3… so, yeah, specify it.

hjh

PS I feel that helping with these questions can/should be a community effort – tagging me repeatedly may work against that ideal. I’m going to get busier for the rest of the week so I’d suggest to leave room for others to chime in too.

no of course i will leave these open for community input, thank u so much for input, ill be getting stuck into this.

ah yes also that x.seti... line. i actually need to remove that as it is not an active node,
is defining x an important part to make seti work? could i use ~ratiotextboxes there also?

A little time before breakfast… :laughing:

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

2 Likes

wow, ok im going to dig in

are message ‘at’ not understood errors directly related to ‘array’ problems?

It seems like whichever variable you are reading from is nil, and in this instance cannot be. Most likely a calculation depends on a real value being there, and nil isn’t actually a value.