In the MIDI Remote API at its latest version (check here: MIDI Remote API 1.3 + Updated Docs) there have been some great additions.
Two of them are the methods getParameterProcessValueType and convertParameterProcessValueToPlain to the Direct Access entity.
I will share why I find them significant for my own workflow.
Having my controllers’ faders/knobs/buttons, it has always been difficult for me to properly assign discrete parameters of VSTs to buttons. I always needed to hard-code in my scripts the steps a discrete parameter has, otherwise handling these parameters with buttons were really of no value at all.
Let me give you an example. Let’s take Retrologue, (one of my favourite software synths) as a starting point. One of the main parameters is Osc 1 Waveform. As we know this can have just 4 values (Sine, Triangle, Saw and Square). Now, the thing is, I don’t want to “spend” a knob of my controller for this parameter. I always try to bind this to a button which by pressing iterates through these four values. Until the recent advancements in MR API, I had to do this manually, by properly handling such parameters inside my script, hard-coded as said.
But now, I no longer have to do this. The above mentioned methods, provide all I need to handle such parameters without even knowing the exact steps’ count they have. This is simply because I can now get this steps’ count with these methods!
The da.getParameterProcessValueType(activeMapping,objectID,parameterTag) will return “discrete” if the parameter is… discrete, while the value of da.convertParameterProcessValueToPlain(activeMapping,objectID,parameterTag,1) is the steps’ count!
That’s all we need to properly bind buttons to such discrete values.
Here’s a small video demonstrating how 4 buttons of my controller alter the Mono, Glide, Osc1 Wave and Osc1 Octave properly. Have a look at the top-left part of the Retrologue window and at how pressing the buttons alter the corresponding values.
And here’s the snippet used. In order for this script to work properly, we first have to set our discrete controls in even rows of the Remote Control Editor for our instrument plugins.
// @ts-nocheck
var midiremote_api = require('midiremote_api_v1')
var deviceDriver = midiremote_api.makeDeviceDriver("Test", "Step Count", "mc")
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 knobs=[]
var buttons=[]
var realButtons=[] //These are our real buttons. Their presses will be used for incrementing the values of the discrete parameters we've setup in Remote Control Editor at its even rows
for(var i=0;i<8;i++){
var knob=surface.makeKnob(4*i,0,4,4)
knob.mSurfaceValue.mMidiBinding
.setInputPort(midiInput)
.bindToControlChange(0,i)
knobs.push(knob)
var button=surface.makeButton(4*i,6,4,1)
buttons.push(button)
var realButton=surface.makeCustomValueVariable("realButton"+i)
realButton.mMidiBinding
.setInputPort(midiInput)
.bindToControlChange(0,8+i)
realButtons.push(realButton)
}
var mapping = deviceDriver.mMapping
//Host Mapping
var page = mapping.makePage("Default")
var daMixer=page.mHostAccess.makeDirectAccess(page.mHostAccess.mMixConsole)
var instrumentSlotBankZone=page.mHostAccess.mTrackSelection.mMixerChannel.mInstrumentPluginSlot.mParameterBankZone
var instrumentParams=[]
var instrumentParamsNames=["","","","","","","",""]
for(var i=0;i<2;i++){
for(var j=0;j<8;j++){
instrumentParams.push(instrumentSlotBankZone.makeParameterValue())
if(i==0){
page.makeValueBinding(knobs[j].mSurfaceValue,instrumentParams[j])
} else {
page.makeValueBinding(buttons[j].mSurfaceValue,instrumentParams[8+j])
buttons[j].mSurfaceValue.mOnTitleChange=function(activeDevice,title1,title2){
instrumentParamsNames[this.j]=title2
}.bind({j})
buttons[j].mSurfaceValue.mOnDisplayValueChange=function(activeDevice,dispVal1,dispVal2){
console.log(instrumentParamsNames[this.j]+": "+dispVal1)
}.bind({j})
}
}
}
for(var k=0;k<8;k++){
realButtons[k].mOnProcessValueChange=function(activeDevice,value,diff){
console.log("Button "+(this.k+1)+" pressed")
var currentParamValue=buttons[this.k].mSurfaceValue.getProcessValue(activeDevice)
var paramName=instrumentParamsNames[this.k]
var paramStepCount=(paramName in pluginParameters) ? Math.max(pluginParameters[paramName],1) : 100 // No high resolution needed for these discrete values
newParamValue=Math.round(100*(currentParamValue+1/paramStepCount))/100
if(newParamValue>1) {newParamValue=0}
buttons[this.k].mSurfaceValue.setProcessValue(activeDevice,newParamValue)
}.bind({k})
}
var selectedTrackInstrumentSlot=page.mHostAccess.mTrackSelection.mMixerChannel.mInstrumentPluginSlot
var pluginParameters={}
selectedTrackInstrumentSlot.mOnTitleChange=function(activeDevice,activeMapping,title){
var slotID=selectedTrackInstrumentSlot.getRuntimeID(activeMapping)
var pluginSlotID=daMixer.getChildObjectID(activeMapping,slotID,0)
var numOfParams=daMixer.getNumberOfParameters(activeMapping,pluginSlotID)
pluginParameters={}
if(numOfParams>0){
for(var i=0;i<numOfParams;i++){
var paramTag=daMixer.getParameterTagByIndex(activeMapping,pluginSlotID,i)
var paramTitle=daMixer.getParameterTitle(activeMapping,pluginSlotID,paramTag,128).trim()
var paramType=daMixer.getParameterProcessValueType(activeMapping,pluginSlotID,paramTag)
if(paramType=="discrete"){
//Only care about discrete parameters here
var paramStepCount=daMixer.convertParameterProcessValueToPlain(activeMapping,pluginSlotID,paramTag,1)
pluginParameters[paramTitle]=paramStepCount
}
}
}
}