Hi,
I thought I just share my perhaps poorly written conversion in case it will save some else some time down the road. I simply parsed through the generic remote XMLs to save my time from editing hundred of entries from my generic remotes. The below script will take a given path to the generic remote XML, and hopefully (worked on my end with my 7 generic remotes) convert it to a JSON file in the same directory this script runs.
this will require Node & a XML to JSON package
// Warning: If the generic remote has any command that is NOT CONNECTED, the
// script will fail. This is because if there is a command that is not
// connected, it is not saved in the XML as a "blank connected" command, rather
// Cubase will skip the node entirely in the bank. This will result in <ctrltable> having more
// entries than <bank>. I simply just removed any non-connected entries in my
// generic remote, save up a new version of the xml before using this script below.
import { readFileSync, writeFile } from "fs"
import { parse } from "path"
import { xml2json } from "xml-js"
main("PATH_TO_XML")
function main(filepath) {
const readXML = readFileSync(filepath, { encoding: "utf-8" })
const xmlToString = xml2json(readXML, {
compact: true,
spaces: 4,
ignoreDeclaration: true,
})
const xmlToJSON = JSON.parse(xmlToString).remotedescription
const itemsCtrl = xmlToJSON.ctrltable.ctrl
const itemsBank1 = xmlToJSON.bank?.entry ?? xmlToJSON.bank[0]?.entry // could be multiple banks
const itemsToBtnJSON = []
if (itemsCtrl.length !== itemsBank1.length) {
console.log(itemsCtrl.length, xmlToJSON.bank?.entry.length, "lengths don't match")
return
}
itemsCtrl.forEach((item, i) => {
if (itemsBank1[i].value !== undefined) {
itemsToBtnJSON.push({
i,
chan: item.chan,
addr: item.addr,
category: itemsBank1[i]?.value?.device,
action: itemsBank1[i]?.value?.chan,
})
return
}
itemsToBtnJSON.push({
i,
chan: item.chan,
addr: item.addr,
category: itemsBank1[i]?.command?.category,
action: itemsBank1[i]?.command?.action,
})
})
writeToFiles(itemsToBtnJSON, filepath)
}
function writeToFiles(data, filepath) {
writeFile(
`./${parse(filepath).name}.json`,
JSON.stringify(data),
{ flag: "a+" },
(err) => {
console.log(err)
}
)
}
package.json
{
"name": "_scratchpad-js",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"type": "module",
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"xml-js": "^1.6.11"
}
}
After, I just created a for loop to create a series of buttons in the remote midi API, reading the JSON created from the above script. I also added a small perhaps bad math example to create a grid of buttons. Here’s what I did below:
//-----------------------------------------------------------------------------
// 1. DRIVER SETUP - create driver object, midi ports and detection information
//-----------------------------------------------------------------------------
// get the api's entry point
var midiremote_api = require("midiremote_api_v1")
// create the device driver main object
var deviceDriver = midiremote_api.makeDeviceDriver(
"Test Studios",
"genericRemote_01",
"Test Studios"
)
// create objects representing the hardware's MIDI ports
var midiInput = deviceDriver.mPorts.makeMidiInput()
var midiOutput = deviceDriver.mPorts.makeMidiOutput()
// define all possible namings the devices MIDI ports could have
deviceDriver
.makeDetectionUnit()
.detectPortPair(midiInput, midiOutput)
.expectInputNameEquals("MIDI_In")
.expectOutputNameEquals("MIDI_Out")
//-----------------------------------------------------------------------------
// 2. SURFACE LAYOUT - create control elements and midi bindings, midi cc channels are 0 based
//-----------------------------------------------------------------------------
function bindMidiCC(button, chn, num) {
button.mSurfaceValue.mMidiBinding
.setInputPort(midiInput)
.bindToControlChange(parseInt(chn), parseInt(num))
}
var genericRemote_01 = require("./imports/genericRemote_01.js")
var midiCCBinds = genericRemote_01.midiCCBinds
var button = []
var btnPos = 0
var yPos = 0
// creating a grid of buttons
for (var index = 0; index < midiCCBinds.length; ++index) {
if (btnPos >= 9) {
btnPos = 0
yPos = yPos + 1
}
var x = btnPos++ * 2
var y = yPos
var w = 2
var h = 1
var newButton = deviceDriver.mSurface.makeButton(x, y, w, h)
button.push(newButton)
}
//-----------------------------------------------------------------------------
// 3. HOST MAPPING - create mapping pages and host bindings
//-----------------------------------------------------------------------------
var page = deviceDriver.mMapping.makePage("Test Page 1")
for (var index = 0; index < midiCCBinds.length; index++) {
var btn = midiCCBinds[index]
bindMidiCC(button[btn.i], btn.chan, btn.addr)
page.makeCommandBinding(button[btn.i].mSurfaceValue, btn.category, btn.action)
}
picture of the button grid
Hopefully, this saves someone else sometime. Feel free to make changes.
Cheers,
DMDComposer