Midi Remote Scripting basic questions

I am attempting to write my own scripts for Cubase’s MIDI Remote feature. Unfortunately, the available instructions seem to omit essential information or assume a level of common knowledge that I don’t possess.

I have experience building my own hardware controllers and writing the C++ code for them, which I’ve successfully used with Cubase’s Generic Remote feature so far. Now, I’m keen to explore the new possibilities offered by MIDI Remote scripting.

I’ve managed to get the simple device example set up using VS Code, and it correctly appears within Cubase. However, beyond this point, the MIDI Remote API documentation seems to jump almost directly into the API reference details, offering little guidance on the necessary script structure.

For example, my goal is to send the RGB color values of the currently selected track as three separate MIDI controller values. I found the following function in the API documentation:

mOnColorChange : function (activeDevice : ActiveDevice, r : number, g : number, b : number, a : number, isActive : boolean)

My confusion lies in understanding when this function is triggered. Does it need to be explicitly called, perhaps within an if statement? Or does the script operate in a loop, constantly checking the state? Is it event-driven?

I’ve consulted general JavaScript tutorials, but they haven’t clarified these specific aspects of the MIDI Remote environment.

I would be very grateful if someone could point me towards resources (like tutorials, examples, or clearer explanations) that could help bridge this knowledge gap, specifically regarding script structure and event handling within the MIDI Remote API.

Hi,

This function is called directly from Cubase. Once you change the color of the given track in Cubase, it will call this function in the script.

Hi Martin, thanks for the quick reply. That answers at least one of many questions :wink:
Unfortunately ther are still many left.

If I try this as a simple Test and added this to the “Simple Device” code to see if the function gets triggered:

selectedTrackChannel.mOnColorChange = function (activeDevice, activeMapping, r, g, b, a, isActive) {
    console.log("Hello");
}

The log shows an error and it oviously does not work. I’m afraid that I’m still missing some fundamentals here. I really would apreciate if someone could hint me to a place where the structure of commands and the synthax is explained in more detail.

Hi,

Have a look into the:
ExampleCompany_RealWorldDevice.js
file.

faderStrip.fader.mSurfaceValue.mOnColorChange = function (context, r, g, b, a, isActive) {
   midiOutput.sendMidi(context, helper.sysex.setDisplayColorOfColumn(channelIndex, 
}

Hi Martin,

I have seen the example code. Unfortunately it is not commented at all, so I have to guess what everything means and why it is in the code.
For Example lets take:

faderStrip.fader.mSurfaceValue.mOnColorChange = function (context, r, g, b, a, isActive)
 {
}

what is faderStrip doing here?
What do I need fader for in this line?
Why do I need ,SurfaceValue, why is mOnColorChange not enough to trigger the function?
What does context do here? What value does it return?
r,g,b,a is the only thing that is somehow selfexplanatory.
What is isActive doing here?

I try to find all this in the API but I’m running in circles there.
I’m sorry for the noob questions.

One way of using mOnColorChange (and I’m sure there are many) is to bind the callback function when you create your page(s).
Here is a mockup example:

function makePage(pageName) {
    var p = driver.mMapping.makePage(pageName)
    var selectedChannel = p.mHostAccess.mTrackSelection.mMixerChannel

    selectedChannel.mOnTitleChange = function (activeDevice, activeMap, title) {
        console.log('mMixerChannel.mOnTitleChange: ' + title)       
    }
    selectedChannel.mOnColorChange = function(activeDevice, activeMap, r, g, b, a, isActive) {
        console.log('mMixerChannel.mOnColorChange: ' + Math.round(r*255) + ' ' + Math.round(g*255) + ' ' + Math.round(b*255))
    }
    return p
}

//Make a page
myPage = makePage('newPage')

1 Like

This is regrettably very true. The documentation is seriously lacking and the API itself feels unfinished.

Hi,

  • faderStrip is an object of multiple other objects (faders, encoders) of multiple channels-
  • fader is an object of the given faderStrip.
    = This is how the author of this script designed his code.
  • mSurfaceValue is the value of the given object (in this case, the fader); see the SurfaceElementValue, please.

Please let me know how this code would be implemented in the simple example, if just paste it in ther I get an error (driver undefined),

here is the code from the api example:

//-----------------------------------------------------------------------------
// 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('ExampleCompany', 'SimpleDevice', 'Steinberg Media Technologies GmbH')

// 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
// NOTE: Windows and MacOS handle port naming differently
deviceDriver.makeDetectionUnit().detectPortPair(midiInput, midiOutput)
    .expectInputNameEquals('SimpleDevice IN')
    .expectOutputNameEquals('SimpleDevice OUT')
    
deviceDriver.makeDetectionUnit().detectPortPair(midiInput, midiOutput)
    .expectInputNameEquals('SimpleDevice (MIDI IN)')
    .expectOutputNameEquals('SimpleDevice (MIDI OUT)')


//-----------------------------------------------------------------------------
// 2. SURFACE LAYOUT - create control elements and midi bindings
//-----------------------------------------------------------------------------

// create control element representing your hardware's surface
var knob1 = deviceDriver.mSurface.makeKnob(0, 0, 1, 1.5)
var knob2 = deviceDriver.mSurface.makeKnob(1, 0, 1, 1.5)
var knob3 = deviceDriver.mSurface.makeKnob(2, 0, 1, 1.5)
var knob4 = deviceDriver.mSurface.makeKnob(3, 0, 1, 1.5)

// bind midi ports to surface elements
knob1.mSurfaceValue.mMidiBinding
    .setInputPort(midiInput)
    .setOutputPort(midiOutput)
    .bindToControlChange (0, 21) // channel 0, cc 21

knob2.mSurfaceValue.mMidiBinding
    .setInputPort(midiInput)
    .setOutputPort(midiOutput)
    .bindToControlChange (0, 22) // channel 0, cc 22

knob3.mSurfaceValue.mMidiBinding
    .setInputPort(midiInput)
    .setOutputPort(midiOutput)
    .bindToControlChange (0, 23) // channel 0, cc 23

knob4.mSurfaceValue.mMidiBinding
    .setInputPort(midiInput)
    .setOutputPort(midiOutput)
    .bindToControlChange (0, 24) // channel 0, cc 24

//-----------------------------------------------------------------------------
// 3. HOST MAPPING - create mapping pages and host bindings
//-----------------------------------------------------------------------------

// create at least one mapping page
var page = deviceDriver.mMapping.makePage('Example Mixer Page')

// create host accessing objects
var hostSelectedTrackChannel = page.mHostAccess.mTrackSelection.mMixerChannel


// bind surface elements to host accessing object values
page.makeValueBinding(knob1.mSurfaceValue, hostSelectedTrackChannel.mValue.mVolume)
page.makeValueBinding(knob2.mSurfaceValue, hostSelectedTrackChannel.mSends.getByIndex(0).mLevel)
page.makeValueBinding(knob3.mSurfaceValue, hostSelectedTrackChannel.mSends.getByIndex(1).mLevel)
page.makeValueBinding(knob4.mSurfaceValue, hostSelectedTrackChannel.mSends.getByIndex(2).mLevel)

ok,

function makeFaderStrip(channelIndex, x, y) {
    var faderStrip = {}

I guess “var faderStrip = {}” is used to name/define the faderstrip so I can refer to it later?

Does this mean if I want something to react to the selected track I have to define something that represent the selected track first?

Is this really the name of your MIDI ports?

You would use page.
Try this:

var selectedChannel = page.mHostAccess.mTrackSelection.mMixerChannel
selectedChannel.mOnColorChange = function(activeDevice, activeMap, r, g, b, a, isActive) {
        console.log('mMixerChannel.mOnColorChange: ' + Math.round(r*255) + ' ' + Math.round(g*255) + ' ' + Math.round(b*255))
    }
1 Like

That code works, thanks.

I try explain what I think I’ve learned so far:

I have to create a variable (in this case selctedChannel) that gets the information from Cubase.
It is mandatory to have a page that you refer to. In this case the onyl page in the script is called page.
mHostAccess seems to be the connection to cubase.mTrackSelection makes sense.
Why I need mMixerChannel on Top of it, I don’t know. Maybe you always need 4 atributes?

Then I can use selectedChannel.mOnColrChange to trigger any function.
I still don’t understand, what activeDevice does. First I thought It has to do with the selected track. Since this is handled in the line before, it does not make sense.
activeMap I also don’t understand (The API entry for that is empty…)
And then again what do I need isActive for? (Also the API does not explain what it is good for)

No, you don’t have to. Creating that variable just acts as a shorthand way of writing:

page.mHostAccess.mTrackSelection.mMixerChannel

Yes, you need to create at least one page. In this example the variable holding the page object is called page but the name of the page is “Example Mixer Page”. (You can of course name both the object variable and the page whatever you want.)

It is a class object of type MR_ActiveDevice and represents a detected and activated device of a specific DeviceDriver.
You would need it if you wanted to send a MIDI message as a result of this callback firing. Example:

selectedChannel.mOnColorChange = function(activeDevice, activeMap, r, g, b, a, isActive) {
    var colorRed7bit = Math.round(r*127)
    var midiCC = 27
    
    //Send a Control Change message on MIDI Channel 1
    midiOutput.sendMidi(activeDevice, [0xb0, midiCC, colorRed7bit])
    }