Using a script, how do I make one subpage have "unbound" values?

To better explain…

I have a controller with an XY Joystick and it can be pushed as a button.

I have successfully written a script so that the joystick has 2 sub-pages and pushing the joystick button toggles between those two mapping pages. On one sub-page, I have the joystick bound to quick controls 1 and 2 (X and Y axes). Can I write the code in the script so that on the other sub-page, the X and Y axes are “unbound”? This way, the CC values that the joystick are sending are being seen by Cubase but it isn’t doing anything with it.

I thought of a couple of possibilities but will have to try them tomorrow….

I could makes another “main page” that has the joystick unassigned and just duplicate the other controls?

Or, can I send a NULL into the binding command? (i.e.)

Hi,

I made a Bypass button this way…

//----------------------------------------------------------------------------------------------------------------------
// 2. SURFACE LAYOUT - create control elements and midi bindings
//----------------------------------------------------------------------------------------------------------------------
surfaceElements.buttonA = surface.makeButton(0, 1, 2, 1)
surfaceElements.buttonA.mSurfaceValue.mMidiBinding.setInputPort(midiInput).bindToControlChange(0, 1)

surfaceElements.buttonB = surface.makeButton(0, 2, 2, 1)
surfaceElements.buttonB.mSurfaceValue.mMidiBinding.setInputPort(midiInput).bindToControlChange(0, 2)

surfaceElements.bypassButton = surface.makeButton(3, 1.5, 1, 1)
surfaceElements.bypassButton.mSurfaceValue.mMidiBinding.setInputPort(midiInput).bindToControlChange(0, 11)

var surfaceElementNames = Object.keys(surfaceElements)
for (var i = 0; i < surfaceElementNames.length; ++i)
	processDummyElements(surfaceElementNames[i])

//----------------------------------------------------------------------------------------------------------------------
// 3. HOST MAPPING - create mapping pages and host bindings
//----------------------------------------------------------------------------------------------------------------------
function makePageWithDefaults(name) {
	var page = deviceDriver.mMapping.makePage(name)

	surfaceElements.bypassButton.mSurfaceValue.mOnProcessValueChange = function(context, value) {
		console.log('bypassState Switched to: ' + bypassState)
		bypassState = value
	}

	return page
}

//----------------------------------------------------------------------------------------------------------------------
function makePageA() {
	var page = makePageWithDefaults('A')

	var selectedTrackChannel = page.mHostAccess.mTrackSelection.mMixerChannel
	page.makeValueBinding(dummySurfaceElements.buttonA, selectedTrackChannel.mValue.mSolo).setTypeToggle()
	page.makeValueBinding(dummySurfaceElements.buttonB, selectedTrackChannel.mValue.mMute).setTypeToggle()

	return page
}

//----------------------------------------------------------------------------------------------------------------------
function makePageB() {
	var page = makePageWithDefaults('B')

	var selectedTrackChannel = page.mHostAccess.mTrackSelection.mMixerChannel
	page.makeValueBinding(dummySurfaceElements.buttonA, selectedTrackChannel.mValue.mRecordEnable).setTypeToggle()
	page.makeValueBinding(dummySurfaceElements.buttonB, selectedTrackChannel.mValue.mMonitorEnable).setTypeToggle()

	return page
}

//----------------------------------------------------------------------------------------------------------------------
// Construct Pages
//----------------------------------------------------------------------------------------------------------------------
var pageA = makePageA()
var pageB = makePageB()

pageA.mOnActivate = function (context) {
	console.log('from Bypass MIDI Remote script: "A" page activated')
}

pageB.mOnActivate = function (context) {
	console.log('from Bypass MIDI Remote script: "B" page activated')
}

If the Bypass is enabled, Cubase passes the MIDI data to the track (like if there is no any MIDI Remote assignment).

Martin, thanks for the reply and your help.

I am sorry but I am not seeing how this works. Is some code missing from your pasted section? I see that when the bypass button is pushed, it sets bypassState but bypassState is only ever used to write to the console. I also don’t see where the dummySurfaceElements are declared or created. Maybe there is something missing there?

I don’t see a connection between the “actual” surfaceElements.buttonA/B and the dummySurfaceElements.buttonA/B.

JL

Bump.

Note that subpages is not the same thing with pages.
Can you share your script so we can have a look?

Certainly, and thank you for the help…

// get the api's entry point
var midiremote_api = require('midiremote_api_v1')

// create the device driver main object
var deviceDriver = midiremote_api.makeDeviceDriver('JasonMade', 'MyMIDIController', 'Jason')


// create objects representing the hardware's MIDI ports
var midiInput = deviceDriver.mPorts.makeMidiInput()
var midiOutput = deviceDriver.mPorts.makeMidiOutput()

// define all possible namings the devices MIDI ports could have
// NOTE: Windows and MacOS handle port naming differently
deviceDriver.makeDetectionUnit().detectPortPair(midiInput, midiOutput)
    .expectInputNameEquals('MyMIDIControl')
    .expectOutputNameEquals('MyMIDIControl')

// create the joystick
var joystickXY = deviceDriver.mSurface.makeJoyStickXY(16, 8, 7, 7)
joystickXY.mX.mMidiBinding.setInputPort(midiInput).bindToControlChange(0,21)
joystickXY.mY.mMidiBinding.setInputPort(midiInput).bindToControlChange(0,22)
var joyButton = deviceDriver.mSurface.makeButton(16, 16, 7, 3);
joyButton.mSurfaceValue.mMidiBinding.setInputPort(midiInput).bindToControlChange(0,23)

// create the default mapping page
var mainPage = deviceDriver.mMapping.makePage('Default')
// create joystick subpages
var subPageJoystick = mainPage.makeSubPageArea('subPageJoystick')
var subPageQCs = subPageJoystick.makeSubPage('Quick Controls')
var subPageCCs = subPageJoystick.makeSubPage('CC 21 & CC 22')

// bind joystick button to switch between subpages
mainPage.makeActionBinding(joyButton.mSurfaceValue, subPageCCs.mAction.mActivate)
    .setSubPage(subPageQCs)
mainPage.makeActionBinding(joyButton.mSurfaceValue, subPageQCs.mAction.mActivate)
    .setSubPage(subPageCCs)


// in the QC subpage, bind the joystick to the Quick Controls 7 & 8.
mainPage.makeValueBinding(joystickXY.mX, mainPage.mHostAccess.mFocusedQuickControls.getByIndex(6)).setSubPage(subPageQCs)
mainPage.makeValueBinding(joystickXY.mY, mainPage.mHostAccess.mFocusedQuickControls.getByIndex(7)).setSubPage(subPageQCs)

// in the CC subpage, leave them "unbound" so they can just pass through...

This is what I have so far… and it works for controlling Quick Controls 7 & 8 (on both “subpages” because I haven’t yet written any binding for the “CC#21 and CC#22” subpage yet). I don’t really know how to do it but what I want is that when I switch to the other subPageCC, I want the midi data to just pass through to the instrument.

So, if I have an older VST instrument that just needs to “learn” CC#21 and/or CC#22 to control additional parameters, I can just click the joystick button and, instead of the joystick controlling Quick Controls 7 & 8, it is now just “unassigned” midi data that gets passed through to the selected instrument. So, maybe I just want to leave the Quick Controls alone and control those with my LaunchKey (with eight encoders already, and perfectly suited for controlling eight quick controls) and then just use the joystick to “pass through” to control two ADDITIONAL parameters in an instrument (like the Sphere in HALion or an XY PAD in one of the Kontakt instruments).

I also understand that I can just leave the joystick unbound all the time but, since it has a button, I thought it would be nice to have two modes. One to control some quick controls and another to simply pass cc data through to an instrument.

Thanks again for your help.

JL

The bad news: When we use subPages, if a control is left unbound to one of them, then the MIDI Remote will use whichever binding it finds to the other subPage. In order to avoid that we can use custom Host Values. However, if we do so, the CC message is consumed again. There is a solution to this, by setting another set of MIDI Ports inside the script, and then instructing the control to send its value to a second port which has to be a loopBack port in order for it to arrive to our VSTi. As you can see, this can get a bit complicated.

The good news is that you don’t really need all this. Instead of having subPages, you can use mapping Pages (this was actually my hint in the previous post).
When we use pages, we can always have controls unbound.
Have a look at this snippet (working):

var midiremote_api = require('midiremote_api_v1')

var deviceDriver=midiremote_api.makeDeviceDriver("Test","Unbound Controls","m.c")

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

var detectionUnit=deviceDriver.makeDetectionUnit()

detectionUnit.detectPortPair(midiInput,midiOutput)
    .expectInputNameEquals("Bome MIDI Translator 1")
    .expectOutputNameEquals("Bome MIDI Translator 1")

var surface=deviceDriver.mSurface
var knob=surface.makeButton(0,0,1,1)
knob.mSurfaceValue.mMidiBinding   
    .setInputPort(midiInput)
    .bindToControlChange(0,0)

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

var mapping=deviceDriver.mMapping

var page1=mapping.makePage("QC")
var page2=mapping.makePage("CC")

page1.makeValueBinding(knob.mSurfaceValue,page1.mHostAccess.mFocusedQuickControls.getByIndex(0))

page1.makeActionBinding(buttonTogglePages.mSurfaceValue,page2.mAction.mActivate)
page2.makeActionBinding(buttonTogglePages.mSurfaceValue,page1.mAction.mActivate)

The main difference from yours is that I don’t use subPages, I define two pages instead. And then, there is another advantage. You can actually use this approach to do the specific task, without even scripting. You can use the mapping assistant for your “Default” page, and leave the other one unmapped.

Thanks for the reply. I also have three push encoders on this controller. I have them currently working, with a script so that one is the transport cursor and pushing the button uses a subpage to make it toggle between nudge and nudge 10seconds. The other push encoder uses subpages to toggle between zoom and vert zoom. The third uses the button to toggle between moving to previous/next events and markers.

I have this all working so far including the joystick assigned to quick controls. So here is my question….

Can I write two “pages” and leave the joystick CC unbound on one “page”. Then, on each page, write all the subpages for the encoders within each page?

I am just trying to save the time of duplicating all of the “subpages” onto another “Main page” if it won’t work…..

I am building this controller myself with a micro-controller so I can change how the joystick behaves from the hardware. If I can’t use the solution I just proposed above, my solution is to use the button on the joystick to simply have the joystick send on two different CC#s and leave those other two CC#s unused in the mid remote. But, it seems more flexible to make the changes at the software level in the midi remote.

JL

I think this might work. I created an altPage in addition to the mainPage and left the joystick unassigned. I mapped the joystick button to switch pages. On the mainPage, everything still works (all the encoders). When I switch to the altPage, the CC data from the joystick is unassigned and passes through. The encoders are all still “unassigned” on this altPage, but, I think if I duplicate the encoder mappings for the altPage, it might work.

Isn’t this what I’ve already written? :slight_smile:

You can place the subPages creation inside a function with a page argument. This way you can have identical subPages for whichever pages you want.

Example:

createDefaultSubPagesAndBindings(page1,"subPagesArea1")
createDefaultSubPagesAndBindings(page2,"subPagesArea2")

function createDefaultSubPagesAndBindings(page,subPagesAreaName){
 
    var subPagesArea=page.makeSubPageArea(subPagesAreaName)
    
    var subPage1=subPagesArea.makeSubPage("subPage1")
    var subPage2=subPagesArea.makeSubPage("subPage2")

    var aHostValue1=page.mHostAccess.mFocusedQuickControls.getByIndex(1)
    var aHostValue2=page.mHostAccess.mFocusedQuickControls.getByIndex(2)
    
    page.makeValueBinding(knob2.mSurfaceValue,aHostValue1).setSubPage(subPage1)
    page.makeValueBinding(knob2.mSurfaceValue,aHostValue2).setSubPage(subPage2)

}

Side note: Please tag the user or hit the reply button, otherwise no notification will be received and your replies may be overlooked.

1 Like

Ok. Thanks for your help. I thought you were saying above that I couldn’t use subPages at all but I think, to be more specific, I can’t use one mainPage with subPages for all the controls independently. But, I can use multiple mainPages with subPages within them. The function idea is great and I was looking at that in the API example where it switches the knobs from “Mixer” page and “Selected Track” page.
Concerning you getting a notification, sorry about that. I am not sure how to “tag” you. Is that where I see some people put @m.c (well ok. I just answered my own question as I see that you and martin both popped up just as I typed the @ symbol). Also, I used the Reply at the bottom which maybe isn’t the same as the reply on the sidebar?

Anyway, I really do appreciate your help and I have already gotten one of the encoders to work with all the subPages on both mainPages.
JL

1 Like

Cool!

It’s not, exactly. You have to hit the reply button at the end of the user’s post.

Ok. More Reply buttons! I now have all three encoders working in all their toggle subModes on both mainPages with the joystick assigned to Quick Controls on one and unassigned on the other.
Thanks again. Oh, and now I see that your avatar is at the top margin of my reply dialog since I used the reply in your post.

1 Like