Combined Panner and MIDI Remote

Colleagues, please tell me how to assign a MIDI controller to the left side of Combined Panner. There is only “Pan-Left-Right” in the list of available functions, but it only controls the right side. I’m sure the developers have come up with some method that everyone has been using for 20 years, but I can’t figure it out. Maybe I’m stupid? :slight_smile:

Moreover, the “Mouse at Value Point” function, on the contrary, controls only the left side. This is absurd. Is that really how it’s meant to be? What’s the point?

Hi,

Left: Pan-Left-Right
Right: Pan-Left-Right2

Hi Martin,
if I want to control Combined Panner via MIDI Remote, I need two Quick Control Slots for this. Normally this is ok. Are there any alternatives? :sunglasses:

Hi,

Unfortunately, for the MIDI Remote (both script and Mapping Assistant), there is no direct access to the Pan-Left-Right2.

Yes, though I’m somehow positive that we can gain access in the near future :slight_smile:

Hi Martin,
if you implement this, then it would also be good if there is a function in the API that can be used to query and set the status of the Panner Type and that the Bypass function for the Panner is also implemented.
I think it would be ideal if you extended the functionality of the API here:

Extract from the current MIDI Remote API 1.1:
MixerChannelValues
Inherits From: HostObject
Referenced by: MixerBankChannel, SelectedTrackChannel
Properties

mPan : PanValue

Proposal for the future expansion of the API::

mPan : PanValue
mPan2 : PanValue
mPanType : PanTypeValue
mPanBypass : PanBypassValue

PanTypeValues :
0 == Standard Panner (default)
1 == Combined Panner

PanBypassValues :
0 == Panner Bypass Off (default)
1 == Panner Bypass On

Some of the MIDI Remote programmers here in this fourm are so advanced that they can use an extension of the API in such a way that the scripts could then automatically set themselves to the different Panners for each track and then assign one or two functions (Pan / Pan2) as required and additionally support the script user, e.g. with special LED coding…

Best regards
CKB :innocent:

I don’t think Steinberg will ever do that. I’ve been waiting for this opportunity for over 10 years. At the moment, the developers decided to make this problem worse by removing the "switch from normal pan to combined"option from the drop-down menu. I think over time, they plan to completely remove this feature, since no one uses this function except for me and a couple of other people.

I would also love to see this feature coming!

Hi @CKB, I totally agree with your (and other friends here) request.

In the mean time, and since I know you like to dive deep into the MR, here’s a snippet that a) changes the stereo mode, b) toggles bypass and c) adds the pan L-R 2 functionality.

I know this may raise some questions, feel free you (and everyone else) to post on a dedicated thread if you need to.

var midiremote_api = require('midiremote_api_v1')

var deviceDriver = midiremote_api.makeDeviceDriver("Test","Pan Modes","m.c")

var midiInput = deviceDriver.mPorts.makeMidiInput("anInput")
var midiOutput = deviceDriver.mPorts.makeMidiOutput("anOutput")

var detectionUnit=deviceDriver.makeDetectionUnit()

detectionUnit.detectPortPair(midiInput, midiOutput)
    .expectInputNameEquals("an input")
    .expectOutputNameEquals("an output")

var surface=deviceDriver.mSurface

var buttonPanMode=surface.makeButton(0,0,1,1)
buttonPanMode.mSurfaceValue.mMidiBinding
    .setInputPort(midiInput)
    .bindToControlChange(0,20)

var buttonPanByPass=surface.makeButton(1,0,1,1)
buttonPanByPass.mSurfaceValue.mMidiBinding
    .setInputPort(midiInput)
    .bindToControlChange(0,21)

var knobPan=surface.makeKnob(2,0,1,1)
knobPan.mSurfaceValue.mMidiBinding
    .setInputPort(midiInput)
    .bindToControlChange(0,22)

var knobPan2=surface.makeKnob(3,0,1,1)
knobPan2.mSurfaceValue.mMidiBinding
    .setInputPort(midiInput)
    .bindToControlChange(0,23)
    
var customModeVar=surface.makeCustomValueVariable("customModeVar")
var customByPassVar=surface.makeCustomValueVariable("customByPassVar")
var customPanVar=surface.makeCustomValueVariable("customPanVar")
var customPan2Var=surface.makeCustomValueVariable("customPan2Var")

var mapping=deviceDriver.mMapping

var page=mapping.makePage("page")

//This is the Direct Access object we start with. I've chosen to control the Pan of the selected track
var selTrackDA=page.mHostAccess.makeDirectAccess(page.mHostAccess.mTrackSelection.mMixerChannel)

//The pan object (child) has an index of 2
var panObjectIndex=2

//The parameter tags we're interested in 
var panStereoModeTag=4302
var panLeftRightTag=4201
var panLeftRight2Tag=4204
var panByPassTag=4301

//We need customHostValue vars in order to expose activeMapping
var customHostValuePanMode=page.mCustom.makeHostValueVariable("customHostValuePanMode")

var customHostValuePanByPass=page.mCustom.makeHostValueVariable("customHostValuePanByPass")

var customHostValuePanValue=page.mCustom.makeHostValueVariable("customHostValuePanValue")

var customHostValuePan2Value=page.mCustom.makeHostValueVariable("customHostValuePan2Value")


buttonPanMode.mSurfaceValue.mOnProcessValueChange=function(activeDevice,value,diff){

    if(value==1){

        customModeVar.setProcessValue(activeDevice,1-customModeVar.getProcessValue(activeDevice))

     }
    
}


page.makeValueBinding(customModeVar,customHostValuePanMode).mOnValueChange=function(activeDevice,activeMapping,value,diff){

    setPanParamValue(activeMapping,panStereoModeTag,0)    

}

buttonPanByPass.mSurfaceValue.mOnProcessValueChange=function(activeDevice,value,diff){

    if(value==1){

        customByPassVar.setProcessValue(activeDevice,1-customByPassVar.getProcessValue(activeDevice))
    
    }
    
}


page.makeValueBinding(customByPassVar,customHostValuePanByPass).mOnValueChange=function(activeDevice,activeMapping,value,diff){

    setPanParamValue(activeMapping,panByPassTag,0)

}

knobPan.mSurfaceValue.mOnProcessValueChange=function(activeDevice,value,diff){

    //adjust accordingly, based on the type of knob (abs, rel, 2sComp)
    //in this example I assume abs mode (0->127)

    var newDiff=value==0 || diff<0 ? -1 : 1
    
    customPanVar.setProcessValue(activeDevice,newDiff*(1+Math.random()))
    //Using random in order to send a different value each time, otherwise the mOnValueChange of the hostValue won't get triggered

}

page.makeValueBinding(customPanVar,customHostValuePanValue).mOnValueChange=function(activeDevice,activeMapping,value,diff){

    setPanParamValue(activeMapping,panLeftRightTag,value)

}

knobPan2.mSurfaceValue.mOnProcessValueChange=function(activeDevice,value,diff){

    //adjust accordingly, based on the type of knob (abs, rel, 2sComp)
    //in this example I assume abs mode (0->127)

    var newDiff=value==0 || diff<0 ? -1 : 1
    
    customPan2Var.setProcessValue(activeDevice,newDiff*(1+Math.random()))

}

page.makeValueBinding(customPan2Var,customHostValuePan2Value).mOnValueChange=function(activeDevice,activeMapping,value,diff){

    setPanParamValue(activeMapping,panLeftRight2Tag,value)

}


function setPanParamValue(activeMapping,paramTag,type){
    
    var selTrackObjectID=selTrackDA.getBaseObjectID(activeMapping)
    var panObjectID=selTrackDA.getChildObjectID(activeMapping,selTrackObjectID,panObjectIndex)

    var panParamValue=selTrackDA.getParameterProcessValue(activeMapping,panObjectID,paramTag)


    if(type==0){

        //toggle 
        panParamValue=1-panParamValue

    } else {

        //inc/dec
        var tmpPanParamValue=panParamValue+(type>0 ? 1 : -1)*1/255 //half step
        if (tmpPanParamValue<=1 && tmpPanParamValue>=0){

            panParamValue=tmpPanParamValue
        
        }
    
    }
    
    selTrackDA.setParameterProcessValue(activeMapping,panObjectID,paramTag,panParamValue)

}

page.mOnActivate=function(activeDevice,activeMapping){

    //The DA object needs to be activated in order for the mOnParameterChange to get triggered, at least so I presume 
    selTrackDA.activate(activeMapping)

}

selTrackDA.mOnParameterChange=function(activeDevice,activeMapping,objectID,paramTag){

    var paramValue=selTrackDA.getParameterProcessValue(activeMapping,objectID,paramTag)
    var paramDisplayValue=selTrackDA.getParameterDisplayValue(activeMapping,objectID,paramTag)

    var paramTitle=selTrackDA.getParameterTitle(activeMapping,objectID,paramTag,255)
    
    console.log(paramTitle+ " val="+paramValue+" disp="+paramDisplayValue)

}
2 Likes

Good work, m.c
I think Steinberg will provide a simple access to the pan parameters in the API later, but it’s hard to say when they will have time for that.

Nice to see that direct access to the pan parameters on an internal basis also works. :smiling_face_with_sunglasses:

1 Like

My point exactly. This new object can handle many cases before (and if) they are implemented.
For example, I recall an older query from you, concerning unique ids of tracks. This is totally doable using this object.

1 Like