Skip to content

Commit 267be83

Browse files
committed
Launchpad Pro MK3 support
1 parent 2b87faa commit 267be83

File tree

2 files changed

+205
-0
lines changed

2 files changed

+205
-0
lines changed

launchpad_py/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@
99
from launchpad_py.launchpad import LaunchpadMiniMk3
1010
from launchpad_py.launchpad import LaunchpadLPX
1111
from launchpad_py.launchpad import MidiFighter64
12+
from launchpad_py.launchpad import LaunchpadProMk3
1213
from launchpad_py import charset

launchpad_py/launchpad.py

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2934,3 +2934,207 @@ def Reset( self ):
29342934
# Also, setting the brightness to 0/off might cause additional trouble.
29352935
# self.LedAllOn( 0 )
29362936
pass
2937+
2938+
########################################################################################
2939+
### CLASS LaunchpadPROMk3
2940+
###
2941+
### For 3-color "X" Launchpads
2942+
########################################################################################
2943+
# +---+ +---+---+---+---+---+---+---+---+ +---+
2944+
# | 90| | 91| | | | | | | 98| | 99|
2945+
# +---+ +---+---+---+---+---+---+---+---+ +---+
2946+
#
2947+
# +---+ +---+---+---+---+---+---+---+---+ +---+
2948+
# | 80| | 81| | | | | | | | | 89|
2949+
# +---+ +---+---+---+---+---+---+---+---+ +---+
2950+
# | 70| | | | | | | | | | | 79|
2951+
# +---+ +---+---+---+---+---+---+---+---+ +---+
2952+
# | 60| | | | | | | | 67| | | 69|
2953+
# +---+ +---+---+---+---+---+---+---+---+ +---+
2954+
# | 50| | | | | | | | | | | 59|
2955+
# +---+ +---+---+---+---+---+---+---+---+ +---+
2956+
# | 40| | | | | | | | | | | 49|
2957+
# +---+ +---+---+---+---+---+---+---+---+ +---+
2958+
# | 30| | | | | | | | | | | 39|
2959+
# +---+ +---+---+---+---+---+---+---+---+ +---+
2960+
# | 20| | | | 23| | | | | | | 29|
2961+
# +---+ +---+---+---+---+---+---+---+---+ +---+
2962+
# | 10| | | | | | | | | | | 19|
2963+
# +---+ +---+---+---+---+---+---+---+---+ +---+
2964+
#
2965+
# +---+---+---+---+---+---+---+---+
2966+
# |101|102| | | | | |108|
2967+
# +---+---+---+---+---+---+---+---+
2968+
# +---+---+---+---+---+---+---+---+
2969+
# | 1| 2| | | | | | 8|
2970+
# +---+---+---+---+---+---+---+---+
2971+
class LaunchpadProMk3( LaunchpadPro ):
2972+
2973+
# COLORS = {'black':0, 'off':0, 'white':3, 'red':5, 'green':17 }
2974+
2975+
#-------------------------------------------------------------------------------------
2976+
#-- Opens one of the attached Launchpad MIDI devices.
2977+
#-- Uses search string "ProMK3", by default.
2978+
#-------------------------------------------------------------------------------------
2979+
# Overrides "LaunchpadPro" method
2980+
# TODO: Find a fix for the two ProMk3 MIDI devices
2981+
def Open( self, number = 0, name = "ProMk3" ):
2982+
retval = super( LaunchpadProMk3, self ).Open( number = number, name = name )
2983+
if retval == True:
2984+
self.LedSetMode( 1 )
2985+
2986+
return retval
2987+
2988+
2989+
#-------------------------------------------------------------------------------------
2990+
#-- Checks if a device exists, but does not open it.
2991+
#-- Does not check whether a device is in use or other, strange things...
2992+
#-- Uses search string "ProMk3", by default.
2993+
#-------------------------------------------------------------------------------------
2994+
# Overrides "LaunchpadBase" method
2995+
def Check( self, number = 0, name = "ProMk3" ):
2996+
return super( LaunchpadProMk3, self ).Check( number = number, name = name )
2997+
2998+
2999+
#-------------------------------------------------------------------------------------
3000+
#-- Sets the button layout (and codes) to the set, specified by <mode>.
3001+
#-- Valid options:
3002+
#-- 00 - Session, 01 - Fader, 02 - Chord, 03 - Custom Mode,
3003+
#-- 04 - Note / Drum, 05 - Scale Settings, 06 - Sequencer Settings, 07 - Sequencer Steps,
3004+
#-- 08 - Sequencer Velocity, 09 - Sequencer Pattern Settings, 0A - Sequencer Probability, 0B - Sequencer Mutation,
3005+
#-- 0C - Sequencer Micro Step, 0D - Sequencer Projects, 0E - Sequencer Patterns, 0F - Sequencer Tempo,
3006+
#-- 10 - Sequencer Swing, 11 - Programmer Mode, 12 - Settings Menu, 13 - Custom mode Settings,
3007+
#-------------------------------------------------------------------------------------
3008+
# TODO: ASkr, Undocumented!
3009+
# TODO: return value
3010+
def LedSetLayout( self, mode ):
3011+
ValidModes = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13]
3012+
if mode not in ValidModes:
3013+
return
3014+
3015+
self.midi.RawWriteSysEx( [ 0, 32, 41, 2, 14, 0, mode ] )
3016+
time.wait(10)
3017+
3018+
3019+
#-------------------------------------------------------------------------------------
3020+
#-- Selects the ProMk3's mode.
3021+
#-- <mode> -> 0 -> "Ableton Live mode"
3022+
#-- 1 -> "Programmer mode" (what we need)
3023+
#-------------------------------------------------------------------------------------
3024+
def LedSetMode( self, mode ):
3025+
if mode < 0 or mode > 1:
3026+
return
3027+
3028+
self.midi.RawWriteSysEx( [ 0, 32, 41, 2, 14, 14, mode ] )
3029+
time.wait(10)
3030+
3031+
3032+
#-------------------------------------------------------------------------------------
3033+
#-- Sets the button layout to "Session" mode.
3034+
#-------------------------------------------------------------------------------------
3035+
# TODO: ASkr, Undocumented!
3036+
def LedSetButtonLayoutSession( self ):
3037+
self.LedSetLayout( 0 )
3038+
3039+
3040+
#-------------------------------------------------------------------------------------
3041+
#-- Controls a grid LED by its position <number> and a color, specified by
3042+
#-- <red>, <green> and <blue> intensities, with can each be an integer between 0..63.
3043+
#-- If <blue> is omitted, this methos runs in "Classic" compatibility mode and the
3044+
#-- intensities, which were within 0..3 in that mode, are multiplied by 21 (0..63)
3045+
#-- to emulate the old brightness feeling :)
3046+
#-- Notice that each message requires 10 bytes to be sent. For a faster, but
3047+
#-- unfortunately "not-RGB" method, see "LedCtrlRawByCode()"
3048+
#-- ProMk3 color data extended to 7-bit but for compatibility we still using 6-bit values
3049+
#-------------------------------------------------------------------------------------
3050+
def LedCtrlRaw( self, number, red, green, blue = None ):
3051+
3052+
if number < 0 or number > 99:
3053+
return
3054+
3055+
if blue is None:
3056+
blue = 0
3057+
red *= 21
3058+
green *= 21
3059+
3060+
limit = lambda n, mini, maxi: max(min(maxi, n), mini)
3061+
3062+
red = limit( red, 0, 63 ) << 1
3063+
green = limit( green, 0, 63 ) << 1
3064+
blue = limit( blue, 0, 63 ) << 1
3065+
3066+
self.midi.RawWriteSysEx( [ 0, 32, 41, 2, 14, 3, 3, number, red, green, blue ] )
3067+
3068+
3069+
#-------------------------------------------------------------------------------------
3070+
#-- Same as LedCtrlRawByCode, but with a pulsing LED.
3071+
#-- Pulsing can be stoppped by another Note-On/Off or SysEx message.
3072+
#-------------------------------------------------------------------------------------
3073+
def LedCtrlPulseByCode( self, number, colorcode = None ):
3074+
3075+
if number < 0 or number > 99:
3076+
return
3077+
3078+
if colorcode is None:
3079+
colorcode = LaunchpadPro.COLORS['white']
3080+
3081+
colorcode = min(127, max(0, colorcode))
3082+
3083+
self.midi.RawWrite( 146, number, colorcode )
3084+
3085+
3086+
#-------------------------------------------------------------------------------------
3087+
#-- Same as LedCtrlPulseByCode, but with a dual color flashing LED.
3088+
#-- The first color is the one that is already enabled, the second one is the
3089+
#-- <colorcode> argument in this method.
3090+
#-- Flashing can be stoppped by another Note-On/Off or SysEx message.
3091+
#-------------------------------------------------------------------------------------
3092+
def LedCtrlFlashByCode( self, number, colorcode = None ):
3093+
3094+
if number < 0 or number > 99:
3095+
return
3096+
3097+
if colorcode is None:
3098+
colorcode = LaunchpadPro.COLORS['white']
3099+
3100+
colorcode = min(127, max(0, colorcode))
3101+
3102+
self.midi.RawWrite( 145, number, colorcode )
3103+
3104+
3105+
#-------------------------------------------------------------------------------------
3106+
#-- Quickly sets all all LEDs to the same color, given by <colorcode>.
3107+
#-- If <colorcode> is omitted, "white" is used.
3108+
#-------------------------------------------------------------------------------------
3109+
def LedAllOn( self, colorcode = None ):
3110+
if colorcode is None:
3111+
colorcode = LaunchpadPro.COLORS['white']
3112+
3113+
colorcode = min(127, max(0, colorcode))
3114+
3115+
# TODO: Maybe the SysEx was indeed a better idea :)
3116+
# Did some tests:
3117+
# MacOS: doesn't matter;
3118+
# Windoze: SysEx much better;
3119+
# Linux: completely freaks out
3120+
for x in range(9):
3121+
for y in range(9):
3122+
self.midi.RawWrite(144, (x + 1) + ((y + 1) * 10), colorcode)
3123+
3124+
3125+
#-------------------------------------------------------------------------------------
3126+
#-- (fake to) reset the Launchpad
3127+
#-- Turns off all LEDs
3128+
#-------------------------------------------------------------------------------------
3129+
def Reset( self ):
3130+
self.LedAllOn( 0 )
3131+
3132+
3133+
#-------------------------------------------------------------------------------------
3134+
#-- Go back to custom modes before closing connection
3135+
#-- Otherwise Launchpad will stuck in programmer mode
3136+
#-------------------------------------------------------------------------------------
3137+
def Close( self ):
3138+
# TODO: redundant (but needs fix for Py2 embedded anyway)
3139+
self.midi.CloseInput()
3140+
self.midi.CloseOutput()

0 commit comments

Comments
 (0)