Same three CC values for many hardware controls... and Rig Manager

@David-san & @dhj

So this worked like a charm… once again great scripting David-San! Thank you!

I ended up injecting 10 controls via this Rig Script and then made MIDI Control Aliases in Rig Manager for those controls.

I changed the created CC numbers in the script to numbers that are by default undefined (CC 102-110).

Here’s that script:

Var
  XK5                        : MidiInDeviceAlias // Define XK5 as a RigManager alias to the real controller
  CC_State, CC99, CC98       : Integer = 0;  

On ControlChangeEvent(CC_msg:ControlChangeMessage) Matching 99,98,6 from XK5
    Select
      CC_State == 0 Do if (CC_msg.GetCCNumber()==99) Then CC99 = CC_msg.GetCCValue(); CC_State = 1 Else CC_State = 0 End
                       
      CC_State == 1 Do if (CC_msg.GetCCNumber()==98) Then CC98 = CC_msg.GetCCValue(); CC_State = 2 Else CC_State = 0 End
    
      CC_State == 2 Do
        if (CC_msg.GetCCNumber()==6)
        then
          Select
			// Leslie Stop => CC#102
            CC99 == 6  && CC98 == 9  Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(102, CC_msg.GetCCValue()*127, 1));
			
            // Leslie Fast => CC#103
            CC99 == 1  && CC98 == 9  Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(103, CC_msg.GetCCValue()*127, 1));
            
            // Overdrive Depth => CC#104
            CC99 == 2  && CC98 == 48 Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(104, CC_msg.GetCCValue(), 1));
			            
            // Effect Amount => CC#105
            CC99 == 8  && CC98 == 48 Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(105, CC_msg.GetCCValue(), 1));
			            
            // Reverb Depth => CC#106
            CC99 == 2  && CC98 == 10 Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(106, CC_msg.GetCCValue(), 1));
						
            // Overdrive => CC#107
            CC99 == 0  && CC98 == 48  Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(107, CC_msg.GetCCValue()*127, 1));
						
            // Effect => CC#108
            CC99 == 4  && CC98 == 48  Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(108, CC_msg.GetCCValue()*127, 1));
						
            // Reverb => CC#109
            CC99 == 0  && CC98 == 10  Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(109, CC_msg.GetCCValue()*127, 1));
						
            // Bypass Leslie => CC#110
            CC99 == 0  && CC98 == 9  Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(110, CC_msg.GetCCValue()*127, 1));
			
          End  
        end
        CC_State = 0; 
     End
End

While I am very happy to just use the sounds in the XK-5 now, the B3-X VST does have a controller profile function within it that auto converts these NRPN values to the B3-X from Hammond keyboards, if I want to use the XK to control the B3-X.

That profile feature would make this scripting unnecessary, except in cases where I want the controls to work with a different VST (in my case, having the stop and fast controls operate the TR5 Leslie VST in my global rackspace) or to use controls from the XK-5 that do not have an profile correlation in the B3-X, like the Overdrive, Effects and Reverb controls.

So thanks again, this script is perfect!

For anyone else doing the same thing, here is a full index of all the NRPN values from the XK5:

Parameter Name CC 99 CC 98 CC 6 (value) TYPE
SPLIT 0 5 0/1 SWITCH
PEDAL TO LOWER 0 11 0/1 SWITCH
PEDAL SUSTAIN 6 34 0/1 SWITCH
UPPER OCTIVE UP 2 5 64 SWITCH
UPPER OCTIVE DOWN 2 5 63 SWITCH
LOWER OCTIVE UP 3 11 64 SWITCH
LOWER OCTIVE DOWN 3 11 63 SWITCH
UPPER TRANSPOSE UP 0 1 64 SWITCH
UPPER TRANSPOSE DOWN 0 1 63 SWITCH
LOWER TRANSPOSE UP 3 11 64 SWITCH
LOWER TRANSPOSE DOWN 3 11 63 SWITCH
BYPASS 0 9 0/1 SWITCH
STOP 6 9 0/1 SWITCH
FAST 1 9 0/1 SWITCH
VIBRATO UPPER 3 9 0/1 SWITCH
VIBRATO LOWER 4 33 0/1 SWITCH
VIBRATO V1 5 9 0 6-WAY
VIBRATO C1 5 9 3 6-WAY
VIBRATO V2 5 9 1 6-WAY
VIBRATO C2 5 9 4 6-WAY
VIBRATO V3 5 9 2 6-WAY
VIBRATO C3 5 9 5 6-WAY
PERCUSSION 0 8 0/1 SWITCH
SOFT 3 8 0/1 SWITCH
FAST 2 8 0/1 SWITCH
THIRD 1 8 0/1 SWITCH
BASS 8 3 0-127 ROTARY
FREQ 11 3 0-127 ROTARY
GAIN 9 3 0-127 ROTARY
TREBLE 10 3 0-127 ROTARY
OVERDRIVE % 2 48 0-127 ROTARY
EFFECT % 8 48 0-127 ROTARY
REVERB % 2 10 0-127 ROTARY
OVERDRIVE 0 48 0/1 SWITCH
EFFECT 4 48 0/1 SWITCH
REVERB 0 10 0/1 SWITCH
2 Likes

I wrote this GPscript in such a way that you can easily notice how to modify and extend it to add or modify some more controls. So, happy it worked for you. :wink:

3 Likes

Howdy
This all makes complete sense except for CC_State; I can see it declared but what is the mechanism that links it to the result of Matching?

EDIT: Im overthinking it…I thought it was a dynamic assignment but it actually needs to get through the gate of the callback first and initial state = 0…just very different to the coding of the last 3 years…

I dont know much about this coding, but what I did is something close to a good old state machine :nerd_face:

2 Likes

Warning! Dad joke
“That would make you a state(sman)!”
Yep its bad…

This example actually demonstrates one important advantage of the constraint mechanism. If that constraint wasn’t there, the ControlChangeEvent callback would have to be able to handle all possible CC messages that you care about, which would just make the callback more complicated and putting the onus on the user to manage all the bookkeeping.

By using the matching constraint, it becomes simple to have clean separation among callbacks that are responsible for different parts of the system without interference. It also becomes easier to read the code since you can just look at the first line of each callback to see what CC numbers it cares about. Same for constrained note events, etc. No need to pay the cost of a callback for every single note you play when you only want to do something whenever the lowest C on your keyboard is played.

2 Likes

Probably because English is not my first language, but I completely missed the joke. Or maybe it’s a really bad one. :face_with_monocle:

Thanks David
But isnt addressing an array ie Matching iTargRange only a small overhead ie to pull it up for comparison? I am a hack so Im sorry if Im simplifying this too much
Bottom line is, you wrote it and understand it way more than I do…just from a user point of view; the range of values is something I would normally do up the top and never look at it again ie grouping control numbers to hardware etc and using a name that doesnt second guess ie iTargetEncoders or something like that

Cheers

What is your first?

French… nobody is perfect :innocent:

1 Like

It’s not you @David-san. It’s particularly bad.

Its prob the worst Ive ever come up with…but my son in law has been teaching me well and he is only 25 :slight_smile: (I have 15+2 grandchildren…so it might be time to step up)

4 posts were split to a new topic: The Global MIDI monitor doesn’t display anymore the original MIDI messages when used in a gig script callback

@David-san

After using this script happily for about a year… I wanted to map my hardware transpose and octive buttons to the GP global transpose features.

The problem I am running into is the two physical transpose up and down buttons on the XK send the same NRPM values for CC98, CC99 and they just send the CC6 value as either 64 for up and 63 for down. So I need to somehow make these two buttons send unique CC commands without breaking the rest of the script.

If I just add this to the script and map the system actions transpose up to the button:

 // Transpose Down => CC#111
            CC99 == 0  && CC98 == 1  Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(111, CC_msg.GetCCValue()*127, 1));

… the result is both buttons make the transpose jump two octives because of course values of 63, 64 are beyond 48 semitones.

I’ve tried a few approaches, none of them work, such as add CC 6 to the if but it’s already part of the getCCNumber () argment so it compiles but with no functionality.

I tried taking the result of the CC injecttion above… and convert that using the WithCCValue into a fresh CC message but I can’t seem to find syntax that works. In other words convert CC 111, value of 64 to CC 112 value of 127… convert CC 111, value of 65 to CC 113, value of 127.

Not even certain that is an approach that makes any sense.

Would you have any clever solutions to this scenario?

Hi @brandon, if I understand well you could do something like that:

// Transpose Down => CC#111
            CC99 == 0  && CC98 == 1  && CC_msg.GetCCValue() == 63 Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(111, 127, 1));

// Transpose Down => CC#112
            CC99 == 0  && CC98 == 1  && CC_msg.GetCCValue() == 64 Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(112, 127, 1));

But, if your transpose controller keys send a unique value (no return to zero), you will have to take this into acount when configuring the assigned widgets and use the momentary to latching option. If you absolutely need a momentary button, we will need to simulate this behavior in a more complicated GPScript.

Please, tell me if the code above works for you. :wink:

1 Like

Thanks @David-san for this. It probably would have worked if I had understood what the XK was actually sending and explained it correctly :crazy_face: Thanks for this though, I understand what you have done with the syntax so I learn a bit more.

What the XK is actually doing as it turns out is sending values of 64 for no transposition (or back to natural) and values of 63 - 58 for each half step down (mining out at -6 half steps = 58) and 65-70 for every raised half step. I didn’t realize this until I tried your script, I had been testing just one increment with those buttons.

So it’s more complicated in that each specfic value it sends should corolate with a specific semi-tone incriment and these two buttons actually behave as one.

However, I’ve also discovered that I can use the ‘zone’ I am sending controlelr data from pre-trasposed from the XK so that I actually don’t need this script function at all for semi-tone transposition.

What I would like to control GP with though is the Octive function on the XK which does not send transposed notes (by factors of 12) via the midi zone. So that would indeed be useful, to have GP follow the octive transposition buttons on the XK.

Like transpose, these buttons send the same values for 99 (2) and 98 (5) and vary the CC6 value in a range of 62 (-2 octives) through 66 (+2 octives) with no transposition if sending a value of 64.

So using your code as a starting point I found that if I put in 5 separate lines, one for each octive state, and had these CC6 values send a specific value, not a range, I could get this to work.

It took some trial and error to get the values to equal 12 semitone increments…

            // Octive 0   => CC#111//
            CC99 == 2  && CC98 == 5  && CC_msg.GetCCValue() == 64 Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(111, 63.5, 63.5));
            
            // Octive +1  => CC#111//
            CC99 == 2  && CC98 == 5  && CC_msg.GetCCValue() == 65 Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(111, 80, 80));
            
            // Octive +2  => CC#111//
            CC99 == 2  && CC98 == 5  && CC_msg.GetCCValue() == 66 Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(111, 95, 95));
            
            // Octive -1  => CC#111//
            CC99 == 2  && CC98 == 5  && CC_msg.GetCCValue() == 63 Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(111, 48, 48));
            
            // Octive -2  => CC#111//
            CC99 == 2  && CC98 == 5  && CC_msg.GetCCValue() == 62 Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(111, 32, 32));

So thank you!

I do wonder if there is a better, more efficient way of coding this but this seems to do the trick.

cheers!

Take care of this:

MakeControlChangeMessageEx(<number : int>, <value : int>, <channel : int>)

The second paramater cannot be a float, it has to be 63 or 64 but not 63.5. There is no float MIDI value. And the last parameter is the MIDI channel, limited to 16! :wink:

But you are right I am lost. Are you talking about buttons (on/off) or knobs which can sen more than 2 values? For sure, I am missing something… :thinking:

1 Like

ah ok…

changed 63.5 to 64, and the script still finds the right octive (no transpose) and it seems all the midi channels need to be set to 16 (or those high erronious numbers) for this to work for some reason eventhough I am sending notes on Ch’s 1-8.

Yes, two buttons on the XK. They both send the same CC99, 98 values… one increases the CC6 value, the other decreases it.

I’ve mapped CC111 to a button widget and this widget to system actions global transpose. It’s working.

So, I think you made me do something stupid. In fact the two buttons allow you to increment and decrement the value of CC#6. In this case you can’t distinguish one button from the other, you have to do as if it were a knob with a value varying from 0 to 127.

Quite possibly, yes. I’m good for that every so often.

exactly

yes, if you were trying to transpose semi-tones you’d either have to treat it as if it were a knob control or you’d have to create a lot of entries in the script (12)… one for each semi-tone.

But as it turns out… I don’t actually need to transpose semitones in GP… local midi in blocks see the notes transposed by using the built in transpose function on the XK… meaning for me I can transpose my organ sounds (comming from the XK as analog audio) and my VST sounds by using the same controls on the XK without scripting, keeping everything transposed the same.

However to use the octive controls, which are also two buttons that you can’t distinguish between, perhaps you can do it as if it were a knob but since it’s just 5 options total, I just did this with five instances of the code you sent, modified as follows:

 // Octive 0   => CC#111//
            CC99 == 2  && CC98 == 5  && CC_msg.GetCCValue() == 64 Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(111, 64, 16));
            
            // Octive +1  => CC#111//
            CC99 == 2  && CC98 == 5  && CC_msg.GetCCValue() == 65 Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(111, 80, 16));
            
            // Octive +2  => CC#111//
            CC99 == 2  && CC98 == 5  && CC_msg.GetCCValue() == 66 Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(111, 95, 16));
            
            // Octive -1  => CC#111//
            CC99 == 2  && CC98 == 5  && CC_msg.GetCCValue() == 63 Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(111, 48, 16));
            
            // Octive -2  => CC#111//
            CC99 == 2  && CC98 == 5  && CC_msg.GetCCValue() == 62 Do InjectMidiEventViaRigManager(XK5, MakeControlChangeMessageEx(111, 32, 16));

works… perhaps not as elegantly as it could but works all the same.

thanks again