GlobalRoutingManager, OSC
There are many concepts in HISE that allow you to control parameters and send values between different HISE modules:
- the plain ol' parameter system using Attribute IDs (eg. SimpleEnvelope.Attack)
- the macro control system that allows 8 controllers to control different modules with customizable ranges
- grabbing references to scriptnode parameters and call
setValue()
using the scriptnode API - Using the
global
variable keyword (not the most recommended way to do things, but that's a comprehensive list right here...)
Depending on your project architecture and development preferences, one of the 4 ways usually get the job done. However there is another system that allows a rather enjoyable workflow with a unique feature set: The Global Routing system. It offers the following features:
- using named pipes that are unique for each HISE project and can send / receive values from / to scriptnode or other sources.
- tools for converting between ranges
- integrated IDE helpers (all global cables show up in the module tree with the possibility of navigating to each connection in the drop down menu).
- send audio signals between nodes (this is rather experimental and quickly leads to issues so use it carefully).
- OSC support
In order to use the global routing system, just use one of the global routing nodes (eg. routing.global_cable
) and / or call Engine.getGlobalRoutingManager()
to register scripted functions as callback when a value is sent through the cable.
The nodes can be either used as source by setting their Value
parameter, or can be used as target by connecting their modulation output to any other parameter in scriptnode.
The communication is supposed to work through normalised (double precision) float numbers (so 0...1), but the scripting API has builtin features to automatically scale, skew or step the incoming value (just like you can edit a parameter range in scriptnode).
As an additional feature, the Global Routing system can also be used to send and receive OSC messages using the connectToOSC()
method.
Class methods
addOSCCallback
Register a scripting callback to be executed when a OSC message that matches the subAddress is received.
GlobalRoutingManager.addOSCCallback(String oscSubAddress, var callback)
In addition to OSC messages being piped into global cables, you can also register script callbacks that will react on incoming OSC messages. The first parameter should contain the subdomain of your OSC address and can contain OSC pattern wildcards to catch multiple OSC messages. The callback must be a callable object with 2 parameters, the first will contain the subdomain of the OSC address and the second one the value.
rm.connectToOSC({"Domain": "/myDomain"});
// React on /myDomain/fader1
rm.addOSCCallback("/fader1", function(id, value) {});
// Catch all - react on every OSC message that starts with /myDomain/
rm.addOSCCallback("/*", function(id, value) {});
Unlike global cables which are limited to single numbers, this system allows you to react on almost every OSC message type:
- floats
- integer values
- Strings
- multiple values (
value
will be an array of one of the types above).
However the parameter range that is defined in the connectToOSC
call is not applied to the incoming / outgoing values, so you will get the raw data.
connectToOSC
Allows the global routing manager to send and receive OSC messages through the cables.
GlobalRoutingManager.connectToOSC(var connectionData, var errorFunction)
The Global Routing Manager can also be set to send and receive OSC messages by calling this method. It expects two arguments, the first one is a JSON object containing the connection data. The second argument is a function with a single argument that will handle any OSC error messages.
The OSC support of this system is not fully standard compliant to OSC, but is limited to the scope of the Global Routing system:
- only single value OSC messages are allowed (anything else will throw a custom error)
- only bool, integer and float messages are allowed (the integer types are converted automatically)
However if you want to use more complex data types in OSC messages, you can send and receive them using scripting callbacks, which give you almost the full feature set of OSC (minus binary data blobs and colours).
The connection data
The JSON object that comes in as first argument describes the URLs and ports for receiving / sending OSC messages. There are sensible default values so you don't need to fill in all properties.
Property | Description | Default |
Domain
|
The "root" URL for your application. Must start with a /
and must not end with a /
|
"/hise_osc_receiver"
|
SourceURL
|
The IP address for the source URL. Can be left empty to use the local host. | "127.0.0.1" |
SourcePort
|
The port number for listening to incoming OSC messages | 9000 |
TargetURL
|
The IP address for the target URL. Can be left empty to use the local host. | "127.0.0.1" |
TargetPort
|
The port number for outgoing OSC messages. If you omit this or set it to -1, it will deactivate OSC output. | -1 |
Parameters
|
By default, HISE expects all incoming OSC messages to be within the 0...1 range. However if you can't control the output of your OSC source / target, you can provide a list of parameter ranges as a JSON object with the OSC subdomain as key and a JSON object with the scriptnode range properties as value. It will then transform incoming and outgoing values using the range (see the example below). |
{}
|
There is a new FloatingTile (the OSCLogger) which logs all incoming messages with filtering and cable based colour coding so make sure you use it during development / prototyping
Example
const var rm = Engine.getGlobalRoutingManager();
inline function printError(message)
{
Console.print(error);
};
rm.connectToOSC({
"Domain": "/myDomain",
"SourcePort": 6666,
"TargetPort": 6667,
"Parameters":
{
"/fader1":
{
"MinValue": -1.0,
"MaxValue": 1.0,
"SkewFactor": 0.25
}
}
}, printError);
// Create a cable with a OSC subdomain
const var testCable = rm.getCable("/fader1");
// register an async callback that just prints the value
testCable.registerCallback(function(newValue)
{
Console.print(newValue);
}, false);
// Now you can start sending OSC messages with the domain "/myDomain/fader1" to the port 6666 and
// it should show up in the HISE console...
// Let's add a knob and send its value through the cable
const var Knob1 = Content.addKnob("Knob1", 0, 0);
inline function onKnob1Control(component, value)
{
// Changing the knob should now update your source OSC app
// (if the port is set to 6667)
testCable.setValue(value);
};
Knob1.setControlCallback(onKnob1Control);
getCable
Returns a scripted reference to the global cable (and creates a cable with this ID if it can't be found.
GlobalRoutingManager.getCable(String cableId)
This method will create a new Global Cable
or return a reference to an existing cable with the given ID.
getEventData
Returns the double value that is written to the data slot using setEventData. If the event ID wasn't written, it will return undefined. Edit on GitHub
GlobalRoutingManager.getEventData(int eventId, int dataSlot)
removeOSCCallback
Removes a OSC callback for the given address. Edit on GitHub
GlobalRoutingManager.removeOSCCallback(String oscSubAddress)
sendOSCMessage
Send an OSC message to the output port.
GlobalRoutingManager.sendOSCMessage(String oscSubAddress, var data)
This function lets you send out an OSC message if there is an TargetPort
specified in the connectTOOSC()
call. The first parameter must be the subdomain, which will be merged with the root domain to the full address and the second parameter must be a value that is convertible to a OSC message:
- float
- int
- String
- Array of the three types above
However the parameter range that is defined in the connectToOSC
call is not applied to the incoming / outgoing values, so you will get the raw data.
// Send a message to a fader
rm.sendOSCMessage("/fader1", 0.4);
// Send a message to a 2D XY Pad
rm.sendOSCMessage("xy1", [0.2, 0.3]);
// Send a message to an external display
rm.sendOSCMessage("/label", "Hello World");
setEventData
Writes a value into the given slot that can be retrieved using the event ID.
GlobalRoutingManager.setEventData(int eventId, int dataSlot, double value)
Using this method, you can attach more data to any event and fetch it later down the processing line. It doesn't directly attach the data to the event (as the size of the event is optimized down to the last bit without any overhead) but use a separate data model that allows the storage of up to 16 double precision numbers for each event.
Once you've written the data it can be retrieved by one of the following targets:
- the API call getEventData()
- the EventData Modulator
- the routing.event_data_reader node
Take a look at the series of examples in the snippet browser called Custom Event Data ...
for an introduction use case of this concept.