Best way to turn OSC messages to Dictionaries

Hey all,

What I am trying to achieve:
Turn my OSC messages to Dictionaries with unique IDs tapped out from the first message in this array.
[ /wfiRwJaZy3wktE2MAAAX, {"0":"455.00","1":"211.00","2":"455.00"} ]

Final goal:
Access the data in the rest of the program by calling the ID of the Dictionary

Long explanation of the objective is below.

I am trying to create incoming osc messages to dictionaries so I can access the in rest of the program later on. The idea is that something like an incoming message (below) will be clamped into a new dictionary if this doesn’t exist by checking its first message from inside the osc array. If it exists obviously will update its values and carry on. Then I can have access to the data from inside the dictionary array or collection by selecting the ID which is the string of the first osc message. I am aware of parseYaml and 'parseJSON` will create a dictionary, and I have also managed to split the osc array into the id to assign the dictionary and key, and values but that is how far I have gone, and I am getting “nil” every time I am calling my dictionary.

Here is the draft of my code thus far:
//incoming osc formated message as object
[ /wfiRwJaZy3wktE2MAAAX, {"0":"455.00","1":"211.00","2":"455.00"} ]

(
var oldUserID,  oldData, d = ();
~listener = { |msg, time, replyAddr, recvPort|
	var oscArray, checkBadVals, userID;
	if(msg[0] != '/status.reply') {
		//"At time %s received message % from % on port%\n".postf( time, msg, replyAddr, recvPort );
		msg.postln;
		userID = msg[0].asString.drop(1);
		oscArray = msg[1].asString.parseJSON;
		checkBadVals = oscArray.collect(_.notNil).reject(_.isNil);
		if(checkBadVals != false && oldUserID != userID && oldData != oscArray) { //proceed on assigning values after check for nils in osc array.
			"User with ID: % sent message % at time %\n".postf(userID, oscArray, time);
			oscArray = oscArray.keysValuesDo({|index, value|
				value = value.interpret;
				//{\notifier.changed(index.asSymbol, userID, value.max(0.01))}.defer; //safely ignored.
				d.add(userID -> oscArray); // d = () -> "Nil"
			});
		};
		oldUserID = userID;
		oldData = oscArray;
		[oldUserID, oldData].postln;
	}
};

thisProcess.addOSCRecvFunc(~listener);
)

Thanks for the time reading this.

K.

Don’t use strings as keys with Event or Environment or IdentityDictionary.

You can use strings as keys with Dictionary.

hjh

Hey James,

I am afraid this wouldn’t fix the issue at least not on my side. If you could be a little more elaborate with a fix about the issue at hand, that would be extremely useful. I added a Dictionary instead of Event, assuming that was what your message meant. But still get nil every time I am evaluating d anyways, this might be related to other things on my side, but I would appreciate any potential solutions, as I am on a dead-end here with this.

Thanks,
K.

Whilst you can use strings as keys in Dictionaries, I don’t think its a good idea. This removes all of the strings.

I also removed the value.interpret line as it doesn’t let you pass strings in. You can add that back if you need it.
Hopefully this fixes the issue?

(

~dict = ();

~listener = { |msg, time, replyAddr, recvPort|
	if(msg[0] != '/status.reply') {
		var userID = msg[0].asString.drop(1).asSymbol;
		var keys_2_symbols = {|v, i| (i % 2 == 0).if(v.asSymbol, v)};
		var oscArray = msg[1].asString.parseJSON.asPairs.collect(keys_2_symbols).asEvent; // make keys symbols and then event - not necessary, but nice
		var checkBadVals = oscArray.collect(_.notNil).reject(_.isNil) != false;
		
		if(checkBadVals && ~dict.includesKey(userID).not) { 
			"User with ID: % sent message % at time %\n".postf(userID, oscArray, time);
			~dict[userID] = oscArray;
		};
	}
};

thisProcess.addOSCRecvFunc(~listener);

)

n = NetAddr("127.0.0.1", 57120); 
n.sendMsg("/addr", "{\"k1\": 10, \"key2\": \"blah\"}")

~dict[\addr]
~dict[\addr][\key2]
~dict[\addr][\k1]

Thanks seem to be working well. Just replaced the userID conditional from the boolean test as I want to receive data from each user continuously as long as their data are note repeated one after the other. Below is what I have jogged from your explanation, hope this is still okay.

if(checkBadVals && ~dict.includesKey(oscArray).not) { 
			"User with ID: % sent message % at time %\n".postf(userID, oscArray, time);
			~dict[userID] = oscArray;
		};

Thanks a lot.

That won’t work as oscArray isn’t a key. I think you would have meant ~dict.includes(oscArray).not.

You meant not repeated right? … what happens when two people send a message at the same time, but one is trying to override a previous value? I think you have weird synchronisation issues, could you describe what you are trying to achieve in a bit more detail?

Besides, if the user sends the same data, isn’t this exactly the same as replacing it?

Hey Jordan,

True, in my way to save some processing due to lots of incoming values I thought maybe I could rule out some data that comes twice at the same time. I think your point about the same data from different users is valid and thereof such a condition would be problematic. So I could just leave this ~dict.includes(oscArray).not out and go just with this if(checkBadVals){...} should be alright I guess.

interpreting while splitting the keys and values iteration causes some errors below.

if(checkBadVals) {
			"User with ID: % sent message % at time %\n".postf(userID, oscArray, time);
			~dict[userID] = oscArray;
			oscArray.keysValuesDo({|index, value|
				{\notifier.changed(index.asSymbol, userID, value.interpret)}.defer;
				[index, value].postln;
			});

Strangely enough, the error is caused when I am evaluating a new SynthDef, having nothing to do with the incoming OSC message processing and even when no message is received.

->-> a SynthDef
User with ID: d_removed sent message ( ‘n’: e, ‘a’: i, ‘s’: i, ‘g’: r ) at time 49398.582845457
[ n, e ]
[ a, i ]
[ s, i ]
[ g, r ]
ERROR: Message ‘interpret’ not understood.
Perhaps you misspelled ‘interpretVal’, or meant to call ‘interpret’ on another receiver?
RECEIVER:

I think this is because you are using thisProcess.addOSCRecvFunc and are interfering with the server client socket.

Instead you really ought to format your osc messages like …
address = ‘/update/keyValuePair’
msg = [‘some id’, “{key: 10}” ]

Then you would use OSCdef as is standard.

… that being said, you can just do this…

thisProcess.addOSCRecvFunc({
	|msg, time, replyAddr, recvPort|
    if(recvPort != 57121) { // not from the server
		... rest of code ...
     }
});

I don’t think it’s quite right to advise against using addOSCRecvFunc in general.

What is true is – when using addOSCRecvFunc, the user takes on all responsibility for filtering incoming messages. The function gets everything that’s coming in. (If a user doesn’t want that responsibility, then they should use OSCFunc / OSCdef.)

So there’s likely to be a problem with the filtering condition. msg[0] != '/status.reply' blocks only status reply messages, but all other server replies will pass through.

recvPort btw would be the language-side port, not the port belonging to the sender. The sending port is in the NetAddr.

hjh

(
~dict = ();
OSCdef(\lickin, {|...args|
	var userID = args[0][1].asString.asSymbol;
	var keys_2_symbols = {|v, i| (i % 2 == 0).if(v.asSymbol, v)};
	var oscArray = args[0][2].asString.parseJSON.asPairs.collect(keys_2_symbols).asEvent; // make keys symbols and then event - not necessary, but nice
	var checkBadVals = oscArray.collect(_.notNil).reject(_.isNil) != false;
	if(checkBadVals) {
		"User with ID: % sent message % at time %\n".postf(userID, oscArray, args[1]);
		~dict[userID] = oscArray;
		oscArray.keysValuesDo({|index, value|
			{ \notifier.changed(index.asSymbol, userID, value.interpret) }.defer;
		});
	};
}, '/lick');
)

Just coming to this back again, would it be possible for an example using the event above?

Sorry, I don’t understand what you mean. An example of what? With which Event?

Here is an example of it working as described.

(
~dict = ();

OSCdef(\update, {
	|msg, time|
	var userID = msg[1].asString.asSymbol;
	var keys_2_symbols = {|v, i| (i % 2 == 0).if(v.asSymbol, v)};
	var event = msg[2].asString.parseJSON.asPairs.collect(keys_2_symbols).asEvent;
	var valsGood = event.collect(_.notNil).reject(_.isNil) != false;
	
	if(valsGood) { 
		"User with ID: % sent message % at time %\n".postf(userID, event, time);
		~dict[userID] = event;
	};
}, '/update')

)

n = NetAddr("127.0.0.1", 57120); 

n.sendMsg("/update", "obj/A", "{\"k1\": 10, \"key2\": \"blah\"}")
n.sendMsg("/update", "obj/B", "{\"k1\": 11, \"key2\": \"abcdef\"}")

result

~dict['obj/A'] = ( 'k1': 10, 'key2': blah )
~dict['obj/B'] = ( 'k1': 11, 'key2': abcdef )

No worries Jordan,

I thought you mean’t something more idiomatic apropos to the approach above. I understand now that this is the standard way to play an event, that’s why I was asking for a specific example, if any. I guess, this is basically what it is then, ( 'k1': 10, 'key2': blah ).play or something like this is what it means, or not?