wiki:pyOSC
Last modified 4 years ago Last modified on 04/19/10 13:39:11

pyOSC

A Simple OpenSoundControl implementation, in Pure Python. This module is loosely based on the good old SimpleOSC implementation by Daniel Holth & Clinton McChesney.

It has been mostly rewritten, and a whole set of new Classes has been added, providing support for OSC-bundles, a simple OSC-client, a simple OSC-server, threading & forking OSC-servers and a more complex 'Multiple-Unicast' OSC-client that supports subscriptions and OSC-address based message-filtering.

Current maintainer of the v2_lab version of pyOSC is artm, the latest development source code tree is hosted at gitorious.

Download

Get the source-archives from here:

Or have a look at the git repository

Documentation

For the full documentation, download the file above, unzip/untar it, and run 'pydoc OSC' from within the 'pyOSC' directory.
But here's a summary of the Classes and supported features:

OpenSoundControl

OpenSoundControl is a network-protocol for sending (small) packets of addressed data over network sockets. This OSC-implementation uses the UDP/IP protocol for sending and receiving packets. (Although it is theoretically possible to send OSC-packets over TCP, almost all known implementations use UDP)
UDP is a 'connectionless' protocol, which means that an OSC-client can send a message at any time, to any UDP-port at any IP-address. If there is no OSC-server listening on that port at that address, the message will just get 'lost'. Even if there is an OSC-server there to receive the message, the OSC-protocol does not require the server to send some sort of 'acknowledge' message back, so normally, a client cannot know whether its message has been received or not.
Of course it is possible to have the server send any sort of reply back to the client, but this is not required by the OSC protocol. Also note that such a reply requires the 'server' to act as a client, and for the 'client' to act as a server, as explained later on.

OSC-message & OSC-bundle

OSC-packets come in two kinds:

  • OSC-messages consist of an 'address'-string (not to be confused with a (host:port) network-address!), followed by a string of 'typetags' associated with the message's arguments (ie. 'payload'), and finally the arguments themselves, encoded in an OSC-specific way.
    The OSCMessage class makes it easy to create & manipulate OSC-messages of this kind in a 'pythonesque' way. (That is, OSCMessage-objects behave a lot like lists)
  • OSC-bundles are a special type of OSC-message containing only OSC-messages as 'payload'. Recursively. (Meaning; an OSC-bundle could contain other OSC-bundles, containing OSC-bundles, etc.) OSC-bundles start with the special keyword '#bundle' and do not have an OSC-address. (but the OSC-messages a bundle contains will have OSC-addresses!) Also, an OSC-bundle can have a timetag, essentially telling the receiving Server to 'hold' the bundle until the specified time.
    The OSCBundle class allows easy creation & manipulation of OSC-bundles.


see also OSC specification 1.0

OSC-client & OSC-server

To send OSC-messages, you need an OSC-client, and to receive OSC-messages you need an OSC-server.
In most cases, it is practical for an OSC-server to also have an OSC-client, so that it can send replies back to the remote clients it receives OSC-messages from. The remote clients, in turn, should have an OSC-server as well, so that they can actually receive the replies from the other server's client. As such, the distinction 'client' / 'server' becomes a bit blurred. Both sides of the conversation tend to incorporate both functionalities.
The OSCClient class/object uses an 'AF_INET / SOCK_DGRAM' type socket (see the 'socket' module) to send binary representations of OSC-messages to a remote host:port address.
The OSCServer class listens on an 'AF_INET / SOCK_DGRAM' type socket bound to a local port, and handles incoming requests. Either one-after-the-other (OSCServer) or in a multi-threaded / multi-process fashion (ThreadingOSCServer / ForkingOSCServer). If the server has a callback-function (a.k.a. handler-function) registered to 'deal with' (i.e. handle) the received message's OSC-address, that function is called, passing it the (decoded) message.
The different OSC-servers implemented here all support the (recursive) un-bundling of OSC-bundles, and OSC-bundle timetags.

In fact, this implementation supports:

  • OSC-messages with 'i' (int32), 'f' (float32), 's' (string) and 'b' (blob / binary data) types
  • OSC-bundles, including timetag-support
  • OSC-address patterns including '*', '?', '{,}' and '[]' wildcards.

(please do read the OSC specification! it explains what these things mean.)

In addition, the OSCMultiClient supports:

  • Sending a specific OSC-message to multiple remote servers
  • Remote server subscription / unsubscription (through OSC-messages, of course)
  • Message-address filtering.


On the network level, as mentioned above, OSC-messages are sent to a specific IP-address & UDP-port combination. In addition, on the OSC-protocol level, OSC-messages have an OSC-address, which should not be confused with an IP- or network-address. One OSC-server at a given IP-address and listening on a given port could handle messages with a great number of different OSC-addresses. Whenever an OSC-server can receive and do something useful with the contents of messages addressed to a specific OSC-address, we say that the OSC-server can 'handle' messages to that OSC-address. In fact, the bits of software that get executed in response to an incoming OSC-message, and which do 'something useful' with the message contents, are called 'message-handler functions'

In order to prepare an OSC-server to receive messages for a certain OSC-address, we have to define a message-handler function for the messages we will be expecting on that address, and then associate the OSC-address with the message-handler function. This last step is called 'registering a message-handler (for an address)'

The OSC-addresses a server has registered from a hierarchical tree, just like directories on a hard-disk or URLs on a webserver, with the levels of the hierarchy separated by the slash '/' character. Valid OSC-addresses, for example, are:

/
/info
/dmx/channel

Client Subscription

The OSCMultiClient supports 'multiple unicasting' of OSC-messages. This means that a given OSC-message can be sent to multiple servers, each with their own IP-address. This is different from sending OSC-messages to a specific multicast or broadcast IP-address (which is also supported, of course). If you want to have multiple clients controlling, and receiving replies from, one OSC-server, it's difficult to say which approach, multiple-unicast, multicast or broadcast, is 'best'. This depends mostly on your network-topology. multicasting is not supported by all routers, and broadcasting generally only works within your local subnet, but both of these methods produce less network-traffic than multiple-unicasting.

To allow for dynamic configuration of which remote clients will receive reply-messages from the OSCMultiClient, an OSC-server that is bound to an OSCMultiClient supports client subscription. By sending messages to the server's '/subscribe' and '/unsubscribe' OSC-addresses, clients can (un)subscribe themselves (or any other client) to receive reply-messages from the OSCMultiClient.

Classes

Objects:

  • OSCRequestHandler(SocketServer.DatagramRequestHandler) : A Request-Handler for the OSCServer class
    • ThreadingOSCRequestHandler(OSCRequestHandler) : A Multi-threaded OSCRequestHandler for the ThreadingOSCServer & ForkingOSCServer classes
  • OSCServer(SocketServer.UDPServer) : A Synchronous OSC-server. Serves one request at-a-time, until the OSCServer is closed. The OSC address-pattern is matched against a set of OSC-adresses that have been registered to the server with a callback-function. If the adress-pattern of the message machtes the registered address of a callback, that function is called.
    • ForkingOSCServer(SocketServer.ForkingMixIn, OSCServer) : An Asynchronous OSCServer. This server forks a new process to handle each incoming request.
    • ThreadingOSCServer(SocketServer.ThreadingMixIn, OSCServer) : An Asynchronous OSCServer. This server starts a new thread to handle each incoming request.
  • OSCClient(object) : A simple OSC-client. Handles the sending of OSC-packets (OSCMessage or OSCBundle) via a UDP-socket.
    • OSCMultiClient(OSCClient) : A 'Multiple-Unicast' OSC-client. Handles the sending of OSC-packets via a UDP-socket. This client keeps a dict of 'OSCTargets'. and sends each OSC-message to each OSCTarget.
      The OSCTargets are simply (host, port) tuples, and may be associated with an OSC-address prefix. The OSCTarget's prefix gets prepended to the OSC-address pattern of each OSC-message sent to that target.
  • OSCMessage(object) : Builds typetagged OSC messages. OSCMessage objects are container objects for building OSC-messages. On the 'front' end, they behave much like list-objects, and on the 'back' end they generate a binary representation of the message, which can be sent over a network socket.
    • OSCBundle(OSCMessage) : Builds a 'bundle' of OSC-messages. OSCBundle objects are container objects for building OSC-bundles of OSC-messages. An OSC-bundle is a special kind of OSC-message which contains a list of OSC-messages. (And yes, OSC-bundles may contain other OSC-bundles...)

Exceptions:

  • OSCError(exceptions.Exception) : Base Class for all OSC-related errors
    • OSCClientError(OSCError) : Class for all OSC-client errors
      • NotSubscribedError(OSCClientError) : This error is raised (by an OSCMultiClient) when an attempt is made to unsubscribe a host that isn't subscribed.
    • OSCServerError(OSCError) : Class for all OSC-server errors
      • NoCallbackError(OSCServerError) : This error is raised (by an OSCServer) when an OSC-message with an 'unmatched' address-pattern is received, and no 'default' handler is registered.

ChangeLog

  • v0.3.0 - 27 Dec. 2007
    • Started out to extend the 'SimpleOSC' implementation (v0.2.3) by Daniel Holth & Clinton McChesney.
    • Rewrote OSCMessage
    • Added OSCBundle

  • v0.3.1 - 3 Jan. 2008
    • Added OSClient
    • Added OSCRequestHandler, loosely based on the original CallbackManager
    • Added OSCServer
    • Removed original CallbackManager
    • Adapted testing-script (the if __name__ == "__main__": block at the end) to use new Server & Client

  • v0.3.2 - 6 Jan. 2008
    • Added 'container-type emulation' methods (getitem(), setitem(), __iter__() & friends) to OSCMessage
    • Added ThreadingOSCServer & ForkingOSCServer
    • Added OSCMultiClient
    • Added command-line options to testing-script (try 'python OSC.py --help')
  • v0.3.3 - 9 Jan. 2008
    • Added OSC-timetag support to OSCBundle & OSCRequestHandler
    • Added ThreadingOSCRequestHandler

  • v0.3.4 - 13 Jan. 2008
    • Added message-filtering to OSCMultiClient
    • Added subscription-handler to OSCServer
    • Added support fon numpy/scipy int & float types. (these get converted to 'standard' 32-bit OSC ints / floats!)
    • Cleaned-up and added more Docstrings
  • v0.3.4-4954 - 31 Jan. 2008
    • Created setup.py
    • Registed the 'pyOSC' package with PyPI. It's official now...
  • v0.3.4-4993 - 8 Feb. 2008
    • Added OSCServer.sendOSCinfo() method (analogous to OSCServer.sendOSCerror())
    • These methods now send multi-line texts as OSC-bundles of one-line OSC-messages
  • v0.3.4-4996 - 11 Feb. 2008
    • Fixed typo in OSCServer.sendOSCinfo() & OSCServer.sendOSCerror()
  • v0.3.5-5294 - 14 aug. 2008
    • Added OSCServer.reportErr(...) method

Attachments