page.makeValueBinding() unexpected behavior

In the altered version I posted, I tried implementing the select/arm-record approach and also the solo/mute one, based on the REC/SOLO buttons.

At the same time, I’ve tried setting up the EQ bands to toggle ON/OFF when once we’re in a band, we repress this band’s button.

The thing is that since I don’t have the device, I couldn’t test it in real life, especially how the leds react to the subPages changes.

I’m actually almost done with this whole thing… I’m stoked

the only thing I still haven’t quite nailed down is storing the state of certain buttons in custom variables (such as ASGN btn for toggling EQ on/off)…

Any insight into which commands I should use? I don’t know the difference between these two types:

// custom variable type A:
deviceDriver.mSurface.makeCustomValueVariable( ...) 

// custom variable type B:
page.mCustom.makeHostValueVariable( ... )

I also like your idea of just toggling once we are inside a band… that could also apply to AUX’s… and free up the ASGN button for more fun

All my sub pages are working properly, I just need to enable/disable the EQ toggle page somehow based on a custom variable. Thx again so much

Making some progress using “makeCustomValueVariable”. (Still don’t know what host value variables are).

I suppose I’ll need to store the last known state of which EQ band was active? So that I can perform my own logic to switch back to that page when the ASGN button is toggled off…?

Currently I can now enter into the ASGN subpage… I just can’t get back out haha

You can try adding a subPage for getting things back to normal:

var customAssignOn=surface.makeCustomValueVariable("assignOn")
var customAssignOff=surface.makeCustomValueVariable("assignOff")

btnAssign.mSurfaceValue.mOnProcessValueChange=function(activeDevice,value,diff){
    if(value==1){
        var customAssignOnPrevious=customAssignOn.getProcessValue(activeDevice)
        customAssignOn.setProcessValue(activeDevice,1-customAssignOnPrevious)
        customAssignOff.setProcessValue(activeDevice,customAssignOnPrevious)
    }
}

var eqBandsSelectAndEnableSubPageArea=page.makeSubPageArea("eqBandsSelectAndEnableSubPageArea")
var eqBandsSelectSubPage=eqBandsSelectAndEnableSubPageArea.makeSubPage("eqBandsSelectSubPage")
var eqBandsEnableSubPage=eqBandsSelectAndEnableSubPageArea.makeSubPage("eqBandsEnableSubPage")

page.makeActionBinding(customAssignOn,eqBandsEnableSubPage.mAction.mActivate).setSubPage(eqBandsSelectSubPage)
page.makeActionBinding(customAssignOff,eqBandsSelectSubPage.mAction.mActivate).setSubPage(eqBandsEnableSubPage)

You should then assign your action binding for changing eqBand subPage in the eqBandsSelectSubPage, while setting value binding to your eqBand “On” hostValue in the eqBandsEnableSubPage subPage.

You can think of a customValueVariable as a a custom element of your controller (as knobs, faders etc) while the custom hostValueVariable is like the values exposed by the DAW (transports, volumes, etc).

In the snippet earlier, I show one usage of the customValue vars. We can manipulate an element of our controller the way we like, especially when it’s a button. When it’s not a button, things can get complicated.

The hostValueVariable is of usage in some cases, as for example, when we want in a specific subPage that a specific control doesn’t do anything at all. We bind it to a hostValueVariable.
Another, more powerful usage, is to get the instance of the activeMapping inside the mOnValueChange, and trigger things, example:

var aSubPageArea=page.makeSubPageArea("aSubPageArea")
var subPageOne=aSubPageArea.makeSubPage("one")
var subPageTwo=aSubPageArea.makeSubPage("two")

var customSurfaceVar=surface.makeCustomValueVariable("aCustomSurfaceVar")
var customHostVar=page.mCustom.makeHostValueVariable("aCustomHostVar")

page.makeValueBinding(customSurfaceVar,customHostVar).mOnValueChange=function(activeDevice,activeMapping,value,diff){

    if(value==1){
    
        subPageOne.mAction.mActivate.trigger(activeMapping)
    
    } else {

        subPageTwo.mAction.mActivate.trigger(activeMapping)
    
    }

}

I’m still not quite clear on the hierarchy of the EQ subpages…

You suggested a top level “subpageArea” containing two “subpages”, eqBandsSelectSubPage, and eqBandsEnableSubPage.

Doesn’t the “select” subpage need to somehow contain 4 subpages itself (High, Hi-Mid, Low-Mid, Low) ? And each of those subpages contains the 3 knobs.

In my code thus far, I ended up with 4 subpages (1 for each EQ band), containing 3 knobs each. Then a 5th subpage for “enable” mode containing 4 toggle buttons. I was trying to trigger between these pages depending on the ASGN button.

No.

Yes, and this is good. You’re using the 4 dedicated EQ buttons to select each band, so that a dedicated subPage belonging to a specific subPagesArea, for each of these bands is triggered.
Now you want that these 4 buttons behave differently under the condition that the AUX button toggles.So, forget all about the EQ bands knobs, and focus on what we want the 4 EQ buttons to perform. This is easily doable using a new subPagesArea as shown in my snippet. At one AUX state, the buttons will trigger band changes, while in the other, they will treat the on/off state of the bands.

I think I’m starting to grok most of this… thank you man!!

The whole custom variable thing seems like sort of a hack to me. All of this would be so much simpler with object oriented programming :nerd_face: But who knows how complicated this all gets under the hood.

There’s no subpage.mAction.mDeactive event, only mActivate. Not sure of the reasoning behind that.

This is because we can only have one of the subPages in a subPagesArea active. This means that once you activate one, the previous gets automatically deactivated. If you then want to control some actions, there is the subPage.mOnDeactivate event.

I’ve almost got it…

How do you suggest handling the LED’s in the enable mode? Do those need custom vars each to hold the host EQ band enabled state?

When you set your controls to the “selected” subPage, the events for the mOn of the eqBands should get automatically triggered. Now, since these mOn values are bound to your buttons, the leds should turn on/off automatically. Isn’t this what you’re experiencing?

I’m missing whatever code is needed to display the LED’s for the “enable” page (which I’m calling the “assign” page in my code). My brain is seizing up on me for the moment. But everything else works!

function toggleAssignModeVars(context) {
    // swap states of assign mode custom variables
    var assignModeOn = var_assignModeOn.getProcessValue(context)
    var assignModeOff = var_assignModeOff.getProcessValue(context)

    var_assignModeOn.setProcessValue(context, assignModeOff)
    var_assignModeOff.setProcessValue(context, assignModeOn)
    
    displayAsgnLED(context)
}

function assignEQToggleControls() {

    btnAsgn.mSurfaceValue.mOnProcessValueChange = function(context, newValue, diff) {        
        if(newValue > 0) {
            // button pressed (ignore button release)
            toggleAssignModeVars(context)            
        }
    }
}

function assignEQBandControls() {
     
    // create EQ Assign Modes Subpage area
    var area_eqAsgnSubPages = page.makeSubPageArea('EQ Assign Mode Subpage Area')

    // create EQ Assign Modes Subpages
    var subpage_EQBandSelect = area_eqAsgnSubPages.makeSubPage('EQ Band Select')
    var subpage_EQAssign = area_eqAsgnSubPages.makeSubPage('EQ Bands Assign')

    // bind EQ Assign Modes Subpages activation to custom variables states
    page.makeActionBinding(var_assignModeOn, subpage_EQAssign.mAction.mActivate).setSubPage(subpage_EQAssign)
    page.makeActionBinding(var_assignModeOff, subpage_EQBandSelect.mAction.mActivate).setSubPage(subpage_EQBandSelect)

    // create EQ Bands Subpage area
    var area_eqBandsSubPages = page.makeSubPageArea("EQ Bands Subpage Area")
    
    // 4 host EQ bands for the selected channel
    var hostEQBand_High = hostSelectedEQChannel.mBand4
    var hostEQBand_HighMid = hostSelectedEQChannel.mBand3
    var hostEQBand_LowMid = hostSelectedEQChannel.mBand2
    var hostEQBand_Low = hostSelectedEQChannel.mBand1
    
    // create subpages for each EQ band    
    var subpage_EQBand_High = area_eqBandsSubPages.makeSubPage('High EQ Band')
    var subpage_EQBand_HighMid = area_eqBandsSubPages.makeSubPage('HighMid EQ Band')
    var subpage_EQBand_LowMid = area_eqBandsSubPages.makeSubPage('LowMid EQ Band')
    var subpage_EQBand_Low = area_eqBandsSubPages.makeSubPage('Low EQ Band')
  
    // bind EQ knobs to each band subpage
    assignEQKnobsToSubpage(hostEQBand_High, subpage_EQBand_High)
    assignEQKnobsToSubpage(hostEQBand_HighMid, subpage_EQBand_HighMid)
    assignEQKnobsToSubpage(hostEQBand_LowMid, subpage_EQBand_LowMid)
    assignEQKnobsToSubpage(hostEQBand_Low, subpage_EQBand_Low)

    // bind EQ band button presses to activate corresponding band subpage
    page.makeActionBinding(btnHigh.mSurfaceValue, subpage_EQBand_High.mAction.mActivate).setSubPage(subpage_EQBandSelect)
    page.makeActionBinding(btnHighMid.mSurfaceValue, subpage_EQBand_HighMid.mAction.mActivate).setSubPage(subpage_EQBandSelect)
    page.makeActionBinding(btnLowMid.mSurfaceValue, subpage_EQBand_LowMid.mAction.mActivate).setSubPage(subpage_EQBandSelect)
    page.makeActionBinding(btnLow.mSurfaceValue, subpage_EQBand_Low.mAction.mActivate).setSubPage(subpage_EQBandSelect)
    
    // bind EQ buttons presses to toggle band on/off within the assign EQ subpage
    page.makeValueBinding(btnHigh.mSurfaceValue, hostEQBand_High.mOn).setTypeToggle().setSubPage(subpage_EQAssign)
    page.makeValueBinding(btnHighMid.mSurfaceValue, hostEQBand_HighMid.mOn).setTypeToggle().setSubPage(subpage_EQAssign)
    page.makeValueBinding(btnLowMid.mSurfaceValue, hostEQBand_LowMid.mOn).setTypeToggle().setSubPage(subpage_EQAssign)
    page.makeValueBinding(btnLow.mSurfaceValue, hostEQBand_Low.mOn).setTypeToggle().setSubPage(subpage_EQAssign)

    // bind EQ LED's to band subpage activations
    subpage_EQBand_High.mOnActivate = function(context) { displayEQLED(context, EQ_BANDS.High) }
    subpage_EQBand_HighMid.mOnActivate = function(context) { displayEQLED(context, EQ_BANDS.HighMid) }
    subpage_EQBand_LowMid.mOnActivate = function(context) { displayEQLED(context, EQ_BANDS.LowMid) }
    subpage_EQBand_Low.mOnActivate = function(context) { displayEQLED(context, EQ_BANDS.Low) } 
}

function assignEQKnobsToSubpage(hostEQBand, subpage) {
    page.makeValueBinding(knobGain.mSurfaceValue, hostEQBand.mGain).setValueTakeOverModeScaled().setSubPage(subpage)        
    page.makeValueBinding(knobFreq.mSurfaceValue, hostEQBand.mFreq).setValueTakeOverModeScaled().setSubPage(subpage)        
    page.makeValueBinding(knobQ.mSurfaceValue, hostEQBand.mQ).setValueTakeOverModeScaled().setSubPage(subpage)        
}

If I add this code, it all works …

But it’s ugly… there’s got to be a better way.

Also I’m having to explicitly refresh the LED’s when the “select” subpage is activated. Otherwise they don’t go back to normal when “assign” mode is exited.

    // display the last selected EQ led when select subpage is activated
    subpage_EQBandSelect.mOnActivate = function(context) { displaySelectedEQLED(context, lastSelectedEQBand)}

    // bind high LED display to button value in assign mode
    btnHigh.mSurfaceValue.mOnProcessValueChange = function(context, newValue, diff) {
        var assignModeOn = var_assignModeOn.getProcessValue(context)
        var isEnabled = newValue > 0

        if (assignModeOn) {
            // display LED's based on host band state, only within assign mode
            displayAssignedEQLED(context, EQ_BANDS.High, isEnabled)
        }
    }

    // bind highMid LED display to button value in assign mode
    btnHighMid.mSurfaceValue.mOnProcessValueChange = function(context, newValue, diff) {
        var assignModeOn = var_assignModeOn.getProcessValue(context)
        var isEnabled = newValue > 0

        if (assignModeOn) {
            // display LED's based on host band state, only within assign mode
            displayAssignedEQLED(context, EQ_BANDS.HighMid, isEnabled)
        }
    }

    // bind lowMid LED display to button value in assign mode
    btnLowMid.mSurfaceValue.mOnProcessValueChange = function(context, newValue, diff) {
        var assignModeOn = var_assignModeOn.getProcessValue(context)
        var isEnabled = newValue > 0

        if (assignModeOn) {
            // display LED's based on host band state, only within assign mode
            displayAssignedEQLED(context, EQ_BANDS.LowMid, isEnabled)
        }
    }

    // bind low LED display to button value in assign mode
    btnLow.mSurfaceValue.mOnProcessValueChange = function(context, newValue, diff) {
        var assignModeOn = var_assignModeOn.getProcessValue(context)
        var isEnabled = newValue > 0

        if (assignModeOn) {
            // display LED's based on host band state, only within assign mode
            displayAssignedEQLED(context, EQ_BANDS.Low, isEnabled)
        }
    }

Want my advice/opinion on this? If it works, it works.

Initially, before adding the “enabled” subPage, I remember you wanted the leds to light up once you select a band? What are your plans on this? I mean, you want when you turn AUX on, the leds to light up based on the on/off state of the bands, and when you turn it off, the led of the selected band to light up again?

Clearly, yes I want your advise! :cowboy_hat_face: Imma roll wit it

Yea, it’s all working… and it’s magical.

I can at least refactor that LED stuff out into a “make EQ assign buttons display feedback” method.

It’s working just like the original TASCAM cubase spec did before they killed it… You hit the assign button and the 4 EQ LED’s show you which bands are enabled in cubase. Hit assign again, and you’re back to a single LED showing you which band your knobs are controlling.

Really the “assign” mode should be affecting several other controls on the board (mainly the 4 AUX send enables)

This is the remaining wish list for this project:


// TODO

// ASGN button .. subPage for enabling AUX's
// Solo button mode
// REC button attach master fader to current selected track
// Stop+REW = RTZ
// Page over when left/right track select reaches beyond bank
// ASGN mode.. locators set CYCLE
// ASGN mode.. jog wheel controls master fader
// Play+SET.. bypass/enable master bus comp
// Redo remote grid X,Y layout to look pretty

You’re doing great so far. My only concern is that you’re not using arrays of controls/subpages which can make it difficult IF you want to proceed with more complicated stuff. However, seeing your toDo list, it’s going to be fine, I guess :slight_smile:

Yea I’m opting for readability over code size in general here. The way I figure it, they ain’t changing any thing on this TASCAM … anytime… uhh… ever

I am doing programmatic stuff for the 8 channel banks, but the way I figure the rest of the design is already “hard coded” into the board itself

Usually I’m going to order gluten-free object meatballs…

But I’ll eat code spaghetti on a special occasion. :spaghetti:

Almost got solo mode happening…

hmm… can you call makeValueBinding() twice on the same button? with different .setSubPage() calls? it seems you cannot…

Not sure I understand. This is actually the functionality we want when using subPages.

Example:

page.makeValueBinding(aControlSurfaceValue,aHostValue).setSubPage(aSubPage)
page.makeValueBinding(aControlSurfaceValue,anotherHostValue).setSubPage(anotherSubPage)

Based on the subPage we’re in, the very same control will be bound to a different host value.

Here I can do the 4 makeActionBinding() calls on a single subpage, but when I do all 8 calls for both subpages, only the last 4 seem to trigger.

The bug may be elsewhere, or my subpages are wrong… I’m using 2 different subPageAreas with 3 subpages each.

function assignChannelBankControls() {
    // assign 3 banks of 8 channels to F1, F2, F3 subpages
    assignSingleChannelBank(subpage_ChannelBankF1, subpage_ChannelBankF1_SoloMode, F1)
    assignSingleChannelBank(subpage_ChannelBankF2, subpage_ChannelBankF2_SoloMode, F2)
    assignSingleChannelBank(subpage_ChannelBankF3, subpage_ChannelBankF3_SoloMode, F3)
} 

function assignSingleChannelBank(subpage_mutemode, subpage_solomode, bankNum) {
    var channelBankItems = []
    for (var slot=0; slot<8; slot++)
    {
        channelBankItems.push(hostMixerBankZone.makeMixerBankChannel())

        // bind channel bank buttons and faders to host subpage events (Mute Mode)
        page.makeValueBinding(btnMutes[slot].mSurfaceValue, channelBankItems[slot].mValue.mMute).setSubPage(subpage_mutemode).setTypeToggle()
        page.makeValueBinding(btnRecs[slot].mSurfaceValue, channelBankItems[slot].mValue.mRecordEnable).setSubPage(subpage_mutemode).setTypeToggle()
        page.makeValueBinding(btnSelects[slot].mSurfaceValue, channelBankItems[slot].mValue.mSelected).setSubPage(subpage_mutemode).setTypeToggle()
        page.makeValueBinding(fdrFaders[slot].mSurfaceValue, channelBankItems[slot].mValue.mVolume).setSubPage(subpage_mutemode).setValueTakeOverModeScaled()

        // bind channel bank buttons and faders to host subpage events (Solo Mode)
        page.makeValueBinding(btnMutes[slot].mSurfaceValue, channelBankItems[slot].mValue.mSolo).setSubPage(subpage_solomode).setTypeToggle()
        page.makeValueBinding(btnRecs[slot].mSurfaceValue, channelBankItems[slot].mValue.mRecordEnable).setSubPage(subpage_solomode).setTypeToggle()
        page.makeValueBinding(btnSelects[slot].mSurfaceValue, channelBankItems[slot].mValue.mSelected).setSubPage(subpage_solomode).setTypeToggle()
        page.makeValueBinding(fdrFaders[slot].mSurfaceValue, channelBankItems[slot].mValue.mVolume).setSubPage(subpage_solomode).setValueTakeOverModeScaled()

        // bind mute, record, and select LED's to host events per bank/slot
        makeChannelBankLEDsDisplayFeedback(channelBankItems, bankNum, slot)                   
    }
}