Cubase Midi Remote - Bank Navigation Problem

I bought a Stream Deck Plus XL and I’m redoing the script I had made with midi remote api.

Everything works correctly except when I change banks in Cubase by pressing the bank change buttons on the created surface.

When I change banks by pressing the buttons everything is synchronized correctly except the names of the tracks, the colors, the panorama value and the fader position value.

In other words, every time I change banks by pressing the midi remote buttons, the names, colors, and numerical values ​​of fader and panorama are always the values ​​of the tracks in bank number 1.

That is, when I press the next bank and previous bank buttons, the sysex I receive are always from bank 1, while the rest of the information is correct.

On the other hand, if I select tracks directly in the cubase project with the mouse, the bank change is completely performed, everything is synchronized correctly.

What is wrong with my code? Why do the bank change buttons change the bank but not update the sysex messages?

var midiremote_api = require('midiremote_api_v1')
var deviceDriver = midiremote_api.makeDeviceDriver('elgato', 'Stream Deck Plus', 'JCT')
var midiInput = deviceDriver.mPorts.makeMidiInput()
var midiOutput = deviceDriver.mPorts.makeMidiOutput()
deviceDriver.makeDetectionUnit().detectPortPair(midiInput, midiOutput)
    .expectInputNameEquals('IAC_EverybodyToCubaseMidi')
    .expectOutputNameEquals('IAC_CubaseMidiToEverybody')

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

var bankSize = 6
var bankMidiChannel = 10 //channel 11
var bankSysexHeader = 0x01

var hostBankZone = page.mHostAccess.mMixConsole.makeMixerBankZone('BankZone')
hostBankZone.excludeInputChannels()
hostBankZone.excludeOutputChannels()
hostBankZone.excludeWindowZoneLeftChannels()
hostBankZone.excludeWindowZoneRightChannels()
hostBankZone.setFollowVisibility(true)

var hostBankTrack = []
for (var i = 0; i < bankSize; i++) {
    hostBankTrack[i] = hostBankZone.makeMixerBankChannel()
}

var surface = deviceDriver.mSurface

var surfaceRows_BankMutes = 1
var surfaceRows_BankSolos = 2
var surfaceRows_BankPans = 3
var surfaceRows_BankVols = 4
var surfaceRows_BankSelects = 8

// MUTE
var surfaceBankMuteButton = []
for (var i = 0; i < bankSize; i++) {
    surfaceBankMuteButton[i] = surface.makeButton (i, surfaceRows_BankMutes, 1, 1)
    surfaceBankMuteButton[i].mSurfaceValue.mMidiBinding
        .setInputPort(midiInput)
        .setOutputPort(midiOutput)
        .bindToControlChange(bankMidiChannel, 21 + i) 
}

// SOLO
var surfaceBankSoloButton = []
for (var i = 0; i < bankSize; i++) {
    surfaceBankSoloButton[i] = surface.makeButton (i, surfaceRows_BankSolos, 1, 1)
    surfaceBankSoloButton[i].mSurfaceValue.mMidiBinding
        .setInputPort(midiInput)
        .setOutputPort(midiOutput)
        .bindToControlChange(bankMidiChannel, 31 + i) 
}

// PAN
var surfaceBankPanoramaKnob = []
for (var i = 0; i < bankSize; i++) {
    surfaceBankPanoramaKnob[i] = surface.makeKnob(i, surfaceRows_BankPans, 1, 1)
    surfaceBankPanoramaKnob[i].mSurfaceValue.mMidiBinding
        .setInputPort(midiInput)
        .setOutputPort(midiOutput)
        .bindToControlChange(bankMidiChannel, 11 + i) 
}

// VOLUME
var surfaceBankVolumeFader = []
for (var i = 0; i < bankSize; i++) {
    surfaceBankVolumeFader[i] = surface.makeFader(i, surfaceRows_BankVols, 1, 3)
    surfaceBankVolumeFader[i].mSurfaceValue.mMidiBinding
        .setInputPort(midiInput)
        .setOutputPort(midiOutput)
        .bindToControlChange(bankMidiChannel, 1 + i) 
}

// SELECT
var surfaceBankSelectButton = []
for (var i = 0; i < bankSize; i++) {
    surfaceBankSelectButton[i] = surface.makeButton (i, surfaceRows_BankSelects, 1, 1)
    surfaceBankSelectButton[i].mSurfaceValue.mMidiBinding
        .setInputPort(midiInput)
        .setOutputPort(midiOutput)
        .bindToControlChange(bankMidiChannel, 41 + i) 
}

// BANK NAVIGATION
var surfaceBankButtonSwitchToPrevBank = surface.makeButton (0,10,0,0)
var surfaceBankButtonSwitchToNextBank = surface.makeButton (1,10,0,0)
var surfaceBankButtonShiftLeft = surface.makeButton (2,10,0,0)
var surfaceBankButtonShiftRight = surface.makeButton (3,10,0,0)
var surfaceBankButtonResetBank = surface.makeButton (4,10,0,0)
surfaceBankButtonSwitchToPrevBank.mSurfaceValue.mMidiBinding
    .setInputPort(midiInput)
    .setOutputPort(midiOutput)
    .bindToControlChange (bankMidiChannel, 126) // channel 11, cc 126
surfaceBankButtonSwitchToNextBank.mSurfaceValue.mMidiBinding
    .setInputPort(midiInput)
    .setOutputPort(midiOutput)
    .bindToControlChange (bankMidiChannel, 127) // channel 11, cc 127
surfaceBankButtonShiftLeft.mSurfaceValue.mMidiBinding
    .setInputPort(midiInput)
    .setOutputPort(midiOutput)
    .bindToControlChange (bankMidiChannel, 101) // channel 11, cc 101
surfaceBankButtonShiftRight.mSurfaceValue.mMidiBinding
    .setInputPort(midiInput)
    .setOutputPort(midiOutput)
    .bindToControlChange (bankMidiChannel, 102) // channel 11, cc 102
surfaceBankButtonResetBank.mSurfaceValue.mMidiBinding
    .setInputPort(midiInput)
    .setOutputPort(midiOutput)
    .bindToControlChange (bankMidiChannel, 103) // channel 11, cc 103

for (var i = 0; i < bankSize; i++) {
    page.makeValueBinding(surfaceBankVolumeFader[i].mSurfaceValue, hostBankTrack[i].mValue.mVolume)
    page.makeValueBinding(surfaceBankPanoramaKnob[i].mSurfaceValue, hostBankTrack[i].mValue.mPan)
    page.makeValueBinding(surfaceBankMuteButton[i].mSurfaceValue, hostBankTrack[i].mValue.mMute)
    page.makeValueBinding(surfaceBankSoloButton[i].mSurfaceValue, hostBankTrack[i].mValue.mSolo)
    page.makeValueBinding(surfaceBankSelectButton[i].mSurfaceValue, hostBankTrack[i].mValue.mSelected)
}
page.makeActionBinding(surfaceBankButtonSwitchToPrevBank.mSurfaceValue, hostBankZone.mAction.mPrevBank)
page.makeActionBinding(surfaceBankButtonSwitchToNextBank.mSurfaceValue, hostBankZone.mAction.mNextBank)
page.makeActionBinding(surfaceBankButtonShiftLeft.mSurfaceValue, hostBankZone.mAction.mShiftLeft)
page.makeActionBinding(surfaceBankButtonShiftRight.mSurfaceValue, hostBankZone.mAction.mShiftRight)
page.makeActionBinding(surfaceBankButtonResetBank.mSurfaceValue, hostBankZone.mAction.mResetBank)


var bankTrackNames = []
var bankTrackNamesSysexHeader = 1

var bankTrackColors = []
var bankTrackColorsSysexHeader = 21

var bankTrackPanoramas = []
var bankTrackPanoramasSysexHeader = 41

var bankTrackFaderPositions = []
var bankTrackFaderPositionsSysexHeader = 61

var bankTrackTypes = []
var bankTrackTypesSysexHeader = 81

var daMixer = page.mHostAccess.makeDirectAccess(hostBankZone) //used for getting the track type in the "for" loop

for (var i = 0; i < bankSize; i++) {

    // get name and get type of every track of bank 
    hostBankTrack[i].mOnTitleChange = (function(trackIndex) {
        return function(activeDevice, activeMapping, title) {
            var validString = title.length === 0 ? " " : title
            bankTrackNames[trackIndex+1] = validString
            var bankTrackNamesConsoleLogPrefix = "Bank, Track " + (trackIndex+1) + " - Name: "
            sendSysexString(activeDevice, validString, bankSysexHeader, bankTrackNamesSysexHeader + trackIndex, bankTrackNamesConsoleLogPrefix)
            var trackID = hostBankTrack[trackIndex].getRuntimeID(activeMapping)
            var objectUniqueName = daMixer.getObjectUniqueName(activeMapping, trackID)
            var trackType = objectUniqueName.replace(/\s?\d+$/, '')
            bankTrackTypes[trackIndex + 1] = trackType
            var bankTrackTypesConsoleLogPrefix = "Bank, Track " + (trackIndex+1) + " - Type: "
            sendSysexString(activeDevice, trackType, bankSysexHeader, bankTrackTypesSysexHeader + trackIndex, bankTrackTypesConsoleLogPrefix)            
        }
    })(i)

    // get color of every track of bank
    hostBankTrack[i].mOnColorChange = (function(trackIndex) {
        return function(activeDevice,activeMapping,r,g,b,a){
            var validColorCode = getColorCodeWithoutAlphaChannel (r,g,b,a)
            bankTrackColors[trackIndex+1] = validColorCode
            var bankTrackColorsConsoleLogPrefix = "Bank, Track " + (trackIndex+1) + " - Color: "
            sendSysexString(activeDevice, validColorCode ,bankSysexHeader, bankTrackColorsSysexHeader + trackIndex, bankTrackColorsConsoleLogPrefix)
        }
    })(i)

    // get pan value of every track of bank: setup custom vars to control the pan, in order for the mOnDisplayValueChange to refresh properly
    var bankPanValues=surface.makeCustomValueVariable("bankPanValues" + i)
    page.makeValueBinding(bankPanValues, hostBankTrack[i].mValue.mPan)
    hostBankTrack[i].mValue.mPan.mOnDisplayValueChange = (function(trackIndex) {
        return function(activeDevice, activeMapping, displayValue) {
            var panValue = displayValue
            if (panValue === "") {panValue = "X"}
            bankTrackPanoramas[trackIndex+1] = panValue
            var bankTrackPanoramasConsoleLogPrefix = "Bank, Track " + (trackIndex+1) + " - Pan: "
            sendSysexString(activeDevice, panValue, bankSysexHeader, bankTrackPanoramasSysexHeader + trackIndex, bankTrackPanoramasConsoleLogPrefix)
        }
    })(i)

    // get faderPosition value of every track of bank: setup custom vars to control the faderPosition, in order for the mOnDisplayValueChange to refresh properly
    var bankFaderValues=surface.makeCustomValueVariable("bankFaderValues" + i)
    page.makeValueBinding(bankFaderValues, hostBankTrack[i].mValue.mVolume)
    hostBankTrack[i].mValue.mVolume.mOnDisplayValueChange = (function(trackIndex) {
        return function(activeDevice, activeMapping, displayValue) {
            var faderPosition = displayValue        
            bankTrackFaderPositions[trackIndex+1] = faderPosition
            var bankTrackFaderPositionsConsoleLogPrefix = "Bank, Track " + (trackIndex+1) + " - FaderPosition: "
            sendSysexString(activeDevice, faderPosition, bankSysexHeader, bankTrackFaderPositionsSysexHeader + trackIndex, bankTrackFaderPositionsConsoleLogPrefix)
        }
    })(i)
}


// GLOBAL FUNCTIONS

// convert r,g,b to hexadecimal color code "rrggbb"
function getColorCodeWithoutAlphaChannel(redCode,greenCode,blueCode,alphaChannel){
    var r255=Math.round(255*redCode)
    var g255=Math.round(255*greenCode)
    var b255=Math.round(255*blueCode)
    var a255=Math.round(255*alphaChannel)
    var r255hex=r255.toString(16)
    var g255hex=g255.toString(16)
    var b255hex=b255.toString(16)
    var a255hex=a255.toString(16)
    if (r255hex==="0") {r255hex="00"}
    if (g255hex==="0") {g255hex="00"}
    if (b255hex==="0") {b255hex="00"}
    if (a255hex==="0") {a255hex="00"}
    var colorCodeWithoutAlphaChannel=r255hex+g255hex+b255hex
    return colorCodeWithoutAlphaChannel
}

// send sysex and show log in console
function sendSysexString(activeDevice,receivedString,sysexHeader1,sysexHeader2,consolePrefix){
    var sysexMessage=[0xF0,sysexHeader1,sysexHeader2].concat(receivedString.split('').map(function(c){return c.charCodeAt(0)})).concat([0xF7])
    midiOutput.sendMidi(activeDevice,sysexMessage)
    console.log(consolePrefix + receivedString)
}

…calling @m.c :slightly_smiling_face:

By the way, I’m running Cubase 15.0.21 and macOS 15.7.7

I have changed the parameters to show the bank navigation buttons (making them visible) and the issue has been fixed.

I mean this:

var surfaceBankButtonSwitchToPrevBank = surface.makeButton (0,10,1,1)
var surfaceBankButtonSwitchToNextBank = surface.makeButton (1,10,1,1)
var surfaceBankButtonShiftLeft = surface.makeButton (2,10,1,1)
var surfaceBankButtonShiftRight = surface.makeButton (3,10,1,1)
var surfaceBankButtonResetBank = surface.makeButton (4,10,1,1)

This has nothing to do with the issue. Here, your script works as expected, I mean the original one. I have some comments to make, but totally irrelevant, so I will avoid it. Most probably, you faced a temporary glitch in the MIDI Remote, glad it’s now solved :slight_smile:

I’d like to hear them, if you don’t mind… :slightly_smiling_face:

Sure. I see duplicates. For example:

page.makeValueBinding(surfaceBankVolumeFader[i].mSurfaceValue, hostBankTrack[i].mValue.mVolume)

and later:

// get faderPosition value of every track of bank: setup custom vars to control the faderPosition, in order for the mOnDisplayValueChange to refresh properly
    var bankFaderValues=surface.makeCustomValueVariable("bankFaderValues" + i)
    page.makeValueBinding(bankFaderValues, hostBankTrack[i].mValue.mVolume)

I know why you setup these custom vars, however they’re not needed. The mOnDisplayValyeChange will get triggered, since you already properly bound the faders to the volume :slight_smile:

Thanks for your reply @m.c.

I can’t get the sysex values ​​for pan/volume using the binding I already have.

This is not working:

surfaceBankPanoramaKnob[i].mValue.mPan.mOnDisplayValueChange = (function(trackIndex) {

return function(activeDevice, activeMapping, displayValue) {

var panValue = displayValue

if (panValue === "") {panValue = "X"}  //si la pista no te slider de panorama el valor que es rep de Cubase es "". En aquests casos assignem "X" a la variable per tal d'enviar "X" com a missatge sysex.

bankTrackPanoramas[trackIndex+1] = panValue

var bankTrackPanoramasConsoleLogPrefix = "Bank, Track " + (trackIndex+1) + " - Pan: "

sendSysexString(activeDevice, panValue, bankSysexHeader, bankTrackPanoramasSysexHeader + trackIndex, bankTrackPanoramasConsoleLogPrefix)

        }

    })(i)

What am I doing wrong?

Since surfaceBankPanoramaKnob is most probably a surface element, remove the activeMapping argument. At the same time, remove the mPan. This has nothing to do with the knob.

surfaceBankPanoramaKnob[i].mSurfaceValue.mOnDisplayValueChange = (function(trackIndex) {

return function(activeDevice, displayValue,units) { ....