Buses and Functions: Questions

Hello,
For a RPI project,I would like to create a kind of library in SC to process the value collected at the GPIO:

an Oscdef function is sending values in range [0,1] to a control bus.

I would like to processs these values from the control bus through reusable function to another bus.
I do not wish to hard code the processing of the value in the OSCdef as these might change depending on the sc project I work on.
Most of the time, the process is a very simple arithmetic operation to re-scale the [0-1] value to another range (i.e. to quantize it to midi notes [ 1-127].
To summarize :

RPI -> OSCDef [0,1] -> ~FromOSCBus - > "Unknow solution [x, y]" ->~FromOSCBus ->  a_SynthDef or any usage

I tried Functionā€¦ but it seems they cannot cope with Buses.
Should I make a pseudo Ugen ? and how do I retrieve the new values ?

Should this not be possible, I think I could make X OSCdefā€¦ but I donā€™t want to have them all in my main code ā€¦ So is it possible to call them from a file (like an #include statement in python or like pseudo-UGens)?

The SC doc are not really clear on hat partā€¦
Thank you for help .
Thank a lot you for your help or pointing me to any tutorial in this direction.

Hmm - not sure exactly what youā€™re going for, but hereā€™s a short attempt:

(
// An OSC input...
OSCdef(\in, {
	|msg|
	// Assuming you're recieving a numerical value here...
	var value = msg[1];
	
	// Send to your Synth
	Ndef(\process).set(\value, value);
},'/someAddress');

// A synth....
Ndef(\process, {
	|value|
	
	// Re-map your value from 0..1 to 0..127
	value = value.linlin(
		0, 1,
		0, 127
	);

	// And send back via OSC, 20x per second
	SendReply.kr(Impulse.kr(20), '/processResult', value);
});

// An OSCdef for your remapped value
OSCdef(\result, {
	|msg|
	"Remapped value is %".format(msg[3]).postln;
}, '/processResult')
)

m.sendMsg('/someAddress', 0.3);
m.sendMsg('/someAddress', 0.6);

Iā€™m also not sure about one thing: why do you want to use busses for that? Maybe itā€™s worth it to clarify one thing: busses are meant to help communication between nodes on scā€™s server. OSCdefs live on scā€™s client. I would go from one to the other only when I need to control something on the Server, like Synths, and I would do something like @scztt.
But if you need to preprocess the data to make it available on another OSC path, I would skip the server entirely and do it all on the client.

// a dictionary of reusable scale functions
// it could be defined in another file (see .loadRelative below)
~scaleFuncs = (
	midi: _.linlin(0,1,0,127),
	freq: \freq.asSpec.map(_),
	cmajor: {|val| 60 + val.linlin(0,1,-7,7).degreeToKey(Scale.major)},
	custom: {|val| val * pi / 2 + 10}
)

// -------------------------------------------------------------

(
// OSCdef #1: preprocess the data
// use an environment variable ~preprocess_func
// so you can choose your scale function on the fly
OSCdef(\preprocess, {|msg| 
	var value = msg[1];
	var scaled = ~preprocess_func.(value);
	// send scaled value to another path
	NetAddr.localAddr.sendMsg('/gpio/scaled',scaled);
}, '/gpio');

// OSCdef #2: just print out preprocessed data
OSCdef(\process, {|msg| 
	var value = msg[1];
	"scaled: %".format(value).postln;
}, '/gpio/scaled');
)

// now let's choose a function
~preprocess_func = ~scaleFuncs[\midi]
// and simulate some input
NetAddr.localAddr.sendMsg('/gpio',0)   // -> scaled: 0
NetAddr.localAddr.sendMsg('/gpio',0.5) // -> scaled: 63.5
NetAddr.localAddr.sendMsg('/gpio',1)   // -> scaled: 127
// and now another function
~preprocess_func = ~scaleFuncs[\freq]
NetAddr.localAddr.sendMsg('/gpio',0)   // -> scaled: 20.0
NetAddr.localAddr.sendMsg('/gpio',0.5) // -> scaled: 632.45550537109
NetAddr.localAddr.sendMsg('/gpio',1)   // -> scaled: 20000.0
// and now another function
~preprocess_func = ~scaleFuncs[\cmajor]
NetAddr.localAddr.sendMsg('/gpio',0)   // -> scaled: 48.0
NetAddr.localAddr.sendMsg('/gpio',0.5) // -> scaled: 60.0
NetAddr.localAddr.sendMsg('/gpio',1)   // -> scaled: 72.0

Strings have load() and loadRelative()

// "like an #include"
"oscdefs.scd".loadRelative;

This will just execute ā€œoscdef.scdā€, so if you have defined any OSCdef there, they will be defined :slight_smile: If in that file you define environment variables (like ~scaleFuncs above), they will be defined :slight_smile: :slight_smile:

Thank you a lot for your solutions.
Maybe I am wrong in my approach and I am opened to any more efficient solution.
The problem is the following :
The RPi is sending a value X via OSC. This value X enters the SC OSC receiver. This value needs to be scaled to Y (ex : midi note or a freq ā€¦). and then the value Y enters a Synth (i.e. as the Freq of the synth).

Maybe I am totally wrong trying to use bussesā€¦ but I thought that the only way to pilot a synth parameters in realtime from OSC was to use busses.
So any exemple of this process is very welcome ( and I think it might help other users too) .

thank a lot !

On Facebook, I suggested a different approach (using dependants).

EDIT: Apparently the question on FB and the question here are from different people, despite being almost exactly the same and being posted at about the same time.

Itā€™s late here in China. I can post an example in the morning.

hjh

1 Like

Hereā€™s an approach using dependants to decouple OSC receipt from the eventual values (since you said ā€œI do not wish to hard code the processing of the value in the OSCdef as these might change depending on the sc project I work onā€ ā€“ in this approach, the OSCdef only receives and broadcasts the new value ā€“ any arbitrary calculations can be attached to that, and removed at any time).

// a consistent reference-point for your value
v = Ref(0);

(
// let's say the message coming in is ['/value', x]
OSCdef(\getValue, { |msg|
	// '.changed' is what broadcasts the status
	v.value_(msg[1]).changed(\value);
}, '/value');
)

a = Synth(\default);

(
// function arguments are object, what, ... args
c = SimpleController(v).put(\value, { |obj, what|
	// 'what' == 'value' here... doesn't really matter
	a.set(\freq, \freq.asSpec.map(obj.value));
});
)

// test it
NetAddr.localAddr.sendMsg(\value, rrand(0.1, 0.7));

// important! clean up after you're finished with this linkage
c.remove;

a.release;

EDIT: Though, actually, you donā€™t need dependants at all. Maybe youā€™re making an assumption that you can have only one OSCdef per incoming message, but in fact you can have as many as you want. So ā€œthe processing of the value ā€¦ might change depending on the sc projectā€ but then the OSCdef could be different too. That is, whatā€™s the difference between OSC message ā†’ one standard OSCdef ā†’ many SimpleControllers, vs OSC message ā†’ multiple OSCdefs?

The dependency approach makes more sense if the OSCdef is normalizing real world input data, and the later operations should all feed off of the normalized value.

Definitely not true ā€“ see Nodeā€™s ā€˜setā€™ method.

hjh

1 Like

Thank you a lot for your help.