Cubase 12.0.50 MIDI Remote "Parameter Bank" controls

Oh goodness, @saxmand, you found another “accidentally slipped” thing here. I’m afraid you have to ignore the SimpleExample entirely. I must have committed that file without checking it. But it gives an interesting insight of my thinking process ;-).

Sorry for the mess :exploding_head:

Thanks for confirming this @Jochen_Trappe.

Yeah I’ve been sitting with it scratching my head as I couldn’t make anything work really. Seemed to be a lot of functions that are also named different now.

Is it possible to assign parameters for the channelstrip section via script, or is it only available in the MIDI Remote Mapping Assistant?

yes, of cause!

Example code:

var page = deviceDriver.mMapping.makePage('Default')

var stripEffects = page.mHostAccess.mTrackSelection.mMixerChannel.mInsertAndStripEffects.mStripEffects
var compressor = stripEffects.mCompressor
var comprParam1 = compressor.mParameterBankZone.makeParameterValue()
var comprParam2 = compressor.mParameterBankZone.makeParameterValue()
var comprParam3 = compressor.mParameterBankZone.makeParameterValue()

page.makeValueBinding(knob1.mSurfaceValue, comprParam1)
page.makeValueBinding(knob2.mSurfaceValue, comprParam2)
page.makeValueBinding(knob3.mSurfaceValue, comprParam3)
page.makeActionBinding(button1.mSurfaceValue, compressor.mParameterBankZone.mAction.mPrevBank)
page.makeActionBinding(button2.mSurfaceValue, compressor.mParameterBankZone.mAction.mNextBank)

1 Like

Awesome Thanks @Jochen_Trappe. That worked. Surprisingly easy.
I didn’t realised that they are just assigned one after the other.

So I’d like to try and have all strip effects on on page. And then trying to figure out how to assign specific subpages (various inserts) to a specific button on my mixer.

Right now I can only go to the prev page, next page or reset page.

Is there a way to do this. This is how my script looks right now:
(also there could be a much better way of doing this then what I’m currently doing)


function makePageChannelStrip() {
    var page = makePageWithDefaults('Channelstrip')

    var strip = page.makeSubPageArea('strip')
    var gatePage = makeSubPage(strip, 'Gate')
    var compressorPage = makeSubPage(strip, 'Compressor')
    var toolsPage = makeSubPage(strip, 'Tools')
    var saturatorPage = makeSubPage(strip, 'Saturator')
    var limiterPage = makeSubPage(strip, 'Limiter')

    var selectedTrackChannel = page.mHostAccess.mTrackSelection.mMixerChannel

    page.makeActionBinding(surfaceElements.channelControls[0].sel_button.mSurfaceValue, strip.mAction.mPrev)
    page.makeActionBinding(surfaceElements.channelControls[1].sel_button.mSurfaceValue, strip.mAction.mNext)

    var stripEffects = selectedTrackChannel.mInsertAndStripEffects.mStripEffects


    for (var idx = 0; idx < surfaceElements.numStrips; ++idx) {
        var faderSurfaceValue = surfaceElements.channelControls[idx].fader.mSurfaceValue;

        page.makeValueBinding(faderSurfaceValue, stripEffects.mGate.mParameterBankZone.makeParameterValue()).setSubPage(gatePage).setValueTakeOverModeJump()
        page.makeValueBinding(faderSurfaceValue, stripEffects.mCompressor.mParameterBankZone.makeParameterValue()).setSubPage(compressorPage).setValueTakeOverModeJump()
        page.makeValueBinding(faderSurfaceValue, stripEffects.mTools.mParameterBankZone.makeParameterValue()).setSubPage(toolsPage).setValueTakeOverModeJump()
        page.makeValueBinding(faderSurfaceValue, stripEffects.mSaturator.mParameterBankZone.makeParameterValue()).setSubPage(saturatorPage).setValueTakeOverModeJump()
        page.makeValueBinding(faderSurfaceValue, stripEffects.mLimiter.mParameterBankZone.makeParameterValue()).setSubPage(limiterPage).setValueTakeOverModeJump()
    }


    return page
}

as ‘jump’ is the default you can remove it, makes the code cleaner :wink:

gatePage.mAction.mActivate // should be available to be bound via "makeActionBinding"

Really nice @Jochen_Trappe. That worked smoothly.

I guess I’m only really missing one thing now, which is to be able to enable as well as change between the different fx from the midi controller?

Skærmbillede 2022-11-13 kl. 23.02.21

My guess is that maybe it would be using .mEdit as it’s the only available parameter left, but I haven’t managed to had luck making it do anything.

There’s also mOnChangePluginIdentity which I haven’t figured out what is doing. I don’t get anything when I try to do this:

    stripEffects.mCompressor.mOnChangePluginIdentity = function (activeDevice, arg1, arg2, arg3, arg4, arg5) {
        console.log(arg2 + '')
        console.log(arg3 + '')
        console.log(arg4 + '')
        console.log(arg5 + '')
    }

Thanks again.

Also, I was also wondering, I have this page structure:
(example code)

var sendsPage = makePageSends()
var channelStripPage = makePageChannelStrip()
var insertsPage = makePageInserts()


function makePageSends() {
    var page = makePageWithDefaults('Sends')
   ... etc
    return page
}

function makePageChannelStrip() {
    var page = makePageWithDefaults('ChannelStrip')
   ... etc
    return page
}
...etc

function makePageWithDefaults(name) {
    var page = deviceDriver.mMapping.makePage(name)
    ...etc    
    return page
}

...etc

Is there a way to script it, so I can assign a midi button to change to a specific of those pages.
I figured out to do it “manually” via the mapping assistant, but doesn’t seem optimal, as I experienced when working with the script that it lost those manual settings. Also cause I have 6 different ones.

So basically how you showed me for the subPage, but for the mapping pages…

Thanks in advance

Thanks for all the explanation above, this works brilliantly with the channel strip effects.

I tried to set up the control of insert effects in a similar fashion, but I can’t seem to make it work using the script. Can someone point me in the right direction?

There doesn’t seem to be a makeParameterValue() function for the insert effects viewer.
I assume it should be done using makeInsertEffectsViewer() function but I can’t figure out right the way to do it.

Here we go, answering my own question for reference :slight_smile:

Happy days!

var HostInsertViewer = page.mHostAccess.mTrackSelection.mMixerChannel.mInsertAndStripEffects.makeInsertEffectViewer('')
    .excludeEmptySlots()

var PrevInsert = HostInsertViewer.mAction.mPrev
var NextInsert = HostInsertViewer.mAction.mNext

for(var r = 0; r < 16; ++r) {
    var ChannelInsertParameter = HostInsertViewer.mParameterBankZone.makeParameterValue()
    page.makeValueBinding(knobs[r].mSurfaceValue, ChannelInsertParameter)

}

page.makeActionBinding(buttons[3].mSurfaceValue, PrevInsert)
page.makeActionBinding(buttons[7].mSurfaceValue, NextInsert)

4 Likes

I’ll answer myself on this one too, as I just found a way around to do it. Probably not the best way but works great so far.
Basically what I did is add the pages as blank ones as one of the first things in the script. This way they are available when the scripts creates action bindings.
To fill it in on my pseudo script from above.

// This is the added code to make them available
var sendsPage = deviceDriver.mMapping.makePage('Sends')
var sendsPage = deviceDriver.mMapping.makePage('ChannelStrip')
etc...

var sendsPage = makePageSends()
var channelStripPage = makePageChannelStrip()
var insertsPage = makePageInserts()


function makePageSends() {
    var page = makePageWithDefaults('Sends')
   ... etc
    return page
}

function makePageChannelStrip() {
    var page = makePageWithDefaults('ChannelStrip')
   ... etc
    return page
}
...etc

function makePageWithDefaults(name) {
    var page = deviceDriver.mMapping.makePage(name)
    ...etc    
    return page
}

...etc

And I can change to the page with this command as an example:

page.makeActionBinding(surfaceElements.channelControls[0].page_button.mSurfaceValue, eqPage.mAction.mActivate)

Starting to enjoy all the functionality.
I have another question I was hoping to clear up, maybe @Jochen_Trappe knows.

When hoovering over a surface element that has been mapped as an ActionBinding, is there a way to get that information/title/name (so I can send it to the MIDI controller display):
Here’s are two examples:

Skærmbillede 2022-11-21 kl. 23.25.06

And also there’s more info when hoovering over a surface elements that has been ValueBind. In the picture below I can only get “HiPass” and “H-Delay Stereo”, using mOnTitleChange

It would be really awesome and helpful if we could get more of those infos available in our script somehow, to get better overview of what the MIDI device is doing.

Thanks as always in advance

Hi Thomas,
may I ask how you exactly send parameter names and values to external displays?
Thanks in advance,
Emre

Using following code I am able to control the first parameter of a plugin which is assigned in the Remote Editor. How can I control the rest of my assigned parameters?

var knob1 = deviceDriver.mSurface.makeKnob(0, 0, 1, 1.5)

knob1.mSurfaceValue.mMidiBinding
.setInputPort(midiInput)
.setOutputPort(midiOutput)
.bindToControlChange (0, 21) // channel 0, cc 21

var HostInsertViewer = page.mHostAccess.mTrackSelection.mMixerChannel.mInsertAndStripEffects.makeInsertEffectViewer(‘’)
.followPluginWindowInFocus ()

var ChannelInsertParameter = HostInsertViewer.mParameterBankZone.makeParameterValue()
page.makeValueBinding(knob1.mSurfaceValue, ChannelInsertParameter)

If I recall correctly, make another call to the …makeParameterValue() and it will give you the next one

Brilliant, thanks a lot robw, this works!
I also managed to push the value to an external (MCU) display, but don’t know how to grab the parameter NAME. So for instance I can display “3.3k” from my inserted SSL plugin, but not “HM Freq”, any help?
Heres my code for one knobs (well, not my code, but copied bits and pieces from here and there):

var hostSelectedTrackChannel = page.mHostAccess.mTrackSelection.mMixerChannel

var knob1 = deviceDriver.mSurface.makeKnob(0, 0, 1, 1.5)

knob1.mSurfaceValue.mMidiBinding
.setInputPort(midiInput)
.setOutputPort(midiOutput)
.bindToControlChange (0, 21) // channel 0, cc 21

var HostInsertViewer = hostSelectedTrackChannel.mInsertAndStripEffects.makeInsertEffectViewer(‘’)
.followPluginWindowInFocus ()

var ChannelInsertParameter1 = HostInsertViewer.mParameterBankZone.makeParameterValue()
var ChannelInsertParameter2 = HostInsertViewer.mParameterBankZone.makeParameterValue()
var ChannelInsertParameter3 = HostInsertViewer.mParameterBankZone.makeParameterValue()

page.makeValueBinding(knob1.mSurfaceValue, ChannelInsertParameter3)

ChannelInsertParameter3.mOnDisplayValueChange = function (activeDevice, activeMapping, value) {
    //console.log(value)
    var dataDisplay = [0xf0, 0x00, 0x00, 0x66, 0x14, 0x12, 0x38];
    var dataDisplayReset = [0xf0, 0x00, 0x00, 0x66, 0x14, 0x12, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7]
    for (var i = 0, len = value.length; i < len; ++i)
        dataDisplay.push(value.charCodeAt(i))
        dataDisplay.push(0xF7)
        midiOutput.sendMidi(activeDevice, dataDisplayReset)
        midiOutput.sendMidi(activeDevice, dataDisplay)
    }

I’ve got a question that, after reading through this thread, someone here may be able to answer, I hope. It’s related to the insert effect parameters discussions, but with a slightly different application to the usual.
I’m building a software-based control surface, and I’ve created a zone for insert plug-in parameter knobs (kind of like the plug-in Remote Control Editor layout) that follow the plug-in in focus. However, because I’m creating the parameter knobs dynamically, I’d like MIDI Remote to somehow get the number of parameters for the in-focus plug-in. Hopefully this helps explain it:
Surface Layout:

function makeInsertParams(x, y) {
  var params = {}
  var w = 2
  var h = 1
  var currX = x
  var currY = y
  var noOfParams = // number of parameters per insert plug-in
  for (var i = 1; i <= noOfParams; ++i) {
    var param = 'param' + i
    if (i % 8 == 0) {
      params[param] = surface.makeKnob(currX, currY, w, h)
      params[param].mSurfaceValue.mMidiBinding.setInputPort(midiInput).bindToControlChange(1, i)
      currX = currX - (w * 7)
      currY = currY + h
    } else {
      params[param] = surface.makeKnob(currX, currY, w, h)
      params[param].mSurfaceValue.mMidiBinding.setInputPort(midiInput).bindToControlChange(1, i)
      currX = currX + w
    }
    params[param].mSurfaceValue.mOnProcessValueChange = function (context, newValue) {
      midiOutput.sendMidi(context, newValue)
    }
  }
  return params
}

Host Mapping

var page = deviceDriver.mMapping.makePage(name)
var hostSelectedTrackChannel = page.mHostAccess.mTrackSelection.mMixerChannel
var insertViewer = hostSelectedTrackChannel.mInsertAndStripEffects.makeInsertEffectViewer('Inserts Viewer').followPluginWindowInFocus()
var insertsParamZone = insertViewer.mParameterBankZone
var noOfParams = // insertViewer.numberOfParameters (or similar)...
for (var i = 1; i <= noOfParams; ++i) {
  var param = 'param' + i
  var piParam = surfaceElements.params[param]
  var piParamValue = piParam.mSurfaceValue
  page.makeValueBinding(piParamValue, insertsParamZone.makeParameterValue())
    }

Is there something in the API that can look at an insert plug-in in edit mode and “get” the number of editable parameters? For example, if a plug-in has 160 parameters, then the MIDI Remote would dynamically create 160 knobs in the parameters zone (left of screen):

If there isn’t, I guess I’ll create a feature request instead.

Thanking y’all in advance.

1 Like

Sorry - I have a further question that may be best answered by @Jochen_Trappe , but maybe someone has stumbled across it before.
As it stands, value binding a parameter knob to an insert plug-in parameter can be done by:
(SurfaceValue, HostInsertEffectViewer.HostPluginParameterBankZone.makeParameterValue())
However, it appears that there’s no way to determine the parameter number the parameter knob is bound to. The binding defaults to Parameter 1 and additional parameter knobs are bound to the plug-in parameters in sequential order (Parameter 2, Parameter 3… Parameter n) . I guess this wouldn’t be such an issue if there weren’t unmapped plug-in parameters, like Parameters 14 and 21 below:

So, my question is, do either of the MR_HostPluginParameterBank Zone or MR_HostPluginParameterBankValue classes have methods for:

  1. Returning a number that represents a plug-in’s number of active parameters (from my previous post)?

  2. Binding a chosen parameter’s value to a surface value, say, Parameter 6 to parameter knob 2?

  3. If you’re keeping the existing sequential order of binding new parameter values, is there a way to skip/ignore the unmapped parameters, such as adding Parameters 1, 2, 3, …, 13, 15, 16, …, 20, 22, 23, … in the example above?

Sorry for the long posts - I hope they make sense! :roll_eyes:

I see no automatic way to do this.
The way I dealt with it is by creating maps of controls for each effect. At first I finished with channelStrip plugins, but with third party vsts I left it for now :slight_smile:

Long story short, you do something like this:

mapOfStripControls[constSubPageCompressor]['Standard Compressor']=[0,1,2,3,19,4,18,5,6,7,8,9,10,11,12,13]

The numbers you see in this list are some of my controller’s knobs/faders/buttons.
I have assigned these controls to custom variables. When these variables are triggered, I’m searching their index in the above list to alter the value of another custom variable binded to the specific param of the plugin.

I guess it’s a bit complicated, but it works,though not auto obviously :frowning:

Check my video, you’ll see params changing on the screen while I’m turning knobs in not a serial way (sorry, this is out of view unfortunately):

Hi @m.c !

Thanks for your response.

I’m happy to say that I believe I’m getting closer to a solution. I’ve finally sorted out accessing a plugin’s automatically-generated param labels (the Main Label field in the Remote Control Editor) and add them to an array. There always seems to be duplicate param controls assigned to a plugin, so I think the next step will be to filter the duplicate params from the array, leaving unique params only, then use the array’s param elements somehow when mapping surface parameter knobs to the unique params.

Like you, I think this process is unnecessarily long and convoluted. I’m glad to see you’ve worked out a solution that works for you. I believe the MIDI Remote API is a continuallly-evolving project for Steinberg, so hopefully these posts will help inform their development.