X-Touch (+Extender) MIDI Remote Script (MCU mode)

Hey @bjoluc!

Again, thanks for your job.
With Cubase 12.0.60 and your script 1.5.0, channel names are still buggy: they only refresh when I select the track (so I need to select all of them each time I switch fader banks). Also, unused faders still display the name and color of the previous track used by this fader.

Finally, would you mind adding an option to activate/desactivate .excludeWindowZoneLeftChannels() and .excludeWindowZoneRightChannels()? It would be awesome when the .setFollowVisibility(true) will finally work :smiley:

1 Like

Unfortinately yes, I can get rid of that copy for a while, but when I restart cubase or open a new project its back again. Cubase 12.6, Scrip 1.5ā€¦ remote

Thatā€™s unlucky. Interestingly though, the v1.5 workaround works on my dev machine and (reportedly) for another user on 12.0.60. I could invest more time to see if I can find another workaround, but without being able to reproduce it, that time may quite possibly be wasted. I think youā€™ll have to revert to CB 12.0.52 then (Cubase Pro 12 Downloads | Steinberg) until Steinberg fixes the issue.

Makes perfect sense :+1: I just added a set of visibility config options locally, just to find that neither .excludeWindowZoneLeftChannels() nor .excludeWindowZoneRightChannels() do currently work, which has already been reported last year (MIDI Remote - Mixer Bank follow visibility not working? - #3 by Jochen_Trappe). Iā€™ll release the new options once they actually work in Cubase.

Just saw the same thing happening when I was opening a second project and activating it:

Though I would prefer

or probably better

:grin: Kidding aside, there seems to be nothing we can do about it despite deleting the second surface when it gets too annoying. I guess weā€™ll have to live with it when switching projects.

1 Like

Ok thank you for the reply, not that bad in the end. One functional question: I have some of the function buttons set up for toggle functions. Is it somehow possible to edit the code so that they behave like that (light on/off)? If so - where would I do that?
Thanks again:)

1 Like

This works out of the box for host values (e.g., Selected Track > Edit Instrument). For key commands, thereā€™s no way to retrieve the underlying host state, so toggling the light ā€“ while achievable script-wise ā€“ would be meaningless. A good example for this is the PUNCH button which is bound to ā€œTransport > Key Commands > Activate Punch Inā€. Thereā€™s no way the script can know whether ā€œPunch Inā€ is active, so blindly toggling the light might not reflect the actual ā€œPunch Inā€ status in Cubase. The same thing applies to all function buttons: Either they reflect the host state automatically, or thereā€™s currently no way to make them reflect itā€¦

2 Likes

Exactly this.
Took me some time to workaround this, by adding a generic remote just for such things, in case anyone would bother to play with this. This way my script receives such states now, but this is NOT the way to go. I think they should and hopefully be added in a next updated API.

1 Like

Thanks again for clarifying! So lets see if its possible in the future:)

The main problem I see with the midi remote and button states is, that the midi remote is NOT loaded at Cubase start. It is loaded AFTER a project. If the midi remote would be loaded at Cubase start, it would be easy to have all the button states, that are currently not known to our scripts. If there is a initial state (and I believe there is) of Cubase functions and buttons that are set, before a project is loaded or a new one created, then we would not need to take care about these problems and it would be a LOT easier to handle these cases.

Once a project loads, it should return values for ALL the states, buttons, faders etc. This way every midi remote should be properly ā€žARMEDā€œ and donā€™t need complex written state machines in the script.

It does return these values when you actually want to read them in your script. It is up to you to select which ones to grab, this is why you should do it inside the appropriate events. I do it all the time, solo,mute,arm,volume,pan etc etc.

I think you underestimate (same like me, to be honest) how hard are some situations to handle. My post was mainly a answer to @bjoluc and @David_781 observations. There are many buttons where you can not grab or receive the current state easily or without state machines you would need to write in a script. Solo, Mute, Volume or Pan are easy stuff :wink: .

If youā€™re talking about states that are not available via the API yet, of course youā€™re right. Other than that, which other states are you referring to? Can you give me an example, so that I can understand the situation better?

Yes, I will give you some examples. States become essential, once you want to use lights,lamps,LEDs etc.
Basically real world objects that should reflect situations visually for the user.
So letā€™s say you want to use a red light/lamp as soon as record in the transport field is selected. Now letā€™s assume, you want the same red light/lamp to start blinking, if the record button in the transport field is enabled AND the song is playing.
Try to script that and you will very fast see, that you canā€™t establish this, without writing very much code for such a simple function.

Oh well, even if this is not an example of a hostValue, I can provide you some code for this function here, and I donā€™t see it as a big one :slight_smile:

page.mHostAccess.mTrackSelection.mMixerChannel.mValue.mRecordEnable.mOnProcessValueChange=function(activeDevice,activeMapping,value){
    
    activeDevice.setState("selectedTrackArmed",value.toString())
    //if not armed, do what you want with your led

    // no big deal here, actually we most of the time want this function in order to change our "record-arm" leds

}

page.mHostAccess.mTransport.mValue.mStart.mOnProcessValueChange=function(activeDevice,activeMapping,value){
    
    activeDevice.setState("isPlaying",value.toString())
    //if not playing, do what you want with your led
    
   //again, we always enter this sub, since we want to change the "Play" button's led
  
}

page.mHostAccess.mTransport.mTimeDisplay.mPrimary.mTransportLocator.mOnChange=function(activeDevice,activeMapping,arg2,arg3){
    
    if(activeDevice.getState("selectedTrackArmed")=="1" && activeDevice.getState("isPlaying")=="1"){
        var indexOfBeat=arg2.indexOf(".")+1
        var beat=parseInt(arg2.substring(indexOfBeat,indexOfBeat+2))
        var prevBeat=parseInt(activeDevice.getState("beat"))
        if(beat!=prevBeat){
            //toggle your led here. If you want a more frequent toggle, instead of beats, you can try 16ths or the even smaller unit 
        }
    }

     //I personally use this sub always in order to get to my display current transport location

}

1 Like

To be honest, I donā€™t have much clue about scripting with API. Here is the code from someone who helped me with this:

// Write Automation Test for UMan
  //this.chanStrip
  //this.func

  // Write Automation Test for UMan
	var selectedTrackChannel = page.mHostAccess.mTrackSelection.mMixerChannel
  
  var self = this;
  selectedTrackChannel.mValue.mAutomationWrite.mOnTitleChange = function (context, mapping, text1, text2) {
    self.selectedTrackName = text1
    console.log(text1)
    console.log(text2)
  }

  // Set the state of the selected track's Write Automation State
  var self = this;
  selectedTrackChannel.mValue.mAutomationWrite.mOnDisplayValueChange = function (context, mapping, text1, text2) {
    switch (text1) {
      case 'Off': self.writeAutomationState = 0; break;
      case 'On':  self.writeAutomationState = 1; break;
    }
   console.log(text1)
   console.log(text2)
   if (self.writeAutomationState == 0) {
    writeAutomation.setLedOff(context)
   } else if (self.transportRecordState == 0) {
     writeAutomation.setLedOn(context)
   } else {
     writeAutomation.setLedFlashing(context)
   }

  }

  var self = this;
  var writeAutomation = mackieC4.spotErase;
  var writeAutomationCvv = mackieC4.surface.makeCustomValueVariable('writeAutomationCvv');
	page.makeValueBinding(writeAutomationCvv, selectedTrackChannel.mValue.mAutomationWrite).setTypeToggle() // Spot Erase button || .setValueTakeOverModeScaled()
  writeAutomation.button.mSurfaceValue.mOnProcessValueChange = function (context, newValue) {
    console.log('writeAutomationProcess_value')
    if (newValue == 1) {
      self.writeAutomationState = 1 - self.writeAutomationState;
      writeAutomationCvv.setProcessValue(context, self.writeAutomationState);
      console.log(self.writeAutomationState.toString())
    }

    if (self.writeAutomationState == 0) {
      writeAutomation.setLedOff(context)
    } else if (self.transportRecordState == 0) {
      writeAutomation.setLedOn(context)
    } else {
      writeAutomation.setLedFlashing(context)
    }
	}

  var self = this;
  var transportRecord = mackieC4.chanStrip;
	page.makeValueBinding(transportRecord.button.mSurfaceValue, page.mHostAccess.mTransport.mValue.mRecord).setTypeToggle() // Spot Erase button || .setValueTakeOverModeScaled()
  transportRecord.button.mSurfaceValue.mOnProcessValueChange = function (context, newValue) {
    console.log('transportRecordProcess_value')
    self.transportRecordState = newValue;
    console.log(self.transportRecordState.toString())
    if (self.writeAutomationState == 0) {
      writeAutomation.setLedOff(context)
    } else if (self.transportRecordState == 0) {
      writeAutomation.setLedOn(context)
    } else {
      writeAutomation.setLedFlashing(context)
    }
	}

  var transportStop = mackieC4.function;
	page.makeValueBinding(transportStop.button.mSurfaceValue, page.mHostAccess.mTransport.mValue.mStop).setTypeToggle() // Spot Erase button || .setValueTakeOverModeScaled()
  transportStop.button.mSurfaceValue.mOnProcessValueChange = function (context, newValue) {
	}

  return page;
}

The only difference was, that the lamp/LED was bound to write automation and in case of record playback, it is flashing. As you can see, this code is much larger. If I do the same with BOME Midi Translator, I have just four lines of code.

1 Like

Hi there, Iā€™m very interested to know more details on this. BUT,I feel like weā€™re abusing this thread which is for a specific shared script. If you have the time, could you please open a new thread (by the tag Midi remote or even in the Lounge), with both the above code AND the BMTā€™s one,so we can have a better look there? Iā€™m always interested in optimisations :slight_smile:

1 Like

It might look like abusing, but I donā€™t think so. I donā€™t own a X-touch, but I am pretty sure, that the lamps are working all the same, as it is MCU based (X-touch and even the C4 from my script. Just in case people donā€™t know, but on a real MCU, every Lamp can have three states: off, on and flashing (cc 0, 1, 127).

And I am still referring to post 45 from this thread, that brings up the problem of not knowing the states of some important functions and therefore problematic visual reflect of these states, be it lamps,LEDs,text etc.

Just to say I was running this script on a standard MCU last night, and it works pretty well on original hardware too. Doesnā€™t make full use of the commands, but I managed to add a page for the ā€˜selected trackā€™ destinations via the surface editor and even that worked, which surprised me.

So, itā€™s easy to get the full panel (i.e. pots and faders) to control plugins and instruments too, if anyone is curious. Shame about the bug in 10.0.60 though, hopefully SB get this fixed ASAP.

1 Like

@skijumptoes Thatā€™s great news! Iā€™ve been toying with the idea of supporting other MCU-speaking devices a few times. Script-wise, that shouldnā€™t be too much of a problem but Iā€™m unsure for a couple of reasons:

  • I only own X-Touches
  • The original motivation for the script was to make Cubaseā€™s MCU implementation X-Touch specific, although I came to notice that most of the scriptā€™s advantages apply to other devices too.
  • I donā€™t want to over-generify the project. On the other hand, implementation-wise, it is farily generic already.

I realize adding support for other devices would mostly mean adapting the surface layout, so I wonder if it would even be worth it as the devices should already work without the script explicitly supporting them. I also wonder how many people would be interested to see more devices supported. @skijumptoes You should be able to get padding characters by searching the script for the abbreviateString function definition and replacing the hardcoded 7 with 6, for what itā€™s worth.

2 Likes

Thatā€™s right. I think your blinking-button use case is better discussed in another thread though, so we donā€™t annoy everyone watching this thread for updates related to the X-Touch script :slightly_smiling_face: