Web-ui

Calicofx is headless by design, I.e, there is no UI tied to the DSP processing. The ability to control and configure plugins is provided by a web interface that spawns as another process. In other words, the User facing control ability and the DSP engine are architect-ed as a client and server process respectively.

Interface #

A websocket interface is intentionally chosen to enable web-ui to communicate with the core directly without any bridges.

Another important design consideration is to use JSON + ZMQ

ZMQ is used for following reasons

  • Allows multiple clients to connect
  • Abstracts the Protocol (can switch from ws://* to tcp://* easily)
  • Enables multiple design patterns

Communication pattern #

Important design consideration is to be aware that multiple clients(a.k.a UIs) can be connected to the singleton instance of the core. Any changes made by a client should be broadcast to all the other clients.

For example, if one of the UI triggers a tuner, all the other UI instance should be forced to show the UI for tuner. This way we achieve uniformity.

Hence, by definition, we want 2 communication endpoints

Sync endpoint #

A synchronous endpoint that takes in request from the client and returns result synchronously. Think Adding a node, Deleting a node, Updating a parameter etc… In ZMQ, this is achieved by REQ / REP design pattern

Subscription endpoint #

All the frontend UIs should be notified when some changes is triggered by a UI. Hence, there is another endpoint that follows a publisher-subscriber model where the server notifies all the “subscribed” clients of the change In ZMQ, this is achieved by PUBSUB design pattern

Message #

ZMQ provides the communication between 2 process (a.k.a IPC), but the packet is structured to be a JSON structure. JSON is chosen as the message format due to its wide acceptance across all programming languages and flexibility

The generic message structure looks something like below

{
    command: <SUPPORTED_CMD>,
    payload: [
      {"operation-1": "value"},
      {"operation-2": "value"},
      ....
    ]
}

The response for each synchronous message will be as below

{
    "result":"OK/NOK",
    "response":[
      {"resp-1": "val"}
      ...
    ]
}

For any Error, the response will be be in the format. This is common for all the supported commands

{
      "result":"NOK",
      "response":[
      {"message": "<reason-for-failure"}
      ]
  }

Following messages are supported

CALICOFX_ADD_NODE #

Adding a node. Each node is a valid plugin. Currently, only lv2 plugins are supported. The payload message looks like below

{
      command: 0, //a.k.a CALICOFX_ADD_NODE
      payload: [
        {"uri": "<plugin-uri>"},
      ]
  }

As a result, on success, following response is sent back. Where the name would be the name of the plugin appended by 4 decimal number to make it unique for example gx_stereo_amp might be gx_stereo_amp_4241. This way, there is a flexibility to add another instance of the same plugin. The host is expected to save this name and use it for any operation on this plugin

{
      "result":"OK",
      "response":[
      {"name": "name-of-plugin_%4d"}
      ]
  }

CALICOFX_UPDATE_PARAM #

Update a control parameter of the plugin. This requires the plugin to be instantiated (i.e loaded).

The message looks like

{
      command: 1, //a.k.a CALICOFX_UPDATE_PARAM
      payload: [
          {"name": "name-of-plugin_%4d"},
        {"param": "<name-of-control-param>"},
        {"val":0.0f}
      ]
  }

Link a port between 2 nodes, the source node should be of type “output” and the destination port must be “input”. The following message is expected

{
    command: 2, //a.k.a CALICOFX_LINK
    payload: [
        {"src-node": "name-of-plugin_%4d"},
      {"src-port": "port-name"},
      {"dst-node": "name-of-plugin_%4d"},
      {"dst-port": "port-name"}
    ]
}

Unlink is opposite to CALICOFX_LINK and removes the link between 2 nodes. Following is the expected message

{
    command: 3, //a.k.a CALICOFX_UNLINK
    payload: [
        {"src-node": "name-of-plugin_%4d"},
      {"src-port": "port-name"},
      {"dst-node": "name-of-plugin_%4d"},
      {"dst-port": "port-name"}
    ]
}

CALICOFX_REMOVE_NODE #

Remove a node from the patchbay. Expected message structure

{
      command: 4, //a.k.a CALICOFX_REMOVE_NODE
      payload: [
          {"name": "name-of-plugin_%4d"},
      ]
  }