OSC decoding in VL: best practices? And some comments

vl

#1

Hiya,

I developed a system in VVVV that I’m now in the process of converting to VL (which I am new to) so as to be able to use custom datatypes and other OOP concepts. I watched many tutorial videos and read what documentation there is on VL but I have some questions as to best practices.

My patch starts with receiving OSC messages over UDP. The messages are essentially note data from different instruments, each with their own OSC address. In my VVVV prototype, this is done like so:

Simple and works, but I hate that I have to have a static number of outputs set. When/if I want to change the instruments, it’s quite tedious and error-prone making all the changes all the places.

Enter VL. I want to follow the approach I’d take in C#/C++: create a type called Instrument that contains some properties (like the OSC address) as well as other custom types. I would then build VL operations that I’d use in VVVV to harness the power of Spreading over my types and so on. In this case, I made an operation called FromOSC to decode the OSC data and process it to get a spread of Instrument objects that contain NoteGroups that contain Notes.

After making my types, as well as Join / Split operations (scaffolding to do this automatically would be smart), I would up with this:

…which is in turn called like this:

This is doing what I want it to. But I have some questions:

  1. ToOSCMessages takes a Spread<byte>. I am using AsValue (Raw) to get this from the UDP output. Is there a better way?
  2. I still do not understand exactly what the ♦ symbol does in a the context of a loop. What is it called/used for and is it explained anywhere in documentation? My best guess is that it makes an object immutable for that region.
  3. The links from Match to IsEmpty and the ForEach spreader are called out with “The transported value is mutable…” Is there a better pattern to do an evaluation while also then passing through to a loop the object that’s evaluated?
  4. Same thing with the OSCMessage Split -> GetSlice section. In practice this seems to work but is there a better way? Is there, for example, a Using region that’d make the value immutable within it?
  5. My Note object is strongly typed (I’d like to be able to use it within VVVV), therefore I had to do CastAs. This seems hella janky. What’s the right way to do it?
  6. I want the ForEach to return only NoteGroups with values (Match is not empty). I had to add “Keep” to the ForEach to accomplish this. I thought the If region would do this by itself, as NoteGroup (Join) is within that region, but that does not seem to work; without Keep, 3 objects (one for each Instrument) are always returned, rather than only the ones which match the If condition.

I have two additional comments on VL generally, as a new user:

  1. Scaffolding to create join/split operations automatically would be a big help.
  2. I like that I can right click and “Open” any built-in VL object in the VL editor (for example, right-click ToOSCMessages and you are taken to the OSCUtils group). But these should really be read-only. I would hate to accidentally make a change to a library when hitting “save all” during quitting, for example.

Thank you in advance and I hope this is helpful!


#2

ad 1) use ReadAll [IO.Stream] in vl which takes a vvvv RAW (=vl stream) as input and returns the Spread
ad 2) i’m afraid not yet documented: the ♦ is an input to the IF that is passed on to its corresponding output unchanged in the ELSE case. (nothing to do with mutability).
ad 3) the mutability warning is because the internal OSCMessage type is mutable (which is probably not necessary, will check) anyway it is just a warning so you know in case you’d change something here you’d need to be sure what you do. in your case you’re only reading, so all is fine.
ad 4) see 3
ad 5) looks clunky indeed but apart from the looks it is just what needs to happen here. the output of OSCMessage is a Spread whose individual slices you have to type there. instead of using those generic osc nodes you could make your own parser that directly parses into your given patch… we should at some point provide some helpers for a thing like that…
ad 6) this is exactly where you missed the ♦ in your inner IF: in the IF case the NoteGroup is returned but in the ELSE case nothing is connected to the ♦, so also a default/empty NoteGroup is added. using the Keep is a correct strategy. another would be to hand a SpreadBuilder in from outside and add the NoteGroup to it inside the IF case…

re comments:
ad 1) absolutely, we’re thinking about different ways…
ad 2) they should actually be readonly. ie, you can modify but not save them!

so, those just quick, good to see you getting started with vl. let me know if anything needs more clarification!


#3

Thank you! This is very helpful.

I understand now from your answer what ♦ does in an If region, but what then does ♦ mean in the context of a ForEach? When would I want to use it?


#4

It is called an accumulator. You need it, so you have an updated object in each iteration of your loop with all actions in previous iterations already applied.

Think of a dictionary that you keep adding items to in a Loop. If you didn’t use the Accumulator and instead were linking the dictionary from the outside directly, you’d return the initial dictionary with just a single item added.


#5

Thank you for the good example, @mfan!

(Just decided to put my 2ct in a new thread since it’s grown to a feature request once again…)


#6

Hello @mfan,

in the newest Alpha we’ve added:

  • generic GetValue / GetValues which can be used for any type
  • and also GetVector2 / 3 / 4

The GetValues combined with the ValuesToVectors can give you spread of vectors.
See the screenshot attached.

Best,
Anton


#7

continues here: Simple OSC nodes