Discussion:
Draft Proposal: Fast Method Lookup for Embedded OSC Implementations
(too old to reply)
Brian Willoughby
2016-07-15 10:14:56 UTC
Permalink
Since I let the cat out of the bag, I might as well describe my suggested enhancement for OSC.


Draft Proposal: Fast Method Lookup for Embedded OSC Implementations
Draft Proposal

Brian Willoughby
***@audiobanshee.com

Sound Consulting, Seattle
http://www.audiobanshee.com/

15th July 2016


PROBLEM

OSC Address Patterns are variable-length ASCII strings which pose problems for fast matching on embedded systems, particularly 8-bit micro-controllers with lower clock speeds and simple instruction sets. Without breaking from the spirit of OSC, the goal of the following proposal is to improve embedded response time in a way that is maximally compatible.

The first task of an embedded processor upon receipt of an OSC Message is to look up the corresponding Method(s). Variable-length strings and pattern matching can consume hundreds of processor cycles, which can add up on low-power embedded systems. Typical OSC Address Patterns are quite long, often with redundant sections, and this is not conducive to prompt event responses.


BACKGROUND

OSC combines a human-readable Address Pattern (and optional Type Tag String) with machine-readable Arguments. The promise of human-readable Address Patterns is to allow easy configuration by users. The machine-readable Arguments are presumably encoded in binary format for efficient packing on 32-bit boundaries at the sacrifice of human readability. Unfortunately, the human-readable Address Pattern sacrifices machine readability at a potentially time-critical point in an embedded processor's communications parser and method executor. Poignantly, entry and viewing of Address Patterns typically only happen at setup time, whereas performers might desire to view actual Arguments during a live performance. Since viewing of live Arguments requires machine translation, it seems acceptable for the Address Patterns to be similarly encoded separate from the entry and viewing phase and thus use machine translation.

An analogy from the internet is that servers have human-readable names that are translated by distributed databases known as Domain Name Servers into fixed-length 32-bit (IPv4), 128-bit (IPv6), and 48-bit (MAC) values for the low-level routers and other network transport, so that the fastest possible responses can be attained. The human-readable host name is silently translated to a fixed-width bit pattern behind the scenes, offering the best of both worlds. These fixed-width bit patterns can be quickly matched or ignored by simple processors and even field-programmable gate array circuits.

We do not have the equivalent of DNS servers for OSC, but even the modern trends of IoT have required DNS services to be supplanted by options like Link-local Addressing and Zeroconf (a.k.a. Bonjour or mDNS and DNS-SD). Todays electronics users do not want to configure DNS or even DHCP servers. Thankfully, OSC has already embraced Zeroconf for discovery of OSC Servers.

It seems prudent that an option like Zeroconf for OSC Address Patterns could help improve embedded performance without changing the way developers or performers use OSC. Admittedly, mapping of Address Patterns to fixed-width bit values is more akin to web servers mapping a URL to a file system iNode, but the DNS analogy seems more understandable.

In contrast, MIDI was designed for embedded processors and requires only a few bits within the first byte to determine the equivalent of a method. MIDI was designed for quick and simple decoding to enable timely responses to synchronization messages. Within the realm of note-based performances with timing synchronization and dedicated and general real-time controllers, MIDI offers an incredibly efficient communications encoding. OSC does not need to change the gist of its design in order to gain a great deal of efficiency of coding.


RECOMMENDATION

Inspired by the 2004 Schmeder/Wright CNMAT paper, "A Query System for Open Sound Control," it seems that it would actually be quite simple for any OSC Server to act as something like DNS by providing a hash value (iNode) for any Address Pattern.

A #hash query command with an int32 response would provide a substitute for the provided Address Pattern, and this might even work for wild cards if the OSC Server supports them. Subsequent OSC Messages of Bundles sent to the same OSC Server could use the int32 hash instead of the OSC Address Pattern, with the simple caveat that any int32 with an MSB of '/' or '#' would be considered illegal. Thus 0x23000000 through 0x23FFFFFF and 0x2F000000 through 0x2FFFFFFF would certainly be off-limits for hash values, but theoretically all other int32 values would be fair game. It might be prudent to reserve 0xFFFFFFFF as a NULL indicator to signify invalid hash requests.

OSC Clients using these int32 hash values would also omit the OSC Type Tag String, since it is another variable-length string. This seems perfectly workable since any such Address Pattern would have already been queried.


EXAMPLE

A simple OSC synthesizer with 128 notes and some continuous controllers might response to the following queries:

/spastic/machine/note/#type-signature
/spastic/machine/note/#hash (ii) 60 64

with:

#reply (ss) '/spastic/machine/note/#type-signature' 'ii'
#reply (si) '/spastic/machine/note/#hash' 0

Future note events, subsequent to these queries, would no longer have to be of the form:

/spastic/machine/note (ii) 60 64

… but would instead be simply a sequence of binary values:

0 60 64

(Note that OSC Bundles should continue to follow the established format, with int32 size followed by a multiple of 4 bytes. Only the OSC Message format would be extended to allow an Address Pattern to start with something other than '/' or '#')


WILD CARDS

It may be more difficult to conjure an example of hash values for wild cards, but it seems at least theoretically possible that a given OSC Server could anticipate specific useful wild card patterns and provide hash values for those. An one example, an All Notes Off hash might be anticipated and provided in response to a wild card Address Pattern that is equivalent to making all notes silent. It would probably be more difficult to provide hash values on the fly for random note ranges, but it's an interesting exercise to consider. It would also be helpful to consider real-world uses of wild cards in OSC to see if there are any clues as to how to implement hashes for them.


Brian Willoughby
Brian Willoughby
2016-07-15 10:24:05 UTC
Permalink
Since I let the cat out of the bag, I might as well describe my suggested enhancement for OSC.


Fast Method Lookup for Embedded OSC Implementations
Draft Proposal

Brian Willoughby
***@audiobanshee.com

Sound Consulting, Seattle
http://www.audiobanshee.com/

15th July 2016


PROBLEM

OSC Address Patterns are variable-length ASCII strings which pose problems for fast matching on embedded systems, particularly 8-bit micro-controllers with lower clock speeds and simple instruction sets. Without breaking from the spirit of OSC, the goal of the following proposal is to improve embedded response time in a way that is maximally compatible.

The first task of an embedded processor upon receipt of an OSC Message is to look up the corresponding Method(s). Variable-length strings and pattern matching can consume hundreds of processor cycles, which can add up on low-power embedded systems. Typical OSC Address Patterns are quite long, often with redundant sections, and this is not conducive to prompt event responses.


BACKGROUND

OSC combines a human-readable Address Pattern (and optional Type Tag String) with machine-readable Arguments. The promise of human-readable Address Patterns is to allow easy configuration by users. The machine-readable Arguments are presumably encoded in binary format for efficient packing on 32-bit boundaries at the sacrifice of human readability. Unfortunately, the human-readable Address Pattern sacrifices machine readability at a potentially time-critical point in an embedded processor's communications parser and method executor. Poignantly, entry and viewing of Address Patterns typically only happen at setup time, whereas performers might desire to view actual Arguments during a live performance. Since viewing of live Arguments requires machine translation, it seems acceptable for the Address Patterns to be similarly encoded separate from the entry and viewing phase and thus use machine translation.

An analogy from the internet is that servers have human-readable names that are translated by distributed databases known as Domain Name Servers into fixed-length 32-bit (IPv4), 128-bit (IPv6), and 48-bit (MAC) values for the low-level routers and other network transport, so that the fastest possible responses can be attained. The human-readable host name is silently translated to a fixed-width bit pattern behind the scenes, offering the best of both worlds. These fixed-width bit patterns can be quickly matched or ignored by simple processors and even field-programmable gate array circuits.

We do not have the equivalent of DNS servers for OSC, but even the modern trends of IoT have required DNS services to be supplanted by options like Link-local Addressing and Zeroconf (a.k.a. Bonjour or mDNS and DNS-SD). Todays electronics users do not want to configure DNS or even DHCP servers. Thankfully, OSC has already embraced Zeroconf for discovery of OSC Servers.

It seems prudent that an option like Zeroconf for OSC Address Patterns could help improve embedded performance without changing the way developers or performers use OSC. Admittedly, mapping of Address Patterns to fixed-width bit values is more akin to web servers mapping a URL to a file system iNode, but the DNS analogy seems more understandable.

In contrast, MIDI was designed for embedded processors and requires only a few bits within the first byte to determine the equivalent of a method. MIDI was designed for quick and simple decoding to enable timely responses to synchronization messages. Within the realm of note-based performances with timing synchronization and dedicated and general real-time controllers, MIDI offers an incredibly efficient communications encoding. OSC does not need to change the gist of its design in order to gain a great deal of efficiency of coding.


RECOMMENDATION

Inspired by the 2004 Schmeder/Wright CNMAT paper, "A Query System for Open Sound Control," it seems that it would actually be quite simple for any OSC Server to act as something like DNS by providing a hash value (iNode) for any Address Pattern.

A #hash query command with an int32 response would provide a substitute for the provided Address Pattern, and this might even work for wild cards if the OSC Server supports them. Subsequent OSC Messages of Bundles sent to the same OSC Server could use the int32 hash instead of the OSC Address Pattern, with the simple caveat that any int32 with an MSB of '/' or '#' would be considered illegal. Thus 0x23000000 through 0x23FFFFFF and 0x2F000000 through 0x2FFFFFFF would certainly be off-limits for hash values, but theoretically all other int32 values would be fair game. It might be prudent to reserve 0xFFFFFFFF as a NULL indicator to signify invalid hash requests.

OSC Clients using these int32 hash values would also omit the OSC Type Tag String, since it is another variable-length string. This seems perfectly workable since any such Address Pattern would have already been queried.


EXAMPLE

A simple OSC synthesizer with 128 notes and some continuous controllers might response to the following queries:

/spastic/machine/note/#type-signature
/spastic/machine/note/#hash (ii) 60 64

with:

#reply (ss) '/spastic/machine/note/#type-signature' 'ii'
#reply (si) '/spastic/machine/note/#hash' 0

Future note events, subsequent to these queries, would no longer have to be of the form:

/spastic/machine/note (ii) 60 64

… but would instead be simply a sequence of binary values:

0 60 64

(Note that OSC Bundles should continue to follow the established format, with int32 size followed by a multiple of 4 bytes. Only the OSC Message format would be extended to allow an Address Pattern to start with something other than '/' or '#')


WILD CARDS

It may be more difficult to conjure an example of hash values for wild cards, but it seems at least theoretically possible that a given OSC Server could anticipate specific useful wild card patterns and provide hash values for those. An one example, an All Notes Off hash might be anticipated and provided in response to a wild card Address Pattern that is equivalent to making all notes silent. It would probably be more difficult to provide hash values on the fly for random note ranges, but it's an interesting exercise to consider. It would also be helpful to consider real-world uses of wild cards in OSC to see if there are any clues as to how to implement hashes for them.


ADDENDUM

Embedded implementations will still need to support OSC Address Patterns, but can do so as slowly as necessary. The point of the #hash query is to move this potentially slow operation to a non-critical time, preferably during setup, and certainly not during performance. The code would be designed to offer the shortest possible execution path to OSC Messages with OSC Address Hashes. Detection of '/' and '#' should only take a couple of cycles to escape the preferred hash code path. However, it seems possible that some embedded processors will be so limited in code space resources that this might not be possible. In such cases, the OSC Address Hashes for that particular device would have to somehow be provided to OSC Clients without interactive queries.


Brian Willoughby
Thomas Brand
2016-07-15 12:24:18 UTC
Permalink
Post by Brian Willoughby
Since I let the cat out of the bag, I might as well describe my suggested
enhancement for OSC.
...
Post by Brian Willoughby
EXAMPLE
A simple OSC synthesizer with 128 notes and some continuous controllers
/spastic/machine/note/#type-signature
/spastic/machine/note/#hash (ii) 60 64
#reply (ss) '/spastic/machine/note/#type-signature' 'ii'
#reply (si) '/spastic/machine/note/#hash' 0
This would require a 'smart' OSC synthesizer which is totally fine.
However when it comes to custom protocols defining a sequence of
communication (like: how to query the messages understood by synth? What
are the signatures?) this is outside of the scope of what OSC currently
defines in the standard. It's questionable if such (basic) procedures for
common use cases should be added or left to the applications to define and
handle.
Post by Brian Willoughby
/spastic/machine/note (ii) 60 64
0 60 64
(Note that OSC Bundles should continue to follow the established format,
with int32 size followed by a multiple of 4 bytes. Only the OSC Message
format would be extended to allow an Address Pattern to start with
something other than '/' or '#')
What you propose is to get a hash value per message incl. signature
understood by synth (kind of like a shortened alias in a generic sense).

The use of the hash would overthrow the OSC message format (no path, no
typetags). For the benefit of sending just bytes. If this is what embedded
systems can/want to do, then why not really just sending these bytes over
to the host without any OSC invovled.

The idea of using hashes / aliases can be interesting nevertheless..
You could even think the other way around and let the sender make the
mapping of an alias to an existing (OSC) method. Then we are close to what
any intermediary does: translating between ~incompatible domains. Of
course the intermediary can be part of either side of the communication
session. But we should not make that a prerequisite to have per standard
IMHO.

Greetings
Thomas
Roger Dannenberg
2016-07-15 13:09:58 UTC
Permalink
I have a few comments on Brian's interesting proposal.

1) Have you measured where time is going in an embedded implementation,
including the communication costs? I recently did some measurements of
OSC running locally (using "localhost" and 2 processes), and it seemed
that time was completely dominated by the kernel-level IP protocol.

2) Address processing in OSC is "scary": It's an "obviously" expensive
feature that is very tempting to optimize. I use "quotes" because I have
never seen any real measurements or profiling to assess the impact of
this design decision. Compared to IP, or maybe in an embedded processor
taking an interrupt on every serial byte or running a Bluetooth
protocol, it does seem that address processing might just be an easy
target that's not really significant. Therefore, I say we should measure
first to see if there's anything worth optimizing.

3) I believe a very similar approach was implemented. I'm drawing a
blank, but I think there's a conference paper, maybe SMC, and I'm
guessing about 5 to 10 years ago.

4) Another related approach is SuperCollider, which I believe uses small
integers for addresses -- I don't know the details, but should be easy
to look up.

5) I've been going in the opposite direction, i.e. assuming embedded
processors are getting faster and more powerful, able to connect to WiFi
or Ethernet. We're about to release a system named O2, which is similar
to OSC, but adds clock synchronization, named services, and automatic
discovery. So instead of opening a port and IP address and sending to
/note, the destination advertises a service, e.g. "synth" and client
send to /synth/note. In O2, I couldn't resist another small address
optimization: Addresses without wildcards (which seems standard) can
have the form "!synth/note". The leading "!" avoids searching the string
for wildcard characters. We then form a hash 4-bytes at a time (because
strings are always padded to 4-byte boundaries). I think this makes
address lookup several times faster, but again, I should follow my own
advice to measure and establish a problem before fixing it!

5b) O2 has some compatibility support for OSC and embedded systems not
running IP, so I'm very interested in this discussion of best practice
for OSC to very minimal embedded controllers.

-Roger
Post by Thomas Brand
Post by Brian Willoughby
Since I let the cat out of the bag, I might as well describe my suggested
enhancement for OSC.
...
Post by Brian Willoughby
EXAMPLE
A simple OSC synthesizer with 128 notes and some continuous controllers
/spastic/machine/note/#type-signature
/spastic/machine/note/#hash (ii) 60 64
#reply (ss) '/spastic/machine/note/#type-signature' 'ii'
#reply (si) '/spastic/machine/note/#hash' 0
This would require a 'smart' OSC synthesizer which is totally fine.
However when it comes to custom protocols defining a sequence of
communication (like: how to query the messages understood by synth? What
are the signatures?) this is outside of the scope of what OSC currently
defines in the standard. It's questionable if such (basic) procedures for
common use cases should be added or left to the applications to define and
handle.
Post by Brian Willoughby
/spastic/machine/note (ii) 60 64
0 60 64
(Note that OSC Bundles should continue to follow the established format,
with int32 size followed by a multiple of 4 bytes. Only the OSC Message
format would be extended to allow an Address Pattern to start with
something other than '/' or '#')
What you propose is to get a hash value per message incl. signature
understood by synth (kind of like a shortened alias in a generic sense).
The use of the hash would overthrow the OSC message format (no path, no
typetags). For the benefit of sending just bytes. If this is what embedded
systems can/want to do, then why not really just sending these bytes over
to the host without any OSC invovled.
The idea of using hashes / aliases can be interesting nevertheless..
You could even think the other way around and let the sender make the
mapping of an alias to an existing (OSC) method. Then we are close to what
any intermediary does: translating between ~incompatible domains. Of
course the intermediary can be part of either side of the communication
session. But we should not make that a prerequisite to have per standard
IMHO.
Greetings
Thomas
_______________________________________________
OSC_dev mailing list
http://lists.create.ucsb.edu/mailman/listinfo/osc_dev
Brian Willoughby
2016-07-15 16:37:09 UTC
Permalink
Thanks for the comments, Roger, and Thomas. Responses inline below.
Post by Roger Dannenberg
I have a few comments on Brian's interesting proposal.
1) Have you measured where time is going in an embedded implementation,
including the communication costs? I recently did some measurements of
OSC running locally (using "localhost" and 2 processes), and it seemed
that time was completely dominated by the kernel-level IP protocol.
Not all embedded system have a kernel, and those that do can use a real-time OS. Did you measure the Linux kernel (uClinux) or an RTOS?

Without a kernel, the options for IP include lwip, uIP and uIPv6. Have you measured any of those implementations?

As I hinted earlier, I'm working on an embedded design that will hopefully support OSC. I plan to use that as a test bed to prove the feasibility of my proposal. I had originally planned to wait until I had something working before "letting the cat out of the bag," but recent discussions made me decide to "put the cart before the horse." Sorry for the played-out expressions. My hardware is ARM (TM4C1294) and will likely be running TI-RTOS. If I run into any issues with TI-RTOS, the embedded code will be bare metal.
Post by Roger Dannenberg
2) Address processing in OSC is "scary": It's an "obviously" expensive
feature that is very tempting to optimize. I use "quotes" because I have
never seen any real measurements or profiling to assess the impact of
this design decision. Compared to IP, or maybe in an embedded processor
taking an interrupt on every serial byte or running a Bluetooth
protocol, it does seem that address processing might just be an easy
target that's not really significant. Therefore, I say we should measure
first to see if there's anything worth optimizing.
Very good point. However, I have two concerns about OSC Addressing. First, the decoding is not fixed-time but variable, thus making the latency unpredictable. Second, no other real-time control syntax uses variable-length addressing (AVB, Dante, etc). That tells me it's very likely to be significant for low-latency responsiveness. Of course, just because no professional standard has variable-length addressing is no guarantee that it's a bad idea, but let's just say that I have a strong hunch about what measurements will reveal, at least for very demanding applications.
Post by Roger Dannenberg
3) I believe a very similar approach was implemented. I'm drawing a
blank, but I think there's a conference paper, maybe SMC, and I'm
guessing about 5 to 10 years ago.
You have an excellent memory. Better than mine, it seems. Late last night, I ran across the paper you're probably thinking of and couldn't understand why I'd forgotten about it.

IMPROVING THE EFFICIENCY OF OPEN SOUND
CONTROL WITH COMPRESSED ADDRESS STRINGS
Jari Kleimola , Finland Patrick J. McGlynn , Ireland
2011

Their proposal seems very similar to mine, including the analogy to DNS, but I'll have to study it more closely to be sure I understand what they're suggesting. It seems that queries of an OSC Server return an integer hash, and subsequent messages use "/" as an address escape to signal that a int32 hash is inserted before the first Argument. It seems the only difference between our proposals is that mine removes the Address Pattern so that the first word is the hash. Either change will break existing OSC parsers.
Post by Roger Dannenberg
4) Another related approach is SuperCollider, which I believe uses small
integers for addresses -- I don't know the details, but should be easy
to look up.
Thanks for the hint. I've heard of SuperCollider for years, but never realized there was a communications protocol.
Post by Roger Dannenberg
5) I've been going in the opposite direction, i.e. assuming embedded
processors are getting faster and more powerful, able to connect to WiFi
or Ethernet. We're about to release a system named O2, which is similar
to OSC, but adds clock synchronization, named services, and automatic
discovery. So instead of opening a port and IP address and sending to
/note, the destination advertises a service, e.g. "synth" and client
send to /synth/note.
How does the destination advertise a service?
I'm hoping your answer will be Zeroconf (mDNS and DNS-SD).

How does the client send messages to the destination? With WiFi and Ethernet, you still have to have a port and IP address. I don't see any way to avoid that without switching from Ethernet to USB or some other serial, point-to-point link.

Clock synchronization will be an incredibly important feature!
Post by Roger Dannenberg
In O2, I couldn't resist another small address
optimization: Addresses without wildcards (which seems standard) can
have the form "!synth/note". The leading "!" avoids searching the string
for wildcard characters.
That alone is a great suggestion for OSC 2.0 - a way to rule out wildcards *before* scanning the full, variable-length Address Pattern. I'd be inclined to use '=' instead of '!' but that's just a personal preference. e.g. "=synth/note" (sort of like fgrep versus egrep)
Post by Roger Dannenberg
We then form a hash 4-bytes at a time (because
strings are always padded to 4-byte boundaries). I think this makes
address lookup several times faster, but again, I should follow my own
advice to measure and establish a problem before fixing it!
I think you're on the right track with 4-byte (1-word) hash chunks. However, when would you really need more than 4.2 million hashes? A single int32 hash is enough to cover millions of OSC Methods. Why use a variable-length hash when it's not really needed? I've even subtracted all of the illegal hashes that start with '/' and '#' or '!' or '=' and there are still millions left.

Keep in mine that 32-bit OSC Arguments follow the hash, and each OSC Method can have as many Arguments as needed. It might even be useful for the specification to require that each Method have a fixed number of Arguments. It's always possible to specify a different Method for those cases that need a different number of Arguments. This would remove the need for the OSC Type Tag String during real-time performances - in other words, the hash would imply the Type Tags.
Post by Roger Dannenberg
5b) O2 has some compatibility support for OSC and embedded systems not
running IP, so I'm very interested in this discussion of best practice
for OSC to very minimal embedded controllers.
I'm glad to hear about O2. I'm hoping it's something that will be as popular as OSC, even if the low-level protocol itself is not exactly OSC 1.1 compliant. The authors of OSC seem to be looking for a more official standard, even if that requires significant changes. The time is right to define a new protocol.

I've implemented many embedded MIDI systems, as well as many USB-MIDI systems. Any new standard would benefit from a full understanding of how MIDI codes are optimized for really fast decoding without variable latency. Variable latency is more distracting than fixed latency, even if the latency must be longer in order to be fixed.

I think it's a step forward in the way that OSC is built on 32-bit words rather than 8-bit bytes. The few parsing issues posed by MIDI are due to the limitations of 8-bit chunks. Using 32-bit words should avoid those challenges. Keeping the Address Hash to a single 32-bit word should be of universal benefit. Allowing for OSC 1.1 style Address Pattern browsing could be a useful non-real-time feature for compatibility between smart systems, while still requiring fixed-width elements for the real-time performance messages.

As a side note, I'm wondering why OSC specifies sizes in bytes with the requirement that the size be a multiple of 4, when a better choice would be to specify sizes in words. That way, payloads can be 4 times as large and there aren't 2 wasted bits that would otherwise always be 0 in compliant data sets.
Post by Roger Dannenberg
-Roger
Brian
Roger Dannenberg
2016-07-16 03:07:36 UTC
Permalink
More inline responses below...
Post by Brian Willoughby
Thanks for the comments, Roger, and Thomas. Responses inline below.
Post by Roger Dannenberg
I have a few comments on Brian's interesting proposal.
1) Have you measured where time is going in an embedded implementation,
including the communication costs? I recently did some measurements of
OSC running locally (using "localhost" and 2 processes), and it seemed
that time was completely dominated by the kernel-level IP protocol.
Not all embedded system have a kernel, and those that do can use a
real-time OS. Did you measure the Linux kernel (uClinux) or an RTOS?
I was just running on OS X. My point has nothing to do with kernels. My
point is that it may be pointless to optimize address pattern matching
if the vast majority of the time is spent moving bytes from one machine
to another via the IP stack. I was measuring throughput of some
different implementations of OSC-like protocols, but the performance
seemed to be completely dominated by the details of
send/recv/select/poll operations.
Post by Brian Willoughby
Without a kernel, the options for IP include lwip, uIP and uIPv6. Have
you measured any of those implementations?
As I hinted earlier, I'm working on an embedded design that will
hopefully support OSC. I plan to use that as a test bed to prove the
feasibility of my proposal. I had originally planned to wait until I
had something working before "letting the cat out of the bag," but
recent discussions made me decide to "put the cart before the horse."
Sorry for the played-out expressions. My hardware is ARM (TM4C1294)
and will likely be running TI-RTOS. If I run into any issues with
TI-RTOS, the embedded code will be bare metal.
Post by Roger Dannenberg
2) Address processing in OSC is "scary": It's an "obviously" expensive
feature that is very tempting to optimize. I use "quotes" because I have
never seen any real measurements or profiling to assess the impact of
this design decision. Compared to IP, or maybe in an embedded processor
taking an interrupt on every serial byte or running a Bluetooth
protocol, it does seem that address processing might just be an easy
target that's not really significant. Therefore, I say we should measure
first to see if there's anything worth optimizing.
Very good point. However, I have two concerns about OSC Addressing.
First, the decoding is not fixed-time but variable, thus making the
latency unpredictable. Second, no other real-time control syntax uses
variable-length addressing (AVB, Dante, etc). That tells me it's very
likely to be significant for low-latency responsiveness. Of course,
just because no professional standard has variable-length addressing
is no guarantee that it's a bad idea, but let's just say that I have a
strong hunch about what measurements will reveal, at least for very
demanding applications.
I think you'll be surprised. Fixed-length addressing is great for
high-throughput network switches that handle a message every
microsecond, and maybe there's a small gain for software, but I suspect
it won't matter unless you have a very low-cost method of moving bits
over the wire.
Post by Brian Willoughby
Post by Roger Dannenberg
3) I believe a very similar approach was implemented. I'm drawing a
blank, but I think there's a conference paper, maybe SMC, and I'm
guessing about 5 to 10 years ago.
You have an excellent memory. Better than mine, it seems. Late last
night, I ran across the paper you're probably thinking of and couldn't
understand why I'd forgotten about it.
IMPROVING THE EFFICIENCY OF OPEN SOUND
CONTROL WITH COMPRESSED ADDRESS STRINGS
Jari Kleimola , Finland Patrick J. McGlynn , Ireland
2011
Their proposal seems very similar to mine, including the analogy to
DNS, but I'll have to study it more closely to be sure I understand
what they're suggesting. It seems that queries of an OSC Server return
an integer hash, and subsequent messages use "/" as an address escape
to signal that a int32 hash is inserted before the first Argument. It
seems the only difference between our proposals is that mine removes
the Address Pattern so that the first word is the hash. Either change
will break existing OSC parsers.
Post by Roger Dannenberg
4) Another related approach is SuperCollider, which I believe uses small
integers for addresses -- I don't know the details, but should be easy
to look up.
Thanks for the hint. I've heard of SuperCollider for years, but never
realized there was a communications protocol.
Post by Roger Dannenberg
5) I've been going in the opposite direction, i.e. assuming embedded
processors are getting faster and more powerful, able to connect to WiFi
or Ethernet. We're about to release a system named O2, which is similar
to OSC, but adds clock synchronization, named services, and automatic
discovery. So instead of opening a port and IP address and sending to
/note, the destination advertises a service, e.g. "synth" and client
send to /synth/note.
How does the destination advertise a service?
I'm hoping your answer will be Zeroconf (mDNS and DNS-SD).
We started by looking at Zeroconf, but then decided it would be hard for
people to install/configure, and it's pretty simple (or so we thought)
to use simple broadcast UDP messages for discovery, so O2 is
self-contained with its own discovery protocol. It turned out to be much
trickier than I initially thought, although I think most of the
complexity comes from asynchrony and unreliable messages which you'd
have to deal with using Zeroconf as well.
Post by Brian Willoughby
How does the client send messages to the destination? With WiFi and
Ethernet, you still have to have a port and IP address. I don't see
any way to avoid that without switching from Ethernet to USB or some
other serial, point-to-point link.
Every process announces itself by broadcasting IP addresses and TCP
server ports to a handful of UDP ports (we use multiple UDP ports to
avoid conflicts with other applications), and processes link up via
pair-wise TCP connections. Once connected, processes exchange a list of
services they provide as well as their UDP port numbers. When you send
to a service, you do a local hash table lookup to find the socket or
address of the service provider. There are options for sending via UDP -
o2_send - and reliably over TCP - o2_send_command(); it's the sender's
choice.
Post by Brian Willoughby
Clock synchronization will be an incredibly important feature!
Post by Roger Dannenberg
In O2, I couldn't resist another small address
optimization: Addresses without wildcards (which seems standard) can
have the form "!synth/note". The leading "!" avoids searching the string
for wildcard characters.
That alone is a great suggestion for OSC 2.0 - a way to rule out
wildcards *before* scanning the full, variable-length Address Pattern.
I'd be inclined to use '=' instead of '!' but that's just a personal
preference. e.g. "=synth/note" (sort of like fgrep versus egrep)
Post by Roger Dannenberg
We then form a hash 4-bytes at a time (because
strings are always padded to 4-byte boundaries). I think this makes
address lookup several times faster, but again, I should follow my own
advice to measure and establish a problem before fixing it!
I think you're on the right track with 4-byte (1-word) hash chunks.
However, when would you really need more than 4.2 million hashes? A
single int32 hash is enough to cover millions of OSC Methods. Why use
a variable-length hash when it's not really needed? I've even
subtracted all of the illegal hashes that start with '/' and '#' or
'!' or '=' and there are still millions left.
What I meant is that rather than processing the string
one-byte-at-a-time to form a 32-bit hash value, I process the string
4-bytes-at-a-time to form a 32-bit hash value. On a 32-bit machine, you
compute the hash should run about 4x faster.
Post by Brian Willoughby
Keep in mine that 32-bit OSC Arguments follow the hash, and each OSC
Method can have as many Arguments as needed. It might even be useful
for the specification to require that each Method have a fixed number
of Arguments. It's always possible to specify a different Method for
those cases that need a different number of Arguments. This would
remove the need for the OSC Type Tag String during real-time
performances - in other words, the hash would imply the Type Tags.
I implemented a few options for type tag string handling, which adds
some flags to the routine that registers a handler for an address, but
also gives some flexibility. You can do strict types where we compare
the type string to the expected types and reject the message if there's
a mismatch. Or, you can request type coercion where args are coerced to
the requested types if possible. Finally, you can have O2 build an arg
vector (array of pointers to possibly coerced args - based on liblo's
API). A C-based server would probably use strict types or at most
type-coercion, while an external for MAX/Pd/Python could avoid type
checking at this level and just pass typed arguments along to the
MAX/Pd/Python handler.
Post by Brian Willoughby
Post by Roger Dannenberg
5b) O2 has some compatibility support for OSC and embedded systems not
running IP, so I'm very interested in this discussion of best practice
for OSC to very minimal embedded controllers.
I'm glad to hear about O2. I'm hoping it's something that will be as
popular as OSC, even if the low-level protocol itself is not exactly
OSC 1.1 compliant. The authors of OSC seem to be looking for a more
official standard, even if that requires significant changes. The time
is right to define a new protocol.
I've implemented many embedded MIDI systems, as well as many USB-MIDI
systems. Any new standard would benefit from a full understanding of
how MIDI codes are optimized for really fast decoding without variable
latency. Variable latency is more distracting than fixed latency, even
if the latency must be longer in order to be fixed.
There are so many sources of latency, including the cache behavior and
interrupts that are pretty much out of your control. I disagree that
this is a big issue. It seems that even with MIDI, if the performer
generates a chord of 10 notes in the space of a few milliseconds, you've
added milliseconds of jitter (or maybe less if it's faster USB MIDI, but
still it's not very deterministic). I'd say as long as you use
constant-time algorithms and computation time is small relative to
requirements or perception, e.g. a couple of milliseconds for music
events, then you should be in good shape. Address translation is orders
of magnitude faster than that, so that's why I asked if you've measured
the potential gain of optimization.
Post by Brian Willoughby
I think it's a step forward in the way that OSC is built on 32-bit
words rather than 8-bit bytes. The few parsing issues posed by MIDI
are due to the limitations of 8-bit chunks. Using 32-bit words should
avoid those challenges. Keeping the Address Hash to a single 32-bit
word should be of universal benefit. Allowing for OSC 1.1 style
Address Pattern browsing could be a useful non-real-time feature for
compatibility between smart systems, while still requiring fixed-width
elements for the real-time performance messages.
As a side note, I'm wondering why OSC specifies sizes in bytes with
the requirement that the size be a multiple of 4, when a better choice
would be to specify sizes in words. That way, payloads can be 4 times
as large and there aren't 2 wasted bits that would otherwise always be
0 in compliant data sets.
I assumed it was to potentially speed up low-level processing. Some
processors require wide numbers to fall on word boundaries, so using a
value in a message would require you to copy the value byte-by-byte to a
word-aligned address before reading it if sizes were not a multiple of 4
(you still have the alignment problem for 64-bit ints and doubles, and
I'm not sure how many processors are affected). You can find the end of
a string 4 bytes at a time instead of 1 at a time, and there's the
4-byte-at-a-time hash computation trick as well. I don't think the
designers were worried about payloads anywhere close to 32 bits.
Post by Brian Willoughby
Post by Roger Dannenberg
-Roger
Brian
_______________________________________________
OSC_dev mailing list
http://lists.create.ucsb.edu/mailman/listinfo/osc_dev
Adrian Freed
2016-07-16 19:10:17 UTC
Permalink
Interesting ideas! As with all performance optimization, the context and quality of measurements is critical.
It is becoming very difficiult to generalize now across the many contexts OSC is used. For example I am looking
at our OSC library for embedded controllers these days. Performance issues on the upcoming Teensy (ARM with FPU)
are completely different from those on the older 8-bit chips. Generally speaking more cycles are being wasted crossing interfaces
of various API and KPI (Kernel programming interfaces) than in processing the OSC payloads, but
we are rapidly tuning away those problems so that presumably the OSC processing will be interesting to tune eventually.
My intuition is that caching a perfect hash of all the “static” elements of a bundle will satisfy many folk
concerned with keeping wireless packets small and lookups for static schema fast.

In my own experience, address lookup is a minor performance hit because most patterns fail fast and also most
interesting control structures require considerable computation for the case that patterns succeed (e.g. recomputing and
interpolating co-efficients for a filter bank).
Post by Roger Dannenberg
More inline responses below...
Post by Brian Willoughby
Thanks for the comments, Roger, and Thomas. Responses inline below.
Post by Roger Dannenberg
I have a few comments on Brian's interesting proposal.
1) Have you measured where time is going in an embedded implementation,
including the communication costs? I recently did some measurements of
OSC running locally (using "localhost" and 2 processes), and it seemed
that time was completely dominated by the kernel-level IP protocol.
Not all embedded system have a kernel, and those that do can use a real-time OS. Did you measure the Linux kernel (uClinux) or an RTOS?
I was just running on OS X. My point has nothing to do with kernels. My point is that it may be pointless to optimize address pattern matching if the vast majority of the time is spent moving bytes from one machine to another via the IP stack. I was measuring throughput of some different implementations of OSC-like protocols, but the performance seemed to be completely dominated by the details of send/recv/select/poll operations.
Post by Brian Willoughby
Without a kernel, the options for IP include lwip, uIP and uIPv6. Have you measured any of those implementations?
As I hinted earlier, I'm working on an embedded design that will hopefully support OSC. I plan to use that as a test bed to prove the feasibility of my proposal. I had originally planned to wait until I had something working before "letting the cat out of the bag," but recent discussions made me decide to "put the cart before the horse." Sorry for the played-out expressions. My hardware is ARM (TM4C1294) and will likely be running TI-RTOS. If I run into any issues with TI-RTOS, the embedded code will be bare metal.
Post by Roger Dannenberg
2) Address processing in OSC is "scary": It's an "obviously" expensive
feature that is very tempting to optimize. I use "quotes" because I have
never seen any real measurements or profiling to assess the impact of
this design decision. Compared to IP, or maybe in an embedded processor
taking an interrupt on every serial byte or running a Bluetooth
protocol, it does seem that address processing might just be an easy
target that's not really significant. Therefore, I say we should measure
first to see if there's anything worth optimizing.
Very good point. However, I have two concerns about OSC Addressing. First, the decoding is not fixed-time but variable, thus making the latency unpredictable. Second, no other real-time control syntax uses variable-length addressing (AVB, Dante, etc). That tells me it's very likely to be significant for low-latency responsiveness. Of course, just because no professional standard has variable-length addressing is no guarantee that it's a bad idea, but let's just say that I have a strong hunch about what measurements will reveal, at least for very demanding applications.
I think you'll be surprised. Fixed-length addressing is great for high-throughput network switches that handle a message every microsecond, and maybe there's a small gain for software, but I suspect it won't matter unless you have a very low-cost method of moving bits over the wire.
Post by Brian Willoughby
Post by Roger Dannenberg
3) I believe a very similar approach was implemented. I'm drawing a
blank, but I think there's a conference paper, maybe SMC, and I'm
guessing about 5 to 10 years ago.
You have an excellent memory. Better than mine, it seems. Late last night, I ran across the paper you're probably thinking of and couldn't understand why I'd forgotten about it.
IMPROVING THE EFFICIENCY OF OPEN SOUND
CONTROL WITH COMPRESSED ADDRESS STRINGS
Jari Kleimola , Finland Patrick J. McGlynn , Ireland
2011
Their proposal seems very similar to mine, including the analogy to DNS, but I'll have to study it more closely to be sure I understand what they're suggesting. It seems that queries of an OSC Server return an integer hash, and subsequent messages use "/" as an address escape to signal that a int32 hash is inserted before the first Argument. It seems the only difference between our proposals is that mine removes the Address Pattern so that the first word is the hash. Either change will break existing OSC parsers.
Post by Roger Dannenberg
4) Another related approach is SuperCollider, which I believe uses small
integers for addresses -- I don't know the details, but should be easy
to look up.
Thanks for the hint. I've heard of SuperCollider for years, but never realized there was a communications protocol.
Post by Roger Dannenberg
5) I've been going in the opposite direction, i.e. assuming embedded
processors are getting faster and more powerful, able to connect to WiFi
or Ethernet. We're about to release a system named O2, which is similar
to OSC, but adds clock synchronization, named services, and automatic
discovery. So instead of opening a port and IP address and sending to
/note, the destination advertises a service, e.g. "synth" and client
send to /synth/note.
How does the destination advertise a service?
I'm hoping your answer will be Zeroconf (mDNS and DNS-SD).
We started by looking at Zeroconf, but then decided it would be hard for people to install/configure, and it's pretty simple (or so we thought) to use simple broadcast UDP messages for discovery, so O2 is self-contained with its own discovery protocol. It turned out to be much trickier than I initially thought, although I think most of the complexity comes from asynchrony and unreliable messages which you'd have to deal with using Zeroconf as well.
Post by Brian Willoughby
How does the client send messages to the destination? With WiFi and Ethernet, you still have to have a port and IP address. I don't see any way to avoid that without switching from Ethernet to USB or some other serial, point-to-point link.
Every process announces itself by broadcasting IP addresses and TCP server ports to a handful of UDP ports (we use multiple UDP ports to avoid conflicts with other applications), and processes link up via pair-wise TCP connections. Once connected, processes exchange a list of services they provide as well as their UDP port numbers. When you send to a service, you do a local hash table lookup to find the socket or address of the service provider. There are options for sending via UDP - o2_send - and reliably over TCP - o2_send_command(); it's the sender's choice.
Post by Brian Willoughby
Clock synchronization will be an incredibly important feature!
Post by Roger Dannenberg
In O2, I couldn't resist another small address
optimization: Addresses without wildcards (which seems standard) can
have the form "!synth/note". The leading "!" avoids searching the string
for wildcard characters.
That alone is a great suggestion for OSC 2.0 - a way to rule out wildcards *before* scanning the full, variable-length Address Pattern. I'd be inclined to use '=' instead of '!' but that's just a personal preference. e.g. "=synth/note" (sort of like fgrep versus egrep)
Post by Roger Dannenberg
We then form a hash 4-bytes at a time (because
strings are always padded to 4-byte boundaries). I think this makes
address lookup several times faster, but again, I should follow my own
advice to measure and establish a problem before fixing it!
I think you're on the right track with 4-byte (1-word) hash chunks. However, when would you really need more than 4.2 million hashes? A single int32 hash is enough to cover millions of OSC Methods. Why use a variable-length hash when it's not really needed? I've even subtracted all of the illegal hashes that start with '/' and '#' or '!' or '=' and there are still millions left.
What I meant is that rather than processing the string one-byte-at-a-time to form a 32-bit hash value, I process the string 4-bytes-at-a-time to form a 32-bit hash value. On a 32-bit machine, you compute the hash should run about 4x faster.
Post by Brian Willoughby
Keep in mine that 32-bit OSC Arguments follow the hash, and each OSC Method can have as many Arguments as needed. It might even be useful for the specification to require that each Method have a fixed number of Arguments. It's always possible to specify a different Method for those cases that need a different number of Arguments. This would remove the need for the OSC Type Tag String during real-time performances - in other words, the hash would imply the Type Tags.
I implemented a few options for type tag string handling, which adds some flags to the routine that registers a handler for an address, but also gives some flexibility. You can do strict types where we compare the type string to the expected types and reject the message if there's a mismatch. Or, you can request type coercion where args are coerced to the requested types if possible. Finally, you can have O2 build an arg vector (array of pointers to possibly coerced args - based on liblo's API). A C-based server would probably use strict types or at most type-coercion, while an external for MAX/Pd/Python could avoid type checking at this level and just pass typed arguments along to the MAX/Pd/Python handler.
Post by Brian Willoughby
Post by Roger Dannenberg
5b) O2 has some compatibility support for OSC and embedded systems not
running IP, so I'm very interested in this discussion of best practice
for OSC to very minimal embedded controllers.
I'm glad to hear about O2. I'm hoping it's something that will be as popular as OSC, even if the low-level protocol itself is not exactly OSC 1.1 compliant. The authors of OSC seem to be looking for a more official standard, even if that requires significant changes. The time is right to define a new protocol.
I've implemented many embedded MIDI systems, as well as many USB-MIDI systems. Any new standard would benefit from a full understanding of how MIDI codes are optimized for really fast decoding without variable latency. Variable latency is more distracting than fixed latency, even if the latency must be longer in order to be fixed.
There are so many sources of latency, including the cache behavior and interrupts that are pretty much out of your control. I disagree that this is a big issue. It seems that even with MIDI, if the performer generates a chord of 10 notes in the space of a few milliseconds, you've added milliseconds of jitter (or maybe less if it's faster USB MIDI, but still it's not very deterministic). I'd say as long as you use constant-time algorithms and computation time is small relative to requirements or perception, e.g. a couple of milliseconds for music events, then you should be in good shape. Address translation is orders of magnitude faster than that, so that's why I asked if you've measured the potential gain of optimization.
Post by Brian Willoughby
I think it's a step forward in the way that OSC is built on 32-bit words rather than 8-bit bytes. The few parsing issues posed by MIDI are due to the limitations of 8-bit chunks. Using 32-bit words should avoid those challenges. Keeping the Address Hash to a single 32-bit word should be of universal benefit. Allowing for OSC 1.1 style Address Pattern browsing could be a useful non-real-time feature for compatibility between smart systems, while still requiring fixed-width elements for the real-time performance messages.
As a side note, I'm wondering why OSC specifies sizes in bytes with the requirement that the size be a multiple of 4, when a better choice would be to specify sizes in words. That way, payloads can be 4 times as large and there aren't 2 wasted bits that would otherwise always be 0 in compliant data sets.
I assumed it was to potentially speed up low-level processing. Some processors require wide numbers to fall on word boundaries, so using a value in a message would require you to copy the value byte-by-byte to a word-aligned address before reading it if sizes were not a multiple of 4 (you still have the alignment problem for 64-bit ints and doubles, and I'm not sure how many processors are affected). You can find the end of a string 4 bytes at a time instead of 1 at a time, and there's the 4-byte-at-a-time hash computation trick as well. I don't think the designers were worried about payloads anywhere close to 32 bits.
Post by Brian Willoughby
Post by Roger Dannenberg
-Roger
Brian
_______________________________________________
OSC_dev mailing list
http://lists.create.ucsb.edu/mailman/listinfo/osc_dev
_______________________________________________
OSC_dev mailing list
http://lists.create.ucsb.edu/mailman/listinfo/osc_dev
Adrian Freed
2016-07-16 19:47:04 UTC
Permalink
Query protocols are hard to design and implement well, and I am concerned as I see them called on (as with this thread)
as the core solution to OSC application challenges.

We have been looking at queries again in the trillion device
IOT context where the difficulty is at the point of nearly impossible and may be impossible when you add security and trust concerns.

The heart of the difficulty is the assumption that remote registries can be maintained and queried reliably. In practice
one has to design on different assumptions: any node (and hoped for response) may fail at any time + the response to a query represents the state
of a registry some time in the past. It’s sobering to look at the solutions to these problems in the bluetooth, and newer wireless
protocols. A lot of ingenious hand-off and resynchronization is implemented to handle node failure.

My reflex (grounded in the “stateless” tradition) has been to step around queries by sending as much of the
context needed to process communicated values in the bundle with the values. This creates potentially larger payloads
but much less code to handle them.
IOhannes m zmoelnig
2016-07-18 07:43:18 UTC
Permalink
Post by Roger Dannenberg
We started by looking at Zeroconf, but then decided it would be hard for
people to configure
this deserves to go into one of those cool-quote signatures :-)
Post by Roger Dannenberg
Post by Brian Willoughby
As a side note, I'm wondering why OSC specifies sizes in bytes with
the requirement that the size be a multiple of 4, when a better choice
would be to specify sizes in words. That way, payloads can be 4 times
as large and there aren't 2 wasted bits that would otherwise always be
0 in compliant data sets.
I assumed it was to potentially speed up low-level processing. Some
processors require wide numbers to fall on word boundaries, so using a
value in a message would require you to copy the value byte-by-byte to a
word-aligned address before reading it if sizes were not a multiple of 4
(you still have the alignment problem for 64-bit ints and doubles, and
I'm not sure how many processors are affected). You can find the end of
a string 4 bytes at a time instead of 1 at a time, and there's the
4-byte-at-a-time hash computation trick as well. I don't think the
designers were worried about payloads anywhere close to 32 bits.
i think the questions was not about "why OSC uses 4 byte alignment"
(which afaic everybody agrees is a good thing™) but why the "size"
specifier (as found in #bundles and blobs) are counting bytes (which -
due to the alignment requirement - must always be multiples of 4; and
therefore have redundant information) rather than 32-bit words
(bytecount>>2).

fgsdm
IOhannes
Adrian Freed
2016-07-18 21:16:17 UTC
Permalink
I can’t remember why we chose to count bytes instead of 32-bit words. One can see benefits either way.
For example the redundency can be useful for error detection.
Post by IOhannes m zmoelnig
Post by Roger Dannenberg
We started by looking at Zeroconf, but then decided it would be hard for
people to configure
this deserves to go into one of those cool-quote signatures :-)
Post by Roger Dannenberg
Post by Brian Willoughby
As a side note, I'm wondering why OSC specifies sizes in bytes with
the requirement that the size be a multiple of 4, when a better choice
would be to specify sizes in words. That way, payloads can be 4 times
as large and there aren't 2 wasted bits that would otherwise always be
0 in compliant data sets.
I assumed it was to potentially speed up low-level processing. Some
processors require wide numbers to fall on word boundaries, so using a
value in a message would require you to copy the value byte-by-byte to a
word-aligned address before reading it if sizes were not a multiple of 4
(you still have the alignment problem for 64-bit ints and doubles, and
I'm not sure how many processors are affected). You can find the end of
a string 4 bytes at a time instead of 1 at a time, and there's the
4-byte-at-a-time hash computation trick as well. I don't think the
designers were worried about payloads anywhere close to 32 bits.
i think the questions was not about "why OSC uses 4 byte alignment"
(which afaic everybody agrees is a good thing™) but why the "size"
specifier (as found in #bundles and blobs) are counting bytes (which -
due to the alignment requirement - must always be multiples of 4; and
therefore have redundant information) rather than 32-bit words
(bytecount>>2).
fgsdm
IOhannes
_______________________________________________
OSC_dev mailing list
http://lists.create.ucsb.edu/mailman/listinfo/osc_dev
Roger Dannenberg
2016-07-18 21:49:48 UTC
Permalink
Post by IOhannes m zmoelnig
Post by Roger Dannenberg
We started by looking at Zeroconf, but then decided it would be hard for
people to configure
this deserves to go into one of those cool-quote signatures :-)
Yes, I wasn't even thinking about the self-referential nature of
configuring Zeroconf, but it runs as a separate process, normally a
system service, so on small systems or shared systems there could be a
lot of issues about permissions, priorities, who Zeroconf is talking to,
etc.
Post by IOhannes m zmoelnig
Post by Roger Dannenberg
Post by Brian Willoughby
As a side note, I'm wondering why OSC specifies sizes in bytes with
the requirement that the size be a multiple of 4, when a better choice
would be to specify sizes in words. That way, payloads can be 4 times
as large and there aren't 2 wasted bits that would otherwise always be
0 in compliant data sets.
I assumed it was to potentially speed up low-level processing. Some
processors require wide numbers to fall on word boundaries, so using a
value in a message would require you to copy the value byte-by-byte to a
word-aligned address before reading it if sizes were not a multiple of 4
(you still have the alignment problem for 64-bit ints and doubles, and
I'm not sure how many processors are affected). You can find the end of
a string 4 bytes at a time instead of 1 at a time, and there's the
4-byte-at-a-time hash computation trick as well. I don't think the
designers were worried about payloads anywhere close to 32 bits.
i think the questions was not about "why OSC uses 4 byte alignment"
(which afaic everybody agrees is a good thing™) but why the "size"
specifier (as found in #bundles and blobs) are counting bytes (which -
due to the alignment requirement - must always be multiples of 4; and
therefore have redundant information) rather than 32-bit words
(bytecount>>2).
A good example would be a MIDI sysex message, which is not required to
have a multiple-of-4 length. If you want to send 37 bytes as a blob, you
need a length count to say it's 37 bytes, not 10 4-byte words, even if
40 (or 44?) bytes are allocated in the OSC message.
Post by IOhannes m zmoelnig
fgsdm
IOhannes
_______________________________________________
OSC_dev mailing list
http://lists.create.ucsb.edu/mailman/listinfo/osc_dev
Brian Willoughby
2016-07-15 15:51:15 UTC
Permalink
Post by Thomas Brand
Post by Brian Willoughby
Since I let the cat out of the bag, I might as well describe my suggested
enhancement for OSC.
...
Post by Brian Willoughby
EXAMPLE
A simple OSC synthesizer with 128 notes and some continuous controllers
/spastic/machine/note/#type-signature
/spastic/machine/note/#hash (ii) 60 64
#reply (ss) '/spastic/machine/note/#type-signature' 'ii'
#reply (si) '/spastic/machine/note/#hash' 0
This would require a 'smart' OSC synthesizer which is totally fine.
What do you mean by 'smart'? Even an OSC synth that doesn't implement this proposal would have to be smart enough to do matching on the OSC Address Pattern. In other words, I don't see how any additional requirements for 'smartness' are added by this proposal. Additional code is required, but the same amount of smarts. Am I missing something?
Post by Thomas Brand
However when it comes to custom protocols defining a sequence of
communication (like: how to query the messages understood by synth? What
are the signatures?) this is outside of the scope of what OSC currently
defines in the standard. It's questionable if such (basic) procedures for
common use cases should be added or left to the applications to define and
handle.
Of course it's outside the scope. OSC 2.0 may need to change in order to be more widely adopted, and this proposal is hopefully adding something that's needed.
Post by Thomas Brand
Post by Brian Willoughby
/spastic/machine/note (ii) 60 64
0 60 64
(Note that OSC Bundles should continue to follow the established format,
with int32 size followed by a multiple of 4 bytes. Only the OSC Message
format would be extended to allow an Address Pattern to start with
something other than '/' or '#')
What you propose is to get a hash value per message incl. signature
understood by synth (kind of like a shortened alias in a generic sense).
There would be one hash per OSC Method. Once the hash is determined, OSC Messages could be sent multiple times using that same hash but with different OSC Arguments.
Post by Thomas Brand
The use of the hash would overthrow the OSC message format (no path, no
typetags). For the benefit of sending just bytes. If this is what embedded
systems can/want to do, then why not really just sending these bytes over
to the host without any OSC involved.
Good point. We'd have to call this format something. Why not OSC 2.0?
Rather than bytes, it's actually 32-bit words, something that is at the core of OSC (although sometimes the words can be interpreted as ASCII byte strings with padding).
Post by Thomas Brand
The idea of using hashes / aliases can be interesting nevertheless..
You could even think the other way around and let the sender make the
mapping of an alias to an existing (OSC) method. Then we are close to what
any intermediary does: translating between ~incompatible domains. Of
course the intermediary can be part of either side of the communication
session. But we should not make that a prerequisite to have per standard
IMHO.
If the sender does the mapping, then the receiver has a more complex job of finding the corresponding OSC Method. The hash lookup is designed to allow the embedded receiver to have its own efficient mapping that the sender must use. Meanwhile, the OSC Address Pattern and the Schmeder/Wright Query syntax can be used to discover all of the available Methods as well as their hashes.
Post by Thomas Brand
Greetings
Thomas
Brian
Thomas Brand
2016-07-15 18:32:45 UTC
Permalink
Post by Brian Willoughby
Post by Thomas Brand
Post by Brian Willoughby
Since I let the cat out of the bag, I might as well describe my
suggested enhancement for OSC.
...
Post by Brian Willoughby
EXAMPLE
A simple OSC synthesizer with 128 notes and some continuous
/spastic/machine/note/#type-signature
/spastic/machine/note/#hash (ii) 60 64
#reply (ss) '/spastic/machine/note/#type-signature' 'ii'
#reply (si) '/spastic/machine/note/#hash' 0
This would require a 'smart' OSC synthesizer which is totally fine.
What do you mean by 'smart'? Even an OSC synth that doesn't implement
this proposal would have to be smart enough to do matching on the OSC
Address Pattern. In other words, I don't see how any additional
requirements for 'smartness' are added by this proposal. Additional code
is required, but the same amount of smarts. Am I missing something?
Smart is not a good term to describe what i meant which is that an
implementing library must be able to answer to a hash requester and manage
a list of possible messages and already assigned hashes (if that's not a
static list which in that case could as well be transmitted previously by
other means to the calling process).
Post by Brian Willoughby
Post by Thomas Brand
However when it comes to custom protocols defining a sequence of
communication (like: how to query the messages understood by synth? What
are the signatures?) this is outside of the scope of what OSC
currently defines in the standard. It's questionable if such (basic)
procedures for common use cases should be added or left to the
applications to define and handle.
Of course it's outside the scope. OSC 2.0 may need to change in order to
be more widely adopted, and this proposal is hopefully adding something
that's needed.
The proposal to rip off any markup and make a contract on a skeleton via
hash is not a bad idea (and not new how we learned) but it should not be
called OSC in that case (making the difference with a change in version
number is even worse in that case IMHO).
'Packed OSC' or something ..
One basic feature would be that for every valid OSC message, a
non-ambiguous way to derive a hash is known.
Post by Brian Willoughby
Post by Thomas Brand
Post by Brian Willoughby
Future note events, subsequent to these queries, would no longer have
/spastic/machine/note (ii) 60 64
0 60 64
(Note that OSC Bundles should continue to follow the established
format, with int32 size followed by a multiple of 4 bytes. Only the
OSC Message
format would be extended to allow an Address Pattern to start with
something other than '/' or '#')
What you propose is to get a hash value per message incl. signature
understood by synth (kind of like a shortened alias in a generic sense).
There would be one hash per OSC Method. Once the hash is determined, OSC
Messages could be sent multiple times using that same hash but with
different OSC Arguments.
Post by Thomas Brand
The use of the hash would overthrow the OSC message format (no path, no
typetags). For the benefit of sending just bytes. If this is what
embedded systems can/want to do, then why not really just sending these
bytes over to the host without any OSC involved.
Good point. We'd have to call this format something. Why not OSC 2.0?
Rather than bytes, it's actually 32-bit words, something that is at the
core of OSC (although sometimes the words can be interpreted as ASCII
byte strings with padding).
Post by Thomas Brand
The idea of using hashes / aliases can be interesting nevertheless..
You could even think the other way around and let the sender make the
mapping of an alias to an existing (OSC) method. Then we are close to
what any intermediary does: translating between ~incompatible domains.
Of
course the intermediary can be part of either side of the communication
session. But we should not make that a prerequisite to have per
standard IMHO.
If the sender does the mapping, then the receiver has a more complex job
of finding the corresponding OSC Method. The hash lookup is designed to
allow the embedded receiver to have its own efficient mapping that the
sender must use. Meanwhile, the OSC Address Pattern and the
Schmeder/Wright Query syntax can be used to discover all of the available
Methods as well as their hashes.
I'd like to add another related topic. When thinking of possible
improvements of the OSC specification, the following would be wishful
(totally application-agnostic):

-query the implementing OSC library which version of the spec or which
parts it does support. In the most simple case this would be which
typetags are understood.

-query the implementing OSC library if it supports TCP

-query the implementing OSC library about maximum accepted payloads for
UDP and TCP messages

These three cases would all ask for a 'smart' or say a responsive
counterpart/library that delivers back these informations in a pre-defined
way, making the OSC spec more than a description on how to align datatypes
in a byte-per-byte way (which was chosen for good reason). The examples
are network-centric (while OSC is not bound to ethernet, just one of the
most obvious uses).

Finally aligned to existing types and parsers, add an optional type that
is a 'b' but has an additional bit of information attached saying "this
'b' is an OSC blob. parse like an OSC message". This could be denoted as
'o'.

Cheers
Tom
Post by Brian Willoughby
Post by Thomas Brand
Greetings
Thomas
Brian
Brian Willoughby
2016-07-15 19:51:21 UTC
Permalink
Post by Thomas Brand
Post by Brian Willoughby
Post by Thomas Brand
Post by Brian Willoughby
EXAMPLE
A simple OSC synthesizer with 128 notes and some continuous
/spastic/machine/note/#type-signature
/spastic/machine/note/#hash (ii) 60 64
#reply (ss) '/spastic/machine/note/#type-signature' 'ii'
#reply (si) '/spastic/machine/note/#hash' 0
This would require a 'smart' OSC synthesizer which is totally fine.
What do you mean by 'smart'? Even an OSC synth that doesn't implement
this proposal would have to be smart enough to do matching on the OSC
Address Pattern. In other words, I don't see how any additional
requirements for 'smartness' are added by this proposal. Additional code
is required, but the same amount of smarts. Am I missing something?
Smart is not a good term to describe what i meant which is that an
implementing library must be able to answer to a hash requester and manage
a list of possible messages and already assigned hashes (if that's not a
static list which in that case could as well be transmitted previously by
other means to the calling process).
Thanks for clarifying.

I should probably clarify my proposal. Hashes would not be dynamic, and thus they would not be assigned on the fly. There would be no need to manage a list of assigned hashes.

It might help if I contrast embedded implementation of standard OSC 1.1 and my proposed alteration.

OSC 1.1
An embedded system will have code for a finite number of OSC Methods
The embedded OSC Address Pattern parser will be hard-coded to map certain Patterns to Methods
Any Address Patterns which do not map to Methods will necessarily be ignored

OSC (2.0?) Fast Method Lookup
An embedded system will have code for a finite number of OSC Methods, each with a pre-determined hash value that is specific to the implementation. It could be as straightforward as the address of the Method function, or it could be an index into an array of OSC Method function pointers.
The embedded OSC Address Pattern parser will be hard-coded to map certain Patterns to Methods. If the /#hash query is detected at the end of an Address Pattern, the usual lookup to find the OSC Method can be performed and then the pre-determined hash will already be known. The hash can be returned just as easily as executing the Method.
Any Address Patterns which do not map to Methods will have a hash value of 0xFFFFFFFF
Post by Thomas Brand
Post by Brian Willoughby
Post by Thomas Brand
However when it comes to custom protocols defining a sequence of
communication (like: how to query the messages understood by synth? What
are the signatures?) this is outside of the scope of what OSC
currently defines in the standard. It's questionable if such (basic)
procedures for common use cases should be added or left to the
applications to define and handle.
Of course it's outside the scope. OSC 2.0 may need to change in order to
be more widely adopted, and this proposal is hopefully adding something
that's needed.
The proposal to rip off any markup and make a contract on a skeleton via
hash is not a bad idea (and not new how we learned) but it should not be
called OSC in that case (making the difference with a change in version
number is even worse in that case IMHO).
'Packed OSC' or something ..
I see your point, and I wouldn't want to cause confusion.

However, it seems to me that nearly all users of OSC are focused on the Address Pattern without any regard for how the message is actually formatted at the lowest levels. Particularly, no OSC user is directly exposed to the binary coding of OSC Arguments. In fact, OSC doesn't even have all of the OSI Layers defined. What I'm proposing is this: If OSC 2.0 could still appear to users as having the exact same Address Patterns of OSC 1.1, but the low-level communications data were based on address hashes, it should pose no problem. The scheme is backwards compatible. It should be equivalent to the way that DNS moved from more restrictive host name conventions to less restrictive - people just continued to use their web browsers without being aware of the low-level data communications changes.

Of course, if we can design a 32-bit, network order, typed communications standard with hashed addressing and call it something other than OSC, it doesn't really matter to me so long as it is adapted. If such a new, non-OSC standard could be agreed upon, it seems that there might be a way to allow OSC-style browsing of the parameter space, and OSC-style configuration of the message addressing control. My point is that if people interact with this new, non-OSC standard in the same ways that they're currently interacting with OSC 1.1, then why not call it OSC 2.0? Why would they care if a few details of the packed data have new options in addition to the old?
Post by Thomas Brand
One basic feature would be that for every valid OSC message, a
non-ambiguous way to derive a hash is known.
Yes, that is important. I think that I explained it above in this message, although I didn't explain it clearly in my proposal.

My proposal is to use the OSC Server as a sort of DNS (Domain Name Server). There would not be a universal way to map any arbitrary OSC Address Pattern to a specific hash. The requirement is that the OSC Client do an initial query to look up the hash for the particular embedded device (or any OSC Server, for that matter). Once the query response is obtained, the hash can be cached by the client for that particular server. A different OSC Server will most likely have a different hash for the same Address Pattern.

Now that you've challenged me to explain this in more detail, I'm realizing that this proposal improves the efficiency of an embedded OSC Server, but it might actually make embedded OSC Clients more difficult to implement. About the only way that an embedded OSC Client could work is to have the user enter Address Patterns during setup, and then have some sort of point in time where the user-provided Address Pattern is converted (internally) to the appropriate hash for the selected client. Note that changing the OSC Server -or- changing an assigned Address Pattern for a control would both require the hash query to be repeated and cached again. This can all happen behind the scenes without bothering the user.

Brian
Thomas Brand
2016-07-15 22:33:37 UTC
Permalink
Hi Brian,

to sum up the need for a hash (testing my understanding) is this:

-allow a receiving end to quickly map a message to an internal method
-fixed length 'header' -> known byte boundaries -> easy parsing
-no space wasted

By using a pre-defined, pre-shared list of hashes which both sender and
receiver must know. For a given hash, the parts formerly being the path
and typetag must be known or stored along the hash (say for initial
caching of hashes).

Could the following be an acceptable hybrid alternative?

-using standard OSC infrastructure with path '/' and type 'i' or 'h' as
the hash and b for the payload i.e.

/, i(b) -> call to hash i with b (how b is formatted is known via the
pre-defined, pre-shared list of hashes)

This would include:
-allow a receiving end to quickly map that message to an internal method
-fixed length 'header' -> known byte boundaries -> easy parsing
-only some space wasted

(This is not a proposal for a standard)

Greetings
Tom
Post by Brian Willoughby
Post by Thomas Brand
Post by Brian Willoughby
Post by Thomas Brand
Post by Brian Willoughby
EXAMPLE
A simple OSC synthesizer with 128 notes and some continuous
/spastic/machine/note/#type-signature
/spastic/machine/note/#hash (ii) 60 64
#reply (ss) '/spastic/machine/note/#type-signature' 'ii'
#reply (si) '/spastic/machine/note/#hash' 0
This would require a 'smart' OSC synthesizer which is totally fine.
What do you mean by 'smart'? Even an OSC synth that doesn't implement
this proposal would have to be smart enough to do matching on the OSC
Address Pattern. In other words, I don't see how any additional
requirements for 'smartness' are added by this proposal. Additional
code is required, but the same amount of smarts. Am I missing
something?
Smart is not a good term to describe what i meant which is that an
implementing library must be able to answer to a hash requester and
manage a list of possible messages and already assigned hashes (if
that's not a static list which in that case could as well be transmitted
previously by other means to the calling process).
Thanks for clarifying.
I should probably clarify my proposal. Hashes would not be dynamic, and
thus they would not be assigned on the fly. There would be no need to
manage a list of assigned hashes.
It might help if I contrast embedded implementation of standard OSC 1.1
and my proposed alteration.
OSC 1.1
An embedded system will have code for a finite number of OSC Methods
The embedded OSC Address Pattern parser will be hard-coded to map certain
Patterns to Methods
Any Address Patterns which do not map to Methods will necessarily be ignored
OSC (2.0?) Fast Method Lookup
An embedded system will have code for a finite number of OSC Methods, each
with a pre-determined hash value that is specific to the implementation.
It could be as straightforward as the address of the Method function, or
it could be an index into an array of OSC Method function pointers. The
embedded OSC Address Pattern parser will be hard-coded to map certain
Patterns to Methods. If the /#hash query is detected at the end of an
Address Pattern, the usual lookup to find the OSC Method can be performed
and then the pre-determined hash will already be known. The hash can be
returned just as easily as executing the Method. Any Address Patterns
which do not map to Methods will have a hash value of 0xFFFFFFFF
Post by Thomas Brand
Post by Brian Willoughby
Post by Thomas Brand
However when it comes to custom protocols defining a sequence of
communication (like: how to query the messages understood by synth? What
are the signatures?) this is outside of the scope of what OSC
currently defines in the standard. It's questionable if such (basic)
procedures for common use cases should be added or left to the
applications to define and handle.
Of course it's outside the scope. OSC 2.0 may need to change in order
to be more widely adopted, and this proposal is hopefully adding
something that's needed.
The proposal to rip off any markup and make a contract on a skeleton
via hash is not a bad idea (and not new how we learned) but it should
not be called OSC in that case (making the difference with a change in
version number is even worse in that case IMHO). 'Packed OSC' or
something ..
I see your point, and I wouldn't want to cause confusion.
However, it seems to me that nearly all users of OSC are focused on the
Address Pattern without any regard for how the message is actually
formatted at the lowest levels. Particularly, no OSC user is directly
exposed to the binary coding of OSC Arguments. In fact, OSC doesn't even
have all of the OSI Layers defined. What I'm proposing is this: If OSC
2.0 could still appear to users as having the exact same Address Patterns
of OSC 1.1, but the low-level communications data were based on address
hashes, it should pose no problem. The scheme is backwards compatible. It
should be equivalent to the way that DNS moved from more restrictive host
name conventions to less restrictive - people just continued to use their
web browsers without being aware of the low-level data communications
changes.
Of course, if we can design a 32-bit, network order, typed communications
standard with hashed addressing and call it something other than OSC, it
doesn't really matter to me so long as it is adapted. If such a new,
non-OSC standard could be agreed upon, it seems that there might be a way
to allow OSC-style browsing of the parameter space, and OSC-style
configuration of the message addressing control. My point is that if
people interact with this new, non-OSC standard in the same ways that
they're currently interacting with OSC 1.1, then why not call it OSC 2.0?
Why would they care if a few details of the packed data have new options
in addition to the old?
Post by Thomas Brand
One basic feature would be that for every valid OSC message, a
non-ambiguous way to derive a hash is known.
Yes, that is important. I think that I explained it above in this
message, although I didn't explain it clearly in my proposal.
My proposal is to use the OSC Server as a sort of DNS (Domain Name
Server). There would not be a universal way to map any arbitrary OSC
Address Pattern to a specific hash. The requirement is that the OSC
Client do an initial query to look up the hash for the particular
embedded device (or any OSC Server, for that matter). Once the query
response is obtained, the hash can be cached by the client for that
particular server. A different OSC Server will most likely have a
different hash for the same Address Pattern.
Now that you've challenged me to explain this in more detail, I'm
realizing that this proposal improves the efficiency of an embedded OSC
Server, but it might actually make embedded OSC Clients more difficult to
implement. About the only way that an embedded OSC Client could work is
to have the user enter Address Patterns during setup, and then have some
sort of point in time where the user-provided Address Pattern is
converted (internally) to the appropriate hash for the selected client.
Note that changing the OSC Server -or- changing an assigned Address
Pattern for a control would both require the hash query to be repeated
and cached again. This can all happen behind the scenes without bothering
the user.
Brian
_______________________________________________
OSC_dev mailing list
http://lists.create.ucsb.edu/mailman/listinfo/osc_dev
Hanspeter Portner
2016-07-15 22:01:18 UTC
Permalink
Post by Thomas Brand
I'd like to add another related topic. When thinking of possible
improvements of the OSC specification, the following would be wishful
-query the implementing OSC library which version of the spec or which
parts it does support. In the most simple case this would be which
typetags are understood.
-query the implementing OSC library if it supports TCP
-query the implementing OSC library about maximum accepted payloads for
UDP and TCP messages
Some of this is kind of defined in OSC 1.1 to be discovered via DNS-SD
for network devices or via the device descriptor for USB devices [1].

[1] http://cnmat.berkeley.edu/system/files/attachments/Nime09OSCfinal.pdf
Loading...