Arturia Keylab : display on LCD screen

I’m not the author of this script, but since it was buried deep in a thread, I thought I could post it here so it could help others, like it helped me. I only tweaked it so it could display the current song name (if in setlist mode) and the rackspace name on the LCD screen of the Keylab. You can of course create your own display logic by modifying the strings fed to the KeylabLCD function on line 74.

It has been tested with the Keylab Essential and the Keylab MkII.

var
    SysexMng         : SysexManager
    KeylabMidiOut    : MidiOutBlock  // Replace 'KeylabMidiOut' with your own handle 
    KEYLAB_LCD_PRE   : String
    KEYLAB_LCD_SEP   : String
    KEYLAB_LCD_END   : String
    ASCII_STRING     : String
    ASCII_HEX_STRING : String array
 
Initialization
    ASCII_STRING     = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[Y]^_`abcdefghijklmnopqrstuvwxyz{|}><";
    ASCII_HEX_STRING = ["20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F","30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F","40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F","50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F","60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F","70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F"];
    KEYLAB_LCD_PRE  = "# F0 00 20 6B 7F 42 04 00 60 01 "
    KEYLAB_LCD_SEP  = " 00 02 "
    KEYLAB_LCD_END  = " 00 F7"
End



// ******************** Utils ********************

    
Function Trim(s : String) returns string
    /* Trim the string if its length is greater than 16 characters */

    var temp : String  // Avoid modifying argument in-place
    
    If Length(s) > 16 Then
        temp = CopySubstring(s, 0, 16)
    Else
        temp = s
    End
    result = temp
End


Function StringToHexString(str : String) Returns String
    /* Hack provided by David-san */
    
    var i : Integer
    result = ""

    For i=0; i<Length(str) ; i=i+1 Do
        result = result + ASCII_HEX_STRING[IndexOfSubstring(ASCII_STRING, CopySubstring(str,i,1), True)]
    End
End


// ***************** Keylab display ***************** 


Function KeylabLCD(line1 : String, line2 : String)
    /* Display each line on the LCD screen of the Keylab */
    
    var msg : String
    
    msg = KEYLAB_LCD_PRE + StringToHexString(Trim(line1)) + KEYLAB_LCD_SEP + StringToHexString(Trim(line2)) + KEYLAB_LCD_END
    SM_CreateSysexFromString(SysexMng, msg)
    SM_SendMidiOut(SysexMng, KeylabMidiOut)
End


// ******************* Callbacks *******************

On Activate
    var song : String
    
    If InSetlistMode() Then
        song = GetCurrentSongName()
    Else
        song = "N/A"
    End

    KeylabLCD("S:" + song, "R:" + GetRackspaceName())
End

The original post can be found here: Dynamic composition of a SysEx message with variable content and length

3 Likes

That’s a good initiative to make us remember this script from @rbmj. I hope you enjoyed the nice possibilities of the GPScript and of course I encourage you to go on scripting :wink:

Man, GPScript is honestly the best feature I wasn’t aware of when I bought GP3! I look forward to the day when global scripts and code reuse will be a thing :wink:

I also have a question:
I’m trying to execute code when I press the arrow buttons on my Keylab, which send a SysEx message. So I’m trying to find a callback function for SysEx messages. I read the documentation, but there doesn’t seem to be a way to catch those. I tried using the MidiEvent callback, which seemed to be the most generic one, but to no avail, even if the message shows up in the MIDI console. Is this feasible?

Hummm, I think there is no callback responding to SysEx, but it could be a good idea to put an entry in the feature request category. I don’t know if it is doable.

Hi - This Script is working great on my Keylab MKii - BUT only if I have it in β€œAnalog Lab” or β€œDAW” modes - If I set the keyboard to β€œUser”, the display changes to reflect the button I pressed to increment the song in the setlist - have you found a way around this?

If it works, why couldn’t you use this DAW mode?

@subsonicman unfortunately that’s a β€œfeature” of the User Mode… What you could try ist to send out the SysEx from GP with some delay (until the Keylab finished to write the corresponding button mapped MIDI…).

But just for clarification: which buttons do you use to navigate through your GP songs/racks?
I assigned buttons out of the DAW / User area (the 10 buttons above the transport control) in user mode. I use the Arturia navigation buttons only in Analog Lab mode to adjust a synth or the analog lab plugin used in a dedicated rack.

BBB

Hi David-San - I’m quite new to the Keylab (delivered yesterday) but it appears that in DAW mode, most of the assignable knobs and buttons aren’t assigned (or assignable), while in β€œAnalog Lab” mode, it doesn’t appear you can change any assignments.

Hi tripleB - in USER mode, even if I initiate the song change from the GP software, the Keylab won’t display the info, so I don’t think it has to do with pressing buttons, or that a delay would solve the issue. But yes, like you say, it appears to be a β€œfeature” - If I can’t figure how to block it, I’ll just use it in AL mode. Thanks for your responses guys.

@subsonicman What I mean with delay in User mode: The key lab shows it’s own message when changing any user control. If you wait until this has finished there’s a chance to send any message (e.g. by the script shown above :wink: ) by SysEx to show up. Unfortunately you would have to do this after any control message, so I think this would cause a lot of traffic and flickering of (external) message and (internal) CC assignments.

BBB

@tripleB Thank you for your input! Yes, I think I understand what you are saying, but I am finding that even if I load a song or Rackspace using the controls on the host laptop (ie not pressing any buttons or controls on the keylab) the message is still not displayed on the LCD while the keylab is in user mode. So perhaps incoming sysex is filtered in User mode? Anyway - its working well enough in Analog Lab mode, I’ve discovered that if you change the functions of certain buttons and 16 pads in user mode, these assignments carry over when you change back to AL mode, so I think I’ve got a good work-around for now.

1 Like

Don’t you think you guys could ask Arturia for an option not to display anything on the diplay but messages received by SysEx via MIDI? (basically what should happen when in DAW mode but for any controls of the keyboard).

Unfortunately, I don’t access to my music gear since the new lockdown happened in my region, but I’ll look into this issue and update the script when things go back to normal (also, with the upcoming release of GP4, chances are that this script will be refactored to be more efficient)

1 Like

many thanks for posting this script @Couack ! I wouldn’t have had the time (or patience) to learn yet another coding language this year!

I’ve been playing with version 4 - due to a couple of changes in v4 of GP script the above script doesn’t work. I was getting errors…

Mismatched Input β€˜song’ expecting identifier

and also errors relating to
SM_CreateSysexFromString(SysexMng, msg)
SM_SendMidiOut(SysexMng, Keylab88MidiOut)
SM_SendMidiOut(SysexMng, Keylab61MidiOut)

I have adjusted the below, but it appears you can’t address Midi Out blocks from the global script? so it looks like this has to be per Rackspace as before…

var
SysexMng : SysexMessage
Keylab88MidiOut : MidiOutBlock // Replace β€˜KeylabMidiOut’ with your own handle
Keylab61MidiOut : MidiOutBlock // Replace β€˜KeylabMidiOut’ with your own handle
KEYLAB_LCD_PRE : String
KEYLAB_LCD_SEP : String
KEYLAB_LCD_END : String
ASCII_STRING : String
ASCII_HEX_STRING : String array

Initialization
ASCII_STRING = " !"#$%&’()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[Y]^_`abcdefghijklmnopqrstuvwxyz{|}><";
ASCII_HEX_STRING = [β€œ20”,β€œ21”,β€œ22”,β€œ23”,β€œ24”,β€œ25”,β€œ26”,β€œ27”,β€œ28”,β€œ29”,β€œ2A”,β€œ2B”,β€œ2C”,β€œ2D”,β€œ2E”,β€œ2F”,β€œ30”,β€œ31”,β€œ32”,β€œ33”,β€œ34”,β€œ35”,β€œ36”,β€œ37”,β€œ38”,β€œ39”,β€œ3A”,β€œ3B”,β€œ3C”,β€œ3D”,β€œ3E”,β€œ3F”,β€œ40”,β€œ41”,β€œ42”,β€œ43”,β€œ44”,β€œ45”,β€œ46”,β€œ47”,β€œ48”,β€œ49”,β€œ4A”,β€œ4B”,β€œ4C”,β€œ4D”,β€œ4E”,β€œ4F”,β€œ50”,β€œ51”,β€œ52”,β€œ53”,β€œ54”,β€œ55”,β€œ56”,β€œ57”,β€œ58”,β€œ59”,β€œ5A”,β€œ5B”,β€œ5C”,β€œ5D”,β€œ5E”,β€œ5F”,β€œ60”,β€œ61”,β€œ62”,β€œ63”,β€œ64”,β€œ65”,β€œ66”,β€œ67”,β€œ68”,β€œ69”,β€œ6A”,β€œ6B”,β€œ6C”,β€œ6D”,β€œ6E”,β€œ6F”,β€œ70”,β€œ71”,β€œ72”,β€œ73”,β€œ74”,β€œ75”,β€œ76”,β€œ77”,β€œ78”,β€œ79”,β€œ7A”,β€œ7B”,β€œ7C”,β€œ7D”,β€œ7E”,β€œ7F”];
KEYLAB_LCD_PRE = β€œ# F0 00 20 6B 7F 42 04 00 60 01 "
KEYLAB_LCD_SEP = " 00 02 "
KEYLAB_LCD_END = " 00 F7”
End

// ******************** Utils ********************

Function Trim(s : String) returns string
/* Trim the string if its length is greater than 16 characters */

var temp : String  // Avoid modifying argument in-place

If Length(s) > 16 Then
    temp = CopySubstring(s, 0, 16)
Else
    temp = s
End
result = temp

End

Function StringHexString(str : String) Returns String
/* Hack provided by David-san */

var i : Integer
result = ""

For i=0; i<Length(str) ; i=i+1 Do
    result = result + ASCII_HEX_STRING[IndexOfSubstring(ASCII_STRING, CopySubstring(str,i,1), True)]
End

End

// ***************** Keylab display *****************

Function KeylabLCD(line1 : String, line2 : String)
/* Display each line on the LCD screen of the Keylab */
// next line is for v3, now unused.
// var msg : String
var msg : SysexMessage

msg = KEYLAB_LCD_PRE + StringHexString(Trim(line1)) + KEYLAB_LCD_SEP + StringToHexString(Trim(line2)) + KEYLAB_LCD_END

// next three lines were relevant to GPScript V3 but are now depreciated!
//SM_CreateSysexFromString(SysexMng, msg)
//SM_SendMidiOut(SysexMng, Keylab88MidiOut)
//SM_SendMidiOut(SysexMng, Keylab61MidiOut)
SendSysexExternal(Keylab88MidiOut, msg) // new v4 script
SendSysexExternal(Keylab61MidiOut, msg) // new v4 script

End

// ******************* Callbacks *******************

On Activate
var NewSong : String

If InSetlistMode() Then
    NewSong = GetCurrentSongName()
Else
    NewSong = "N/A"
End

KeylabLCD("S:" + NewSong, "R:" + GetRackspaceName())

End

This version is now adapted to run on the Global Rackspace - so you don’t need to put the script into every Rackspace, or insert the midi out blocks into every Rackspace.

var
SysexMng : SysexMessage
Keylab88MidiOut : MidiOutBlock // Replace β€˜KeylabMidiOut’ with your own handle
Keylab61MidiOut : MidiOutBlock // Replace β€˜KeylabMidiOut’ with your own handle
MidiIn : MidiInBlock
KEYLAB_LCD_PRE : String
KEYLAB_LCD_SEP : String
KEYLAB_LCD_END : String
ASCII_STRING : String
ASCII_HEX_STRING : String array

Initialization
ASCII_STRING = " !"#$%&’()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[Y]^_`abcdefghijklmnopqrstuvwxyz{|}><";
ASCII_HEX_STRING = [β€œ20”,β€œ21”,β€œ22”,β€œ23”,β€œ24”,β€œ25”,β€œ26”,β€œ27”,β€œ28”,β€œ29”,β€œ2A”,β€œ2B”,β€œ2C”,β€œ2D”,β€œ2E”,β€œ2F”,β€œ30”,β€œ31”,β€œ32”,β€œ33”,β€œ34”,β€œ35”,β€œ36”,β€œ37”,β€œ38”,β€œ39”,β€œ3A”,β€œ3B”,β€œ3C”,β€œ3D”,β€œ3E”,β€œ3F”,β€œ40”,β€œ41”,β€œ42”,β€œ43”,β€œ44”,β€œ45”,β€œ46”,β€œ47”,β€œ48”,β€œ49”,β€œ4A”,β€œ4B”,β€œ4C”,β€œ4D”,β€œ4E”,β€œ4F”,β€œ50”,β€œ51”,β€œ52”,β€œ53”,β€œ54”,β€œ55”,β€œ56”,β€œ57”,β€œ58”,β€œ59”,β€œ5A”,β€œ5B”,β€œ5C”,β€œ5D”,β€œ5E”,β€œ5F”,β€œ60”,β€œ61”,β€œ62”,β€œ63”,β€œ64”,β€œ65”,β€œ66”,β€œ67”,β€œ68”,β€œ69”,β€œ6A”,β€œ6B”,β€œ6C”,β€œ6D”,β€œ6E”,β€œ6F”,β€œ70”,β€œ71”,β€œ72”,β€œ73”,β€œ74”,β€œ75”,β€œ76”,β€œ77”,β€œ78”,β€œ79”,β€œ7A”,β€œ7B”,β€œ7C”,β€œ7D”,β€œ7E”,β€œ7F”];
KEYLAB_LCD_PRE = β€œ# F0 00 20 6B 7F 42 04 00 60 01 "
KEYLAB_LCD_SEP = " 00 02 "
KEYLAB_LCD_END = " 00 F7”
End

// ******************** Utils ********************

Function Trim(s : String) returns string
/* Trim the string if its length is greater than 16 characters */

var temp : String  // Avoid modifying argument in-place

If Length(s) > 16 Then
    temp = CopySubstring(s, 0, 16)
Else
    temp = s
End
result = temp

End

Function StringHexString(str : String) Returns String
/* Hack provided by David-san */

var i : Integer
result = ""

For i=0; i<Length(str) ; i=i+1 Do
    result = result + ASCII_HEX_STRING[IndexOfSubstring(ASCII_STRING, CopySubstring(str,i,1), True)]
End

End

// ***************** Keylab display *****************

Function KeylabLCD(line1 : String, line2 : String)
/* Display each line on the LCD screen of the Keylab */

var msg : SysexMessage

msg = KEYLAB_LCD_PRE + StringHexString(Trim(line1)) + KEYLAB_LCD_SEP + StringToHexString(Trim(line2)) + KEYLAB_LCD_END

// next three lines were relevant to GPScript V3 but are now depreciated!
//SM_CreateSysexFromString(SysexMng, msg)
//SM_SendMidiOut(SysexMng, Keylab88MidiOut)
//SM_SendMidiOut(SysexMng, Keylab61MidiOut)
SendSysexExternal(Keylab88MidiOut, msg)
SendSysexExternal(Keylab61MidiOut, msg)

End

// ******************* Callbacks *******************

// Called when you switch to another rackspace
On Rackspace(oldRackspaceIndex : integer, newRackspaceIndex : integer)

var NewSong : String


If InSetlistMode() Then
    NewSong = GetCurrentSongName()
Else
    NewSong = "N/A"
End

KeylabLCD("S:" + NewSong, "R:" + GetRackspaceNameAtIndex(newRackspaceIndex))

End

Hey β€” thanks so much for this. If I may give a little feedback (which you’re free to ignore):

In your Trim function, you don’t really need that β€˜temp’ variable. You can just use β€˜result’

But more importantly, your Trim function could be simplified as follows:

Function Trim(s : String) returns string
/* Trim the string if its length is greater than 16 characters */

   result = CopySubstring(s, 0, 16)

End

In other words, there’s no need to actually test if the length of the string is greater than 16

1 Like

Ah cool, I’ll have a look at that
To be clear, I’m not claiming authorship of this script! It appears to have been a collaboration of several people and many different iterations - I just got excited about the possibilities of the new features in GP4 and decided to have a go at adapting it to work on the new platform.

@subsonicman thank you for adapting this script for the global rackspace
@dhj good catch on the Trim function, I was less experienced at the time

There’s also a bunch of Sysex hacks that are no longer necessary in GP4, so I will refactor the whole thing.

This is not necessary anymore as @dhj made a GPScript function with it which is much faster. :wink: