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.edu

Acknowledgements

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*/g
matches
/sine1/g and /sine2/g

Sample 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 developer’s 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 application’s address space

• How to integrate the OSC Kit into your application’s 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