Reviving my old CMC-CH using Midi Remote API

Hello guys!

I’m trying to revive my old CMC-CH controller using Midi Remote API in Cubase Pro 12 (on Mac). My js file is nearly complete and so far, I managed to make the most of features to work.

Unfortunately, I’m still missing some features:

  1. I can’t make Pan knob LED to respond on value change - no matter, what I do, the LED remains off… Does anyone know, what controls it? Is it some SysEx, or is it some “hard-coded” thing?
var panKnob = surface.makeKnob(6, 2, 2, 2);

panKnob.mSurfaceValue.mMidiBinding
  .setInputPort(midiInput)
  .setOutputPort(midiOutput)
  .bindToControlChange(0, 16) // channel 1, cc 16
  .setTypeRelativeSignedBit();

page.makeValueBinding(
  panKnob.mSurfaceValue,
  hostSelectedMixerChannel.mValue.mPan
);
  1. Freeze, Folder, Inserts, EQ and Sends Bypass buttons can’t be toggled - as it uses “makeCommandBinding” function. Right now, toggling can be enabled only for “makeValueBinding” function. For “makeActionBinding” and “makeCommandBinding” functions, it does nothing… Does exist some other way, how to do it? If not, would it be possible to implement it is some future Cubase release? (just a visual detail, otherwise it works fine)

  2. “SHIFT” feature - I couldn’t figure out yet, how to do it. The only way that worked was to bind “SHIFT” button od Main/Default page to Shift page and on Shift page to bind it to Main/Default page, which was really confusing, because the button also can’t be toggled as it uses “makeActionBinding” function (as mentioned previously in 2.). Is it possible to somehow activate the other page directly inside function?

Right now I’ve created this function, which is probably the closest what I want:

var shiftBtn = surface.makeTriggerPad(3, 10, 2, 2); // doesn't make a difference if it's Button or TriggerPad

shiftBtn.mSurfaceValue.mMidiBinding
  .setInputPort(midiInput)
  .setOutputPort(midiOutput)
  .bindToNote(0, 70); // channel 1, A#3

// Change a page when SHIFT button is pressed and revert it back to Main page after it's released
shiftBtn.mSurfaceValue.mOnProcessValueChange = function (
  activeDevice,
  value,
  diffValue
) {
  if (value == 1) {
    shiftPage.mAction.mActivate; // doesn't do anything
    console.log("SHIFT page");
  } else {
    mainPage.mAction.mActivate; // doesn't do anything
    console.log("Default page");
  }
};

I’ve also tried to add .trigger(activeMapping) after “mActivate”, but I have absolutely no clue, what “activeMapping” should be, as there is nothing about it in Documentation

If anyone could help me to solve these features, I would be very glad!

Thank you.

1 Like

wrong post

Hi, some suggestions to try out.

You may want to try to remark the .setOutputPort(midiOutput) .
Then, use this:

panKnob.mSurfaceValue.mOnProcessValueChange=function(activeDevice,value,diff){
    var newValue=Math.round(127*value)
    midiOutput.sendMidi(activeDevice,[0xB0,0x10,newValue])
}

Can you share the buttons creation code segment you have?
Are the hardware buttons to gate or toggle mode? I suspect they are in toggle mode. If so, you can create a custom var and set it to pseudo toggle.

There is more than one ways to try.

You can try a dual approach:

var shiftButtonOff=surface.makeCustomValueVariable("shiftButtonOff")
var shiftButtonOn=surface.makeCustomValueVariable("shiftButtonOn")

shiftBtn.mSurfaceValue.mOnProcessValueChange = function (activeDevice,value, diffValue) {
  if (value==1) {
       shiftButtonOn.setProcessValue(activeDevice,1)
  } else {
       shiftButtonOff.setProcessValue(activeDevice,1) 
  }
}

mainPage.makeActionBinding(shiftButtonOn,shiftPage.mAction.mActivate)
shiftPage.makeActionBinding(shiftButtonOff,mainPage.mAction.mActivate)

This is the correct syntax. In order to get the activeMapping, you need to execute your command inside an event that exposes activeMapping. The mOnProcessValue obviously doesn’t .
Here’s a trick to do this:

var dummyHostAccessObject=mainPage.mCustom.makeHostValueVariable("dummyAccess")
page.makeValueBinding(shiftBtn.mSurfaceValue,dummyHostAccessObject).mOnValueChange=function(activeDevice,activeMapping,value){
   if(value==1){
        shiftPage.mAction.mActivate.trigger(activeMapping)
   }
}
//You should place the above code in the shiftPage segment as well, but this time you should trigger when value is 0
1 Like

Thank you!

I tried “dual approach” for SHIFT you’ve suggested, and it’s working great! :partying_face:

What do you mean by “remark the .setOutputPort(midiOutput)”? I tried your code and I can’t make it work. But I think, I must have some error somewhere, because when I try to add console.log(JSON.stringify(activeDevice)); inside this function, it just returns empty object. So that’s gonna probably be the problem…

Sure. Here it is:

var freezeBtn = surface.makeButton(2, 4, 2, 2);
var folderBtn = surface.makeButton(4, 4, 2, 2);
var bypassInsertsBtn = surface.makeButton(6, 4, 2, 2);
var bypassEQBtn = surface.makeButton(6, 6, 2, 2);
var bypassSendsBtn = surface.makeButton(6, 8, 2, 2);

freezeBtn.mSurfaceValue.mMidiBinding
  .setInputPort(midiInput)
  .setOutputPort(midiOutput)
  .bindToNote(0, 76); // channel 1, E4
folderBtn.mSurfaceValue.mMidiBinding
  .setInputPort(midiInput)
  .setOutputPort(midiOutput)
  .bindToNote(0, 71); // channel 1, B3
bypassInsertsBtn.mSurfaceValue.mMidiBinding
  .setInputPort(midiInput)
  .setOutputPort(midiOutput)
  .bindToNote(0, 43); // channel 1, G1
bypassEQBtn.mSurfaceValue.mMidiBinding
  .setInputPort(midiInput)
  .setOutputPort(midiOutput)
  .bindToNote(0, 40); // channel 1, E1
bypassSendsBtn.mSurfaceValue.mMidiBinding
  .setInputPort(midiInput)
  .setOutputPort(midiOutput)
  .bindToNote(0, 41); // channel 1, F1

page.makeCommandBinding(
  freezeBtn.mSurfaceValue,
  "Automation",
  "Freeze Trim Automation of Selected Tracks"
);
page.makeCommandBinding(
  folderBtn.mSurfaceValue,
  "Media",
  "Open/Close File Browser"
);
page.makeCommandBinding(
  bypassInsertsBtn.mSurfaceValue,
  "Process Project Logical Editor",
  "Toggle Inserts Bypass of Selected Tracks"
);
page.makeCommandBinding(
  bypassEQBtn.mSurfaceValue,
  "Process Project Logical Editor",
  "Toggle EQ Bypass of Selected Tracks"
);
page.makeCommandBinding(
  bypassSendsBtn.mSurfaceValue,
  "Process Project Logical Editor",
  "Toggle Sends Bypass of Selected Tracks"
);
// .setOutputPort(midiOutput)

Why do you need this? It shouldn’t return something useful anyway. However, as you see, the event is triggered. Not sure what the CMC is expecting to update its led, never owned one unfortunately to test it.

All these toggle as expected, not sure why they’re not on your implementation, since from what I see these buttons are bound to note message. You don’t really need a toggle method in the command binding, since toggling is handled internally by the command when it’s supposed to.

That’s what I’ve tried, but it still doesn’t work.

I was just curious, what could cause the error. And since activeDevice parameter is required in midiOutput.sendMidi() function, it should return at least some information, not just empty object, right?

Yeah, I’m also not sure, why they’re not toggling on my implementation… Nevermind. You’ve already helped me to enable the feature I wanted the most (SHIFT)! :pray:
All other features are just minor visual details, that would be nice to have, but they have no impact on actual functionality (buttons work fine, just its LEDs won’t stay toggled) :slightly_smiling_face:

Now I can finally “Rock ‘n’ roll” with this old controller again! :cowboy_hat_face:

If anyone have some suggestions, how to make these work, please let me know.

Nope :slight_smile: However rest assured that the issue is not on the activeDevice.

Ah, now I understand better, I thought the commands were not being executed.

Anyway, let’s make a test.
Inside your mainPage let’s add:

mainPage.mHostAccess.mTrackSelection.mMixerChannel.mValue.mPan.mOnProcessValueChange=function(activeDevice,activeMapping,arg2,arg3){
     
     var newValue=Math.round(arg2*127)
     console.log("Our pan is set to:  "+newValue)
     midiOutput.sendMidi(activeDevice,[0xB0,0x10,newValue])
     
}

Is this logging correctly the 0-127 pan range?
Do the pan’s led change accordingly?

Thank you for your insights!

Yes, the log shows it’s the correct range.

Unfortunately, it doesn’t. :slightly_frowning_face:

Not sure if this would be helpful:

Maybe @steve has some hint :slight_smile:

Yeah, @mchantzi I’ve been watching this topic… I might have the answer in a Bome script, I’ll try to fish it out of wherever I put it tomorrow. Today, gig.

2 Likes

Very interesting!

Maybe some one will do [or did] the same midi remote work for the CMC QC? :grin::+1:Thanks!

Sorry, no luck.

It’s been a few years, but I seem to recall that I never found a way to address some of the button LEDs, at least by using the same method as lighting the LEDs on the other units.

If ccs didn’t work, perhaps it uses sysex as my keylab does. However, the pan led should work with just a cc, pretty strange… Except if it’s of hi res.

Yeah… Another strange thing I found out during testing: If I remove " .setTypeRelativeSignedBit()", it just returns only 2 values - 65 when turned left and 1 when turned right (and LED still doesn’t work)… :man_shrugging:

If I have also QC, I would definitely do that. But unfortunately, I don’t have it :slightly_frowning_face:

But if you’re comfortable with javascript programming a little bit, I can try to help you to create it by yourself :wink:

2 Likes

Have you seen page 38 of this manual? It lists the messages the CH sends and receives.

1 Like

Oh man. :exploding_head: :zzz:

And they are all correct! The messages to control the LEDs are the same as the midi message that the given button/encoder sends too. First 2 bytes address the channel and command, third byte is the value to send.

As if they were custom made to be customized.

1 Like

I’ve downloaded the very same manual and for whatever reason completely missed to read this page :joy:

So, for example for panning, instead of the B0 10 Val, @Daniel_Pitra use B0 30 xy. You’ll see in this page what x and y stand for. From what I understand you have to set x to the desired lightning type, and then you have y for the pattern. You have to make some tests to see how this y works in order to send the correct value from the event I’ve suggested previously.

This is the expected for the buttons. The “problem” with our friend’s script is that since it relies on commands (for example for “freeze”) there is no obvious way to really know when it’s on or off to send the appropriate value. I have a suggestion but it won’t be stable IF we do use these commands outside of our controller too. I faced the same thing with my own scripts, and occasionally I’ve solved it by mixing it with generic remote assignments.

1 Like

I wouldn’t know about that, I’m not familiar with this type of messages. It’s just that I saw that for pan specifically, there’s a lighting type and a lighting pattern, but it’s not clear to me how this translates to values. Lighting pattern takes values from 1 to B, so, is that 11(?), one light for each segment??? I don’t know. For a script it would be ok, but for the surface editor, does the CH auto-translate the incoming CC values? Could the “CMC Tools” offer some customization? I vaguely remember that for the QC it did, but it’s not mine to test anymore.

0x0b=11 correct. Eleven segments? I don’t know either.

Nope. It would mirror the incoming CC to the device. Even in the script would do the same if we don’t introduce the correct event and translation.

1 Like