This openFrameworks addon provides a simple way to remotely manipulate ofParameters
via an OSC connection. It uses a client-server architecture, where the server is the ofApp
you want to control remotely, and the client modifies the values of the ofParameters
. The addon focuses on the following:
- Minimal code to get it running: You just call a setup function, and for most situations that is all you need to do.
- Straightforward extensibility: Adding new data types is possible and simple.
- Platform-independent clients: Because it uses OSC, any client that follows the simple protocol can interface with your ofApp.
- BYO UI: In OF you can use ofxGui or ofxImGui, which is just a simple matter of passing the ofParameterGroup to either of those UI addons.
A mobile and desktop compatible Flutter client is now available! Check out RemoteRemote for more info.
The addon has two classes, which are independent from one another:
ofxRemoteParameters::Server
, which serves a "model" that includes the ofParameterGroup.ofxRemoteParameters::Client
, which is an OF client implementation.
The Server communicates bi-directionally via OSC, and defines a simple API that is used to request data and handle responses. Upon connection, a client requests the model from the server; the model is an XML representation of an ofParameterGroup, with additional metadata for each ofParameter specifying type, name, minimum and maximum values, in addition to the value itself. Because of this, clients do not need to know the structure of the ofParameterGroup ahead of time, and can instead recreate it based on the sent metadata.
Once a client has the model representation, it can use it to create a local clone so that it can be manipulated. Any changes are sent to the server.
The Server can also respond to messages that can modify the model unidirectionally (See Server API).
- Tested on OF 0.11.x, but should work on 0.10.x.
- Probably not compatible with 0.9.x and earlier.
- Tested on Mac and Linux, and Windows.
- Compile and run the
basic_server
example. Leave it running. - Locate the
of_client
example. InofApp.cpp
, change the value ofSERVER_IP_ADDRESS
to the ip address of the server. Compile and run. - In the of_client app, press the "connect" ofxGui button.
- If the connection is successful,
of_client
should receive and display the parameters ofbasic_server
. Changing the parameters inof_client
and they should change inbasic_server
!
- Declare an
ofxRemoteParameters::Server
. - Declare an
ofParameterGroup
that will hold all of your parameters. - On your app's
setup()
, callyourServerInstance.setup(yourOfParameterGroup)
. - And that's it! Any changes that the client makes on the parameters in the
ofParameterGroup
should be reflected in your app.
- Declare an
ofxRemoteParameters::Client
- Declare an
ofParameterGroup
that will mirror the remote parameters. - Call
myClient.setup(parameterGroup, serverAddress)
- Call
myClient.connect()
- Done! Now you can feed the ofParameterGroup to ofxGui or ofxImGui to manipulate the parameters and they will be synced with the Server's!
The Server comes with some built-in parameter types that it works with:
- int
- float
- double
- bool
- std::string
- ofFloatColor
- ofParameterGroup
- glm::vec2
- glm::vec3
- glm::vec4
- ofRectangle
- ofQuaternion
- ofMatrix3x3
- ofMatrix4x4
Serving custom types is a two step process:
- Register the type using Server::addParameterType:
myServer.addParameterType<myType>("myType");
- Make your custom type (de)serializable with
ofToString
. As an example, this is how OF (de)serializes)ofRectangle
:
ostream& operator<<(ostream& os, const ofRectangle& rect){
os << rect.position << ", " << rect.width << ", " << rect.height;
return os;
}
istream& operator>>(istream& is, ofRectangle& rect){
is >> rect.position;
is.ignore(2);
is >> rect.width;
is.ignore(2);
is >> rect.height;
return is;
}
Thus, the general approach is:
inline std::ostream& operator<<(std::ostream& os, const customType& instance)
{
os << [[some function that turns instance into a string]]
return os;
}
inline std::istream& operator>>(std::istream& is, customType& instance)
{
... (get values from the stream and assign them to your instance)
return is;
}
Be aware that the functions need to be declared at global scope, otherwise ofxRemoteParameters
might not find them.
The client needs to be aware of the new type as well. The provided ofxRemoteParameters::Client
also has an addParameterType<T>(string)
method that works in a similar way to Server's. See the examples for more details.
The Server uses a simple API for communication via OSC, which you can use to build your own client. To talk to the Server, your OSC message should have the following address:
/ofxrpMethod/(methodId)
The Server will respond with the following:
/ofxrpResponse/(medthodId)
Any response payload will be in the OSC message arguments.
There are 3 built-in methods:
Send this to the Server to register yourself as the Client.
Outbound OSC Arguments: none.
Response OSC Argument 0: string "OK" if successful.
Send this after calling connect
to retrieve the ofParameterGroup from the server in XML format. See below for the XML format of the response.
Outbound OSC Arguments: none.
Response OSC Argument 0: a string (XML) representation of the model.
Send this to set the value of an ofParameter.
Outbound OSC Argument 0: Parameter path as a string.
Outbound OSC Argument 1: Parameter value as a string.
Response: none.
Closes the Server's OSC receiver and resets the OSC sender. No further communication with the Server will be possible until it is restarted using Server::setup(...)
.
The model is sent as an XML string. The basic format is as follows:
<ofxRemoteParameters>
<Parameters>
... (parameters go here)
</Parameters>
<Methods>
... (server methods go here)
</Methods>
</ofxRemoteParameters>
<Escaped_Param_Name type="type name" name="Non-escaped parameter name">
<value>(the parameter value as a string)</value>
<min>(optional minimum)</min>
<max>(optional maximum)</max>
</Escaped_Param_Name>
<methodName uiName="A friendlier name" />
<ofxRemoteParameters>
<Parameters>
<Parameter_Server_Simple_Example type="group" name="Parameter Server Simple Example">
<Circle_Radius type="float" name="Circle Radius">
<value>10</value>
<min>1</min>
<max>500</max>
</Circle_Radius>
<Circle_Color type="color" name="Circle Color">
<value>255, 165, 0, 255</value>
</Circle_Color>
<Circle_Position type="vec2" name="Circle Position">
<value>200, 200</value>
<min>0, 0</min>
<max>1024, 768</max>
</Circle_Position>
</Parameter_Server_Simple_Example>
</Parameters>
<Methods>
<set uiName="Set parameter" />
<connect uiName="Connect" />
<getModel uiName="Get model" />
</Methods>
</ofxRemoteParameters>