Code: Select all
"""
controller.py
"""
# This allows us (and the Framework methods) to use the Live API on occasion
import Live
# Base class for all classes representing control elements on a controller
from _Framework.InputControlElement import *
# Central base class for scripts based on the new Framework
from _Framework.ControlSurface import ControlSurface
# Class encapsulating all functions in Live's transport section
from _Framework.TransportComponent import TransportComponent
# Class representing a button a the controller
from _Framework.ButtonElement import ButtonElement
# Class encompassing several scene to cover a defined section of Live's session
from _Framework.SessionComponent import SessionComponent
from datetime import datetime
# import the LiveUtils helpers
from LiveUtils import *
# macro of control channel we're using
# the launchpad transmits on channel 1
CHANNEL = 0
# device offset is the offset for where in the device list the chains start
DEVICE_OFFSET = 1
TRACK_OFFSET = 8
SESSION = 0
USER_1 = 1
USER_2 = 2
MIXER = 3
NOTE_ON_EVENT = 144
NOTE_OFF_EVENT = 128
CC_EVENT = 176
LED_rgb = 0x00
LED_Rgb = 0x10
LED_rGb = 0x20
LED_rgB = 0x30
LED_RGb = 0x40
LED_RgB = 0x50
LED_rGB = 0x60
LED_RGB = 0x70
# a map of launchpad midi keys to column and row
launchpad_map = {
64: (0,0), 65:(1,0), 66:(2,0), 67:(3,0), 96:(4,0), 97:(5,0), 98:(6,0), 99:(7,0),
60: (0,1), 61:(1,1), 62:(2,1), 63:(3,1), 92:(4,1), 93:(5,1), 94:(6,1), 95:(7,1),
56: (0,2), 57:(1,2), 58:(2,2), 59:(3,2), 88:(4,2), 89:(5,2), 90:(6,2), 91:(7,2),
52: (0,3), 53:(1,3), 54:(2,3), 55:(3,3), 84:(4,3), 85:(5,3), 86:(6,3), 87:(7,3),
48: (0,4), 49:(1,4), 50:(2,4), 51:(3,4), 80:(4,4), 81:(5,4), 82:(6,4), 83:(7,4),
44: (0,5), 45:(1,5), 46:(2,5), 47:(3,5), 76:(4,5), 77:(5,5), 78:(6,5), 79:(7,5),
40: (0,6), 41:(1,6), 42:(2,6), 43:(3,6), 72:(4,6), 73:(5,6), 74:(6,6), 75:(7,6),
36: (0,7), 37:(1,7), 38:(2,7), 39:(3,7), 68:(4,7), 69:(5,7), 70:(6,7), 71:(7,7),
}
launchpad_columns = [
[64,60,56,52,48,44,40,36],
[65,61,57,53,49,45,41,37],
[66,62,58,54,50,46,42,38],
[67,63,59,55,51,47,43,39],
[96,92,88,84,80,76,72,68],
[97,93,89,85,81,77,73,69],
[98,94,90,86,82,78,74,70],
[99,95,91,87,83,79,75,71]
]
class Controller(ControlSurface):
# this controls the size of the coloured box for our script
num_tracks = 8
num_scenes = 8
def __init__(self, c_instance):
ControlSurface.__init__(self, c_instance)
self.log_message("Controller.__init__() %s" % datetime.now() )
self._c_instance = c_instance
# assume mode starts in session mode
self.mode = 0
self.launchpad_buttons = []
self.mode_buttons = []
self.create_mode_buttons()
self.create_patch_buttons()
self.log_message("Controller.__init__() DONE %s" % datetime.now() )
def send_midi(self, midi_event_bytes):
#self.log_message("sending midi: %s" % str(midi_event_bytes) )
self._c_instance.send_midi(midi_event_bytes)
def disconnect(self):
"called by Ableton on unload, should remove any listeners"
for button in self.launchpad_buttons:
button.remove_value_listener(self.cb_activate_chain)
def create_mode_buttons(self):
for cc_num in (108,109,110,111):
mode_btn = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, cc_num)
mode_btn.set_identifier( cc_num - 108 )
mode_btn.add_value_listener(self.cb_switch_mode, identify_sender=True)
self.launchpad_buttons.append(mode_btn)
def cb_switch_mode(self, value, sender):
mode = sender._msg_identifier
self.log_message("cb_switch_mode() changing mode to: %i" % mode)
self.mode = mode
def create_patch_buttons(self):
"create the button callbacks"
# make button elements for each launchpad button
# this is how we deal with midi input, make InputElements for each message
for note_num, (track_num, chain_num) in launchpad_map.items():
# self.log_message(" connecting %i : %i : %i" % (note_num, track_num, chain_num) )
button = ButtonElement(True, MIDI_NOTE_TYPE, CHANNEL, note_num)
# store the number in the launchpad for this button
button.set_identifier( (track_num*8)+chain_num )
# add a callback to the button
button.add_value_listener(self.cb_activate_chain, identify_sender=True)
self.launchpad_buttons.append(button)
def cb_activate_chain(self, value, sender):
"callback fired from a InputControl, value will be the midi vel"
#self.log_message("Controller.activate_chain: value: %s sender: %s" % (value, sender) )
if self.mode != USER_1:
#self.log_message("wrong mode, ignoring")
return
midi_note_num = sender._original_identifier
msg_id = sender._msg_identifier
track_num = ( msg_id / 8 ) + TRACK_OFFSET
param_num = msg_id % 8
if value:
self.log_message(" cb_activate_chain: track %i : param %i " % (track_num, param_num) )
# send light-off to all other rows in the column
for note_num in launchpad_columns[ track_num - TRACK_OFFSET ]:
self.send_midi( (NOTE_ON_EVENT, note_num, LED_rgb) )
# send light on to this one
self.send_midi( (NOTE_ON_EVENT, midi_note_num, LED_rGb) )
try:
chain_device = getTrack(track_num).devices[ DEVICE_OFFSET ]
# loop through the macro params
for i in range(0, 7):
if i == param_num:
#self.log_message(" setting param %i to 127" % i)
chain_device.parameters[i+1].value = 127
else:
chain_device.parameters[i+1].value = 0
except Exception, e:
self.log_message("\n exception: %s\n" % e)
pass