Simple OSC nodes

Here is to simplify the usage of OSC in vl for the most common scenarios: the goal is to be using as few nodes and pins-to-configure to be able to receive and send osc-messages.

Receiving

  • Create an OSCReceiver, give it a name and a port (or leave the defaults)
  • Create OSCValue/s (Receive) nodes that refer to the OSCReceiver by name and listen for a specific osc-address
  • done

image

  • The Index on the OSCValue (Receive) refers to the index of the arguments of the osc-message: say you receive a message with arguments ffff you can use the Index to getslice a specific argument
  • The Index and Count on the OSCValues (Receive) allows you to get a series of consecutive arguments of one message as a spread: say you’re receiving a message with arguments ssffff you can use Index=2 and Count=4 to get a spread of the 4 floats in the arguments
  • OSCValue/s (Receive) are generic, so they can be connected to (spread of) string, float32, float64, int32 and int64, char, bool, color, v2, v3, v4, Spread-of-byte
  • Note: if more than one osc-message with the same address arrives in one frame, only the last one is received
  • Reason for connecting OSCReceivers with OSCValue/s (Receive) nodes via name is so that you don’t have to connect them via link and still be able to change the receiving udp port in one central spot.

Receiving a custom datatype

If you have an osc-message with arguments of different types (say sfff) and you can also parse those into a custom datatype that has those properties. The only thing you have to do, is provide an override for ArgsToValue that builds your datatype out of the osc-arguments, like so:

Sending

  • Create an OSCSender and specify remote address and port
  • Create an OSCValue/s (Send) and connect it to the OSCSender
  • done

image

  • OSCValue/s (Send) are generic, so they can take (spread of) string, float32, float64, int32, int64, char, bool, color, v2, v3, v4, Spread-of-byte

To send a bunch of messages as a bundle, simply cons them together and then use the OSCSender (Spread)

image

Sending a custom datatype

To send a custom datatype, the only thing you have to do, is to provide a custom override to ValueToArgs. See how you need to split down your datatype to types that OSC can understand, ie. string, float32, float64, int32 and int64

image

Download

VL.IO.SimpleOSC.vl (104.1 KB)

Test

  • Requires VL >= 2020.1.x
  • In your VL document set a dependency to both this file and VL Nugets > VL.IO.OSC

Thoughts

Please test and report your thoughts.
It bugs me a bit that Receiving and Sending are not entirely working the same way, ie while the receiver is connected to the values via name, the sender still needs to be connected via link. This should maybe be thought through a bit more… Any feedback welcome

5 Likes

Not bad!
I’m actually playing with the idea of an OSC object serializer/deserializer as well where the OSC address indicates properties in the object graph so let’s say we have these classes

class MyObject
{
    float Scalar;
    string Name;
    Vector3D Pos;
}

class MyRoot
{
    MyObject First;
    MyObject Second;
    MyObject Third;
}

and we’re expecting OSC messages like this:

f /First/Pos/x: 0.23
s /Second/Name: "hello"
f /Third/Scalar: 0.23

so an OSC model service given an object of type MyRoot to work with, it can update nested properties using reflection.
dealing with OSC tuples (fff for vectors for instance) could be solved with custom attributes like this:

class MyObject
{
    float Scalar;
    string Name;

    [OscTupleRouter("x", "y", "z")]
    Vector3D Pos;
}

where the arguments of OscTupleRouter is a params array of property paths relative to the property’s own path. So now we can expect an osc message like:

fff /First/Pos: 0.23, 0.34, 0.45

or to make it more intuitive to understand

class MyRoot
{
    [OscTupleRouter("Scalar", "Name", "Pos.x", "Pos.y", "Pos.z")]
    MyObject First;

    [OscTupleRouter("Scalar", "Name", "Pos.x", "Pos.y", "Pos.z")]
    MyObject Second;

    [OscTupleRouter("Scalar", "Name", "Pos.x", "Pos.y", "Pos.z")]
    MyObject Third;
}

and in that case we can expect OSC message like:

fsfff /First: 0.12, "Hello", 0.23, 0.34, 0.45

of course you can elaborate on that route with promoting properties as object identifiers to support more than one instance of a given object for collections or models, support collections or dictionaries etc…

and then you have a pretty solid usage for sending or receiving as well. that would be my ideal workflow with OSC stuff unfortunately I didn’t have time for proper implementation for that yet.

2 Likes

Hey there @joreg,

Here are my 2 cents on the topic!

For most of our projects we rely extensively on OSC and have ran into quite a bunch of problems/walls in the past due to the logic of the stock OSCDecoder/Encoder; which has lead to my writing of my own couple of nodes.
Pretty excited to hear this coming to VL soon, so here’s my little feedback on what might help based on my example experience:

  • I see you plan to discard all messages but the latest one if many are received within one frame (for one single address). This can be problematic in some contexts where a bunch of messages is riffled at once (ex: new song of a show might trigger a whole lot of init messages - new scene, new video FX, new light FX etc) and which need to be carefully not only received (as opposed to discarded) but also kept in order (cf next point). While there’s always way to make it work despite the Discard logic, it would often imply reworking the message formatting logic to something less explicit / human readable.

  • again as far as I remember, stock nodes outputs messages grouping them by URL, and then by time of reception inside each URL group; i.e. if you are set to monitor “/myURL1”, “/myURL2” and “/myURL3” messages, and then receive:
    – one message addressed “/myURL1” at time t1
    – one message addressed “/myURL2” at time t2
    – one message addressed “/myURL3” at time t3
    – one message addressed “/myURL1” at time t4
    – one message addressed “/myURL2” at time t5
    I think they would be output in that order from the stock nodes:
    – “/myURL1” (t1)
    – “/myURL1” (t4)
    – “/myURL2” (t2)
    – “/myURL2” (t5)
    – “/myURL3” (t3)
    while it is critical sometimes to keep track of the exact order they arrived in, whatever the matching URL; I think this was our main problem in the past with the default nodes. In my plugins I have preserved reception order across the different URL’s watched after, as well.
    I think for some reason relying on Timetags would not help, can’t really remember what was missing.

  • please include support for OSCBlob aside from s, i, f (actually even the new arguments from OSC 1.1 could be interesting but that’s probably not on your critical to-do; they add booleans, impulse, etc. Cf NIME’09 paper). We use OSCBlobs a lot in our hardware as it saves a lot of data when sending a big set of 8bit values for instance, without having to group & decapsulate them in ints or floats. Especially when sending MIDI/DMX values in OSC in large quantities, Blobs allow to be very size optimal without too much byte-grouping hassle or wasting unused MSB.

That’s what comes to my mind first thing, I might add to it in case something else shows up.

Attached is a screenshot of the help patch of my current nodes, which might help understand the logic I went. In case that might help in any way, I’m totally open to sending you my code to use it.

Hope this feedback helps and that other users might relate and/or add to it.

Sorry for all the “as far as I remember” in here haha

Best!
T

1 Like

they don’t have to do anything rocket science to implement those. somebody already did that for C# GitHub - ValdemarOrn/SharpOSC: Full implementation of Open Sound Control in C# .NET 3.5

1 Like

@TremensS

to clarify: please note that this is only a decision for those simplified nodes. using the already existing OSC nodes in vl you can of course receive all, where they would just show up in the order they were received. having said that, i still have to see how a receive-all option for those simple nodes could look like.

of course. vl’s osc nodes already supported s, f, d, i, h and latest alpha builds now support also T, F, b and r (for color) and c (for char). also vectors can be plugged right into a sender and will be translated to a series of floats.

sending all of these is now as trivial as this:

2 Likes

support for all 1.1 spec datatypes has been pullrequested 5 years ago in the vvvv-sdk. unfortunately none of that was used within vvvv, because joreg decided that the old delphi node in its (still current) state is plenty for all use cases.

in vvvv-Message (which has pretty neat support for parsing any incoming osc data) there are a few independent nodes (mostly based on elliots prior work) to bundle and unbundle, etc.
they utilize the code in vvvv-sdk.

this was made years ago to address the very same need of the thread starter, go figure

on a more future-oriented note: what @microdee is proposing sounds like the kings path to deal even with osc updates. I am much in favor, especially the attributable routing seems super useful

continues here: https://vvvv.org/blog/vl-new-osc-and-tuio-nodes