Implementation and Performance Issues with OpenSound Control
The OpenSound Control Kit
http://www.cnmat.berkeley.edu/OSC/Kit
Matthew Wright
CNMAT, UC Berkeley
www.cnmat.berkeley.eduAcknowledgements
Adrian Freed, Sami Khoury, and Amar Chaudhary helped design the Kit.
Mike Berry and Chris Brown used a pre-alpha version of the Kit and helped me debug it.
Outline
What is OpenSound Control, and why?
Features of OpenSound Control
The OpenSound Control Kit implements most of these features
How to use the Kit (code examples)
Efficiency analysis
Conclusions
What is OpenSound Control?
A protocol for communication among computers, sound synthesizers, and other multimedia devices
Not a physical communications medium
Not a prescribed set of messages and features
See Freed and Wright "OpenSound Control: A New Protocol for Communicating with Sound Synthesizers," ICMC 97.
Sample OSC Address Space
OSC Address Patterns
Address pattern
/sine*/gSample OSC Message
Sample OSC Bundle
Features of OpenSound Control
Time Tags
Guaranteed atomicity
Arbitrary hierarchical namespace
Regular expression message patterns
Queries
32-bit integer, 32-bit floating point, and ASCII string argument data
The OpenSound Control Kit!
What Is the OSC Kit?
A developers kit for adding OpenSound Control to a reactive real-time system
A C library implementing features of OSC
An API your program calls
Instructions for adding OSC to your system
Platform-independent
Available at no cost
What You Need to Know to Use the Kit
The OSC Kit implements as many of the features of your OSC-addressable application as possible
How OSC messages get to your application
How to define your applications address space
How to integrate the OSC Kit into your applications control flow
How you provide memory for the OSC Kit
The Life of an OSC Packet
Callback Procedures
You write callback procedures that the Kit will invoke when a message is to take effect.
typedef struct {
float f; /* frequency */
float g; /* gain */
} SineState;
void FreqCB(void *vstate,
int arglen,
const void *vargs,
OSCTimeTag when,
NetworkAddressPtr ra) {
SineState *state = vstate;
const float *args = vargs;
if (arglen == 4) {
state->f = args[0];
} else {
OSCWarning("Bad arguments");
}
}
Building the Address Space
The OSC address space is just a tree that you build (dynamically) node by node.
void InitOSCAddrSpace(void) {
SynthState *v1, *v2;
OSCcontainer root, sine1, sine2;
v1 = NewSynthState();
v2 = NewSynthState();
root = OSCInitAddressSpace();
sine1= OSCNewContainer("sine1", root);
sine2= OSCNewContainer("sine2", root);
OSCNewMethod("f", sine1, FreqCB, v1);
OSCNewMethod("f", sine2, FreqCB, v2);
OSCNewMethod("g", sine1, GainCB, v1);
OSCNewMethod("g", sine2, GainCB, v2);
OSCNewMethod("quit", root, QuitCB, 0);
}
PacketBuffer Objects
The Kit manages a pool of PacketBuffer objects that you use to store incoming network packets.
A PacketBuffer contains
A buffer
A size count
A return address
Your job:
Ask the Kit for a PacketBuffer
Fill it with data from the network
Hand the filled PacketBuffer to the Kit
Example: Receiving Packets from UDP
struct NetworkAddressStruct {
struct sockaddr_in addr;
int len;
int sockfd;
};
void ReceivePacket(int sockfd) {
OSCPacketBuffer pb;
char *buf;
struct NetworkAddressStruct *ra;
int n, *sizep;
int capacity = OSCReceiveBufferSize();
pb = OSCAllocPacketBuffer();
buf = OSCPacketBufferGetBuffer(pb);
sizep = OSCPacketBufferGetSize(pb);
ra =OSCPacketBufferGetClientAddr(pb);
ra->len = sizeof(struct sockaddr_in);
ra->sockfd = sockfd;
n=recvfrom(sockfd, buf, capacity, 0,
&(ra->addr), &(ra->len));
if (n > 0) {
*sizep = n;
OSCAcceptPacket(pb);
} else {
OSCFreePacket(pb);
}
}
Main Control Loop of a Synthesizer
while (1) {
OSCTimeTag now = CurrentTime();
while (!WeAreReallyLate()) {
if (!OSCInvokeMsgsThatAreReady(now))
break;
}
SynthesizeSomeSound();
while (NetworkPacketWaiting()) {
ReceivePacket(p);
}
while (NothingElseToDo()) {
if (!OSCBeProductiveWhileWaiting())
break;
}
}
OSC Kit Memory Usage
No malloc()!
Internally-managed pools for each object type
Preallocates objects at initialization time
Uses your real-time memory allocator if you have one
Memory for the Address Space
You pass memory allocation procedures to the Kit at initialization time.
void *MyInitTimeMalloc(int numBytes) {
return malloc(numBytes);
}
void *MyRealTimeMalloc(int numBytes) {
return 0;
}
void InitOSCAddrSpace(void) {
OSCcontainer root, sine1, sine2;
struct OSCAddressSpaceMemoryTuner t;
t.initNumContainers = 20;
t.initNumMethods = 20;
t.InitTimeMemoryAllocator =
MyInitTimeMalloc;
t.RealTimeMemoryAllocator =
MyRealTimeMalloc;
root = OSCInitAddressSpace(&t);
/* Create containers and methods */
}
Memory for Receiving and Parsing
void InitOSCReceive() {
struct OSCReceiveMemoryTuner rt;
rt.InitTimeMemoryAllocator =
MyInitTimeMalloc;
rt.RealTimeMemoryAllocator =
MyRealTimeMalloc;
rt.receiveBufferSize = 1000;
rt.numReceiveBuffers = 100;
rt.numQueuedObjects = 200;
rt.numCallbackListNodes = 100;
OSCInitReceive(&rt);
}
Efficiency
Address space operations: O(1)
Internal memory management: O(1)
Heap-based priority queue: O(log(n))
All parsing is done in-place; no copies.
Tuned for cache/memory locality
Background processing can happen during otherwise idle moments.
Conclusions
OpenSound Control has musically useful high-level features: unbounded address space, time tags, pattern matching, guaranteed atomicity
The OSC Kit implements most of these features for you
The OSC Kit is efficient and designed for reactive real-time systems
Download the Kit and add OpenSound Control to your application today!
http://www.cnmat.berkeley.edu/OSC/Kit