Open Sound Control

Open Sound Control (OSC) is an open, transport-independent, message-based protocol developed for communication among computers, sound synthesizers, and other multimedia devices.” (from http://opensoundcontrol.org/spec-1_0)

There are a number of OSC implementations, including a couple in C# (1, 2), but I decided to roll my own and make it freely available. This implementation sits atop the .NET 3.5 Framework and uses TCP or UDP as the transport protocol. It includes support for OSC Messages and Bundles, and supports the following payload data types: Int32, Int64, Float, Double, String, and Blob (byte array).

The OscServer class is at the heart of the system, and includes support for unicast, broadcast, and multicast. As OSC packets, bundles, and messages are received corresponding events are fired. Additionally, the system will (optionally) filter OSC address patterns that the user registers.

The package includes simple client and server examples.

Downloads (Current Version: 1.3.0.0 - Release Date: 7/28/2009)

Previous Revisions

18 Comments so far

  1. [...] Open Sound Control [...]

  2. ben on February 17th, 2009

    I’ve been looking for an open source OSC library for .net that works well. You made my day!

  3. Paul on February 17th, 2009

    Thanks Ben. I appreciate the positive feedback.

    Paul

  4. Rich on May 9th, 2009

    This is working great!

    Only thing was I found that the UDP listener thread was polling for available bytes and using 100% CPU in UdpServer.Start.

    I commented that out as below so it block in Receive until packets arrive. Any reason this shouldn’t work?

    while (mAcceptingConnections)
    {
    // This was eating CPU, so block until we get data
    // if (udpClient.Available > 0)
    // {
    IPEndPoint remoteEndPoint = null;
    byte[] data = udpClient.Receive(ref remoteEndPoint);
    if (data != null && data.Length > 0)
    {
    OnDataReceived(new UdpDataReceivedEventArgs(remoteEndPoint, data));
    }
    // }
    }

  5. Paul on May 9th, 2009

    Hi Rich,

    You’re right, doing a spin like that was a poor decision — and guaranteed to peg the CPU. The problem with not checking the udpClient.Available property and simply blocking on the Receive call, is that you can’t interrupt the call — even with a Thread.Abort or Thread.Interrupt. This means, that calling Stop on the UdpServer, which toggles the volatile bool mAcceptingConnections, won’t do anything until the Receive call finishes. If the UdpServer is continuously receiving data, you’ll never notice this. But if not, the Receive call will never finish and the Stop won’t actually stop.

    So a better route is a paired BeginReceive/EndReceive call for asynchronous communication without a while loop. I’ve written this and will upload a new release shortly.

    Paul

  6. [...] Open Sound Control [...]

  7. Paul on May 11th, 2009

    Rich,

    I’ve uploaded a new release which addresses the performance issue. Thanks for the heads up.

    Paul

  8. Chris on May 21st, 2009

    Hey Paul,

    I’ve encountered an unhandled error exception in the UdpServer object used in the client on line 276:

    byte[] data = udpClient.EndReceive(asyncResult, ref ipEndPoint);

    An unhandled exception of type ‘System.Net.Sockets.SocketException’ occurred in System.dll

    Additional information: The I/O operation has been aborted because of either a thread exit or an application request

    I corrected it by blocking lines 276 to 280 and catching the socket exception, while leaving the receive function call in the original try catch.

    Is there any reason why this shouldn’t work for continual use?

    Chris

  9. Paul on May 21st, 2009

    Hi Chris,

    I haven’t encountered that error in any of my testing. The EndReceive() call is paired with the BeginReceive() calls that originate in the Start() method and are continued after each EndReceive() completes. I assume what you mean by “blocking out lines 276-280″ is that you’re commenting out those calls — and within that the OnDataReceived() call is pretty critical. Also, if you’ve changed BeginReceive() to Receive(), you’re changing the behavior from asynchronous communication to a blocking call.

    In short, I’m not entirely clear on why you’re seeing the error and what your fix entails. If you’d like to send me your changes, I’d be happy to take a look. My email address is pvarchol [at] bespokesoftware.org.

    One last thought… are you interrupting the UdpServer thread using a Thread.Kill() anywhere? And is this exception happening in the included demo client/server?

    Paul

  10. Paul on May 22nd, 2009

    Chris,

    I’ve found the problem. With the 1.1 release of the library I changed to asynchronous communication using BeginReceive()/EndReceive(). However, I didn’t change the OscServer.Start() method — which called the underlying UdpServer.Start() in a separate thread. This was necessary in version 1.0 because the UdpServer didn’t use asynchronous communication and I didn’t want to block on the Start() call.

    When I went to asynchronous communication, I left the Start call in its own thread, but this thread now exists very rapidly. I found a 1-line blurb about this at: http://msdn.microsoft.com/en-us/library/dxkwh6zw.aspx which states that

    “All I/O initiated by a given thread is canceled when that thread exits. A pending asynchronous operation can fail if the thread exits before the operation completes.”

    Interestingly, I didn’t see this on my development machine — but when I tested this on a slightly faster computer the SocketException with error code 995 cropped up. This is the nature of thread exceptions and concurrent code — sometimes the bugs are intermittent. Anyhow, I’ve fixed the problem simply by making OscServer.Star()t call UdpServer.Start() in the OscServer thread. I’ve released version 1.2 of the library which includes this change.

    Paul

  11. Chris on May 22nd, 2009

    Paul,

    Thank you for the quick response and solution! Much appreciated. I’ll continue playing with it today :)
    Chris

  12. [...] Open Sound Control [...]

  13. bobo on August 7th, 2009

    how do you reference an appended value to an oscmessage from OscMessageReceivedEventArgs

    Message.append(”Hello”);

  14. Paul on August 8th, 2009

    You reference all appended values through the Data property — an array of objects. For example: mMessage.Data[index].

  15. Frank on January 6th, 2010

    Hi,
    thank you very much for providing this library.
    Is there any other example application (demo) available? Or a more detailled description of the included demo?

    Who can help me to understand what happens here in the demo (server)? :
    sMessage.Append(memoryStream.ToArray());

    Why the server creates the OSC message?

    Sorry for my stupid questions, but I would like to understand this application.

    Thanks
    Best regards Frank

  16. Paul on January 7th, 2010

    Hi Frank,

    The OSC message is created before the statement you’re referencing. Specifically, the message is created on line 24 of Program.cs with the following:

    sMessage = new OscMessage(new IPEndPoint(IPAddress.Loopback, Port), AliveMethod, sOscClient);

    That statement constructs a new OscMessage instance with the source endpoint of the local computer (IPAddress.Loopback and a port number), an OSC address (AliveMethod == “/osctest/alive”), and an OscClient instance if the transport type is TCP/IP (default is UDP).

    With UDP, the OscMessage is transmitted with sOscMessage.Send(Destination). Using TCP it’s simply sOscMessage.Send().

    The statement you asked about:

    sMessage.Append(memoryStream.ToArray());

    Appends a piece of data to the message. In this demo, that piece of data is a JPEG image that gets loaded into a MemoryStream object. The MemoryStream class has a ToArray() method that converts its contents to a byte array. The Bespoke OSC Library supports byte arrays, integers (32- and 64-bit) , floats, doubles, and strings. Thus, the OscMessage.Append() method is a generic method that accepts all of these data types.

    Hope this helps.

    Paul

  17. roxlu on January 27th, 2010

    Hi! Thanks for this perfect and clean implementation.. though I’m wondering how I can create a UDP client which listens on port 3333?

    I’ve been looking at the samples but found it a bit confusing that the ‘client’ examples creates a OscServer object and that the ’server’ example creates a OscClient object.. ?

    Did I miss something?

    Thanks
    Roxlu

  18. Paul on January 27th, 2010

    Hi Roxlu,

    I know, the terms “client” and “server” are a bit confusing. This terminology comes from the OSC specification (http://opensoundcontrol.org/spec-1_0). It states: “The unit of transmission of OSC is an OSC Packet. Any application that sends OSC Packets is an OSC Client; any application that receives OSC Packets is an OSC Server.”

    So, if you’re trying to create an application that listens for OSC packets, you’ll create a Bespoke.Common.Osc.OscServer object. In my OscDemo project (included with the distribution) I name the two sub-projects Client and Server with the more traditional meaning of the terms. In this demo, the “Client” project listens for OSC packets and the “Server” project sends them.

    Creating an OscServer object for UDP, bound to the loopback address (127.0.0.1) and port 3333 is as simple as:

    OscServer oscServer = new OscServer(TransportType.Udp, IPAddress.Loopback, 3333);

    Then you’ll attach a handler to the oscServer.MessageReceived event and start the server with oscServer.Start();

    Hope this helps.

    Paul

Leave a reply