Libwing is a C++ library for interfacing with Behringer Wing digital mixing consoles. It provides functionality for discovering Wing consoles on the network, connecting to them, reading/writing console parameters, and receiving any changes made on the mixer itself.
There is a C wrapper for this library. It generally follows the C++ API. You
can find it in wing_c_api.h
.
The Wing console exposes its functionality through a tree of nodes. Each node has:
- A unique numeric ID
- A hierarchical path name (like a filesystem path)
- A type (string, float, integer, enum, etc.)
- Optional min/max values and units
- Read/write or read-only access
To find Wing consoles on your network:
auto discovered = WingConsole::scan();
if (!discovered.empty()) {
// Found at least one console
auto firstConsole = discovered[0];
std::cout << "Found console: " << firstConsole.name
<< " at IP: " << firstConsole.ip << std::endl;
}
Once you have a console's IP address, you can connect to it:
WingConsole console = WingConsole::connect("192.168.1.100");
The Wing console uses an asynchronous communication model:
- You make requests using methods like
requestNodeDefinition()
andrequestNodeData()
- Responses come back through callback functions you register
- The Wing device also sends unsolicited updates when values change on the mixer
- All messages are received through the
read()
method, which never returns
Register callbacks to handle different types of messages. These callbacks will
be called on the thread from which the read()
method was called.
// Called when a node definition is received
console.onNodeDefinition = [](NodeDefinition node) {
std::cout << "Got definition for node: " << node.name << std::endl;
};
// Called when node data/values are received, in response to a request or
// unsolicited due to manipulation of the console
console.onNodeData = [](uint32_t id, NodeData data) {
std::cout
<< "Node "
<< NodeDefinition::nodeIdToName(id)
<< " ("
<< id
<< ") = "
<< data.getString()
<< std::endl;
};
// Called when a request is complete. You will get one of these for each time
// you call `requestNodeDefinition()` or `requestNodeData()`.
console.onRequestEnd = []() {
std::cout << "Request completed" << std::endl;
};
Nodes are identified by numeric IDs. You can convert between IDs and names:
uint32_t id = WingConsole::nodeNameToId(name); // Returns the ID for this node
and
std::string name = WingConsole::nodeIdToName(id); // Returns the ID for this node
The mapping of name to ID is generated by the wingschema
utility and is
pulled into the lirbary at compile time. It is quite large (over 35k entries as
of firmware 3.0.5), and adds about 1MB to the library size. If you need this
trimmed and can give up this name/id mapping, just delete all (or some) of the
rows in wing-schema.cpp
.
To start receiving data:
console.read(); // This blocks and processes incoming messages
You can request data from the console:
// Request a node definition -- this is the schema of the node.
// You can pass zero (0) to get the root nodes.
console.requestNodeDefinition(id);
// Request a node's data -- this is the parameter value.
console.requestNodeData(id);
These methods can be called on any thread.
To change values on the console:
// Set values by node ID
console.setString(nodeId, "value");
console.setFloat(nodeId, 0.5f);
console.setInt(nodeId, 42);
These methods can be called on any thread.
The wingmon
program demonstrates basic connection and monitoring:
- Discovers consoles on the network
- Connects to the first one found
- Monitors and displays value changes
The wingschema
program shows how to:
- Traverse the entire node tree
- Request and process node definitions
- Generate documentation of the console's parameter space
- Handle asynchronous responses systematically