[LV2] Mapping of OSC to LV2 Atoms

David Robillard d at drobilla.net
Fri Oct 16 20:52:43 PDT 2015


On Fri, 2015-10-16 at 17:38 +0200, Hanspeter Portner wrote:
> On 15.10.2015 20:29, David Robillard wrote:
> > On Fri, 2015-06-19 at 22:33 +0200, Hanspeter Portner wrote:
> >> Potential Issues
> >> ================
> >>
> >> * The OSC bundle timestamp and timestamp type 't' must be cast
> >>   from unsigned to signed as there are no usigned atoms.
> > 
> > Unfortunate, but maybe an atom type for specifically this sort of
> > timestamp wouldn't be a bad idea.
> 
> Are you proposing general purpose unsigned integer Atom types:

No.  Not until there's a really, really compelling reason this is
absolutely required, because it's just more types for everyone to deal
with.

> Or a specific Atom type just for OSC timestamps:

Yes, because...

> typedef struct {
> 	LV2_Atom atom; /**< Atom header. */
> 	uint64_t body; /**< unsigned Integer value. */
> } LV2_OSC_Timestamp;
> 
> OSC timestamps are modified NTP timestamps (offset by ~70years). They
> represent a 32.32 fixed point number.

... this is not a 64 bit unsigned number.

>  If you want to work with the
> timestamps, you need to tear them apart no matter what. If we create a
> new atom type, we could as well represent intergral and fractional parts
> separately.
> 
> typedef struct {
> 	uint32_t integral; /**< Integral part. */
> 	uint32_t fractional; /**< Fractional part. */
> } LV2_OSC_Timestamp_Body;
> 
> typedef struct {
> 	LV2_Atom atom; /**< Atom header. */
> 	LV2_OSC_Timestamp_Body body; /**< Timestamp Body. */
> } LV2_OSC_Timestamp;
> 
> > Though the time stamps don't seem
> > particularly useful in this context, to me.
> 
> The main purpose is to preserve all available information. I would find
> it useful to be able to set timestamps inside a plugin if the OSC bundle
> should be destined to be ejected to the network.

Fair enough.  Round-trip is important.

> > 
> >> I actually only care for the base and extended types but
> >> it may make sense to include the exotic types too for
> >> the sake of completeness.
> >>
> >> * The four exotic OSC types 'T'rue, 'F'alse, 'N'il, 'I'nfinitum have
> >>   no associated data in OSC. Should they be mapped to atoms and
> >>   packed into the arguments tuple or only show up in the message
> >>   format string like in OSC?
> > 
> > I don't know about nil and infinitum, but it seems true and false
> > clearly map to Bool.  There is no type string for atom objects, so a
> > true bidirectional mapping shouldn't be missing bits and rely on some
> > external funny thing (a format string) to fill in the gaps, IMO.
> 
> Whether the OSC format string is needed or not depends on whether we can
> come up with a unique association for all OSC types (or any we want to
> support) with a corresponding LV2 type.
> 
> For the basic types we could do without format string.
> 'i' -> LV2_Atom_Int
> 'f' -> LV2_Atom_Float
> 's' -> LV2_Atom_String
> 'b' -> LV2_Atom_Chunk
> 
> For the extended types, we may or may not need a format string,
> depending on whether we get a special timestamp/unsigned type or not.
> 'h' -> LV2_Atom_Long
> 'd' -> LV2_Atom_Double
> 't' -> LV2_Atom_Long || LV2_Atom_uLong || LV2_OSC_Timestamp
> 
> If 'h' and 't' both map to LV2_Atom_Long, we need the format string for
> bidirectional mapping.
> 
> The booleans and MIDI we can map without format string.
> 
> 'T' -> LV2_Atom_Bool {.body = 1}
> 'F' -> LV2_Atom_Bool {.body = 0}
> 'm' -> LV2_Atom {.type = MIDI__MidiEvent}
> 
> 'N' and 'I' are not of great use, imho. Without format string we would
> have to give them individual types, too, like for the timestamp? We
> could also not implement them...
> 
> 'N' -> LV2_Atom {.size = 0, .type = 0} || ???

This is generally used as the NULL atom, so that seems fine.

> 'I' -> ???

This one, however, is tricky.  Float INFINITY is lossy.
> 
> There are similar issues with 'c'har and 'S'ymbol, which may not be
> discriminatable from 'i' and 's', depending on what we map them to. We
> could also not implement them...
> 'c' -> LV2_Atom_Int || LV2_Atom_Char || ???
> 'S' -> LV2_Atom_String || LV2_Atom_URID
> 
> I've never seen OSC types 'N', 'I', 'c', 'S' being used, we could just
> not implement them...
> 
> So: If we want to get rid of the format string, we either have to add
> the missing unique Atom types or reduce the set of supported OSC types.
> 
> I could well do with this reduced set of types: 'ifsbhdtTFm'.
> 
> Which approach should we take, with or without format string?

A format string is redundant and largely useless when the payload has
type information built in to it.

> >> * Even more exotic types 'c'har, 'S'ymbol and 'm'idi are seldom
> >>   used. 'm' type is special as it's four bytes only and first
> >>   byte represents a port number. So it's not fully up to the
> >>   definition of MIDI__MidiEvent.
> > 
> > Our URID mechanism is really a symbol mechanism that should have been
> > named as such...
> 
> Ah, an OSC symbol ('S') could be mapped to an LV2_Atom_URID, which would
> have the positive side-effect to make it discriminatable from a simple
> OSC string ('s') being an LV2_Atom_String.

Yes, this seems like the obvious place to cram URIs which should be
useful for LV2ey things, but I need to re-read the OSC spec to consider
what to do about these less common types.

> >> * It seems to be possible to send arrayed values with '['
> >>   ']', but I've never seen it used anywhere and can't figure
> >>   out how it's supposed to work. I guess it could be mapped
> >>   to a LV2_Atom_Vector, though.
> > 
> > A vector must contain all the same type, so this won't work.  You'd have
> > to use a tuple.
> 
> Right. I'll leave it unimplemented for now, as I have never seen OSC
> arrays being used in the first place.

Possibly interesting trivia: OSC's failure at sane nesting is
essentially why LV2 Atoms exist.

> > The main thing lacking on the code side of things from my perspective
> > seems to be an actual native/standard representation of OSC
> > bundles/messages?  It would be nice to take a buffer containing an OSC
> > message and simply call a utility function to write that in atom form to
> > a forge (or transmit / work with actual OSC between plugins and
> > whatnot).
> 
> Sure, this is possible. I have code lying around that does exactly that,
> will need to redesign it a bit to match the LV2 API, though, will report
> back here when the missing bits have been added.
> 
> > 
> > Would you be interested in relicensing this under the LV2 license and
> > having an official extension based on it?
> 
> Turning it into an official extension would be great, as others seem to
> be interested, too.
> 
> Relicensing more permissively is no problem and makes only sense.


Great.

-- 
dr




More information about the Devel mailing list