Learn how to harness the Open Sound Control protocol to connect several applications together over a network. Send and receive interaction data between applications.
Level: Intermediate
Platforms: Windows , macOS , Linux , iOS , Android
Classes: OSCSender, OSCReceiver, OSCReceiver::Listener, OSCReceiver::ListenerWithOSCAddress, OSCMessage, OSCBundle
There are several demo projects to accompany this tutorial. Download links to these projects are provided in the relevant sections of the tutorial.
If you need help with this step in each of these sections, see Tutorial: Projucer Part 1: Getting started with the Projucer.
The demo projects provided with this tutorial present different applications necessary for OSC interactions. In summary, these applications are:
Download the demo project for this section here: PIP | ZIP . Unzip the project and open the first header file in the Projucer.
When completed, the OSC sender application will display a single rotary knob that we can interact with when launched:
Let's start by implementing the sender application.
In the MainContentComponent
class, start by declaring private member variables for this application like so:
Add a Slider object to capture user interactions [1] and an OSCSender object to connect to a receiver later on [2] .
In the class constructor, set the slider parameters and attempt to connect to the network like follows:
connect()
function on the OSCSender object. If the connection fails, we call the private function showConnectionErrorMessage()
declared later to display the error message to the user.Next, implement the Slider::onValueChange callback for the Slider object to send the OSC message to the receiver application like this:
In the lambda function, send the OSC message using the send()
function on the OSCSender object with the address and the rotary knob value [5] . The address provided here as "/juce/rotaryknob" allows us to classify messages in the receiver later on. If the message fails to send, we call the same helper function showConnectionErrorMessage()
to display the error.
We implement this function by displaying the error text in a dialog box asynchronously using the function AlertWindow::showMessageBoxAsync()
:
The sender implementation is now complete.
OSCSenderTutorial_02.h
file of the demo project.Download the demo project for this section here: PIP | ZIP . Unzip the project and open the first header file in the Projucer.
When completed, the OSC receiver application will display a single rotary knob that we cannot interact with when launched:
To implement the receiver, simply declare a Slider object in MainContentComponent
class to reflect the changes made with the sender rotary knob:
Instead of declaring an OSCSender object as a member like in the sender application, we inherit this time from OSCReceiver to implement the MainContentComponent as a subclass [1] :
Also we need to inherit from OSCReceiver::ListenerWithOSCAddress<OSCReceiver::MessageLoopCallback> in order to receive callbacks when messages are received [2] .
In the class constructor, set the same parameters for the Slider object as in the sender application:
As a subclass to OSCReceiver, directly connect to the correct port number to receive messages [3] and register this class as a listener to itself by providing the address to listen to [4] .
When an OSCMessage object is received, the oscMessageReceived()
callback function is called:
Override this function by first checking the size of the message and the value type [5] . Then set the value of the rotary knob by making sure that the range conforms to the Slider range [6] .
Implement the same error logging function as the sender application in case the connection fails:
You should now be able to control the receiver knob by moving the sender knob when both applications are launched.
OSCReceiverTutorial_02.h
file of the demo project.Download the demo project for this section here: PIP | ZIP . Unzip the project and open the first header file in the Projucer.
When completed, the OSC monitor application will display a window that logs the interactions. When first launched without a sender instance, the monitor application cannot connect and the window should look something like this:
If launched while a sender instance is running, the monitor application will be able to connect to it and the window should look something like this:
Let's implement a different type of receiver that displays the OSCMessage objects as text.
In the MainContentComponent
class, we can see that the class inherits from the OSCReceiver::Listener class [1] . Notice that we do not inherit from OSCReceiver::ListenerWithOSCAddress as we want to receive all the messages sent regardless of the address:
Declare OSCLogListBox and OSCReceiver objects as private member variables [2] and define a temporary int variable to store the port number [3] as shown below:
In the constructor, set corresponding parameters as follows:
When a button is clicked on the user interface of the monitor application, we call the corresponding function that we implement in the next step.
If the user clicks on the "connect" button, it should connect/disconnect depending on the application state [7] . In addition, every time the "connect" button is pressed, we call the updateConnectionStatusLabel()
helper function [8] as shown here:
To update the status label, simply change the text and colour depending on the connection state as described below:
To determine the connection state, we check whether the port number was changed from its default value of -1:
Otherwise, when the user presses the "clear" button, simply send a clear instruction to the OSCLogListBox and reset the screen log:
To connect to the sender application, let's implement the connect()
function:
handleInvalidPortNumberEntered()
helper function.handleConnectError()
helper function to display the error.In order to verify that the port number is valid, check if the range corresponds to 1 .. 65535 inclusive:
To disconnect from the network, check if the disconnection was successful and if so, reset the port number to -1 and change the "connect" button text [13] . Otherwise, call the handleDisconnectError()
helper function [14] :
When an OSCMessage is received, the following callback function is called and we transfer the content of the message to the OSCLogListBox:
If an OSCBundle is received, a different callback function is called and we transfer the content of the bundle to the OSCLogListBox:
Complete the implementation by showing message box for each type of error:
Now when the sender rotary knob is interacted with, the monitor application will log all the messages received and the window should look something like this:
OSCMonitorTutorial_02.h
file of the demo project.In this tutorial, we have learnt how to implement the OSC protocol to send information between application instances. In particular, we have: