Before the arrival of the new MIDI Remote, I was using Mackie protocol, since this was the one available for my controllers. With it, it was possible to hold Ctrl or Shift on a QWERTY and then do multiple selection of tracks in the mixer bank, pretty much the same way as we do it with mouse clicks and the modifiers mentioned. However, I didn’t like it, cause since I’m always in front of a MIDI Keyboard, I prefer such actions to be taken by directly using it, and not by involving QWERTY (and/or mouse). So I’ve turned to an external utility (BOME MIDI Translator Pro) in order to have two of my MIDI Keyboard buttons acting as CTRL and Shift. This worked well.
When the new MIDI Remote came out, I decided to completely move on if possible from the MCU to it. I soon found out, that these modifiers wouldn’t work., but since they were not really a big part of my workflow, I didn’t bother to look deeper into this. However, seeing some additions in CB13 and 14, I realised that it was actually doable, so even if it was not a crucial functionality for me, I scripted it. Some days ago I saw a thread about this, so I thought I should share my work on this subject.
Initially I thought that since the mSelected property of channels is exposed, I could easily instruct CB to choose multiple channels by simply setting this property on (1). I found out that this fails, and for good reasons I believe, but this is out of the context of this post.
Anyway, the solution I came up with, is to combine the power of MIDI Remote and the Project Process Logical Editor. Here’s the idea:
- Using a PLE, we can always select channels, the names of which contain a keyword.
- MIDI Remote on the other hand, is capable of manipulating channels’ names.
So, if we decide which channels to select, we can always add a keyword to their name using MIDI Remote, and then calling a PLE for selecting the channels containing this keyword. Finally, we need a second PLE to restore the track names (we could use MR for that) to their original ones, by removing this keyword.
Everything else in the code is trivial.
Here’s a MIDI Remote example: Test_Multiple Channels Selection.midiremote (2.3 KB)
We need two PLEs as mentioned, to be installed too:
20251011_selectSelected.xml (3.7 KB)
20251011_removeAddSel.xml (4.4 KB)
Screenshot of the first one:
And the second one:
Here’s the source code of the example:
var midiremote_api = require('midiremote_api_v1')
var deviceDriver = midiremote_api.makeDeviceDriver("Test","Multiple Channels Selection","m.c")
var midiInput = deviceDriver.mPorts.makeMidiInput("anInput")
var midiOutput = deviceDriver.mPorts.makeMidiOutput("anOutput")
var detectionUnit=deviceDriver.makeDetectionUnit()
detectionUnit.detectPortPair(midiInput, midiOutput)
.expectInputNameEquals("Bome MIDI Translator 1")
.expectOutputNameEquals("Bome MIDI Translator 1")
var surface=deviceDriver.mSurface
var mapping=deviceDriver.mMapping
var buttons=[]
for(var i=0;i<8;i++){
buttons.push(createButton(i,i,0))
}
var buttonsActionsDescriptions=["AddToSelected","AddUpToSelected"]
var buttonsActions={}
var labelsActions=[]
var i=0
buttonsActionsDescriptions.forEach(function(description){
buttonsActions[description]=createButton(8+i,i,1)
labelsActions.push(surface.makeLabelField(2*i,1,2,1))
i++
})
function createButton(cc,x,y){
var button=surface.makeButton(2*x,y,2,1)
button.mSurfaceValue.mMidiBinding.setInputPort(midiInput).bindToControlChange(0,cc)
return button
}
var page=mapping.makePage("Default")
var subPagesAreaSelCtrlShift=page.makeSubPageArea("SelCtrlShift")
var subPagesSelCtrlShift=[]
var subPagesDescs=["Select","Ctrl","Shift"]
var i=0
subPagesDescs.forEach(function(desc){
subPagesSelCtrlShift.push(subPagesAreaSelCtrlShift.makeSubPage(desc))
if(i>0){
page.setLabelFieldText(labelsActions[i-1],subPagesDescs[i])
}
i++
})
var mixerZone=page.mHostAccess.mMixConsole.makeMixerBankZone("mixerZone").excludeInputChannels().excludeOutputChannels()
var daMixer=page.mHostAccess.makeDirectAccess(mixerZone)
var tagSelected=4000
var tagName=1024
var channels=[]
var customHostCtrl=[]
var customHostShift=[]
var customPLE=surface.makeCustomValueVariable("customPLE")
for(var i=0;i<8;i++){
var channel=mixerZone.makeMixerBankChannel()
channels.push(channel)
page.makeValueBinding(buttons[i].mSurfaceValue,channel.mValue.mSelected).setSubPage(subPagesSelCtrlShift[0])
var customHostValueCtrl=page.mCustom.makeHostValueVariable("customHostValueCtrl"+i)
var customHostValueShift=page.mCustom.makeHostValueVariable("customHostValueShift"+i)
customHostCtrl.push(customHostValueCtrl)
customHostShift.push(customHostValueShift)
page.makeValueBinding(buttons[i].mSurfaceValue,customHostCtrl[i]).setSubPage(subPagesSelCtrlShift[1]).mOnValueChange=function(activeDevice,activeMapping,value,diff){
if(value==1) prepareSelection(activeDevice,activeMapping,this.i,0)
}.bind({i})
page.makeValueBinding(buttons[i].mSurfaceValue,customHostShift[i]).setSubPage(subPagesSelCtrlShift[2]).mOnValueChange=function(activeDevice,activeMapping,value,diff){
if(value==1) prepareSelection(activeDevice,activeMapping,this.i,1)
}.bind({i})
}
var customCtrlState=surface.makeCustomValueVariable("customCtrlState")
var customShiftState=surface.makeCustomValueVariable("customShiftState")
var customNoState=surface.makeCustomValueVariable("customNoState")
buttonsActions["AddToSelected"].mSurfaceValue.mOnProcessValueChange=function(activeDevice,value,diff){
customCtrlState.setProcessValue(activeDevice,value)
customNoState.setProcessValue(activeDevice,1-value)
if(value==1) customShiftState.setProcessValue(activeDevice,0)
}
buttonsActions["AddUpToSelected"].mSurfaceValue.mOnProcessValueChange=function(activeDevice,value,diff){
customShiftState.setProcessValue(activeDevice,value)
customNoState.setProcessValue(activeDevice,1-value)
if(value==1) customCtrlState.setProcessValue(activeDevice,0)
}
page.makeActionBinding(customNoState,subPagesSelCtrlShift[0].mAction.mActivate)
page.makeActionBinding(customCtrlState,subPagesSelCtrlShift[1].mAction.mActivate)
page.makeActionBinding(customShiftState,subPagesSelCtrlShift[2].mAction.mActivate)
page.makeCommandBinding(customPLE,"Process Project Logical Editor","20251011_selectSelected")
function prepareSelection(activeDevice,activeMapping,channelIndex,ctrlOrShift){
var flagSelected=false
var limitUpChannel=-1
var limitDnChannel=-1
if(ctrlOrShift==1){
//find first selected
for(var i=0;i<8;i++){
if(i!=channelIndex){
var channelID=channels[i].getRuntimeID(activeMapping)
if(channelID>0){
var selected=daMixer.getParameterProcessValue(activeMapping,channelID,tagSelected)
if(selected==1){
limitDnChannel=Math.min(channelIndex,i)
limitUpChannel=Math.max(channelIndex,i)
break
}
}
}
}
}
for(var i=0;i<8;i++){
var channelID=channels[i].getRuntimeID(activeMapping)
if(channelID>0){
var selected=daMixer.getParameterProcessValue(activeMapping,channelID,tagSelected)
if(i==channelIndex && ctrlOrShift==0) selected=1-selected
if(selected==1 || (i>=limitDnChannel && i<=limitUpChannel)){
var channelNameForPLE=daMixer.getParameterDisplayValue(activeMapping,channelID,tagName)+"addSel"
daMixer.setParameterDisplayValue(activeMapping,channelID,tagName,channelNameForPLE)
flagSelected=true
}
}
}
if(flagSelected) customPLE.setProcessValue(activeDevice,1)
}
In this example I’ve assigned MIDI CCs 0-7 (Channel 0) to track selection, while CC 8 acts as a pseudo CTRL and CC 9 as a Shift.
Here’s a video demonstrating this remote:

