Search guides and the APICtrl K
  1. API reference
  2. MIDI and OSC

osc.h

Open Sound Control.

#include <sonore/osc.h>
On this page

Design notes

The protocol control surfaces speak: TouchOSC, Lemur, Open Stage Control, lighting desks, anything that wants to move a parameter from another machine without MIDI's 7-bit ceiling.

OSC 1.0, the part that is actually used: messages with int32, float32, string and blob arguments, and bundles. Everything is 4-byte aligned and BIG-endian, which is the single most common source of "it works on my machine" in a hand-rolled implementation, so the alignment and byte order are asserted in the tests rather than assumed.

The codec has no networking in it at all: encode()/decode() move bytes, and the UDP socket below is a separate, optional convenience. That split is what lets the codec be tested exhaustively without a network.

Types 7

The 'r' argument: a 32-bit colour, red in the high byte and alpha in the low, big-endian on the wire like every other OSC word.

Its own type rather than an int an application has to take apart, because the byte order is exactly the thing that gets taken apart wrongly: ARGB habits from every graphics API put alpha on top, and a lighting desk sent a colour read that way shows a different one.

One OSC argument: its type tag and its value.

Flat rather than a union: every value field is there, and type says which one is meant -- intValue, floatValue, stringValue and blobValue for the four core types, and the comment on each other Type names its field. The make functions set both, and decodeMessage() fills both, so read the field the type names and no other: an Int32 leaves floatValue at 0, it does not convert. A plain value that allocates for strings and blobs, so building one is not for the audio thread.

One OSC message: an address starting with '/' and its arguments in order. What encode() writes into a datagram and decodeMessage() and decodePacket() read out of one -- a bundle arrives as the messages inside it. encode() refuses an address without the leading '/' by returning no bytes.

An OSC address pattern, parsed and checked once so that matching it against many addresses -- every listener a receiver has -- does not re-parse it for each.

Hands each message to the listeners its address pattern reaches.

Exact matching is still the FAST path: a message whose address has no wildcard in it -- nearly all of them -- is one map lookup, exactly as before patterns existed. Only a pattern pays for a scan, and it is parsed once for the whole scan rather than once per listener.

On whatever thread calls dispatch(), which for a Receiver is the thread that polls it: the UI timer, never the audio thread -- a handler is a std::function and may do anything.

A non-blocking UDP receiver. poll() returns whatever arrived since the last call and never waits, so it is safe to call from a UI timer.

Fire-and-forget UDP sender.

Functions 5

Enums 1

↑↓ to move↵ to openEvery public name in the headers, and every guide