def _setup_mixer_control(self): is_momentary = True # We use non-latching buttons (keys) throughout, so we'll set this as a constant num_tracks = 7 # Here we define the mixer width in tracks (a mixer has only one dimension) global mixer # We want to instantiate the global mixer as a MixerComponent object (it was a global "None" type up until now...) mixer = MixerComponent(num_tracks, 0) #(num_tracks, num_returns, with_eqs, with_filters) mixer.set_track_offset(0) #Sets start point for mixer strip (offset from left) """set up the mixer buttons""" self.song().view.selected_track = mixer.channel_strip(0)._track mixer.selected_strip().set_mute_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 64)) #mixer.selected_strip().set_solo_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 44)) mixer.selected_strip().set_arm_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 72)) track_select_notes = [65, 73, 66, 74, 67, 75, 68, 76] #more note numbers need to be added if num_scenes is increased slider_select_notes = [23, 22, 15, 14, 5, 7, 6, 4] pan_select_notes = [21, 20, 13, 12, 3, 1, 0, 2] master_volume_control = SliderElement(MIDI_CC_TYPE, 0, 4) master_select_button = ButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, 76) prehear_control = EncoderElement(MIDI_CC_TYPE, 0, 26, Live.MidiMap.MapMode.absolute) crossfader = SliderElement(MIDI_CC_TYPE, 0, 24) for index in range(num_tracks): mixer.channel_strip(index).set_select_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, track_select_notes[index])) mixer.channel_strip(index).set_volume_control(SliderElement(MIDI_CC_TYPE, CHANNEL, slider_select_notes[index])) mixer.channel_strip(index).set_pan_control(EncoderElement(MIDI_CC_TYPE, CHANNEL, pan_select_notes[index], Live.MidiMap.MapMode.absolute)) crossfader.name = 'Crossfader' master_volume_control.name = 'Master_Volume_Control' master_select_button.name = 'Master_Select_Button' prehear_control.name = 'Prehear_Volume_Control' mixer.set_crossfader_control(crossfader) mixer.set_prehear_volume_control(prehear_control) mixer.master_strip().set_volume_control(master_volume_control) mixer.master_strip().set_select_button(master_select_button)
def _setup_mixer_control(self): is_momentary = True mixer = MixerComponent(1) mixer.selected_strip().set_mute_button( SysexButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KMK_PAD[9])) mixer.selected_strip().set_solo_button( SysexButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KMK_PAD[13])) mixer.set_select_buttons( SysexButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KMK_PAD[15]), SysexButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KMK_PAD[14])) send_faders = [] NUM_CONTROLLABLE_SENDS = 4 for index in range(NUM_CONTROLLABLE_SENDS): send_faders.append( SliderElement(MIDI_CC_TYPE, CHANNEL, KMK_FADER[index + 2])) mixer.selected_strip().set_volume_control( SliderElement(MIDI_CC_TYPE, CHANNEL, KMK_FADER[0])) mixer.selected_strip().set_pan_control( SliderElement(MIDI_CC_TYPE, CHANNEL, KMK_FADER[1])) mixer.selected_strip().set_send_controls(tuple(send_faders)) mixer.set_prehear_volume_control( EncoderElement(MIDI_CC_TYPE, CHANNEL, KMK_FADER[6], Live.MidiMap.MapMode.absolute)) mixer.master_strip().set_volume_control( SliderElement(MIDI_CC_TYPE, CHANNEL, KMK_FADER[7])) return mixer
def _setup_mixer_control(self): is_momentary = True # We use non-latching buttons (keys) throughout, so we'll set this as a constant num_tracks = 7 # Here we define the mixer width in tracks (a mixer has only one dimension) global mixer # We want to instantiate the global mixer as a MixerComponent object (it was a global "None" type up until now...) mixer = MixerComponent(num_tracks, 0) #(num_tracks, num_returns, with_eqs, with_filters) mixer.set_track_offset(0) #Sets start point for mixer strip (offset from left) """set up the mixer buttons""" self.song().view.selected_track = mixer.channel_strip(0)._track mixer.selected_strip().set_mute_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 64)) #mixer.selected_strip().set_solo_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 44)) mixer.selected_strip().set_arm_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 72)) track_select_notes = [65, 73, 66, 74, 67, 75, 68, 76] #more note numbers need to be added if num_scenes is increased slider_select_notes = [3, 2, 1, 0, 5, 4, 6, 7] #pan_select_notes = [21, 20, 13, 12, 3, 1, 0, 2] master_volume_control = SliderElement(MIDI_CC_TYPE, 0, 7) #master_select_button = ButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, 76) #prehear_control = EncoderElement(MIDI_CC_TYPE, 0, 26, Live.MidiMap.MapMode.absolute) #crossfader = SliderElement(MIDI_CC_TYPE, 0, 24) for index in range(num_tracks): #mixer.channel_strip(index).set_select_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, track_select_notes[index])) mixer.channel_strip(index).set_volume_control(SliderElement(MIDI_CC_TYPE, CHANNEL, slider_select_notes[index])) #mixer.channel_strip(index).set_pan_control(EncoderElement(MIDI_CC_TYPE, CHANNEL, pan_select_notes[index], Live.MidiMap.MapMode.absolute)) #crossfader.name = 'Crossfader' master_volume_control.name = 'Master_Volume_Control' #master_select_button.name = 'Master_Select_Button' #prehear_control.name = 'Prehear_Volume_Control' #mixer.set_crossfader_control(crossfader) #mixer.set_prehear_volume_control(prehear_control) mixer.master_strip().set_volume_control(master_volume_control)
def _setup_mixer_control(self): is_momentary = True #mixer = MixerComponent(8) mixer = MixerComponent(1) #for track in range(8): # strip = mixer.channel_strip(track) # strip.set_volume_control(SliderElement(MIDI_CC_TYPE, track, 7)) # strip.set_arm_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, track, 48)) # strip.set_solo_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, track, 49)) # strip.set_mute_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, track, 50)) # #strip.set_select_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, track, 51)) # strip.set_shift_button(self._shift_button) # strip.set_invert_mute_feedback(True) """ WAC addition....................................""" mixer.selected_strip().set_mute_button(SysexButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KMK_PAD[9])) mixer.selected_strip().set_solo_button(SysexButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KMK_PAD[13])) mixer.set_select_buttons(SysexButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KMK_PAD[15]), SysexButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KMK_PAD[14])) send_faders = [] NUM_CONTROLLABLE_SENDS = 4 for index in range(NUM_CONTROLLABLE_SENDS): send_faders.append(SliderElement(MIDI_CC_TYPE, CHANNEL, KMK_FADER[index + 2])) mixer.selected_strip().set_volume_control(SliderElement(MIDI_CC_TYPE, CHANNEL, KMK_FADER[0])) mixer.selected_strip().set_pan_control(SliderElement(MIDI_CC_TYPE, CHANNEL, KMK_FADER[1])) mixer.selected_strip().set_send_controls(tuple(send_faders)) """------------------------------------------------""" #mixer.set_crossfader_control(SliderElement(MIDI_CC_TYPE, 0, 15)) #mixer.set_prehear_volume_control(EncoderElement(MIDI_CC_TYPE, 0, 47, Live.MidiMap.MapMode.relative_two_compliment)) mixer.set_prehear_volume_control(EncoderElement(MIDI_CC_TYPE, CHANNEL, KMK_FADER[6], Live.MidiMap.MapMode.absolute)) #mixer.master_strip().set_volume_control(SliderElement(MIDI_CC_TYPE, 0, 14)) mixer.master_strip().set_volume_control(SliderElement(MIDI_CC_TYPE, CHANNEL, KMK_FADER[7])) #mixer.master_strip().set_select_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, 80)) return mixer
def _setup_mixer_control(self): is_momentary = True num_tracks = 7 #A mixer is one-dimensional; here we define the width in tracks - seven columns, which we will map to seven "white" notes """Here we set up the global mixer""" #Note that it is possible to have more than one mixer... global mixer #We want to instantiate the global mixer as a MixerComponent object (it was a global "None" type up until now...) mixer = MixerComponent( num_tracks, 2, with_eqs=True, with_filters=True ) #(num_tracks, num_returns, with_eqs, with_filters) mixer.set_track_offset( 0) #Sets start point for mixer strip (offset from left) self.song().view.selected_track = mixer.channel_strip( 0 )._track #set the selected strip to the first track, so that we don't, for example, try to assign a button to arm the master track, which would cause an assertion error """set up the mixer buttons""" mixer.set_select_buttons( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 56), ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 54)) #left, right track select mixer.master_strip().set_select_button( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 94)) #jump to the master track mixer.selected_strip().set_mute_button( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 42)) #sets the mute ("activate") button mixer.selected_strip().set_solo_button( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 44)) #sets the solo button mixer.selected_strip().set_arm_button( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 46)) #sets the record arm button """set up the mixer sliders""" mixer.selected_strip().set_volume_control( SliderElement(MIDI_CC_TYPE, CHANNEL, 14)) #sets the continuous controller for volume """note that we have split the mixer functions across two scripts, in order to have two session highlight boxes (one red, one yellow), so there are a few things which we are not doing here..."""
def _create_mixer_control(self): is_momentary = True num_tracks = 7 """Here we set up the global mixer""" global mixer mixer = MixerComponent(name='Mixer', num_tracks=num_tracks, is_enabled=False, num_returns=2) mixer.set_enabled(True) mixer.set_track_offset(0) self.song().view.selected_track = mixer.channel_strip(0)._track mixer.channel_strip(0) """set up the mixer buttons""" mixer.set_select_buttons( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 56), ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 54)) mixer.master_strip().set_select_button( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 94)) mixer.selected_strip().set_mute_button( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 42)) mixer.selected_strip().set_solo_button( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 44)) mixer.selected_strip().set_arm_button( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 46)) """set up the mixer sliders""" mixer.selected_strip().set_volume_control( SliderElement(MIDI_CC_TYPE, CHANNEL, 14)) """note that we have split the mixer functions across two scripts, in order to have two session highlight boxes (one red, one yellow), so there are a few things which we are not doing here... """ self.log_message("Captain's log stardate 2")
def setup_mixer(self): global mixer mixer = MixerComponent(2, 2) mixer.set_track_offset(0) self.song().view.selected_track = mixer.channel_strip( 0 )._track # set the selected strip to the first track, so that we don't, for example, try to assign a button to arm the master track, which would cause an assertion error send_a = EncoderElement(MIDI_CC_TYPE, CHANNEL, SEND_A, mode_absolute) send_b = EncoderElement(MIDI_CC_TYPE, CHANNEL, SEND_B, mode_absolute) send_c = EncoderElement(MIDI_CC_TYPE, CHANNEL, SEND_C, mode_absolute) send_d = EncoderElement(MIDI_CC_TYPE, CHANNEL, SEND_D, mode_absolute) pan = EncoderElement(MIDI_CC_TYPE, CHANNEL, PAN, mode_absolute) mixer.selected_strip().set_send_controls( [send_a, send_b, send_c, send_d]) mixer.selected_strip().set_pan_control(pan)
def _setup_mixer_control(self): is_momentary = True mixer = MixerComponent(1) mixer.selected_strip().set_mute_button(SysexButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KMK_PAD[9])) mixer.selected_strip().set_solo_button(SysexButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KMK_PAD[13])) mixer.set_select_buttons(SysexButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KMK_PAD[15]), SysexButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KMK_PAD[14])) send_faders = [] NUM_CONTROLLABLE_SENDS = 4 for index in range(NUM_CONTROLLABLE_SENDS): send_faders.append(SliderElement(MIDI_CC_TYPE, CHANNEL, KMK_FADER[index + 2])) mixer.selected_strip().set_volume_control(SliderElement(MIDI_CC_TYPE, CHANNEL, KMK_FADER[0])) mixer.selected_strip().set_pan_control(SliderElement(MIDI_CC_TYPE, CHANNEL, KMK_FADER[1])) mixer.selected_strip().set_send_controls(tuple(send_faders)) mixer.set_prehear_volume_control(EncoderElement(MIDI_CC_TYPE, CHANNEL, KMK_FADER[6], Live.MidiMap.MapMode.absolute)) mixer.master_strip().set_volume_control(SliderElement(MIDI_CC_TYPE, CHANNEL, KMK_FADER[7])) return mixer
class OP1(ControlSurface): def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self.c_instance = c_instance self.retries_count = 0 self.device_connected = False self.clip_color_callbacks = {} self.slot_callbacks = {} self.text_start_sequence = (0xf0, 0x0, 0x20, 0x76, 0x00, 0x03) self.text_end_sequence = (0xf7, ) self.enable_sequence = (0xf0, 0x00, 0x20, 0x76, 0x00, 0x01, 0x02, 0xf7) self.disable_sequence = (0xf0, 0x00, 0x20, 0x76, 0x00, 0x01, 0x00, 0xf7) self.id_sequence = (0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7) self.text_color_start_sequence = (0xf0, 0x0, 0x20, 0x76, 0x00, 0x04) self.log('INITIALIZING') self.app = Live.Application.get_application() #maj = self.app.get_major_version() #min = self.app.get_minor_version() #bug = self.app.get_bugfix_version() #self.show_message(str(1) + "." + str(0) + "." + str(9)) self.show_message("Version " + VERSION) # reseting text self.write_text(' ') # reset display clips self.reset_display_clips() # getting browser visible state self.session_browser_visible = self.app.view.is_view_visible("Browser") # getting browser visible state self.arrange_browser_visible = self.app.view.is_view_visible("Browser") # getting session view visible state self.session_visible = self.app.view.is_view_visible("Session") # getting arrange view visible state self.arrange_visible = self.app.view.is_view_visible("Arranger") # getting detail view visible state self.detail_visible = self.app.view.is_view_visible("Detail") # getting back to arranger state self.back_to_arranger_state = self.song().back_to_arranger # initializing channel strip to null self._channel_strip = None # initializing transport component self._transport = TransportComponent() # initializing mixer component self._mixer = MixerComponent(NUM_TRACKS, 2) # initializing session component self._session = SessionComponent(NUM_TRACKS, NUM_ROWS) self._session.add_offset_listener(self.session_offset_changed) # configuring operation mode selector buttons self._operation_mode_buttons = ButtonElement( True, MIDI_CC_TYPE, CHANNEL, OP1_MODE_1_BUTTON), ButtonElement( True, MIDI_CC_TYPE, CHANNEL, OP1_MODE_2_BUTTON), ButtonElement( True, MIDI_CC_TYPE, CHANNEL, OP1_MODE_3_BUTTON), ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_MODE_4_BUTTON), # initializing operation mode selector self._operation_mode_selector = OP1ModeSelectorComponent( self, self._transport, self._mixer, self._session) # setting operation mode selector buttons self._operation_mode_selector.set_mode_buttons( self._operation_mode_buttons) # adding value listener for operation mode index self._operation_mode_selector.add_mode_index_listener( self.mode_index_changed) # setting global transport assignments self._transport.set_record_button( ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_REC_BUTTON)) self._transport.set_play_button( ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_PLAY_BUTTON)) self._transport.set_stop_button( ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_STOP_BUTTON)) self._transport.set_metronome_button( ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_METRONOME_BUTTON)) self._transport.set_tap_tempo_button( ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_HELP_BUTTON)) self._transport.set_punch_buttons( ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS1_BUTTON), ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS2_BUTTON)) self._transport.set_loop_button( ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS3_BUTTON)) self._transport.set_overdub_button( ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS4_BUTTON)) # setting global session assignments self._session.set_scene_bank_buttons( ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_COM), ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_MICRO)) # setting misc listeners self.browser_toggle_button = ButtonElement(False, MIDI_CC_TYPE, CHANNEL, 15) self.browser_toggle_button.add_value_listener( self.browser_toggle_button_callback) self.mainview_toggle_button = ButtonElement(False, MIDI_CC_TYPE, CHANNEL, 16) self.mainview_toggle_button.add_value_listener( self.mainview_toggle_button_callback) self.detailview_toggle_button = ButtonElement(False, MIDI_CC_TYPE, CHANNEL, 17) self.detailview_toggle_button.add_value_listener( self.detailview_toggle_button_callback) self.clear_track_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, 25) self.clear_track_button.add_value_listener( self.clear_track_button_callback) self.back_to_arranger_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, 26) self.back_to_arranger_button.add_value_listener( self.back_to_arranger_button_callback) # adding value listener for selected track change self.song().view.add_selected_track_listener( self.selected_track_changed) # adding value listener for selected scene change self.song().view.add_selected_scene_listener( self.selected_scene_changed) # setting assignments for currently selected track self.selected_track_changed() # setting assignments for currently selected scene self.selected_scene_changed() def handle_sysex(self, midi_bytes): if ((midi_bytes[6] == 32) and (midi_bytes[7] == 118)): self.device_connected = True self.log("OP-1 CONNECTED. SENDING ABLETON LIVE MODE INIT SEQUENCE") self._send_midi(self.enable_sequence) def add_clip_slot_listeners(self): #self.log('ADDING CLIP SLOT LISTENERS') # creating an empty list for all clip slots clip_slots = [] # getting a reference to all tracks tracks = self.song().tracks # appending all tracks clip slots to clip_slots for track in tracks: clip_slots.append(track.clip_slots) # iterating over all clip slots for t in range(len(clip_slots)): for c in range(len(clip_slots[t])): clip_slot = clip_slots[t][c] # adding has clip listener to clip slot self.add_slot_listener(clip_slot) # if clip slot has clip if clip_slot.has_clip: # adding clip listeners self.add_clip_listener(clip_slot.clip) def rem_clip_slot_listeners(self): #self.log('REMOVING CLIP SLOT LISTENERS') # iterate over all clip color change callbacks for c in self.clip_color_callbacks: # if clip still exists if c != None: # and it has a has clip listener if c.color_has_listener(self.clip_color_callbacks[c]) == 1: # remove it c.remove_color_listener(self.clip_color_callbacks[c]) # iterate over all clip slot callbacks for cs in self.slot_callbacks: # if clip slot still exists if cs != None: # and it has a has clip listener if cs.has_clip_has_listener(self.slot_callbacks[cs]) == 1: # remove it cs.remove_has_clip_listener(self.slot_callbacks[cs]) def add_slot_listener(self, cs): # setting has clip listener callback = lambda: self.has_clip_listener(cs) # if we don't have a clip slot has clip listener for this clip slot yet if not (self.slot_callbacks.has_key(cs)): # adding has clip callback to clip slot cs.add_has_clip_listener(callback) # saving callback for future release self.slot_callbacks[cs] = callback def add_clip_listener(self, clip): # setting callback for clip color change color_callback = lambda: self.update_display_clips() # if we don't have a clip color change callback for this clip yet if not (self.clip_color_callbacks.has_key(clip)): # adding clip color change callback clip.add_color_listener(color_callback) # saving callback for future release self.clip_color_callbacks[clip] = color_callback def has_clip_listener(self, cs): # clip slot has clip listener callback if cs.has_clip: # add clip listener self.add_clip_listener(cs.clip) else: # update display if clip slot was removed self.update_display_clips() def session_offset_changed(self): # if session component offset changes, update display self.update_display_clips() def selected_scene_changed(self): # if on clip mode update display if (self._operation_mode_selector.mode_index == OP1_MODE_CLIP): self.update_display_clip_mode() def mode_index_changed(self): # update display to current mode info if (self._operation_mode_selector.mode_index == OP1_MODE_PERFORM): self.update_display_perform_mode() elif (self._operation_mode_selector.mode_index == OP1_MODE_CLIP): self.update_display_clip_mode() elif (self._operation_mode_selector.mode_index == OP1_MODE_TRANSPORT): self.update_display_transport_mode() elif (self._operation_mode_selector.mode_index == OP1_MODE_MIXER): self.update_display_mixer_mode() def clear_track_button_callback(self, value): # if clear track button was called, reset track if (value == 127): for i in range(len(self.song().tracks)): self.song().tracks[i].arm = 0 self.song().tracks[i].solo = 0 self.song().tracks[i].mute = 0 for i in range(len(self.song().return_tracks)): self.song().tracks[i].solo = 0 self.song().tracks[i].mute = 0 def clear_return_track_assignment(self, strip): # clear return track assingments strip.set_volume_control(None) strip.set_pan_control(None) strip.set_mute_button(None) strip.set_solo_button(None) def clear_track_assignment(self, strip): # clear track assignments strip.set_volume_control(None) strip.set_pan_control(None) strip.set_mute_button(None) strip.set_solo_button(None) strip.set_arm_button(None) def clear_tracks_assigments(self): # for all normal tracks, clear assignments for i in range(NUM_TRACKS): strip = self._mixer.channel_strip(i) if (strip != None): self.clear_track_assignment(strip) # for all return tracks, clear assignments for i in range(2): return_strip = self._mixer.return_strip(i) if (return_strip != None): self.clear_return_track_assignment(return_strip) def selected_track_changed(self): # if on mixer mode update display if (self._operation_mode_selector.mode_index == OP1_MODE_MIXER): self.update_display_mixer_mode() # clear track assignments self.clear_tracks_assigments() # getting selected strip self._channel_strip = self._mixer.selected_strip() # perform track assignments self._channel_strip.set_volume_control( EncoderElement(MIDI_CC_TYPE, CHANNEL, OP1_ENCODER_1, Live.MidiMap.MapMode.relative_two_compliment)) self._channel_strip.set_pan_control( EncoderElement(MIDI_CC_TYPE, CHANNEL, OP1_ENCODER_2, Live.MidiMap.MapMode.relative_two_compliment)) # setting a tuple of encoders to control sends send_controls = EncoderElement( MIDI_CC_TYPE, CHANNEL, OP1_ENCODER_3, Live.MidiMap.MapMode.relative_two_compliment), EncoderElement( MIDI_CC_TYPE, CHANNEL, OP1_ENCODER_4, Live.MidiMap.MapMode.relative_two_compliment), # setting send encoders self._channel_strip.set_send_controls(tuple(send_controls)) # setting solo button self._channel_strip.set_solo_button( ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS6_BUTTON)) # if track can be armed, set arm button if (self._channel_strip._track.can_be_armed): self._channel_strip.set_arm_button( ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS7_BUTTON)) # if track is no master, set mute button if (self._channel_strip._track != self.song().master_track): self._channel_strip.set_mute_button( ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS5_BUTTON)) def browser_toggle_button_callback(self, value): if (value == 127): if (self.session_visible): if (self.session_browser_visible == True): self.session_browser_visible = False self.app.view.hide_view("Browser") else: self.session_browser_visible = True self.app.view.show_view("Browser") if (self.arrange_visible): if (self.arrange_browser_visible == True): self.arrange_browser_visible = False self.app.view.hide_view("Browser") else: self.arrange_browser_visible = True self.app.view.show_view("Browser") def back_to_arranger_button_callback(self, value): if (value == 127): self.song().back_to_arranger = False def mainview_toggle_button_callback(self, value): if (value == 127): if (self.session_visible == True): self.session_visible = False self.arrange_visible = True self.app.view.show_view("Arranger") self.arrange_browser_visible = self.app.view.is_view_visible( "Browser") else: self.session_visible = True self.arrange_visible = False self.app.view.show_view("Session") self.session_browser_visible = self.app.view.is_view_visible( "Browser") def detailview_toggle_button_callback(self, value): if (value == 127): if (self.detail_visible == True): self.detail_visible = False self.app.view.hide_view("Detail") else: self.detail_visible = True self.app.view.show_view("Detail") def write_text(self, msg): text_list = [] sequence = () text_list.append(len(msg.strip())) for i in msg.strip(): text_list.append(ord(i)) sequence = self.text_start_sequence + tuple( text_list) + self.text_end_sequence self._send_midi(sequence) def suggest_input_port(self): return "OP-1 Midi Device" def suggest_output_port(self): return "OP-1 Midi Device" def update_display_perform_mode(self): self.write_text("perform\rmode") def reset_display_clips(self): count = 0 colors = [] length = [] sequence = () for i in range(NUM_TRACKS): count += 1 colors.append(0x00) colors.append(0x00) colors.append(0x00) length.append(count) sequence = self.text_color_start_sequence + tuple(length) + tuple( colors) + self.text_end_sequence self._send_midi(sequence) def update_display_clips(self): #self.log("UPDATING DISPLAY CLIPS") count = 0 colors = [] length = [] sequence = () tracks_len = len(self.song().tracks) - self._session._track_offset if (tracks_len > NUM_TRACKS): tracks_len = NUM_TRACKS for i in range(tracks_len): count += 1 clip_slot = self._session.scene(0).clip_slot(i) if (clip_slot != None): if (clip_slot.has_clip() != False): clip_color = clip_slot._clip_slot.clip.color colors.append(((clip_color >> 16) & 0x000000ff) >> 1) colors.append(((clip_color >> 8) & 0x000000ff) >> 1) colors.append((clip_color & 0x000000ff) >> 1) else: colors.append(0x00) colors.append(0x00) colors.append(0x00) else: colors.append(0x00) colors.append(0x00) colors.append(0x00) length.append(count) sequence = self.text_color_start_sequence + tuple(length) + tuple( colors) + self.text_end_sequence self._send_midi(sequence) def update_display_clip_mode(self): self.write_text( "sel. scene\r" + str(self.song().view.selected_scene.name.lower().strip())) def update_display_transport_mode(self): song_time = str(self.song().get_current_beats_song_time()) self.write_text("song pos.\r" + song_time[:len(song_time) - 4]) def update_display_mixer_mode(self): self.write_text("sel. track\r" + str(self.song().view.selected_track.name.lower())) def update_display(self): if not (self.device_connected): if (self.retries_count < 5): self.log("TRYING OP-1 CONNECTION") self.retries_count += 1 self._send_midi(self.id_sequence) time.sleep(1) # if in transport mode, update display with song position if (self._operation_mode_selector.mode_index == OP1_MODE_TRANSPORT): self.update_display_transport_mode() # checking if app current view is session if (self.app.view.is_view_visible("Session")): # checking if session browser state is diferent from the internal if (self.session_browser_visible != self.app.view.is_view_visible("Browser")): self.session_browser_visible = self.app.view.is_view_visible( "Browser") # checking if app current view is arrange if (self.app.view.is_view_visible("Arranger")): # checking if arrange browser state is diferent from the internal if (self.arrange_browser_visible != self.app.view.is_view_visible("Browser")): self.arrange_browser_visible = self.app.view.is_view_visible( "Browser") # checking if app current view is detail if (self.app.view.is_view_visible("Detail")): # checking if detail state is diferent from the internal if (self.detail_visible != self.app.view.is_view_visible("Detail")): self.detail_visible = self.app.view.is_view_visible("Detail") def refresh_state(self): self.log("REFRESH STATE") self.retries_count = 0 self.device_connected = False def build_midi_map(self, midi_map_handle): #self.log("BUILD MIDI MAP") assert (self._suppress_requests_counter == 0) self._in_build_midi_map = True self._midi_map_handle = midi_map_handle self._forwarding_registry = {} for control in self.controls: if isinstance(control, InputControlElement): control.install_connections() self._midi_map_handle = None self._in_build_midi_map = False if (self._pad_translations != None): self._c_instance.set_pad_translation(self._pad_translations) # remove clip listeners self.rem_clip_slot_listeners() # add clip listeners self.add_clip_slot_listeners() # update display self.update_display_clips() def log(self, msg): self.c_instance.log_message("[TE OP-1] " + msg) def disconnect(self): # removing clip slots listeners self.rem_clip_slot_listeners() # removing value listener for track changed self.song().view.remove_selected_track_listener( self.selected_track_changed) # removing value listener for scene changed self.song().view.remove_selected_scene_listener( self.selected_scene_changed) # removing value listener for operation mode index self._operation_mode_selector.remove_mode_index_listener( self.mode_index_changed) # removing global transport assignments self._transport.set_punch_buttons(None, None) self._transport.set_loop_button(None) self._transport.set_overdub_button(None) self._transport.set_record_button(None) self._transport.set_play_button(None) self._transport.set_stop_button(None) self._transport.set_metronome_button(None) self._transport.set_tap_tempo_button(None) # removing global session assignments self._session.set_scene_bank_buttons(None, None) # removing misc listeners self.browser_toggle_button.remove_value_listener( self.browser_toggle_button_callback) self.mainview_toggle_button.remove_value_listener( self.mainview_toggle_button_callback) self.detailview_toggle_button.remove_value_listener( self.detailview_toggle_button_callback) self.clear_track_button.remove_value_listener( self.clear_track_button_callback) self.back_to_arranger_button.remove_value_listener( self.back_to_arranger_button_callback) # sending special ableton mode disable sequence self._send_midi(self.disable_sequence) # disconnecting control surface ControlSurface.disconnect(self) self.log("DISCONNECTED")
def _setup_mixer_control(self): is_momentary = True num_tracks = 4 #A mixer is one-dimensional; here we define the width in tracks - seven columns, global mixer #We want to instantiate the global mixer as a MixerComponent object (it was a global "None" type up until now...) mixer = MixerComponent(num_tracks, 0, with_eqs=False, with_filters=False) #(num_tracks, num_returns, with_eqs, with_filters) mixer.set_track_offset(0) #Sets start point for mixer strip (offset from left) self.song().view.selected_track = mixer.channel_strip(0)._track #set the selected strip to the first track, so that we don't, for example, try to assign a button to arm the master track, which would cause an assertion error #"""set up the mixer buttons""" #mixer.set_select_buttons(ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 56),ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 54)) #left, right track select #mixer.master_strip().set_select_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 94)) #jump to the master track #mixer.selected_strip().set_mute_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 42)) #sets the mute ("activate") button #mixer.selected_strip().set_solo_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 44)) #sets the solo button #mixer.selected_strip().set_arm_button(ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, 46)) #sets the record arm button #"""set up the mixer sliders""" mixer.selected_strip().set_volume_control(SliderElement(MIDI_CC_TYPE, CHANNEL, 48)) #sets the continuous controller for volume #"""note that we have split the mixer functions across two scripts, in order to have two session highlight boxes (one red, one yellow), so there are a few things which we are not doing here...""" # this tells Live the filename in which to look for my code import wfK1 # this is the standardized function with which Live loads # any script. c_instance is the Control Surface slot in Live's # prefs, as far as I can tell def create_instance(c_instance): # this is what it should load # (the thing inside my file that's called 'K1') return wfK1.K1(c_instance) # this lets me use Live's own generic Control Surface code # to handle a bunch of background tasks in interfacing # with the app, so I will only have to customize the behavior # I want and not re-do all the plumbing from _Framework.ControlSurface import ControlSurface # this is the thing that'll be loaded in the __init__.py file. # it's going to be based on the generic Control Surface # (the slot that was called c_instance in __init__.py) class K1(ControlSurface): # this defines the function to construct what code # ('self', i.e. this very thing I wrote below) the slot # ('instance') will be assigned def __init__(self, instance): # this tells the compiler (which turns the script into # instructions Live can actually execute) to start with # the generic Control Surface initial setup. Super # means that we're executing a command in there # instead of code in here. super(K1, self).__init__(instance, False) # this is, as far as I can tell, a protection against crashes, # everything I do will be encapsulated in this guard. I # found a bunch of sources and scripts that # recommended to import the 'with' command, which # wasn't available in older versions of the script language, # but that turned out to not be necessary in Live 10 with self.component_guard(): # now we can do things! The first line shows a message # in Live's own status bar, the second adds a line to Log.txt self.show_message("Hello World") self.log_message("wayfinder K1 remote script loaded") # code snippet from The Rabbits at Ableton forum for RACKS def get_device_list(self, container): # add each device in order. if device is a rack, process each chain recursively # don't add racks that are not showing devices. lst = [] for dev in container: lst.append(dev) if dev.can_have_chains: # is a rack and it's open if dev.view.is_showing_chain_devices: for ch in dev.chains: lst += self.get_device_list(ch.devices) return lst # Called this way. device_list = self.get_device_list(current_track.devices) # for foldable groups if track.is_foldable: track.fold_state = not track.fold_state else: track.view.is_collapsed = not track.view.is_collapsed # fold_state refers to groups .is_foldable is true for Groups. # .is_collapsed refers to tracks that are collapsed in Arrangement view. # I combined the two because it means one less pad used and the two are never both true.
class Codex(Codec): def __init__(self, c_instance, *a, **k): self._shifted = False super(Codex, self).__init__(c_instance, *a, **k) with self.component_guard(): self._setup_alt_mixer() self._setup_alt_device_selector() self._setup_alt_send_reset() self._setup_alt_device_control() self.log_message( '<<<<<<<<<<<<<<<<<<<<<<<<< Codex subclass log opened >>>>>>>>>>>>>>>>>>>>>>>>>' ) self.schedule_message(1, self._mode_update) def _setup_alt_device_selector(self): self._alt_device_selector = NewDeviceSelectorComponent(self) self._alt_device_selector.name = 'Alt_Device_Selector' def _setup_alt_send_reset(self): self._alt_send_reset = ResetSendsComponent(self) self._alt_send_reset.name = 'Alt_Reset_Sends' """these two secondary DeviceComponents are only set up if the MONOHM_LINK flag in Map is turned on""" def _setup_alt_device_control(self): self._device1 = NewDeviceComponent() self._device1.name = 'Device_Component1' self._device2 = NewDeviceComponent() self._device2.name = 'Device_Component2' def _setup_alt_mixer(self): is_momentary = True self._num_tracks = (8) #A mixer is one-dimensional self._mixer2 = MixerComponent(8, 4, False, False) self._mixer2.name = 'Mixer' #self._mixer2.set_track_offset(4) #Sets start point for mixer strip (offset from left) for index in range(8): self._mixer2.channel_strip( index).name = 'Mixer_ChannelStrip_' + str(index) self._mixer2.channel_strip(index)._invert_mute_feedback = True """Mode Functions""" def _enable_alt(self): if self._main_mode._mode_index == 0: for encoder, _ in self._encoder_matrix.submatrix[:, 0:2].iterbuttons( ): encoder.set_enabled(False) encoder.reset() self._alt_enabled = True self._mode_update() super(Codex, self)._enable_alt() def _disable_alt(self): if self._main_mode._mode_index == 0: self._button_matrix.submatrix[:, 0:2].reset() for encoder, _ in self._encoder_matrix.submatrix[:, 0:2].iterbuttons( ): encoder.set_enabled(True) super(Codex, self)._disable_alt() def _deassign_all(self): self._alt_send_reset.set_buttons(tuple([None for index in range(4)])) self._alt_send_reset.set_enabled(False) self._alt_device_selector.set_buttons(None) self._alt_device_selector.set_enabled(False) self._mixer2.selected_strip().set_send_controls(None) for index in range(8): self._mixer2.channel_strip(index).set_volume_control(None) self._mixer2.channel_strip(index).set_select_button(None) self._mixer2.channel_strip(index).set_mute_button(None) self._dial[index][2].release_parameter() for index in range(3): self._mixer2.return_strip(index).set_volume_control(None) self._device1.set_enabled(False) self._device1._parameter_controls = None self._device2.set_enabled(False) self._device2._parameter_controls = None self._mixer2.return_strip(0).set_send_controls(None) self._mixer2.return_strip(1).set_send_controls(None) self._mixer2.return_strip(2).set_send_controls(None) super(Codex, self)._deassign_all() def _assign_volume(self): if self._alt_enabled: inputs = self.find_inputs() if not inputs is None: for index in range(4): self._dial[index][2].connect_to(inputs.parameters[index + 1]) self._dial[6][2].connect_to(inputs.parameters[5]) xfade = self.find_perc_crossfader() if not xfade is None: self._dial[7][3].connect_to(xfade) self._alt_device_selector.set_matrix( self._button_matrix.submatrix[:, 0:2]) self._alt_device_selector.set_enabled(True) self._mixer2.return_strip(0).set_send_controls( [None, self._dial[4][2]]) self._mixer2.return_strip(1).set_send_controls( [self._dial[5][2], None]) else: self._alt_send_reset.set_buttons( tuple([ self._button[4][2], self._button[5][2], self._button[6][2], self._button[7][2] ])) self._alt_send_reset.set_enabled(True) self._mixer2.selected_strip().set_send_controls([ self._dial[0][2], self._dial[1][2], self._dial[2][2], self._dial[3][2] ]) for index in range(3): self._mixer2.return_strip(index).set_volume_control( self._dial[index + 4][2]) self._mixer2.set_crossfader_control(self._dial[7][2]) self._device1.set_parameter_controls( tuple([ self._dial[index % 4][int(index / 4)] for index in range(8) ])) self._device2.set_parameter_controls( tuple([ self._dial[(index % 4) + 4][int(index / 4)] for index in range(8) ])) self._device1.set_enabled(True) self._device2.set_enabled(True) self._find_devices() self._device1.update() self._device2.update() for index in range(8): self._mixer2.channel_strip(index).set_select_button( self._column_button[index]) for index in range(8): self._mixer2.channel_strip(index).set_volume_control( self._dial[index][3]) self._mixer2.channel_strip(index).set_mute_button( self._button[index][3]) self._mixer2.update() self.request_rebuild_midi_map() #self._mixer2.set_track_offset(TROLL_OFFSET) def find_inputs(self): found_device = None tracks = self.song().tracks for track in tracks: if track.name == 'Inputs': for device in track.devices: if bool(device.can_have_chains ) and device.name == 'Inputs': found_device = device return found_device def find_perc_crossfader(self): found_parameter = None tracks = self.song().tracks for track in tracks: if track.name == 'Perc': for device in track.devices: if bool(device.can_have_chains) and device.name == 'Perc': for parameter in device.parameters: if parameter.name == 'XFade': found_parameter = parameter return found_parameter """this method is used to find the devices the alt controls will latch to""" def _find_devices(self): if self._device1: if len(self.song().return_tracks) > 0: if len(self.song().return_tracks[0].devices) > 0: if self._device1._locked_to_device: self._device1.set_lock_to_device( False, self._device1._device) self._device1.set_lock_to_device( True, self.song().return_tracks[0].devices[0]) if self._device2: if len(self.song().return_tracks) > 1: if len(self.song().return_tracks[1].devices) > 0: if self._device2._locked_to_device: self._device2.set_lock_to_device( False, self._device2._device) self._device2.set_lock_to_device( True, self.song().return_tracks[1].devices[0])
class xtouch(ControlSurface): def __init__(self, c_instance): super(xtouch, self).__init__(c_instance) with self.component_guard(): self.__c_instance = c_instance self._highlighting_session_component = None self._device_selection_follows_track_selection = False self._device_component = None self._pad_control_device = None self._track_control_device = None self._should_update_tracks = 0 self._map_modes = Live.MidiMap.MapMode self.current_track_offset = 0 self.current_scene_offset = 0 self.mixer = MixerComponent(128, 24) num_of_tracks = len(self.song().tracks) for index in range(num_of_tracks): self.song().tracks[index].view.add_selected_device_listener( self._reload_track_control) self._reload_track_control() def best_of_parameter_bank(self): return [] def _do_deactivate_pad_control(self): with self.component_guard(): if (self._pad_control_device is not None): device_controls = (None, None, None, None, None, None, None, None) self._pad_control_device.set_parameter_controls( tuple(device_controls)) self._pad_control_device.set_lock_to_device( "", self.actual_device) self._pad_control_device.set_device(None) self._pad_control_device = None def activate_pad_control(self, track, pad): self._do_deactivate_pad_control() with self.component_guard(): self._deactivate_track_control() track_id = track + 8 tracks = self.mixer.song().tracks if track_id < len(tracks): track = tracks[track_id] if (len(track.devices) > 0): drumpad = track.devices[0].drum_pads[36 + pad] if len(drumpad.chains) > 0: if len(drumpad.chains[0].devices) > 0: self.actual_device = drumpad.chains[0].devices[0] def log(msg): self.log_message(msg) self._pad_control_device = CustomDeviceComponent( log) device_controls = ( EncoderElement(MIDI_CC_TYPE, 10, 1, self._map_modes.absolute), EncoderElement(MIDI_CC_TYPE, 10, 2, self._map_modes.absolute), EncoderElement(MIDI_CC_TYPE, 10, 3, self._map_modes.absolute), EncoderElement(MIDI_CC_TYPE, 10, 4, self._map_modes.absolute), EncoderElement(MIDI_CC_TYPE, 10, 5, self._map_modes.absolute), EncoderElement(MIDI_CC_TYPE, 10, 6, self._map_modes.absolute), EncoderElement(MIDI_CC_TYPE, 10, 7, self._map_modes.absolute), EncoderElement(MIDI_CC_TYPE, 10, 8, self._map_modes.absolute), ) self.actual_device.playback_mode = 0 self.actual_device.parameters[31].value = 1.0 self._pad_control_device.set_device( self.actual_device) self._pad_control_device.set_parameter_controls( tuple(device_controls)) self._pad_control_device.set_lock_to_device( "lock", self.actual_device) self.log_message("AA " + str(self._pad_control_device. _current_bank_details())) def deactivate_pad_control(self): with self.component_guard(): self._do_deactivate_pad_control() self._reload_track_control() def _activate_track_control(self): if self._pad_control_device is not None: return if (len(self.mixer.selected_strip()._track.devices) > 0): devices = self.mixer.selected_strip()._track.devices self.actual_device = devices[0] self._track_control_device = DeviceComponent() device_controls = ( EncoderElement(MIDI_CC_TYPE, 10, 1, self._map_modes.absolute), EncoderElement(MIDI_CC_TYPE, 10, 2, self._map_modes.absolute), EncoderElement(MIDI_CC_TYPE, 10, 3, self._map_modes.absolute), EncoderElement(MIDI_CC_TYPE, 10, 4, self._map_modes.absolute), EncoderElement(MIDI_CC_TYPE, 10, 5, self._map_modes.absolute), EncoderElement(MIDI_CC_TYPE, 10, 6, self._map_modes.absolute), EncoderElement(MIDI_CC_TYPE, 10, 7, self._map_modes.absolute), EncoderElement(MIDI_CC_TYPE, 10, 8, self._map_modes.absolute), ) self._track_control_device.set_device(self.actual_device) self._track_control_device.set_parameter_controls( tuple(device_controls)) self._track_control_device.set_lock_to_device( "lock", self.actual_device) def _deactivate_track_control(self): if (self._track_control_device is not None): device_controls = (None, None, None, None, None, None, None, None) self._track_control_device.set_parameter_controls( tuple(device_controls)) self._track_control_device.set_lock_to_device( "", self.actual_device) self._track_control_device.set_device(None) self._track_control_device = None def _reload_track_control(self, value=None): self._deactivate_track_control() self._activate_track_control() def _highlight_tracks(self): j = [0] def update_next_track(selected): if selected: self.__c_instance.send_midi((10 | 0x90, 8 + j[0], 127)) else: self.__c_instance.send_midi((10 | 0x80, 8 + j[0], 0)) j[0] += 1 selected_track = self.song().view.selected_track for i in [0, 1, 2, 3, 4, 5, 6, 7, 17, 18]: track_selected = False if i < len(self.song().tracks): if self.song().tracks[i] == selected_track: track_selected = True update_next_track(track_selected) update_next_track(self.song().return_tracks[0] == selected_track) update_next_track(self.song().return_tracks[1] == selected_track) update_next_track(self.song().master_track == selected_track) def _on_selected_track_changed(self): ControlSurface._on_selected_track_changed(self) self._display_reset_delay = 0 self._reload_track_control() self._should_update_tracks = 3 def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) for i in range(0, 16): Live.MidiMap.forward_midi_note(self.__c_instance.handle(), midi_map_handle, 10, 8 + i) def receive_midi(self, midi_bytes): ControlSurface.receive_midi(self, midi_bytes) if (midi_bytes[0] == 10 | 0x90) or (midi_bytes[0] == 10 | 0x80): if midi_bytes[1] >= 8 and midi_bytes[1] <= 24: track_id = midi_bytes[1] - 8 if (track_id < 8): self.song().view.selected_track = self.song( ).tracks[track_id] elif (track_id < 10): self.song().view.selected_track = self.song().tracks[ 9 + track_id] elif (track_id < 12): self.song().view.selected_track = self.song( ).return_tracks[track_id - 10] else: self.song().view.selected_track = self.song().master_track self._should_update_tracks = 2 def update_display(self): ControlSurface.update_display(self) if self._should_update_tracks == 1: self._highlight_tracks() self._should_update_tracks = 0 if self._should_update_tracks > 1: self._should_update_tracks -= 1 def disconnect(self): super(xtouch, self).disconnect()
class OP1(ControlSurface): def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self.c_instance = c_instance self.retries_count = 0 self.device_connected = False self.clip_color_callbacks = {} self.slot_callbacks = {} self.text_start_sequence = (0xf0, 0x0, 0x20, 0x76, 0x00, 0x03) self.text_end_sequence = (0xf7,) self.enable_sequence = (0xf0, 0x00, 0x20, 0x76, 0x00, 0x01, 0x02, 0xf7) self.disable_sequence = (0xf0, 0x00, 0x20, 0x76, 0x00, 0x01, 0x00, 0xf7) self.id_sequence = (0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7) self.text_color_start_sequence = (0xf0, 0x0, 0x20, 0x76, 0x00, 0x04) self.log('INITIALIZING') self.app = Live.Application.get_application() #maj = self.app.get_major_version() #min = self.app.get_minor_version() #bug = self.app.get_bugfix_version() #self.show_message(str(1) + "." + str(0) + "." + str(9)) self.show_message("Version " + VERSION) # reseting text self.write_text(' ') # reset display clips self.reset_display_clips() # getting browser visible state self.session_browser_visible = self.app.view.is_view_visible("Browser") # getting browser visible state self.arrange_browser_visible = self.app.view.is_view_visible("Browser") # getting session view visible state self.session_visible = self.app.view.is_view_visible("Session") # getting arrange view visible state self.arrange_visible = self.app.view.is_view_visible("Arranger") # getting detail view visible state self.detail_visible = self.app.view.is_view_visible("Detail") # getting back to arranger state self.back_to_arranger_state = self.song().back_to_arranger # initializing channel strip to null self._channel_strip = None # initializing transport component self._transport = TransportComponent() # initializing mixer component self._mixer = MixerComponent(NUM_TRACKS,2) # initializing session component self._session = SessionComponent(NUM_TRACKS,NUM_ROWS) self._session.add_offset_listener(self.session_offset_changed) # configuring operation mode selector buttons self._operation_mode_buttons = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_MODE_1_BUTTON), ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_MODE_2_BUTTON), ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_MODE_3_BUTTON), ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_MODE_4_BUTTON), # initializing operation mode selector self._operation_mode_selector = OP1ModeSelectorComponent(self, self._transport, self._mixer, self._session) # setting operation mode selector buttons self._operation_mode_selector.set_mode_buttons(self._operation_mode_buttons) # adding value listener for operation mode index self._operation_mode_selector.add_mode_index_listener(self.mode_index_changed) # setting global transport assignments self._transport.set_record_button(ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_REC_BUTTON)) self._transport.set_play_button(ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_PLAY_BUTTON)) self._transport.set_stop_button(ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_STOP_BUTTON)) self._transport.set_metronome_button(ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_METRONOME_BUTTON)) self._transport.set_tap_tempo_button(ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_HELP_BUTTON)) self._transport.set_punch_buttons(ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS1_BUTTON), ButtonElement(True,MIDI_CC_TYPE, CHANNEL, OP1_SS2_BUTTON)) self._transport.set_loop_button(ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS3_BUTTON)) self._transport.set_overdub_button(ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS4_BUTTON)) # setting global session assignments self._session.set_scene_bank_buttons(ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_COM),ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_MICRO)) # setting misc listeners self.browser_toggle_button = ButtonElement(False, MIDI_CC_TYPE, CHANNEL, 15) self.browser_toggle_button.add_value_listener(self.browser_toggle_button_callback) self.mainview_toggle_button = ButtonElement(False, MIDI_CC_TYPE, CHANNEL, 16) self.mainview_toggle_button.add_value_listener(self.mainview_toggle_button_callback) self.detailview_toggle_button = ButtonElement(False, MIDI_CC_TYPE, CHANNEL, 17) self.detailview_toggle_button.add_value_listener(self.detailview_toggle_button_callback) self.clear_track_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, 25) self.clear_track_button.add_value_listener(self.clear_track_button_callback) self.back_to_arranger_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, 26) self.back_to_arranger_button.add_value_listener(self.back_to_arranger_button_callback) # adding value listener for selected track change self.song().view.add_selected_track_listener(self.selected_track_changed) # adding value listener for selected scene change self.song().view.add_selected_scene_listener(self.selected_scene_changed) # setting assignments for currently selected track self.selected_track_changed() # setting assignments for currently selected scene self.selected_scene_changed() def handle_sysex(self, midi_bytes): if ((midi_bytes[6]==32) and (midi_bytes[7]==118)): self.device_connected = True self.log("OP-1 CONNECTED. SENDING ABLETON LIVE MODE INIT SEQUENCE") self._send_midi(self.enable_sequence) def add_clip_slot_listeners(self): #self.log('ADDING CLIP SLOT LISTENERS') # creating an empty list for all clip slots clip_slots = [] # getting a reference to all tracks tracks = self.song().tracks # appending all tracks clip slots to clip_slots for track in tracks: clip_slots.append(track.clip_slots) # iterating over all clip slots for t in range(len(clip_slots)): for c in range(len(clip_slots[t])): clip_slot = clip_slots[t][c] # adding has clip listener to clip slot self.add_slot_listener(clip_slot) # if clip slot has clip if clip_slot.has_clip: # adding clip listeners self.add_clip_listener(clip_slot.clip) def rem_clip_slot_listeners(self): #self.log('REMOVING CLIP SLOT LISTENERS') # iterate over all clip color change callbacks for c in self.clip_color_callbacks: # if clip still exists if c != None: # and it has a has clip listener if c.color_has_listener(self.clip_color_callbacks[c]) == 1: # remove it c.remove_color_listener(self.clip_color_callbacks[c]) # iterate over all clip slot callbacks for cs in self.slot_callbacks: # if clip slot still exists if cs != None: # and it has a has clip listener if cs.has_clip_has_listener(self.slot_callbacks[cs]) == 1: # remove it cs.remove_has_clip_listener(self.slot_callbacks[cs]) def add_slot_listener(self, cs): # setting has clip listener callback = lambda :self.has_clip_listener(cs) # if we don't have a clip slot has clip listener for this clip slot yet if not(self.slot_callbacks.has_key(cs)): # adding has clip callback to clip slot cs.add_has_clip_listener(callback) # saving callback for future release self.slot_callbacks[cs] = callback def add_clip_listener(self, clip): # setting callback for clip color change color_callback = lambda :self.update_display_clips() # if we don't have a clip color change callback for this clip yet if not(self.clip_color_callbacks.has_key(clip)): # adding clip color change callback clip.add_color_listener(color_callback) # saving callback for future release self.clip_color_callbacks[clip] = color_callback def has_clip_listener(self, cs): # clip slot has clip listener callback if cs.has_clip: # add clip listener self.add_clip_listener(cs.clip) else: # update display if clip slot was removed self.update_display_clips() def session_offset_changed(self): # if session component offset changes, update display self.update_display_clips() def selected_scene_changed(self): # if on clip mode update display if (self._operation_mode_selector.mode_index==OP1_MODE_CLIP): self.update_display_clip_mode() def mode_index_changed(self): # update display to current mode info if (self._operation_mode_selector.mode_index==OP1_MODE_PERFORM): self.update_display_perform_mode() elif (self._operation_mode_selector.mode_index==OP1_MODE_CLIP): self.update_display_clip_mode() elif (self._operation_mode_selector.mode_index==OP1_MODE_TRANSPORT): self.update_display_transport_mode() elif (self._operation_mode_selector.mode_index==OP1_MODE_MIXER): self.update_display_mixer_mode() def clear_track_button_callback(self, value): # if clear track button was called, reset track if (value==127): for i in range(len(self.song().tracks)): self.song().tracks[i].arm = 0 self.song().tracks[i].solo = 0 self.song().tracks[i].mute = 0 for i in range(len(self.song().return_tracks)): self.song().tracks[i].solo = 0 self.song().tracks[i].mute = 0 def clear_return_track_assignment(self, strip): # clear return track assingments strip.set_volume_control(None) strip.set_pan_control(None) strip.set_mute_button(None) strip.set_solo_button(None) def clear_track_assignment(self, strip): # clear track assignments strip.set_volume_control(None) strip.set_pan_control(None) strip.set_mute_button(None) strip.set_solo_button(None) strip.set_arm_button(None) def clear_tracks_assigments(self): # for all normal tracks, clear assignments for i in range(NUM_TRACKS): strip = self._mixer.channel_strip(i) if (strip!=None): self.clear_track_assignment(strip) # for all return tracks, clear assignments for i in range(2): return_strip = self._mixer.return_strip(i) if (return_strip!=None): self.clear_return_track_assignment(return_strip) def selected_track_changed(self): # if on mixer mode update display if (self._operation_mode_selector.mode_index==OP1_MODE_MIXER): self.update_display_mixer_mode() # clear track assignments self.clear_tracks_assigments() # getting selected strip self._channel_strip = self._mixer.selected_strip() # perform track assignments self._channel_strip.set_volume_control(EncoderElement(MIDI_CC_TYPE, CHANNEL, OP1_ENCODER_1, Live.MidiMap.MapMode.relative_two_compliment)) self._channel_strip.set_pan_control(EncoderElement(MIDI_CC_TYPE, CHANNEL, OP1_ENCODER_2, Live.MidiMap.MapMode.relative_two_compliment)) # setting a tuple of encoders to control sends send_controls = EncoderElement(MIDI_CC_TYPE, CHANNEL, OP1_ENCODER_3, Live.MidiMap.MapMode.relative_two_compliment), EncoderElement(MIDI_CC_TYPE, CHANNEL, OP1_ENCODER_4, Live.MidiMap.MapMode.relative_two_compliment), # setting send encoders self._channel_strip.set_send_controls(tuple(send_controls)) # setting solo button self._channel_strip.set_solo_button(ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS6_BUTTON)) # if track can be armed, set arm button if (self._channel_strip._track.can_be_armed): self._channel_strip.set_arm_button(ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS7_BUTTON)) # if track is no master, set mute button if (self._channel_strip._track!=self.song().master_track): self._channel_strip.set_mute_button(ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS5_BUTTON)) def browser_toggle_button_callback(self, value): if (value==127): if (self.session_visible): if (self.session_browser_visible==True): self.session_browser_visible=False self.app.view.hide_view("Browser") else: self.session_browser_visible=True self.app.view.show_view("Browser") if (self.arrange_visible): if (self.arrange_browser_visible==True): self.arrange_browser_visible=False self.app.view.hide_view("Browser") else: self.arrange_browser_visible=True self.app.view.show_view("Browser") def back_to_arranger_button_callback(self, value): if (value==127): self.song().back_to_arranger = False def mainview_toggle_button_callback(self, value): if (value==127): if (self.session_visible==True): self.session_visible=False self.arrange_visible=True self.app.view.show_view("Arranger") self.arrange_browser_visible = self.app.view.is_view_visible("Browser"); else: self.session_visible=True self.arrange_visible=False self.app.view.show_view("Session") self.session_browser_visible = self.app.view.is_view_visible("Browser"); def detailview_toggle_button_callback(self, value): if (value==127): if (self.detail_visible==True): self.detail_visible=False self.app.view.hide_view("Detail") else: self.detail_visible=True self.app.view.show_view("Detail") def write_text(self, msg): text_list = [] sequence = () text_list.append(len(msg.strip())) for i in msg.strip(): text_list.append(ord(i)) sequence = self.text_start_sequence + tuple(text_list) + self.text_end_sequence self._send_midi(sequence) def suggest_input_port(self): return "OP-1 Midi Device" def suggest_output_port(self): return "OP-1 Midi Device" def update_display_perform_mode(self): self.write_text("perform\rmode") def reset_display_clips(self): count = 0 colors = [] length = [] sequence = () for i in range (NUM_TRACKS): count+=1 colors.append(0x00) colors.append(0x00) colors.append(0x00) length.append(count) sequence = self.text_color_start_sequence + tuple(length) + tuple(colors) + self.text_end_sequence self._send_midi(sequence) def update_display_clips(self): #self.log("UPDATING DISPLAY CLIPS") count = 0 colors = [] length = [] sequence = () tracks_len = len(self.song().tracks)-self._session._track_offset if (tracks_len>NUM_TRACKS): tracks_len = NUM_TRACKS for i in range (tracks_len): count+=1 clip_slot = self._session.scene(0).clip_slot(i) if (clip_slot!=None): if (clip_slot.has_clip()!=False): clip_color = clip_slot._clip_slot.clip.color colors.append(((clip_color>>16)&0x000000ff)>>1) colors.append(((clip_color>>8)&0x000000ff)>>1) colors.append((clip_color&0x000000ff)>>1) else: colors.append(0x00) colors.append(0x00) colors.append(0x00) else: colors.append(0x00) colors.append(0x00) colors.append(0x00) length.append(count) sequence = self.text_color_start_sequence + tuple(length) + tuple(colors) + self.text_end_sequence self._send_midi(sequence) def update_display_clip_mode(self): self.write_text("sel. scene\r" + str(self.song().view.selected_scene.name.lower().strip())) def update_display_transport_mode(self): song_time = str(self.song().get_current_beats_song_time()) self.write_text("song pos.\r" + song_time[:len(song_time)-4]) def update_display_mixer_mode(self): self.write_text("sel. track\r" + str(self.song().view.selected_track.name.lower())) def update_display(self): if not(self.device_connected): if (self.retries_count<5): self.log("TRYING OP-1 CONNECTION") self.retries_count+=1 self._send_midi(self.id_sequence) time.sleep(1) # if in transport mode, update display with song position if (self._operation_mode_selector.mode_index==OP1_MODE_TRANSPORT): self.update_display_transport_mode() # checking if app current view is session if (self.app.view.is_view_visible("Session")): # checking if session browser state is diferent from the internal if (self.session_browser_visible != self.app.view.is_view_visible("Browser")): self.session_browser_visible = self.app.view.is_view_visible("Browser") # checking if app current view is arrange if (self.app.view.is_view_visible("Arranger")): # checking if arrange browser state is diferent from the internal if (self.arrange_browser_visible != self.app.view.is_view_visible("Browser")): self.arrange_browser_visible = self.app.view.is_view_visible("Browser") # checking if app current view is detail if (self.app.view.is_view_visible("Detail")): # checking if detail state is diferent from the internal if (self.detail_visible != self.app.view.is_view_visible("Detail")): self.detail_visible = self.app.view.is_view_visible("Detail") def refresh_state(self): self.log("REFRESH STATE") self.retries_count = 0 self.device_connected = False def build_midi_map(self, midi_map_handle): #self.log("BUILD MIDI MAP") assert (self._suppress_requests_counter == 0) self._in_build_midi_map = True self._midi_map_handle = midi_map_handle self._forwarding_registry = {} for control in self.controls: if isinstance(control, InputControlElement): control.install_connections() self._midi_map_handle = None self._in_build_midi_map = False if (self._pad_translations != None): self._c_instance.set_pad_translation(self._pad_translations) # remove clip listeners self.rem_clip_slot_listeners() # add clip listeners self.add_clip_slot_listeners() # update display self.update_display_clips() def log(self, msg): self.c_instance.log_message("[TE OP-1] " + msg) def disconnect(self): # removing clip slots listeners self.rem_clip_slot_listeners() # removing value listener for track changed self.song().view.remove_selected_track_listener(self.selected_track_changed) # removing value listener for scene changed self.song().view.remove_selected_scene_listener(self.selected_scene_changed) # removing value listener for operation mode index self._operation_mode_selector.remove_mode_index_listener(self.mode_index_changed) # removing global transport assignments self._transport.set_punch_buttons(None, None) self._transport.set_loop_button(None) self._transport.set_overdub_button(None) self._transport.set_record_button(None) self._transport.set_play_button(None) self._transport.set_stop_button(None) self._transport.set_metronome_button(None) self._transport.set_tap_tempo_button(None) # removing global session assignments self._session.set_scene_bank_buttons(None, None) # removing misc listeners self.browser_toggle_button.remove_value_listener(self.browser_toggle_button_callback) self.mainview_toggle_button.remove_value_listener(self.mainview_toggle_button_callback) self.detailview_toggle_button.remove_value_listener(self.detailview_toggle_button_callback) self.clear_track_button.remove_value_listener(self.clear_track_button_callback) self.back_to_arranger_button.remove_value_listener(self.back_to_arranger_button_callback) # sending special ableton mode disable sequence self._send_midi(self.disable_sequence) # disconnecting control surface ControlSurface.disconnect(self) self.log("DISCONNECTED")
class MPK_SessionControl(ControlSurface): __module__ = __name__ __doc__ = "MPK Session Control Script" def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): self._setup_mixer_control() self._setup_transport_control() self._setup_session_control() self._setup_channel_strip_control() self.set_highlighting_session_component(self.session) # Sets up the control surface ('colored box') def _setup_session_control(self): num_tracks = 3 # 3 columns (tracks) num_scenes = 1 # 1 row (scenes) # a session highlight ("red box") will appear with any two non-zero values self.session = SessionComponent(num_tracks, num_scenes) # (track_offset, scene_offset) Sets the initial offset of the "red box" from top left self.session.set_offsets(0, 0) self.session.set_select_buttons( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KEYBOARD_HIGH_C - 7), ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KEYBOARD_HIGH_C - 6)) # These calls control the actual movement of the box; however, we're just # using scene and track select to move around # self.session.set_scene_bank_buttons(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, 86), ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, 85)) # self.session.set_track_bank_buttons(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, 15), ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, 14)) # Launch current scene with top right pad self.session.selected_scene().set_launch_button( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, BANK_B[3])) # Stop all clips with bottom right pad self.session.set_stop_all_clips_button( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, BANK_B[7])) # First three pads launch clips in box clip_launch_notes = [BANK_B[0], BANK_B[1], BANK_B[2]] clip_select_notes = [ KEYBOARD_MID_C - 6, KEYBOARD_MID_C - 4, KEYBOARD_MID_C - 2 ] for tracks in range(num_tracks): self.session.scene(0).clip_slot(tracks).set_launch_button( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, clip_launch_notes[tracks])) self.session.scene(0).clip_slot(tracks).set_select_button( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, clip_select_notes[tracks])) self.session.scene(0).clip_slot(tracks).set_started_value(1) self.session.scene(0).clip_slot(tracks).set_stopped_value(0) # Bottom three pads stop current tracks in box track_stop_notes = [BANK_B[4], BANK_B[5], BANK_B[6]] # This looks unnecessary but I don't know the actual API call to to set the stop track button for the selected track stop_track_buttons = [] for tracks in range(num_tracks): stop_track_buttons.append( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, track_stop_notes[tracks])) self.session.set_stop_track_clip_buttons(tuple(stop_track_buttons)) #here we set up a mixer and channel strip(s) which move with the session self.session.set_mixer( self.mixer ) #bind the mixer to the session so that they move together selected_scene = self.song( ).view.selected_scene #this is from the Live API all_scenes = self.song().scenes index = list(all_scenes).index(selected_scene) self.session.set_offsets(0, index) #(track_offset, scene_offset) def _setup_transport_control(self): self.transport = TransportComponent() self.transport.set_stop_button( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KEYBOARD_LOW_C)) self.transport.set_play_button( ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, 113)) self.transport.set_metronome_button( ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, 114)) self.transport.set_tap_tempo_button( ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, 81)) def _setup_mixer_control(self): #set up the mixer self.mixer = MixerComponent( NUM_TRACKS, 2) #(num_tracks, num_returns, with_eqs, with_filters) self.mixer.set_track_offset( 0) #sets start point for mixer strip (offset from left) self.mixer.selected_strip().set_arm_button( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KEYBOARD_HIGH_C)) self.mixer.set_select_buttons( ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KEYBOARD_HIGH_C - 2), ButtonElement(is_momentary, MIDI_NOTE_TYPE, CHANNEL, KEYBOARD_HIGH_C - 4)) self.mixer.master_strip().set_volume_control( SliderElement(MIDI_CC_TYPE, CHANNEL, KNOBS[3])) #self.mixer.master_strip().set_pan_control(SliderElement(MIDI_CC_TYPE, CHANNEL, KNOBS[7])) #set the selected strip to the first track, so that we don't assign a button to arm the master track, which would cause an assertion error self.song().view.selected_track = self.mixer.channel_strip(0)._track self.mixer.selected_strip().set_volume_control( SliderElement(MIDI_CC_TYPE, CHANNEL, KNOBS[0])) #self.mixer.selected_strip().set_pan_control(SliderElement(MIDI_CC_TYPE, CHANNEL, KNOBS[4])) selected_track = self.song().view.selected_track all_tracks = ((self.song().tracks + self.song().return_tracks) + (self.song().master_track, )) currentTrackIndex = list(all_tracks).index(selected_track) if currentTrackIndex < len(all_tracks) - 1: self.mixer.channel_strip(currentTrackIndex + 1).set_volume_control( SliderElement(MIDI_CC_TYPE, CHANNEL, KNOBS[1])) #self.mixer.channel_strip(currentTrackIndex + 1).set_pan_control(SliderElement(MIDI_CC_TYPE, CHANNEL, KNOBS[5])) if currentTrackIndex < len(all_tracks) - 2: self.mixer.channel_strip(currentTrackIndex + 2).set_volume_control( SliderElement(MIDI_CC_TYPE, CHANNEL, KNOBS[2])) #self.mixer.channel_strip(currentTrackIndex + 2).set_pan_control(SliderElement(MIDI_CC_TYPE, CHANNEL, KNOBS[6])) def _setup_channel_strip_control(self): self.channelstrip = ChannelStripComponent() self.channelstrip.set_track(self.mixer.channel_strip(0)._track) def _on_selected_track_changed(self): """This is an override, to add special functionality (we want to move the session to the selected track, when it changes) Note that it is sometimes necessary to reload Live (not just the script) when making changes to this function""" ControlSurface._on_selected_track_changed( self ) # This will run component.on_selected_track_changed() for all components """here we set the mixer and session to the selected track, when the selected track changes""" selected_track = self.song( ).view.selected_track #this is how to get the currently selected track, using the Live API self.mixer.channel_strip(0).set_track(selected_track) all_tracks = ( (self.song().tracks + self.song().return_tracks) + (self.song().master_track, ) ) #this is from the MixerComponent's _next_track_value method index = list(all_tracks).index(selected_track) #and so is this self.session.set_offsets( index, self.session._scene_offset ) #(track_offset, scene_offset); we leave scene_offset unchanged, but set track_offset to the selected track. This allows us to jump the red box to the selected track. def _on_selected_scene_changed(self): """This is an override, to add special functionality (we want to move the session to the selected scene, when it changes)""" """When making changes to this function on the fly, it is sometimes necessary to reload Live (not just the script)...""" ControlSurface._on_selected_scene_changed( self ) # This will run component.on_selected_scene_changed() for all components """Here we set the mixer and session to the selected track, when the selected track changes""" selected_scene = self.song( ).view.selected_scene #this is how we get the currently selected scene, using the Live API all_scenes = self.song().scenes #then get all of the scenes index = list(all_scenes).index( selected_scene ) #then identify where the selected scene sits in relation to the full list self.session.set_offsets( self.session._track_offset, index ) #(track_offset, scene_offset) Set the session's scene offset to match the selected track (but make no change to the track offset) def disconnect(self): #clean things up on disconnect #create entry in log file self.log_message( time.strftime("%d.%m.%Y %H:%M:%S", time.localtime()) + "----------MPK SessionControl log closed----------") ControlSurface.disconnect(self) return None
class Codex(Codec): def __init__(self, c_instance, *a, **k): self._shifted = False super(Codex, self).__init__(c_instance, *a, **k) with self.component_guard(): self._setup_alt_mixer() self._setup_alt_device_selector() self._setup_alt_send_reset() self._setup_alt_device_control() self.log_message('<<<<<<<<<<<<<<<<<<<<<<<<< Codex subclass log opened >>>>>>>>>>>>>>>>>>>>>>>>>') self.schedule_message(1, self._mode_update) def _setup_alt_device_selector(self): self._alt_device_selector = NewDeviceSelectorComponent(self) self._alt_device_selector.name = 'Alt_Device_Selector' def _setup_alt_send_reset(self): self._alt_send_reset = ResetSendsComponent(self) self._alt_send_reset.name = 'Alt_Reset_Sends' """these two secondary DeviceComponents are only set up if the MONOHM_LINK flag in Map is turned on""" def _setup_alt_device_control(self): self._device1 = NewDeviceComponent() self._device1.name = 'Device_Component1' self._device2 = NewDeviceComponent() self._device2.name = 'Device_Component2' def _setup_alt_mixer(self): is_momentary = True self._num_tracks = (8) #A mixer is one-dimensional self._mixer2 = MixerComponent(8, 4, False, False) self._mixer2.name = 'Mixer' #self._mixer2.set_track_offset(4) #Sets start point for mixer strip (offset from left) for index in range(8): self._mixer2.channel_strip(index).name = 'Mixer_ChannelStrip_' + str(index) self._mixer2.channel_strip(index)._invert_mute_feedback = True """Mode Functions""" def _enable_alt(self): if self._main_mode._mode_index == 0: for encoder, _ in self._encoder_matrix.submatrix[:, 0:2].iterbuttons(): encoder.set_enabled(False) encoder.reset() self._alt_enabled = True self._mode_update() super(Codex, self)._enable_alt() def _disable_alt(self): if self._main_mode._mode_index == 0: self._button_matrix.submatrix[:, 0:2].reset() for encoder, _ in self._encoder_matrix.submatrix[:, 0:2].iterbuttons(): encoder.set_enabled(True) super(Codex, self)._disable_alt() def _deassign_all(self): self._alt_send_reset.set_buttons(None) self._alt_send_reset.set_enabled(False) self._alt_device_selector.set_buttons(None) self._alt_device_selector.set_enabled(False) self._mixer2.selected_strip().set_send_controls(None) for index in range(8): self._mixer2.channel_strip(index).set_volume_control(None) self._mixer2.channel_strip(index).set_select_button(None) self._mixer2.channel_strip(index).set_mute_button(None) self._dial[index][2].release_parameter() for index in range(3): self._mixer2.return_strip(index).set_volume_control(None) self._device1.set_enabled(False) self._device1._parameter_controls = None self._device2.set_enabled(False) self._device2._parameter_controls = None self._mixer2.return_strip(0).set_send_controls(None) self._mixer2.return_strip(1).set_send_controls(None) self._mixer2.return_strip(2).set_send_controls(None) super(Codex, self)._deassign_all() def _assign_volume(self): if self._alt_enabled: inputs = self.find_inputs() if not inputs is None: for index in range(4): self._dial[index][2].connect_to(inputs.parameters[index+1]) self._dial[6][2].connect_to(inputs.parameters[5]) xfade = self.find_perc_crossfader() if not xfade is None: self._dial[7][3].connect_to(xfade) self._alt_device_selector.set_matrix(self._button_matrix.submatrix[:, 0:2]) self._alt_device_selector.set_enabled(True) self._mixer2.return_strip(0).set_send_controls([None, self._dial[4][2]]) self._mixer2.return_strip(1).set_send_controls([self._dial[5][2], None]) else: self._alt_send_reset.set_buttons(self._button_matrix.submatrix[4:, 2]) self._alt_send_reset.set_enabled(True) self._mixer2.selected_strip().set_send_controls([self._dial[0][2], self._dial[1][2], self._dial[2][2], self._dial[3][2]]) for index in range(3): self._mixer2.return_strip(index).set_volume_control(self._dial[index+4][2]) self._mixer2.set_crossfader_control(self._dial[7][2]) self._device1.set_parameter_controls(tuple([self._dial[index%4][int(index/4)] for index in range(8)])) self._device2.set_parameter_controls(tuple([self._dial[(index%4)+4][int(index/4)] for index in range(8)])) self._device1.set_enabled(True) self._device2.set_enabled(True) self._find_devices() self._device1.update() self._device2.update() for index in range(8): self._mixer2.channel_strip(index).set_select_button(self._column_button[index]) for index in range(8): self._mixer2.channel_strip(index).set_volume_control(self._dial[index][3]) self._mixer2.channel_strip(index).set_mute_button(self._button[index][3]) self._mixer2.update() self.request_rebuild_midi_map() #self._mixer2.set_track_offset(TROLL_OFFSET) def find_inputs(self): found_device = None tracks = self.song().tracks for track in tracks: if track.name == 'Inputs': for device in track.devices: if bool(device.can_have_chains) and device.name.endswith('Inputs'): found_device = device return found_device def find_perc_crossfader(self): found_parameter = None tracks = self.song().tracks for track in tracks: if track.name == 'Perc': for device in track.devices: if bool(device.can_have_chains) and device.name == 'Perc': for parameter in device.parameters: if parameter.name == 'XFade': found_parameter = parameter return found_parameter """this method is used to find the devices the alt controls will latch to""" def _find_devices(self): if self._device1: if len(self.song().return_tracks) > 0: if len(self.song().return_tracks[0].devices) > 0: if self._device1._locked_to_device: self._device1.set_lock_to_device(False, self._device1._device) self._device1.set_lock_to_device(True, self.song().return_tracks[0].devices[0]) if self._device2: if len(self.song().return_tracks) > 1: if len(self.song().return_tracks[1].devices) > 0: if self._device2._locked_to_device: self._device2.set_lock_to_device(False, self._device2._device) self._device2.set_lock_to_device(True, self.song().return_tracks[1].devices[0]) # #
class midi_twister_110(ControlSurface): def __init__(self, c_instance): super(midi_twister_110, self).__init__(c_instance) with self.component_guard(): global active_mode active_mode = "_mode1" self._set_active_mode() self.show_message("Modified by XXPW") def _mode1(self): self.show_message("_mode1 is active") # mixer global mixer num_tracks = 32 num_returns = 7 self.mixer = MixerComponent(num_tracks, num_returns) self.mixer.set_track_offset(0) self.song().view.selected_track = self.mixer.channel_strip(0)._track # sends send0_controls = ( SliderElement(MIDI_CC_TYPE, 0, 32), SliderElement(MIDI_CC_TYPE, 0, 36), SliderElement(MIDI_CC_TYPE, 0, 40), None, None, None, None, None, ) self.mixer.channel_strip(0).set_send_controls(tuple(send0_controls)) self.mixer.channel_strip(0).set_volume_control( SliderElement(MIDI_CC_TYPE, 0, 44)) send1_controls = ( SliderElement(MIDI_CC_TYPE, 0, 33), SliderElement(MIDI_CC_TYPE, 0, 37), SliderElement(MIDI_CC_TYPE, 0, 41), None, None, None, None, None, ) self.mixer.channel_strip(1).set_send_controls(tuple(send1_controls)) self.mixer.channel_strip(1).set_volume_control( SliderElement(MIDI_CC_TYPE, 0, 45)) send2_controls = ( SliderElement(MIDI_CC_TYPE, 0, 34), SliderElement(MIDI_CC_TYPE, 0, 38), SliderElement(MIDI_CC_TYPE, 0, 42), None, None, None, None, None, ) self.mixer.channel_strip(2).set_send_controls(tuple(send2_controls)) self.mixer.channel_strip(2).set_volume_control( SliderElement(MIDI_CC_TYPE, 0, 46)) send3_controls = ( SliderElement(MIDI_CC_TYPE, 0, 35), SliderElement(MIDI_CC_TYPE, 0, 39), SliderElement(MIDI_CC_TYPE, 0, 43), None, None, None, None, None, ) self.mixer.channel_strip(3).set_send_controls(tuple(send3_controls)) self.mixer.channel_strip(3).set_volume_control( SliderElement(MIDI_CC_TYPE, 0, 47)) # session global _session num_tracks = 4 num_scenes = 3 session_buttons = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] self._pads = [ ButtonElement(1, MIDI_CC_TYPE, 1, session_buttons[index]) for index in range(num_tracks * num_scenes) ] self._grid = ButtonMatrixElement(rows=[ self._pads[(index * num_tracks):(index * num_tracks) + num_tracks] for index in range(num_scenes) ]) self._session = SessionComponent(num_tracks, num_scenes) self._session.set_clip_launch_buttons(self._grid) self.set_highlighting_session_component(self._session) # session track stop stop_track_buttons = [12, 13, 14, 15] self._track_stop_buttons = [ ButtonElement(1, MIDI_CC_TYPE, 1, stop_track_buttons[index]) for index in range(num_tracks) ] self._session.set_stop_track_clip_buttons( tuple(self._track_stop_buttons)) # session navigation self.session_left = ButtonElement(1, MIDI_CC_TYPE, 3, 8) self._session.set_page_left_button(self.session_left) self.session_left.add_value_listener(self._reload_active_devices, identify_sender=False) self.session_right = ButtonElement(1, MIDI_CC_TYPE, 3, 11) self._session.set_page_right_button(self.session_right) self.session_right.add_value_listener(self._reload_active_devices, identify_sender=False) self.session_up = ButtonElement(1, MIDI_CC_TYPE, 3, 10) self._session.set_page_up_button(self.session_up) self.session_up.add_value_listener(self._reload_active_devices, identify_sender=False) self.session_down = ButtonElement(1, MIDI_CC_TYPE, 3, 13) self._session.set_page_down_button(self.session_down) self.session_down.add_value_listener(self._reload_active_devices, identify_sender=False) self._session._link() self._session.set_mixer(self.mixer) #self._session.set_mixer(self.mixer) self._mode1_devices() self.add_device_listeners() # button: next device self.next_device = ButtonElement(0, MIDI_CC_TYPE, 1, 25) self.next_device.add_value_listener(self._next_device_value, identify_sender=False) # button: prev device self.previous_device = ButtonElement(1, MIDI_CC_TYPE, 1, 24) self.previous_device.add_value_listener(self._prev_device_value, identify_sender=False) # transport global transport self.transport = TransportComponent() self.transport.name = 'Transport' loop_button = ButtonElement(1, MIDI_CC_TYPE, 1, 50) loop_button.name = 'loop_button' self.transport.set_loop_button(loop_button) stop_button = ButtonElement(1, MIDI_CC_TYPE, 1, 49) stop_button.name = 'stop_button' self.transport.set_stop_button(stop_button) play_button = ButtonElement(1, MIDI_CC_TYPE, 1, 48) play_button.name = 'play_button' self.transport.set_play_button(play_button) # button: track navigation right self.track_navigation_right = ButtonElement(1, MIDI_CC_TYPE, 3, 17) self.track_navigation_right.add_value_listener( self._track_navigation_right_track_nav, identify_sender=False) # button: track navigation left self.track_navigation_left = ButtonElement(1, MIDI_CC_TYPE, 3, 14) self.track_navigation_left.add_value_listener( self._track_navigation_left_track_nav, identify_sender=False) def _remove_mode1(self): # mixer global mixer self._remove_mode1_devices() self.remove_device_listeners() send0_controls = ( None, None, None, None, None, None, None, None, ) self.mixer.channel_strip(0).set_send_controls(tuple(send0_controls)) send1_controls = ( None, None, None, None, None, None, None, None, ) self.mixer.channel_strip(1).set_send_controls(tuple(send1_controls)) send2_controls = ( None, None, None, None, None, None, None, None, ) self.mixer.channel_strip(2).set_send_controls(tuple(send2_controls)) send3_controls = ( None, None, None, None, None, None, None, None, ) self.mixer.channel_strip(3).set_send_controls(tuple(send3_controls)) # session global _session self._session.set_clip_launch_buttons(None) self.set_highlighting_session_component(None) self._session.set_mixer(None) self._session.set_stop_all_clips_button(None) # session track stop self._track_stop_buttons = None self._session.set_stop_track_clip_buttons(None) # session scene launch self._scene_launch_buttons = None self._session.set_scene_launch_buttons(None) # session navigation self.session_left.remove_value_listener(self._reload_active_devices) self._session.set_page_left_button(None) self.session_right.remove_value_listener(self._reload_active_devices) self._session.set_page_right_button(None) self.session_up.remove_value_listener(self._reload_active_devices) self._session.set_page_up_button(None) self.session_down.remove_value_listener(self._reload_active_devices) self._session.set_page_down_button(None) self._session = None self.next_device.remove_value_listener(self._next_device_value) self.next_device = None self.previous_device.remove_value_listener(self._prev_device_value) self.previous_device = None # transport global transport self.transport.set_loop_button(None) self.transport.set_stop_button(None) self.transport.set_play_button(None) self.transport = None self.track_navigation_right.remove_value_listener( self._track_navigation_right_track_nav) self.track_navigation_right = None self.track_navigation_left.remove_value_listener( self._track_navigation_left_track_nav) self.track_navigation_left = None def _mode1_devices(self): global mixer global _session # device self.mixer.selected_strip().set_volume_control( SliderElement(MIDI_CC_TYPE, 0, 28)) self.mixer.selected_strip().set_pan_control( SliderElement(MIDI_CC_TYPE, 0, 29)) self.mixer.selected_strip().set_mute_button( ButtonElement(1, MIDI_CC_TYPE, 1, 28)) self.mixer.selected_strip().set_solo_button( ButtonElement(1, MIDI_CC_TYPE, 1, 29)) if (len(self.mixer.selected_strip()._track.devices) > 0): global device_tracktype_selected__chain_number_selected self.device_tracktype_selected__chain_number_selected = DeviceComponent( ) device_controls = ( SliderElement(MIDI_CC_TYPE, 0, 16), SliderElement(MIDI_CC_TYPE, 0, 17), SliderElement(MIDI_CC_TYPE, 0, 18), SliderElement(MIDI_CC_TYPE, 0, 19), SliderElement(MIDI_CC_TYPE, 0, 20), SliderElement(MIDI_CC_TYPE, 0, 21), SliderElement(MIDI_CC_TYPE, 0, 22), SliderElement(MIDI_CC_TYPE, 0, 23), ) self.device_tracktype_selected__chain_number_selected.set_parameter_controls( tuple(device_controls)) self.set_device_component( self.device_tracktype_selected__chain_number_selected) self.device_tracktype_selected__chain_number_selected.set_on_off_button( ButtonElement(1, MIDI_CC_TYPE, 1, 26)) self.device_tracktype_selected__chain_number_selected.set_bank_nav_buttons( ButtonElement(0, MIDI_CC_TYPE, 1, 31), ButtonElement(1, MIDI_CC_TYPE, 1, 33)) def _remove_mode1_devices(self): global mixer global _session # device if (hasattr(self, 'device_tracktype_selected__chain_number_selected')): global device_tracktype_selected__chain_number_selected device_controls = ( None, None, None, None, None, None, None, None, ) self.device_tracktype_selected__chain_number_selected.set_parameter_controls( tuple(device_controls)) self.device_tracktype_selected__chain_number_selected.set_on_off_button( None) self.device_tracktype_selected__chain_number_selected.set_bank_nav_buttons( None, None) self.set_device_component( self.device_tracktype_selected__chain_number_selected) def add_device_listeners(self): global mixer num_of_tracks = len(self.song().tracks) value = "add device listener" for index in range(num_of_tracks): self.song().tracks[index].add_devices_listener( self._reload_active_devices) def remove_device_listeners(self): global mixer num_of_tracks = len(self.song().tracks) value = "remove device listener" for index in range(num_of_tracks): self.song().tracks[index].remove_devices_listener( self._reload_active_devices) def _on_selected_track_changed(self): ControlSurface._on_selected_track_changed(self) self._display_reset_delay = 0 value = "selected track changed" self._reload_active_devices(value) def _reload_active_devices(self, value=None): self._remove_active_devices() self._set_active_devices() def _set_active_devices(self): global active_mode # activate mode if (active_mode == "_mode1") and (hasattr(self, '_mode1_devices')): self._mode1_devices() def _remove_active_devices(self): global active_mode # remove activate mode if (active_mode == "_mode1") and (hasattr(self, '_mode1_devices')): self._remove_mode1_devices() def _next_device_value(self, value): if value > 0: self._device = self.song().view.selected_track.view.selected_device if self._device is not None: self.song().view.select_device( self.song().view.selected_track.devices[ self.selected_device_idx() + 1]) def _prev_device_value(self, value): if value > 0: self._device = self.song().view.selected_track.view.selected_device if self._device is not None: self.song().view.select_device( self.song().view.selected_track.devices[ self.selected_device_idx() - 1]) def selected_device_idx(self): self._device = self.song().view.selected_track.view.selected_device return self.tuple_index(self.song().view.selected_track.devices, self._device) def _track_navigation_right_track_nav(self, value): if value > 0: self.song().view.selected_track = self.song().tracks[ self.selected_track_idx() + 1] def _track_navigation_left_track_nav(self, value): if value > 0: self.song().view.selected_track = self.song().tracks[ self.selected_track_idx() - 1] def selected_track_idx(self): return self.tuple_index(self.song().tracks, self.song().view.selected_track) def _set_active_mode(self): global active_mode # activate mode if active_mode == "_mode1": self._mode1() def _remove_active_mode(self): global active_mode # remove activate mode if active_mode == "_mode1": self._remove_mode1() def _activate_mode1(self, value): global active_mode if value > 0: self._remove_active_mode() active_mode = "_mode1" self._set_active_mode() def _activate_shift_mode1(self, value): global active_mode if value > 0: self._remove_active_mode() self._mode1() else: self._remove_mode1() self._set_active_mode() def tuple_index(self, tuple, obj): for i in xrange(0, len(tuple)): if (tuple[i] == obj): return i return (False) def disconnect(self): super(midi_twister_110, self).disconnect()
class joyMIDI(ControlSurface): def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): self.setup_transport() self.setup_mixer() self.setup_session() def setup_transport(self): # elements self.play_button = ButtonElement(True, MIDI_NOTE_TYPE, 0, 93) self.stop_button = ButtonElement(True, MIDI_NOTE_TYPE, 0, 94) self.record_button = ButtonElement(True, MIDI_NOTE_TYPE, 0, 95) # transport self.transport = TransportComponent() self.transport.set_play_button(self.play_button) self.transport.set_stop_button(self.stop_button) self.transport.set_record_button(self.record_button) def setup_mixer(self): # elements self.mute_button = ButtonElement(True, MIDI_NOTE_TYPE, 0, 92) # tracks, return_tracks self.solo_button = ButtonElement(True, MIDI_NOTE_TYPE, 0, 84) # tracks, return_tracks self.arm_button = ButtonElement(True, MIDI_NOTE_TYPE, 0, 85) # tracks self.senda_up_button = ButtonElement(True, MIDI_NOTE_TYPE, 0, 96) # tracks, return_tracks self.senda_down_button = ButtonElement(True, MIDI_NOTE_TYPE, 0, 88) # tracks, return_tracks self.sendb_up_button = ButtonElement(True, MIDI_NOTE_TYPE, 0, 97) # tracks, return_tracks self.sendb_down_button = ButtonElement(True, MIDI_NOTE_TYPE, 0, 89) # tracks, return_tracks self.pan_up_button = ButtonElement( True, MIDI_NOTE_TYPE, 0, 98) # tracks, return_tracks, master_track self.pan_down_button = ButtonElement( True, MIDI_NOTE_TYPE, 0, 90) # tracks, return_tracks, master_track self.volume_up_button = ButtonElement( True, MIDI_NOTE_TYPE, 0, 99) # tracks, return_tracks, master_track self.volume_down_button = ButtonElement( True, MIDI_NOTE_TYPE, 0, 91) # tracks, return_tracks, master_track self.track_nav_encoder = EncoderElement( MIDI_CC_TYPE, 0, 14, Live.MidiMap.MapMode.relative_binary_offset) # mixer self.mixer = MixerComponent(7, 2) self.mixer.selected_strip().set_mute_button(self.mute_button) self.mixer.selected_strip().set_solo_button(self.solo_button) self.mixer.selected_strip().set_arm_button(self.arm_button) # send A/B, pan, volume self.senda_up_button.add_value_listener(self.on_senda_up_changed) self.senda_down_button.add_value_listener(self.on_senda_down_changed) self.sendb_up_button.add_value_listener(self.on_sendb_up_changed) self.sendb_down_button.add_value_listener(self.on_sendb_down_changed) self.pan_up_button.add_value_listener(self.on_pan_up_changed) self.pan_down_button.add_value_listener(self.on_pan_down_changed) self.volume_up_button.add_value_listener(self.on_volume_up_changed) self.volume_down_button.add_value_listener(self.on_volume_down_changed) # nav self.track_nav_encoder.add_value_listener(self.on_mixer_track_nav) def on_senda_up_changed(self, value): if value > 0: param = self.song().view.selected_track.mixer_device.sends[0] param.value = max( param.min, min(param.max, param.value + ((param.max - param.min) / SEND_STEPS))) def on_senda_down_changed(self, value): if value > 0: param = self.song().view.selected_track.mixer_device.sends[0] param.value = max( param.min, min(param.max, param.value - ((param.max - param.min) / SEND_STEPS))) def on_sendb_up_changed(self, value): if value > 0: param = self.song().view.selected_track.mixer_device.sends[1] param.value = max( param.min, min(param.max, param.value + ((param.max - param.min) / SEND_STEPS))) def on_sendb_down_changed(self, value): if value > 0: param = self.song().view.selected_track.mixer_device.sends[1] param.value = max( param.min, min(param.max, param.value - ((param.max - param.min) / SEND_STEPS))) def on_pan_up_changed(self, value): if value > 0: param = self.song().view.selected_track.mixer_device.panning param.value = max( param.min, min(param.max, param.value + ((param.max - param.min) / PAN_STEPS))) def on_pan_down_changed(self, value): if value > 0: param = self.song().view.selected_track.mixer_device.panning param.value = max( param.min, min(param.max, param.value - ((param.max - param.min) / PAN_STEPS))) def on_volume_up_changed(self, value): if value > 0: param = self.song().view.selected_track.mixer_device.volume param.value = max( param.min, min(param.max, param.value + ((param.max - param.min) / VOLUME_STEPS))) def on_volume_down_changed(self, value): if value > 0: param = self.song().view.selected_track.mixer_device.volume param.value = max( param.min, min(param.max, param.value - ((param.max - param.min) / VOLUME_STEPS))) def on_mixer_track_nav(self, value): move = -1 if value > 64 else +1 # get tracks-info tracks = self.song().tracks return_tracks = self.song().return_tracks master_track = self.song().master_track all_tracks = list(chain(tracks, return_tracks)) all_tracks.append(master_track) num_tracks = len(tracks) num_return_tracks = len(return_tracks) num_master_track = 1 num_all_tracks = num_tracks + num_return_tracks + num_master_track # update selected-track index = index_if(lambda t: t == self.song().view.selected_track, all_tracks) index += move index = min(max(index, 0), num_all_tracks - 1) self.song().view.selected_track = all_tracks[index] def setup_session(self): num_tracks = 7 num_scenes = 1 track_offset = 0 scene_offset = 0 # elements self.clip_launch_buttons = ButtonMatrixElement(rows=[[ ButtonElement(True, MIDI_NOTE_TYPE, 0, 76 + i) for i in range(num_tracks) ]]) self.clip_stop_buttons = [ ButtonElement(True, MIDI_NOTE_TYPE, 0, 68 + i) for i in range(num_tracks) ] self.scene_launch_buttons = ButtonMatrixElement(rows=[[ ButtonElement(True, MIDI_NOTE_TYPE, 0, 83 + i) for i in range(num_scenes) ]]) self.scene_stop_button = ButtonElement(True, MIDI_NOTE_TYPE, 0, 75) self.session_scene_nav_encoder = EncoderElement( MIDI_CC_TYPE, 0, 15, Live.MidiMap.MapMode.relative_binary_offset) # session self.session = SessionComponent(num_tracks, num_scenes) self.session.set_offsets(track_offset, scene_offset) self.session.add_offset_listener(self.on_session_offset_changed) self.set_highlighting_session_component(self.session) # clips self.session.set_clip_launch_buttons(self.clip_launch_buttons) self.session.set_stop_track_clip_buttons(self.clip_stop_buttons) self.session.set_scene_launch_buttons(self.scene_launch_buttons) self.session.set_stop_all_clips_button(self.scene_stop_button) # nav self.session_scene_nav_encoder.add_value_listener( self.on_session_scene_nav) def on_session_offset_changed(self): pass def on_session_scene_nav(self, value): value -= 64 value = -value track_offset = self.session.track_offset() scene_offset = max(0, self.session.scene_offset() + value) self.session.set_offsets(track_offset, scene_offset) def disconnect(self): u"""Live -> Script Called right before we get disconnected from Live. """ self.log_message('disconnect') self.show_message('disconnect')
class StackControlSurface(ControlSurface): __doc__ = "Script for Stack" _active_instances = [] def _combine_active_instances(): track_offset = 0 scene_offset = 0 for instance in StackControlSurface._active_instances: instance._activate_combination_mode(track_offset, scene_offset) track_offset += instance._session.width() _combine_active_instances = staticmethod(_combine_active_instances) def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) #self.set_suppress_rebuild_requests(True) with self.component_guard(): self._note_map = [] self._ctrl_map = [] self._load_MIDI_map() self._mixer = None self._setup_mixer_control() self._session = None self._setup_session_control() self._session.set_mixer(self._mixer) self.set_highlighting_session_component(self._session) #self.set_suppress_rebuild_requests(False) self._do_combine() def disconnect(self): self._note_map = None self._ctrl_map = None self._do_uncombine() self._session = None self._mixer = None ControlSurface.disconnect(self) def _do_combine(self): if self not in StackControlSurface._active_instances: StackControlSurface._active_instances.append(self) StackControlSurface._combine_active_instances() def _do_uncombine(self): if ((self in StackControlSurface._active_instances) and StackControlSurface._active_instances.remove(self)): StackControlSurface._session.unlink() StackControlSurface._combine_active_instances() def _activate_combination_mode(self, track_offset, scene_offset): if TRACK_OFFSET != -1: track_offset = TRACK_OFFSET if SCENE_OFFSET != -1: scene_offset = SCENE_OFFSET self._session.link_with_track_offset(track_offset, scene_offset) def _setup_session_control(self): is_momentary = True self._session = StackSessionComponent(self, 1, 8) self._session.name = 'Session_Control' self._session.set_select_buttons(self._ctrl_map[SCENEDN], self._ctrl_map[SCENEUP]) self._session.selected_scene().name = 'Selected_Scene' self._session.set_slot_launch_button(self._ctrl_map[SELCLIPLAUNCH]) self._session.set_slot_gen_button(self._ctrl_map[SELCLIPGEN]) for scene_index in range(8): for track_index in range(8): button = self._note_map[CLIPNOTEMAP[scene_index][track_index]] self._session.set_pad_button(button) def _setup_mixer_control(self): is_momentary = True self._mixer = MixerComponent(8) self._mixer.name = 'Mixer' self._mixer.selected_strip().name = 'Selected_Channel_Strip' self._mixer.set_select_buttons(self._ctrl_map[TRACKRIGHT], self._ctrl_map[TRACKLEFT]) def _load_MIDI_map(self): is_momentary = True for note in range(128): button = ButtonElement(is_momentary, MIDI_NOTE_TYPE, BUTTONCHANNEL, note) button.name = 'Note_' + str(note) self._note_map.append(button) self._note_map.append(None) #add None to the end of the list, selectable with [-1] for cc in range(128): button = ButtonElement(is_momentary, MIDI_CC_TYPE, BUTTONCHANNEL, cc) button.name = 'Ctrl_' + str(cc) self._ctrl_map.append(button) self._ctrl_map.append(None) #add None to the end of the list, selectable with [-1] ''' for ctrl in range(128): control = SliderElement(MIDI_CC_TYPE, SLIDERCHANNEL, ctrl) control.name = 'Ctrl_' + str(ctrl) self._ctrl_map.append(control) self._ctrl_map.append(None) ''' #SysEx Functions TO DO: replace them with functions from framework def snapshot(self): for i in range(1, 5): self.fill_screen(rnd=True) time.sleep(1/60) self.clear_screen() def fill_screen(self, char=29, rnd=False): for offset in range(0, 136): for row in range(0, 4): if rnd: char = random.randint(33, 96) self._send_midi(SYSEX_START + (24+row, 0, 2, offset, char) + SYSEX_END) def print_text(self, text, row=0, offset=0): int_letters = tuple([ord(i) for i in list(text)]) self._send_midi(SYSEX_START + (24+row, offset, len(text)+1, 0) + int_letters + SYSEX_END) def clear_screen(self): for row in range(0, 4): self._send_midi(SYSEX_START + (28 + row, 0, 0) + SYSEX_END) def set_pad(self, column, row, color): if color == "white": PAD_COLOR = (0, 127, 127, 127, 127, 127, 127) if color == "red": PAD_COLOR = (0, 127, 127, 0, 0, 0, 0) self._send_midi(SYSEX_START + (4, 0, 8, column + row*8) + PAD_COLOR + SYSEX_END)