def _setup_device(self): self._encoders = [] for offset in range(8): self._encoders.append( PeekableEncoderElement( MIDI_CC_TYPE, 15, 17 + offset, Live.MidiMap.MapMode.relative_smooth_two_compliment ) ) self._encoders[-1].set_feedback_delay(-1) self._encoders[-1].add_value_listener(self._encoder_value, identify_sender=True) self._encoders[-1].name = "Device_Control_" + str(offset) prev_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 14) next_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 15) prev_bank_button.name = "Device_Bank_Down_Button" next_bank_button.name = "Device_Bank_Up_Button" device = BestBankDeviceComponent() device.name = "Device_Component" self.set_device_component(device) device.set_parameter_controls(tuple(self._encoders)) device.set_bank_nav_buttons(prev_bank_button, next_bank_button) self._device_bank_buttons = (prev_bank_button, next_bank_button) prev_bank_button.add_value_listener(self._device_bank_value) next_bank_button.add_value_listener(self._device_bank_value) self._inst_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 109) self._inst_button.name = "Inst_Button" self._inst_button.add_value_listener(self._inst_value) self._device_navigation = DetailViewCntrlComponent() self._device_navigation.name = "Device_Navigation_Component"
def set_touch_mode(self, touchchannel): self.touch_mode = True nr_dev_ctrl = len(self._parameter_controls) for ctrl in self._parameter_controls: touch_button = ButtonElement(False, MIDI_CC_TYPE, touchchannel, ctrl.message_identifier()) self.del_touch_buttons.append(touch_button) touch_button.add_value_listener(self._clear_param, True)
def _setup_device(self): self._encoders = [] for offset in range(8): self._encoders.append( PeekableEncoderElement( MIDI_CC_TYPE, 15, 17 + offset, Live.MidiMap.MapMode.relative_smooth_two_compliment)) self._encoders[-1].set_feedback_delay(-1) self._encoders[-1].add_value_listener(self._encoder_value, identify_sender=True) self._encoders[-1].name = 'Device_Control_' + str(offset) prev_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 14) next_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 15) prev_bank_button.name = 'Device_Bank_Down_Button' next_bank_button.name = 'Device_Bank_Up_Button' device = BestBankDeviceComponent() device.name = 'Device_Component' self.set_device_component(device) device.set_parameter_controls(tuple(self._encoders)) device.set_bank_nav_buttons(prev_bank_button, next_bank_button) self._device_bank_buttons = (prev_bank_button, next_bank_button) prev_bank_button.add_value_listener(self._device_bank_value) next_bank_button.add_value_listener(self._device_bank_value) self._inst_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 109) self._inst_button.name = 'Inst_Button' self._inst_button.add_value_listener(self._inst_value) self._device_navigation = DetailViewCntrlComponent() self._device_navigation.name = 'Device_Navigation_Component'
def createButton(ccindenfier, nr_freq): button = ButtonElement(True, MIDI_CC_TYPE, 1, ccindenfier) button.add_value_listener(self._select_value, True) button.active = False button.freq = nr_freq button.cfg = False button.hold = False return button
def createCfgButton(ccindenfier, nr_freq_idx): button = ButtonElement(True, MIDI_CC_TYPE, 2, ccindenfier) button.add_value_listener(self._select_value, True) button.active = False button.fr_idx = nr_freq_idx button.freq = CFG_REPEAT_FREQUENCIES[nr_freq_idx] button.cfg = True button.hold = False return button
def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self.sebukyChannel = 15 with self.component_guard(): self._left_button = ButtonElement(True, MIDI_CC_TYPE, self.sebukyChannel, 21) self._right_button = ButtonElement(True, MIDI_CC_TYPE, self.sebukyChannel, 22) self.lockBtn = ButtonElement(True, MIDI_CC_TYPE, self.sebukyChannel, 23) self.onOffBtn = ButtonElement(True, MIDI_CC_TYPE, self.sebukyChannel, 24) encoders = ButtonMatrixElement(rows=[[ EncoderElement(MIDI_CC_TYPE, 0, identifier + 70, Live.MidiMap.MapMode.absolute, name='Encoder_%d' % identifier) for identifier in xrange(4) ]]) device = DeviceComponent( name='Device', is_enabled=False, layer=Layer(parameter_controls=encoders, prev_device_button=self._left_button, next_device_button=self._right_button)) device.set_enabled(True) device.set_lock_button(self.lockBtn) device.set_on_off_button(self.onOffBtn) self.set_device_component(device) self._device_selection_follows_track_selection = True self._long_press = 500 self.__c_instance = c_instance self.c_instance = c_instance self.log_message('sebas initiated') self.conter = 0 Pad1 = ButtonElement(True, MIDI_CC_TYPE, 0, 64) Pad1.add_value_listener(self._session_record_value, identify_sender=False) #self.backBtn = ButtonElement(True, MIDI_CC_TYPE, 0, 105) #self.backBtn.add_value_listener(self.goBack, identify_sender=False) #self.forwardBtn = ButtonElement(True, MIDI_CC_TYPE, 0, 104) #self.forwardBtn.add_value_listener( #self.goFront, identify_sender=False) # mappings for registered MIDI notes/CCs self.midi_callbacks = {} # lookup object for fast lookup of cc to mode self.midi_cc_to_mode = {}
class DeleteClip(ControlSurface): def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): self.log_message("DeleteClip started") self.show_message('DeleteClip started') self.DeleteButton = ButtonElement(IS_MOMENTARY, MIDI_NOTE_TYPE, 8, 39) self.DeleteButton.add_value_listener(self._delete_clip,identify_sender= False) def _delete_clip(self, value): self.log_message('Deleting clip') CurrentSlot = self.song().view.highlighted_clip_slot if CurrentSlot.has_clip: CurrentSlot.delete_clip()
class AKF_Oxy61(ControlSurface): __module__ = __name__ __doc__ = "MAudio Oxygen 61 MIDI Remote script " def __init__(self, c_instance): with self.component_guard(): ControlSurface.__init__(self, c_instance) self.log_message( time.strftime("%d.%m.%Y %H:%M:%S", time.localtime()) + "+ + ========= AKF_Oxy61 log opened ========= + +") # Writes message into Live's main log file. This is a ControlSurface method. self.set_suppress_rebuild_requests( True ) # Turn off rebuild MIDI map until after we're done setting up #paste from Oxygen_3rd_Gen.py is_momentary = True self._suggested_input_port = 'Oxygen' self._suggested_output_port = 'Oxygen' self._has_slider_section = True self._shift_button = ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 57) self._shift_button.add_value_listener(self._shift_value) self._mixer = SpecialMixerComponent(NUM_TRACKS) self._mute_solo_buttons = [] self._track_up_button = ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 111) self._track_down_button = ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 110) self._master_slider = SliderElement(MIDI_CC_TYPE, GLOBAL_CHANNEL, 41) for index in range(NUM_TRACKS): self._mute_solo_buttons.append( ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 49 + index)) self._mixer.channel_strip(index).set_volume_control( SliderElement(MIDI_CC_TYPE, GLOBAL_CHANNEL, 33 + index)) self._setup_mixer_control() # Setup the mixer object self._setup_session_control() # Setup the session object self.set_suppress_rebuild_requests( False) # Turn rebuild back on, once we're done setting up
class LaunchMod(ControlSurface): """ Script for Novation's Launchpad Controller """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): self._monomod_version = 'b995' self._host_name = 'LaunchMod' self._color_type = 'Launchpad' self.hosts = [] self._timer = 0 self._suppress_send_midi = True self._suppress_session_highlight = True self._suppress_highlight = False is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._control_is_with_automap = False self._user_byte_write_button = ButtonElement( is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener( self._user_byte_value) self._wrote_user_byte = False self._challenge = Live.Application.get_random_int( 0, 400000000) & 2139062143 matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [ ConfigurableButtonElement( is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column), str(column) + '_Clip_' + str(row) + '_Button', self) for column in range(8) ] matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) self._config_button.add_value_listener(self._config_value) top_button_names = [ 'Bank_Select_Up_Button', 'Bank_Select_Down_Button', 'Bank_Select_Left_Button', 'Bank_Select_Right_Button', 'Session_Button', 'User1_Button', 'User2_Button', 'Mixer_Button' ] side_button_names = [ 'Vol_Button', 'Pan_Button', 'SndA_Button', 'SndB_Button', 'Stop_Button', 'Trk_On_Button', 'Solo_Button', 'Arm_Button' ] top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, (104 + index), top_button_names[index], self) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index], side_button_names[index], self) for index in range(8) ] self._setup_monobridge() self._setup_monomod() self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self) self._selector.name = 'Main_Modes' for control in self.controls: if isinstance(control, MonoButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component( self._selector.session_component()) self._suppress_session_highlight = False self.log_message( "--------------= " + str(self._monomod_version) + " log opened =--------------") #Create entry in log file def allow_updates(self, allow_updates): for component in self.components: component.set_allow_update(int(allow_updates != 0)) def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._selector = None self._user_byte_write_button.remove_value_listener( self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if len(midi_bytes) == 8: if midi_bytes[1:5] == (0, 32, 41, 6): response = long(midi_bytes[5]) response += long(midi_bytes[6]) << 8 if response == Live.Application.encrypt_challenge2( self._challenge): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector.mode_index == 1: new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes, optimized=None): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): assert value in range(128) enabled = self._wrote_user_byte or value == 1 self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, MonoButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False def _button_value(self, value): assert value in range(128) def _config_value(self, value): assert value in range(128) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks) def _setup_m4l_interface(self): self._m4l_interface = M4LInterfaceComponent( controls=self.controls, component_guard=self.component_guard) self.get_control_names = self._m4l_interface.get_control_names self.get_control = self._m4l_interface.get_control self.grab_control = self._m4l_interface.grab_control self.release_control = self._m4l_interface.release_control """Mono overrides and additions""" def _setup_monobridge(self): self._monobridge = MonoBridgeElement(self) self._monobridge.name = 'MonoBridge' def _setup_monomod(self): self._host = MonomodComponent(self) self._host.name = 'Monomod_Host' self.hosts = [self._host] def update_display(self): ControlSurface.update_display(self) self._timer = (self._timer + 1) % 256 self.flash() def flash(self): if self._host.is_enabled(): for control in self.controls: if isinstance(control, MonoButtonElement): control.flash(self._timer)
class Launchpad(ControlSurface): """ Script for Novation's Launchpad Controller """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._control_is_with_automap = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._wrote_user_byte = False self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143 matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [] for column in range(8): button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, row * 16 + column) button.name = str(column) + '_Clip_' + str(row) + '_Button' button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) self._config_button.add_value_listener(self._config_value) top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, 104 + index) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index]) for index in range(8) ] top_buttons[0].name = 'Bank_Select_Up_Button' top_buttons[1].name = 'Bank_Select_Down_Button' top_buttons[2].name = 'Bank_Select_Left_Button' top_buttons[3].name = 'Bank_Select_Right_Button' top_buttons[4].name = 'Session_Button' top_buttons[5].name = 'User1_Button' top_buttons[6].name = 'User2_Button' top_buttons[7].name = 'Mixer_Button' side_buttons[0].name = 'Vol_Button' side_buttons[1].name = 'Pan_Button' side_buttons[2].name = 'SndA_Button' side_buttons[3].name = 'SndB_Button' side_buttons[4].name = 'Stop_Button' side_buttons[5].name = 'Trk_On_Button' side_buttons[6].name = 'Solo_Button' side_buttons[7].name = 'Arm_Button' self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button) self._selector.name = 'Main_Modes' for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component(self._selector.session_component()) self._suppress_session_highlight = False def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if len(midi_bytes) == 8: if midi_bytes[1:5] == (0, 32, 41, 6): response = long(midi_bytes[5]) response += long(midi_bytes[6]) << 8 if response == Live.Application.encrypt_challenge2(self._challenge): self._on_handshake_successful() def _on_handshake_successful(self): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector.mode_index == 1: new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes, optimized = None): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): if not value in range(128): raise AssertionError enabled = self._wrote_user_byte or value == 1 self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False def _button_value(self, value): raise value in range(128) or AssertionError def _config_value(self, value): raise value in range(128) or AssertionError def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class Novation_Impulse(ControlSurface): """ Script for Novation's Impulse keyboards """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): self.set_pad_translations(PAD_TRANSLATIONS) self._suggested_input_port = b'Impulse' self._suggested_output_port = b'Impulse' self._has_sliders = True self._current_midi_map = None self._display_reset_delay = -1 self._shift_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 39) self._preview_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 41) self._master_slider = SliderElement(MIDI_CC_TYPE, 0, 8) self._shift_button.name = b'Shift_Button' self._master_slider.name = b'Master_Volume_Control' self._master_slider.add_value_listener(self._slider_value, identify_sender=True) self._preview_button.add_value_listener(self._preview_value) self._setup_mixer() self._setup_session() self._setup_transport() self._setup_device() self._setup_name_display() device_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 1, 10) mixer_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 1, 9) device_button.name = b'Encoder_Device_Mode' mixer_button.name = b'Encoder_Mixer_Mode' self._encoder_modes = EncoderModeSelector(self._device_component, self._mixer, self._next_bank_button, self._prev_bank_button, self._encoders) self._encoder_modes.set_device_mixer_buttons( device_button, mixer_button) self._string_to_display = None for component in self.components: component.set_enabled(False) return def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(3, self._send_midi, SYSEX_START + (6, 1, 1, 1, 247)) def handle_sysex(self, midi_bytes): if midi_bytes[0:-2] == SYSEX_START + (7, ) and midi_bytes[(-2)] != 0: self._has_sliders = midi_bytes[(-2)] != 25 self.schedule_message(1, self._show_startup_message) for control in self.controls: if isinstance(control, InputControlElement): control.clear_send_cache() for component in self.components: component.set_enabled(True) if self._has_sliders: self._mixer.master_strip().set_volume_control( self._master_slider) self._mixer.update() else: self._mixer.master_strip().set_volume_control(None) self._mixer.selected_strip().set_volume_control( self._master_slider) for index in range(len(self._sliders)): self._mixer.channel_strip(index).set_volume_control(None) slider = self._sliders[index] slider.release_parameter() if slider.value_has_listener(self._slider_value): slider.remove_value_listener(self._slider_value) self._encoder_modes.set_provide_volume_mode(not self._has_sliders) self.request_rebuild_midi_map() return def disconnect(self): self._name_display_data_source.set_display_string(b' ') for encoder in self._encoders: encoder.remove_value_listener(self._encoder_value) self._master_slider.remove_value_listener(self._slider_value) if self._has_sliders: for slider in tuple(self._sliders): slider.remove_value_listener(self._slider_value) for button in self._strip_buttons: button.remove_value_listener(self._mixer_button_value) self._preview_button.remove_value_listener(self._preview_value) ControlSurface.disconnect(self) self._encoders = None self._sliders = None self._strip_buttons = None self._master_slider = None self._current_midi_map = None self._shift_button = None self._name_display = None self._prev_bank_button = None self._next_bank_button = None self._encoder_modes = None self._transport_view_modes = None self._send_midi(SYSEX_START + (6, 0, 0, 0, 247)) return def build_midi_map(self, midi_map_handle): self._current_midi_map = midi_map_handle ControlSurface.build_midi_map(self, midi_map_handle) def update_display(self): ControlSurface.update_display(self) if self._string_to_display != None: self._name_display_data_source.set_display_string( self._string_to_display) self._string_to_display = None if self._display_reset_delay >= 0: self._display_reset_delay -= 1 if self._display_reset_delay == -1: self._show_current_track_name() return def _setup_mixer(self): mute_solo_flip_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 0, 34) self._next_nav_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 37) self._prev_nav_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 38) self._strip_buttons = [] mute_solo_flip_button.name = b'Mute_Solo_Flip_Button' self._next_nav_button.name = b'Next_Track_Button' self._prev_nav_button.name = b'Prev_Track_Button' self._mixer = SpecialMixerComponent(8) self._mixer.name = b'Mixer' self._mixer.set_select_buttons(self._next_nav_button, self._prev_nav_button) self._mixer.selected_strip().name = b'Selected_Channel_Strip' self._mixer.master_strip().name = b'Master_Channel_Strip' self._mixer.master_strip().set_volume_control(self._master_slider) self._sliders = [] for index in range(8): strip = self._mixer.channel_strip(index) strip.name = b'Channel_Strip_' + str(index) strip.set_invert_mute_feedback(True) self._sliders.append(SliderElement(MIDI_CC_TYPE, 0, index)) self._sliders[(-1)].name = str(index) + b'_Volume_Control' self._sliders[(-1)].set_feedback_delay(-1) self._sliders[(-1)].add_value_listener(self._slider_value, identify_sender=True) strip.set_volume_control(self._sliders[(-1)]) self._strip_buttons.append( ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 9 + index)) self._strip_buttons[(-1)].name = str(index) + b'_Mute_Button' self._strip_buttons[(-1)].add_value_listener( self._mixer_button_value, identify_sender=True) self._mixer.master_strip().set_mute_button( ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 1, 17)) self._mixer.set_strip_mute_solo_buttons(tuple(self._strip_buttons), mute_solo_flip_button) def _setup_session(self): num_pads = len(PAD_TRANSLATIONS) self._track_left_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 0, 36) self._track_right_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 0, 35) self._session = SessionComponent(8, 0) self._session.name = b'Session_Control' self._session.selected_scene().name = b'Selected_Scene' self._session.set_mixer(self._mixer) self._session.set_page_left_button(self._track_left_button) self._session.set_page_right_button(self._track_right_button) pads = [] for index in range(num_pads): pads.append( ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 60 + index)) pads[(-1)].name = b'Pad_' + str(index) clip_slot = self._session.selected_scene().clip_slot(index) clip_slot.set_triggered_to_play_value(GREEN_BLINK) clip_slot.set_triggered_to_record_value(RED_BLINK) clip_slot.set_stopped_value(AMBER_FULL) clip_slot.set_started_value(GREEN_FULL) clip_slot.set_recording_value(RED_FULL) clip_slot.set_launch_button(pads[(-1)]) clip_slot.name = str(index) + b'_Selected_Clip_Slot' def _setup_transport(self): rwd_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 27) ffwd_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 28) stop_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 29) play_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 30) loop_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 31) rec_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 32) ffwd_button.name = b'FFwd_Button' rwd_button.name = b'Rwd_Button' loop_button.name = b'Loop_Button' play_button.name = b'Play_Button' stop_button.name = b'Stop_Button' rec_button.name = b'Record_Button' transport = ShiftableTransportComponent() transport.name = b'Transport' transport.set_stop_button(stop_button) transport.set_play_button(play_button) transport.set_record_button(rec_button) transport.set_shift_button(self._shift_button) self._transport_view_modes = TransportViewModeSelector( transport, self._session, ffwd_button, rwd_button, loop_button) self._transport_view_modes.name = b'Transport_View_Modes' def _setup_device(self): encoders = [] for index in range(8): encoders.append( PeekableEncoderElement( MIDI_CC_TYPE, 1, index, Live.MidiMap.MapMode.relative_binary_offset)) encoders[(-1)].set_feedback_delay(-1) encoders[(-1)].add_value_listener(self._encoder_value, identify_sender=True) encoders[(-1)].name = b'Device_Control_' + str(index) self._encoders = tuple(encoders) self._prev_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 1, 12) self._next_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 1, 11) self._prev_bank_button.name = b'Device_Bank_Down_Button' self._next_bank_button.name = b'Device_Bank_Up_Button' device = DeviceComponent(device_selection_follows_track_selection=True) device.name = b'Device_Component' self.set_device_component(device) device.set_parameter_controls(self._encoders) device.set_bank_nav_buttons(self._prev_bank_button, self._next_bank_button) def _setup_name_display(self): self._name_display = PhysicalDisplayElement(16, 1) self._name_display.name = b'Display' self._name_display.set_message_parts(SYSEX_START + (8, ), (247, )) self._name_display_data_source = DisplayDataSource() self._name_display.segment(0).set_data_source( self._name_display_data_source) def _encoder_value(self, value, sender): assert sender in self._encoders assert value in range(128) if self._device_component.is_enabled(): display_string = b' - ' if sender.mapped_parameter() != None: display_string = sender.mapped_parameter().name self._set_string_to_display(display_string) return def _slider_value(self, value, sender): assert sender in tuple(self._sliders) + (self._master_slider, ) assert value in range(128) if self._mixer.is_enabled(): display_string = b' - ' if sender.mapped_parameter() != None: master = self.song().master_track tracks = self.song().tracks returns = self.song().return_tracks track = None if sender == self._master_slider: if self._has_sliders: track = master else: track = self.song().view.selected_track else: track = self._mixer.channel_strip( self._sliders.index(sender))._track if track == master: display_string = b'Master' elif track in tracks: display_string = str(list(tracks).index(track) + 1) elif track in returns: display_string = str( chr(ord(b'A') + list(returns).index(track))) else: assert False display_string += b' Volume' self._set_string_to_display(display_string) return def _mixer_button_value(self, value, sender): assert value in range(128) if self._mixer.is_enabled() and value > 0: strip = self._mixer.channel_strip( self._strip_buttons.index(sender)) if strip != None: self._string_to_display = None self._name_display.segment(0).set_data_source( strip.track_name_data_source()) self._name_display.update() self._display_reset_delay = STANDARD_DISPLAY_DELAY else: self._set_string_to_display(b' - ') return def _preview_value(self, value): assert value in range(128) for encoder in self._encoders: encoder.set_peek_mode(value > 0) def _show_current_track_name(self): if self._name_display != None and self._mixer != None: self._string_to_display = None self._name_display.segment(0).set_data_source( self._mixer.selected_strip().track_name_data_source()) self._name_display.update() return def _show_startup_message(self): self._name_display.display_message(b'LIVE') self._display_reset_delay = INITIAL_DISPLAY_DELAY def _set_string_to_display(self, string_to_display): assert isinstance(string_to_display, (str, unicode)) self._name_display.segment(0).set_data_source( self._name_display_data_source) self._string_to_display = string_to_display self._display_reset_delay = STANDARD_DISPLAY_DELAY def _on_selected_track_changed(self): ControlSurface._on_selected_track_changed(self) self._show_current_track_name() all_tracks = self._has_sliders or self._session.tracks_to_use() selected_track = self.song().view.selected_track num_strips = self._session.width() if selected_track in all_tracks: track_index = list(all_tracks).index(selected_track) new_offset = track_index - track_index % num_strips if not new_offset / num_strips == int(new_offset / num_strips): raise AssertionError self._session.set_offsets(new_offset, self._session.scene_offset())
class Oxygen_3rd_Gen(ControlSurface): """ Script for the 3rd generation of M-Audio's Oxygen controllers """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): is_momentary = True self._suggested_input_port = 'Oxygen' self._suggested_output_port = 'Oxygen' self._has_slider_section = True self._device_selection_follows_track_selection = True self._shift_button = ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 57) self._shift_button.add_value_listener(self._shift_value) self._mixer = SpecialMixerComponent(NUM_TRACKS) self._mute_solo_buttons = [] self._track_up_button = ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 111) self._track_down_button = ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 110) self._master_slider = SliderElement(MIDI_CC_TYPE, GLOBAL_CHANNEL, 41) for index in range(NUM_TRACKS): self._mute_solo_buttons.append(ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 49 + index)) self._mixer.channel_strip(index).set_volume_control(SliderElement(MIDI_CC_TYPE, GLOBAL_CHANNEL, 33 + index)) self._shift_value(0) self._mixer.master_strip().set_volume_control(self._master_slider) self._mixer.selected_strip().set_volume_control(None) device = DeviceComponent() device.set_parameter_controls(tuple([ EncoderElement(MIDI_CC_TYPE, GLOBAL_CHANNEL, 17 + index, Live.MidiMap.MapMode.absolute) for index in range(8) ])) self.set_device_component(device) ffwd_button = ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 115) rwd_button = ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 114) loop_button = ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 113) transport = TransportComponent() transport.set_stop_button(ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 116)) transport.set_play_button(ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 117)) transport.set_record_button(ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 118)) session = SessionComponent(0, 0) transport_view_modes = TransportViewModeSelector(transport, session, ffwd_button, rwd_button, loop_button) return def disconnect(self): self._shift_button.remove_value_listener(self._shift_value) self._shift_button = None ControlSurface.disconnect(self) return def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._send_midi, IDENTITY_REQUEST) def handle_sysex(self, midi_bytes): if midi_bytes[0:5] == IDENTITY_RESPONSE: if midi_bytes[10] == 38: self._mixer.master_strip().set_volume_control(None) self._mixer.selected_strip().set_volume_control(self._master_slider) return def _shift_value(self, value): raise value in range(128) or AssertionError for index in range(NUM_TRACKS): if value == 0: self._mixer.channel_strip(index).set_solo_button(None) self._mixer.channel_strip(index).set_mute_button(self._mute_solo_buttons[index]) self._mixer.set_bank_buttons(None, None) self._mixer.set_select_buttons(self._track_up_button, self._track_down_button) else: self._mixer.channel_strip(index).set_mute_button(None) self._mixer.channel_strip(index).set_solo_button(self._mute_solo_buttons[index]) self._mixer.set_select_buttons(None, None) self._mixer.set_bank_buttons(self._track_up_button, self._track_down_button) return
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 LaunchMod(ControlSurface): __module__ = __name__ __doc__ = " Script for Novation's Launchpad Controller " def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self._monomod_version = 'b994' self._host_name = 'LaunchMod' self._color_type = 'Launchpad' self.hosts = [] self._timer = 0 self.set_suppress_rebuild_requests(True) self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._wrote_user_byte = False self._control_is_with_automap = False self._challenge = (Live.Application.get_random_int(0, 400000000) & 2139062143) matrix = ButtonMatrixElement() matrix.name = 'ButtonMatrix' for row in range(8): #button_row = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column)) for column in range(8) ] button_row = [ FlashingButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column), 'Button_' + str(row) + '_' + str(column), self) for column in range(8) ] matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0) self._config_button.add_value_listener(self._config_value) top_buttons = [ FlashingButtonElement(is_momentary, MIDI_CC_TYPE, 0, (104 + index), 'Top_Button' + str(index), self) for index in range(8) ] side_buttons = [ FlashingButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index], 'Side_Button' + str(index), self) for index in range(8) ] self._setup_monobridge() self._setup_monomod() self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self) self._suppress_session_highlight = False self._suppress_send_midi = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._suppress_send_midi = True self.set_suppress_rebuild_requests(False) self.log_message("--------------= LaunchMod log opened =--------------" ) #Create entry in log file self.refresh_state() def _setup_monobridge(self): self._monobridge = MonoBridgeElement(self) self._monobridge.name = 'MonoBridge' def _setup_monomod(self): self._host = MonomodComponent(self) self._host.name = 'Monomod_Host' self.hosts = [self._host] def disconnect(self): self._suppress_send_midi = True self._selector = None self._user_byte_write_button.remove_value_listener( self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None self.log_message("--------------= LaunchMod log closed =--------------" ) #Create entry in log file def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if (len(midi_bytes) == 8): if (midi_bytes[1:5] == (0, 32, 41, 6)): response = long(midi_bytes[5]) response += (long(midi_bytes[6]) << 8) if (response == Live.Application.encrypt_challenge2( self._challenge)): self._suppress_send_midi = False self.set_enabled(True) #self.refresh_state() def _send_midi(self, midi_bytes): if (not self._suppress_send_midi): ControlSurface._send_midi(self, midi_bytes) def _update_hardware(self): self._suppress_send_midi = False self._config_button.send_value(40) self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = ((self._challenge >> (8 * index)) & 127) self._send_midi((176, (17 + index), challenge_byte)) def _user_byte_value(self, value): assert (value in range(128)) enabled = (value == 1) if enabled: self._config_button.send_value(40) self._control_is_with_automap = (not enabled) for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() if (not self._wrote_user_byte): self._selector.set_mode(0) self.set_enabled(enabled) else: self._wrote_user_byte = False self.request_rebuild_midi_map() def _config_value(self, value): assert (value in range(128)) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_returns=False): if (not self._suppress_session_highlight): ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_returns) def _install_forwarding(self, control): result = False if ((not self._control_is_with_automap) or (control == self._user_byte_write_button)): result = ControlSurface._install_forwarding(self, control) return result def _translate_message(self, type, from_identifier, from_channel, to_identifier, to_channel): if (not self._control_is_with_automap): ControlSurface._translate_message(self, type, from_identifier, from_channel, to_identifier, to_channel) def update_display(self): """ Live -> Script Aka on_timer. Called every 100 ms and should be used to update display relevant parts of the controller """ for message in self._scheduled_messages: message['Delay'] -= 1 if (message['Delay'] == 0): if (message['Parameter'] != None): message['Message'](message['Parameter']) else: message['Message']() del self._scheduled_messages[ self._scheduled_messages.index(message)] for callback in self._timer_callbacks: callback() self._timer = (self._timer + 1) % 256 self.flash() def flash(self): #if(self.flash_status > 0): for row in range(8): if (self._selector._side_buttons[row]._flash_state > 0): self._selector._side_buttons[row].flash(self._timer) for column in range(8): button = self._selector._matrix.get_button(column, row) if (button._flash_state > 0): button.flash(self._timer) for index in range(4): if (self._selector._nav_buttons[index]._flash_state > 0): self._selector._nav_buttons[index].flash(self._timer) if (self._selector._modes_buttons[index]._flash_state > 0): self._selector._modes_buttons[index].flash(self._timer) def allow_updates(self, allow_updates): for component in self.components: component.set_allow_update(int(allow_updates != 0))
class sparkLE(ControlSurface): __module__=__name__ __doc__="Sparkle function" def __init__(self, c_instance): ControlSurface.__init__(self,c_instance) with self.component_guard(): self.__c_instance = c_instance self.log_message("debut du script") #self.set_suppress_rebuild_requests(True) # pas de midi request lors du chargement #self._setup_session_control() #init session #self._setupsequencer_control() # init sequencer #creation des outils self.actual_song=Live.Application.get_application().get_document() self.cache=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] self._suggested_input_port = 'SparkLE' self._suggested_output_port = 'SparkLE' self._bank_button = ButtonElement(not IS_MOMENTARY, MIDI_NOTE_TYPE, 0, 16) self._patt_button = ButtonElement(not IS_MOMENTARY, MIDI_NOTE_TYPE, 0, 17) self._seq_button = ButtonElement(not IS_MOMENTARY, MIDI_NOTE_TYPE, 0, 18) self._tune_button = ButtonElement(not IS_MOMENTARY, MIDI_NOTE_TYPE, 0, 19) self._select_button = ButtonElement(IS_MOMENTARY, MIDI_NOTE_TYPE, 0, 20) self._18_916_button = ButtonElement(not IS_MOMENTARY, MIDI_NOTE_TYPE, 0, 21) self._mute_button = ButtonElement(not IS_MOMENTARY, MIDI_NOTE_TYPE, 0, 22) self._solo_button = ButtonElement(not IS_MOMENTARY, MIDI_NOTE_TYPE, 0, 23) self._bank_button.name = 'Bank Button' self._patt_button.name = 'Pattern Button' self._seq_button.name = 'Sequencer Button' self._tune_button.name = 'Tune Button' self._select_button.name = 'Select Button' self._18_916_button.name = 'Pad Select 1-8 / 6-16 Button' self._mute_button.name = 'Mute Button' self._solo_button.name = 'solo Buttons' self._bank_button.status=False self._patt_button.status=True self._seq_button.status=False self._tune_button.status=False self._select_button.status=False self._18_916_button.status=False self._mute_button.status=False self._solo_button.status=False self._bank_button.identifier=16 self._patt_button.identifier=17 self._seq_button.identifier=18 self._tune_button.identifier=19 self._select_button.identifier=20 self._18_916_button.identifier=21 self._mute_button.identifier=22 self._solo_button.identifier=23 self._bank_button.add_value_listener(self._mode_buttons_value, identify_sender= True) self._patt_button.add_value_listener(self._mode_buttons_value, identify_sender= True) self._seq_button.add_value_listener(self._mode_buttons_value, identify_sender= True) self._tune_button.add_value_listener(self._mode_buttons_value, identify_sender= True) self._select_button.add_value_listener(self._select_buttons_value, identify_sender= True) self._18_916_button.add_value_listener(self._select_buttons_value, identify_sender= True) #self._mute_button.add_value_listener(self._mute_n_solo_buttons_value, identify_sender= True) #self._solo_button.add_value_listener(self._patt_n_solo_buttons_value, identify_sender= True) self._setup_pads() #setup the 8 pads button self._setup_patt() #setup the 16 pattern button self.actual_pattern_button=self._patt_buttons[0] self.active_pad=self._pads_buttons[0] self.active_pad.bis=False self.translated_channel=0 #live ovject actual_song = Live.Application.get_application().get_document() #init leds self.turn_led_on( self.active_pad.identifier+8 ) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) def disconnect(self) : self._suggested_input_port = 'SparkLE' self._suggested_output_port = 'SparkLE' self._bank_button = None self._patt_button = None self._seq_button = None self._tune_button = None self._select_button = None self._18_916_button = None self._mute_button = None self._solo_button = None for index in range(8): #removing pad self._pads_buttons[index].name= None self._pads_buttons[index].remove_value_listener(self._pads_buttons_value) for index in range(16): #remoing pattern buttons self._patt_buttons[index].name= None self._patt_buttons[index].remove_value_listener(self._patt_buttons_value) for index in range(127): self.turn_led_off(index) ##################################################setup of pads and patterns########################################## def _setup_pads(self): """pads and led setup""" self._pads_buttons=[] self._pads_leds=[] for index in range(8): #setup pad self._pads_buttons.append(ButtonElement( not IS_MOMENTARY, MIDI_NOTE_TYPE, 0, PADS_BUTTON + index)) self._pads_buttons[-1].name= ' PAD '+ str(index+1) self._pads_buttons[-1].add_value_listener(self._pads_buttons_value, identify_sender= True) self._pads_buttons[-1].status=False self._pads_buttons[-1].identifier=index + PADS_BUTTON #update of the sequence def suggest_map_mode(self, cc_no,channel): if cc_no in range(47,55): return Live.MidiMap.MapMode.relative_binary_offset if cc_no in [58,59]: return Live.MidiMap.MapMode.value def _setup_patt(self): """1 to 16 button setup""" self._patt_buttons=[] for index in range(16): #setup patt button self._patt_buttons.append(ButtonElement(not IS_MOMENTARY, MIDI_NOTE_TYPE, 0, PATT_BUTTON + index)) self._patt_buttons[-1].name= ' Pattern button '+ str(index+1) self._patt_buttons[-1].add_value_listener(self._patt_buttons_value, identify_sender= True) self._patt_buttons[-1].status=False self._patt_buttons[-1].identifier= index + PATT_BUTTON ##############################################midi message routing and analysis ################################ # select and 1-8/9-16 button message received and routed trough this function def _select_buttons_value(self,value, sender): if sender==self._select_button: self.log_message(value) if value>0: self._select_button.status=True self.turn_led_on(self._select_button.identifier) return else: self._select_button.status=False self.turn_led_off(self._select_button.identifier) return if sender==self._18_916_button and value>0: if self._18_916_button.status==False: self.log_message("allumage") self._18_916_button.status=True self.turn_led_on(self._18_916_button.identifier) return if self._18_916_button.status==True: self.log_message("off") self._18_916_button.status=False self.turn_led_off(self._18_916_button.identifier) return # pads button message received and routed trough this function def _pads_buttons_value(self, value , sender ): clipslot=self.actual_song.tracks[TRACK].clip_slots[self.actual_pattern_button.identifier-PATT_BUTTON] if self._select_button.status==True: self.turn_led_off( self.active_pad.identifier+8) self.active_pad=sender self.turn_led_on( self.active_pad.identifier+8) self.active_pad.bis=self._18_916_button.status self.updatecache(clipslot) self.sequencerupdate() for index in range(47,55)+[58,59]: self._translate_message(1,index,0,index,9) #pattern bar button message received and routed trough this function def _patt_buttons_value(self, value , sender ): clipslot=self.actual_song.tracks[TRACK].clip_slots[self.actual_pattern_button.identifier-PATT_BUTTON] if self._bank_button.status==True: return elif self._patt_button.status==True: self.turn_led_off(self.actual_pattern_button.identifier) self.actual_pattern_button.status=False self.actual_pattern_button=sender self.actual_pattern_button.status=True self.turn_led_on(self.actual_pattern_button.identifier) self.createclip(TRACK, self.actual_pattern_button.identifier, LENGTH, clipslot) clipslot.clip.fire() self.actual_song.view.highlighted_clip_slot = clipslot return elif self._seq_button.status==True and value>0: _index=self._patt_buttons.index(sender) self.sequencer(_index,clipslot) return elif self._tune_button.status==True: return def _mode_buttons_value(self,value,sender): self._bank_button.status=False self._patt_button.status=False self._seq_button.status=False self._tune_button.status=False self.clear_pattern_button() for index in range(16,20): self.turn_led_off(index) if sender.name=='Bank Button': self._bank_button.status=True self.turn_led_on(16) elif sender.name=='Pattern Button': self._patt_button.status=True self.turn_led_on(PATT_BUTTON+self.actual_pattern_button.identifier) self.turn_led_on(17) elif sender.name=='Sequencer Button': self._seq_button.status=True self.log_message(self.cache) self.turn_led_on(18) self.sequencerupdate() elif sender.name=='Tune Button': self._tune_button.status=True self.turn_led_on(19) #turn off leds of the pattern button bar def clear_pattern_button(self): for index in range(16): self._patt_buttons[index].status=False self.turn_led_off(index + PATT_BUTTON) #function used to easily turn on and off leds def turn_led_on(self,note): self._send_midi((144,note,100)) def turn_led_off(self,note): self._send_midi((128,note,100)) #used to create clips def createclip(self,track,row,length,clipslot): self.cache=16*[0] if clipslot.clip==None: clipslot.create_clip(length) else: self.updatecache(clipslot) #used to update the cache of the active pattern pads during sequencer update def updatecache(self,clipslot): note_tuple=clipslot.clip.get_notes(0.0,self.active_pad.identifier+8*self.active_pad.bis,LENGTH,1) self.cache=16*[0] if len(note_tuple)>0: for tuple in note_tuple: self.cache[int(4*tuple[1])]=1 #Sequencer funcions def sequencerupdate(self): for index in range(len(self.cache)): if self.cache[index]==0: self._patt_buttons[index].status=False self.turn_led_off(self._patt_buttons[index].identifier - PATT_BUTTON) if self.cache[index]==1: self._patt_buttons[index].status=True self.turn_led_on(self._patt_buttons[index].identifier - PATT_BUTTON) def sequencer(self, index,clipslot,track=TRACK): if self.cache[index]==0: self.cache[index]=1 clipslot.clip.set_notes(((self.active_pad.identifier+8*self.active_pad.bis,0.25*index,0.22,100,False),)) self._send_midi((144,self._patt_buttons[index].identifier,100)) else: self.cache[index]=0 clipslot.clip.remove_notes(0.25*index,self.active_pad.identifier+8*self.active_pad.bis,0.22,1) self._send_midi((128,self._patt_buttons[index].identifier,100))
class Axiom_DirectLink(ControlSurface): """ Script for the M-Audio Axiom DirectLink """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): self.set_pad_translations(PAD_TRANSLATIONS) self._device_selection_follows_track_selection = True self._suggested_input_port = "DirectLink" self._suggested_output_port = "DirectLink" self._waiting_for_first_response = True self._has_sliders = True self._current_midi_map = None self._display_reset_delay = -1 self._shift_pressed = False self._shift_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 13) self._master_slider = SliderElement(MIDI_CC_TYPE, 15, 41) self._next_nav_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 111) self._prev_nav_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 110) self._device_bank_buttons = None self._device_navigation = None self._shift_button.name = "Shift_Button" self._master_slider.name = "Master_Volume_Control" self._next_nav_button.name = "Next_Track_Button" self._prev_nav_button.name = "Prev_Track_Button" self._master_slider.add_value_listener(self._slider_value, identify_sender=True) self._shift_button.add_value_listener(self._shift_value) self._setup_mixer() self._setup_transport_and_session() self._setup_device() self._setup_display() for component in self.components: component.set_enabled(False) return def refresh_state(self): ControlSurface.refresh_state(self) self._waiting_for_first_response = True self.schedule_message(3, self._send_midi, SYSEX_START + (32, 46, 247)) def handle_sysex(self, midi_bytes): if midi_bytes[0:-2] == SYSEX_START + (32,) and midi_bytes[-2] != 0: self._has_sliders = midi_bytes[-2] & 8 != 0 if self._waiting_for_first_response: self._waiting_for_first_response = False self.schedule_message(1, self._show_startup_message) for component in self.components: component.set_enabled(True) if self._has_sliders: self._mixer.master_strip().set_volume_control(self._master_slider) self._mixer.update() else: self._mixer.master_strip().set_volume_control(None) self._mixer.selected_strip().set_volume_control(self._master_slider) self.request_rebuild_midi_map() return def disconnect(self): self._display_data_source.set_display_string(" ") self._shift_button.remove_value_listener(self._shift_value) self._inst_button.remove_value_listener(self._inst_value) for encoder in self._encoders: encoder.remove_value_listener(self._encoder_value) for slider in tuple(self._sliders) + (self._master_slider,): slider.remove_value_listener(self._slider_value) for button in tuple(self._strip_buttons) + (self._selected_mute_solo_button,): button.remove_value_listener(self._mixer_button_value) for button in self._device_bank_buttons: button.remove_value_listener(self._device_bank_value) self._encoders = None self._sliders = None self._strip_buttons = None self._master_slider = None self._current_midi_map = None self._selected_mute_solo_button = None self._inst_button = None self._shift_button = None self._device_navigation = None self._display = None ControlSurface.disconnect(self) self._send_midi(SYSEX_START + (32, 0, 247)) return def build_midi_map(self, midi_map_handle): self._current_midi_map = midi_map_handle ControlSurface.build_midi_map(self, midi_map_handle) def update_display(self): ControlSurface.update_display(self) if self._display_reset_delay >= 0: self._display_reset_delay -= 1 if self._display_reset_delay == -1: self._show_current_track_name() def _setup_mixer(self): self._selected_mute_solo_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 12) mute_solo_flip_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 57) self._strip_buttons = [] self._selected_mute_solo_button.name = "Selected_Mute_Button" mute_solo_flip_button.name = "Mute_Solo_Flip_Button" self._selected_mute_solo_button.add_value_listener(self._mixer_button_value, identify_sender=True) self._mixer = ShiftableMixerComponent(8) self._mixer.name = "Mixer" self._mixer.set_shift_button(self._shift_button) self._mixer.set_selected_mute_solo_button(self._selected_mute_solo_button) self._mixer.set_select_buttons(self._next_nav_button, self._prev_nav_button) self._mixer.selected_strip().name = "Selected_Channel_Strip" self._mixer.master_strip().name = "Master_Channel_Strip" self._mixer.master_strip().set_volume_control(self._master_slider) self._sliders = [] for index in range(8): strip = self._mixer.channel_strip(index) strip.name = "Channel_Strip_" + str(index) strip.set_invert_mute_feedback(True) self._sliders.append(SliderElement(MIDI_CC_TYPE, 15, 33 + index)) self._sliders[-1].name = str(index) + "_Volume_Control" self._sliders[-1].set_feedback_delay(-1) self._sliders[-1].add_value_listener(self._slider_value, identify_sender=True) strip.set_volume_control(self._sliders[-1]) self._strip_buttons.append(ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 49 + index)) self._strip_buttons[-1].name = str(index) + "_Mute_Button" self._strip_buttons[-1].add_value_listener(self._mixer_button_value, identify_sender=True) self._mixer.set_strip_mute_solo_buttons(tuple(self._strip_buttons), mute_solo_flip_button) def _setup_transport_and_session(self): ffwd_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 115) rwd_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 114) loop_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 113) play_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 117) stop_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 116) rec_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 118) ffwd_button.name = "FFwd_Button" rwd_button.name = "Rwd_Button" loop_button.name = "Loop_Button" play_button.name = "Play_Button" stop_button.name = "Stop_Button" rec_button.name = "Record_Button" transport = ShiftableTransportComponent() transport.name = "Transport" transport.set_shift_button(self._shift_button) transport.set_stop_button(stop_button) transport.set_play_button(play_button) transport.set_record_button(rec_button) pads = [] for index in range(len(PAD_TRANSLATIONS)): pads.append(ButtonElement(IS_MOMENTARY, MIDI_NOTE_TYPE, 15, PAD_TRANSLATIONS[index][2])) pads[-1].name = "Pad_" + str(index) self._session = ShiftableSessionComponent(8, 0) self._session.name = "Session_Control" self._session.selected_scene().name = "Selected_Scene" self._session.set_mixer(self._mixer) self._session.set_shift_button(self._shift_button) self._session.set_clip_slot_buttons(tuple(pads)) transport_view_modes = TransportViewModeSelector(transport, self._session, ffwd_button, rwd_button, loop_button) transport_view_modes.name = "Transport_View_Modes" def _setup_device(self): self._encoders = [] for offset in range(8): self._encoders.append( PeekableEncoderElement( MIDI_CC_TYPE, 15, 17 + offset, Live.MidiMap.MapMode.relative_smooth_two_compliment ) ) self._encoders[-1].set_feedback_delay(-1) self._encoders[-1].add_value_listener(self._encoder_value, identify_sender=True) self._encoders[-1].name = "Device_Control_" + str(offset) prev_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 14) next_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 15) prev_bank_button.name = "Device_Bank_Down_Button" next_bank_button.name = "Device_Bank_Up_Button" device = BestBankDeviceComponent() device.name = "Device_Component" self.set_device_component(device) device.set_parameter_controls(tuple(self._encoders)) device.set_bank_nav_buttons(prev_bank_button, next_bank_button) self._device_bank_buttons = (prev_bank_button, next_bank_button) prev_bank_button.add_value_listener(self._device_bank_value) next_bank_button.add_value_listener(self._device_bank_value) self._inst_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 109) self._inst_button.name = "Inst_Button" self._inst_button.add_value_listener(self._inst_value) self._device_navigation = DetailViewCntrlComponent() self._device_navigation.name = "Device_Navigation_Component" def _setup_display(self): self._display = PhysicalDisplayElement(5, 1) self._display.name = "Display" self._display.set_message_parts(SYSEX_START + (17, 1, 0, 0), (247,)) self._display_data_source = DisplayDataSource() self._display.segment(0).set_data_source(self._display_data_source) def _on_selected_track_changed(self): ControlSurface._on_selected_track_changed(self) self._show_current_track_name() def _shift_value(self, value): if not value in range(128): raise AssertionError self._shift_pressed = value > 0 for encoder in self._encoders: encoder.set_peek_mode(self._shift_pressed) self._shift_pressed and self._mixer.set_select_buttons(None, None) self._session.set_track_bank_buttons(self._next_nav_button, self._prev_nav_button) self._device_component.set_bank_nav_buttons(None, None) self._device_navigation.set_device_nav_buttons(self._device_bank_buttons[0], self._device_bank_buttons[1]) else: self._session.set_track_bank_buttons(None, None) self._mixer.set_select_buttons(self._next_nav_button, self._prev_nav_button) self._device_navigation.set_device_nav_buttons(None, None) self._device_component.set_bank_nav_buttons(self._device_bank_buttons[0], self._device_bank_buttons[1]) self.request_rebuild_midi_map() return def _encoder_value(self, value, sender): if not sender in self._encoders: raise AssertionError if not value in range(128): raise AssertionError display_string = self._device_component.is_enabled() and " - " display_string = sender.mapped_parameter() != None and sender.mapped_parameter().name self._display_data_source.set_display_string(display_string) self._set_display_data_source(self._display_data_source) self._display_reset_delay = STANDARD_DISPLAY_DELAY return def _slider_value(self, value, sender): if not sender in tuple(self._sliders) + (self._master_slider,): raise AssertionError if not value in range(128): raise AssertionError if self._mixer.is_enabled(): display_string = " - " if sender.mapped_parameter() != None: master = self.song().master_track tracks = self.song().tracks returns = self.song().return_tracks track = None if sender == self._master_slider: track = self._has_sliders and master else: track = self.song().view.selected_track else: track = self._mixer.channel_strip(self._sliders.index(sender))._track display_string = track == master and "Ma" elif track in tracks: display_string = str(list(tracks).index(track) + 1) elif track in returns: display_string = str(chr(ord("A") + list(returns).index(track))) else: raise False or AssertionError display_string += " Vol" self._display_data_source.set_display_string(display_string) self._set_display_data_source(self._display_data_source) self._display_reset_delay = STANDARD_DISPLAY_DELAY return def _mixer_button_value(self, value, sender): if not sender in tuple(self._strip_buttons) + (self._selected_mute_solo_button,): raise AssertionError if not value in range(128): raise AssertionError if self._mixer.is_enabled() and value > 0: strip = None strip = sender == self._selected_mute_solo_button and self._mixer.selected_strip() else: strip = self._mixer.channel_strip(self._strip_buttons.index(sender)) strip != None and self._set_display_data_source(strip.track_name_data_source()) else: self._display_data_source.set_display_string(" - ") self._set_display_data_source(self._display_data_source) self._display_reset_delay = STANDARD_DISPLAY_DELAY return def _device_bank_value(self, value): if not value in range(128): raise AssertionError if self._device_component.is_enabled() and value > 0: data_source = self._device_component.bank_name_data_source() data_source = self._shift_pressed and self._device_component.device_name_data_source() self._set_display_data_source(data_source) self._display_reset_delay = STANDARD_DISPLAY_DELAY def _inst_value(self, value): if not value in range(128): raise AssertionError value > 0 and self._device_component.is_enabled() and self.song().view.selected_track.view.select_instrument() and self._set_display_data_source( self._device_component.device_name_data_source() ) self._display_reset_delay = STANDARD_DISPLAY_DELAY def _show_current_track_name(self): if self._display != None and self._mixer != None: self._set_display_data_source(self._mixer.selected_strip().track_name_data_source()) return def _show_startup_message(self): self._display.display_message("LIVE") self._display_reset_delay = INITIAL_DISPLAY_DELAY def _set_display_data_source(self, data_source): raise isinstance(data_source, DisplayDataSource) or AssertionError self._display.segment(0).set_data_source(data_source) data_source.update()
class MaschineChannelStripComponent(ChannelStripComponent): def __init__(self): ChannelStripComponent.__init__(self) self.deleted = {} self.clear_mode = False self.touch_mode = False self.send_control = None self.clear_vol_button = None self.clear_pan_button = None self.clear_send_button = None def set_touch_mode(self, touchchannel): self.touch_mode = True id_vol = self._volume_control.message_identifier() id_pan = self._pan_control.message_identifier() id_send = None for send in self._send_controls: if send: id_send = send.message_identifier() self.clear_vol_button = ButtonElement(False, MIDI_CC_TYPE, touchchannel, id_vol) self.clear_vol_button.add_value_listener(self._do_clear_vol) self.clear_pan_button = ButtonElement(False, MIDI_CC_TYPE, touchchannel, id_pan) self.clear_pan_button.add_value_listener(self._do_clear_pan) self.clear_send_button = ButtonElement(False, MIDI_CC_TYPE, touchchannel, id_send) self.clear_send_button.add_value_listener(self._do_clear_send) for send in self._send_controls: if send: self.send_control = send def enter_clear(self): self.clear_mode = True self.deleted = {} if not self.touch_mode: self.set_enabled(False) self._volume_control.add_value_listener(self._do_clear_vol) self._pan_control.add_value_listener(self._do_clear_pan) for send in self._send_controls: if send: self.send_control = send send.add_value_listener(self._do_clear_send) def exit_clear(self): self.clear_mode = False if not self.touch_mode: self._volume_control.remove_value_listener(self._do_clear_vol) self._pan_control.remove_value_listener(self._do_clear_pan) for send in self._send_controls: if send: send.remove_value_listener(self._do_clear_send) self.set_enabled(True) def _do_clear_vol(self, value): key = self._volume_control.message_identifier() if self.clear_mode and key not in self.deleted: self.deleted[key] = True playing_clip = self._get_playing_clip() if playing_clip: playing_clip.clear_envelope(self._track.mixer_device.volume) def _do_clear_pan(self, value): key = self._pan_control.message_identifier() if self.clear_mode and key not in self.deleted: self.deleted[key] = True playing_clip = self._get_playing_clip() if playing_clip: playing_clip.clear_envelope(self._track.mixer_device.panning) def _do_clear_send(self, value): key = self.send_control.message_identifier() if self.clear_mode and key not in self.deleted: send_index = len(self._send_controls) - 1 self.deleted[key] = True playing_clip = self._get_playing_clip() if playing_clip and send_index in range(len(self._track.mixer_device.sends)): playing_clip.clear_envelope(self._track.mixer_device.sends[send_index]) def _mute_value(self, value): super(MaschineChannelStripComponent, self)._mute_value(value) key = self._mute_button.message_identifier() if self.clear_mode and key not in self.deleted: self.deleted[key] = True playing_clip = self._get_playing_clip() if playing_clip: playing_clip.clear_envelope(self._track.mixer_device.track_activator) def _get_playing_clip(self): if self._track == None: return clips_slots = self._track.clip_slots for cs in clips_slots: if cs.has_clip and cs.is_playing: return cs.clip def disconnect(self): self.clear_pan_button = None self.clear_send_button = None if self.clear_vol_button != None: self.clear_vol_button.remove_value_listener(self._do_clear_vol) self.clear_vol_button = None if self.clear_pan_button != None: self.clear_pan_button.remove_value_listener(self._do_clear_pan) self.clear_pan_button = None if self.clear_send_button != None: self.clear_send_button.remove_value_listener(self._do_clear_send) self.clear_send_button = None if not self.touch_mode and self.clear_mode: if self.send_control != None: self.send_control.remove_value_listener(self._do_clear_send) self.send_control = None if self._volume_control != None: self._volume_control.remove_value_listener(self._do_clear_vol) self._volume_control = None if self._pan_control != None: self._pan_control.remove_value_listener(self._do_clear_pan) self._pan_control = None super(MaschineChannelStripComponent, self).disconnect()
class NoteRepeatComponent(CompoundComponent): __module__ = __name__ __doc__ = ' Noter Repeat Handler' _knob_handler = None def __init__(self, note_repeat = None, *a, **k): super(NoteRepeatComponent, self).__init__(*a, **k) self._note_repeat = note_repeat self._adjust_cfg_value.subject = SliderElement(MIDI_CC_TYPE, 2, 105) self._note_repeat_button = ButtonElement(True, MIDI_CC_TYPE, 0, 102) self._do_note_repeat.subject = self._note_repeat_button self._cfg_adjust_button = ButtonElement(True, MIDI_CC_TYPE, 2, 106) self._cfg_adjust_button.add_value_listener(self._do_cfg_button) self._cfg_down = False self._hold_mode = False self.nr_down = False self._current_nr_button = None self._do_change_nr_1.subject = SliderElement(MIDI_CC_TYPE, 1, 76) self._do_change_nr_2.subject = SliderElement(MIDI_CC_TYPE, 1, 77) self._do_change_nr_3.subject = SliderElement(MIDI_CC_TYPE, 1, 78) self._do_change_nr_4.subject = SliderElement(MIDI_CC_TYPE, 1, 79) def createButton(ccindenfier, nr_freq): button = ButtonElement(True, MIDI_CC_TYPE, 1, ccindenfier) button.add_value_listener(self._select_value, True) button.active = False button.freq = nr_freq button.cfg = False button.hold = False return button def createCfgButton(ccindenfier, nr_freq_idx): button = ButtonElement(True, MIDI_CC_TYPE, 2, ccindenfier) button.add_value_listener(self._select_value, True) button.active = False button.fr_idx = nr_freq_idx button.freq = CFG_REPEAT_FREQUENCIES[nr_freq_idx] button.cfg = True button.hold = False return button self._cfg_buttons = [ createCfgButton(assign[0], assign[1]) for assign in CTRL_CFG_TO_FREQ ] for button in self._cfg_buttons: button.send_value(button.active and 1 or 0, True) self.nr_frq = CTRL_TO_FREQ[4][1] self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 self.buttons = [ createButton(assign[0], assign[1]) for assign in CTRL_TO_FREQ ] self.buttons[4].active = True self._previous_button = self.buttons[4] self._last_active_button = self.buttons[4] for button in self.buttons: button.send_value(button.active and 1 or 0, True) def update(self): pass def store_values(self, dict): for button, idx in zip(self._cfg_buttons, range(len(self._cfg_buttons))): dict['cofig-nr-val' + str(idx)] = button.fr_idx def recall_values(self, dict): for button, idx in zip(self._cfg_buttons, range(len(self._cfg_buttons))): key = 'cofig-nr-val' + str(idx) if key in dict: fqidx = dict[key] button.fr_idx = fqidx button.freq = CFG_REPEAT_FREQUENCIES[fqidx] def registerKnobHandler(self, handler): self._knob_handler = handler def show_note_rates(self): rates = '' for button, idx in zip(self._cfg_buttons, range(len(self._cfg_buttons))): rates += ' ' + CFG_REPEAT_DISPLAY[button.fr_idx].ljust(5) if idx < 3: rates += '|' self.canonical_parent.timed_message(2, rates) def mod_button(self, button, inc, which): cindex = button.fr_idx maxindex = len(CFG_REPEAT_FREQUENCIES) - 1 minindex = 0 if self.canonical_parent.isShiftDown(): inc *= 2 if not (cindex % 2 == 0 and maxindex): maxindex = maxindex - 1 minindex = cindex % 2 new_idx = max(minindex, min(maxindex, cindex + inc)) if new_idx != cindex: self.canonical_parent.show_message('Note Repeat Button ' + str(which) + ' : ' + CFG_REPEAT_DISPLAY[new_idx]) button.fr_idx = new_idx button.freq = CFG_REPEAT_FREQUENCIES[new_idx] self.nr_frq = button.active and CFG_REPEAT_FREQUENCIES[new_idx] self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 @subject_slot('value') def _do_change_nr_1(self, value): self.mod_button(self._cfg_buttons[0], value == 0 and -1 or 1, 1) self.show_note_rates() @subject_slot('value') def _do_change_nr_2(self, value): self.mod_button(self._cfg_buttons[1], value == 0 and -1 or 1, 2) self.show_note_rates() @subject_slot('value') def _do_change_nr_3(self, value): self.mod_button(self._cfg_buttons[2], value == 0 and -1 or 1, 3) self.show_note_rates() @subject_slot('value') def _do_change_nr_4(self, value): self.mod_button(self._cfg_buttons[3], value == 0 and -1 or 1, 4) self.show_note_rates() def _do_cfg_button(self, value): if value != 0: self._cfg_down = True button = self._current_nr_button if button and button.cfg and button.fr_idx >= 0: self.canonical_parent.show_message('Note Repeat ' + CFG_REPEAT_DISPLAY[button.fr_idx]) else: self._cfg_down = False if self._knob_handler: self._knob_handler.do_main_push(value) @subject_slot('value') def _adjust_cfg_value(self, value): button = self._current_nr_button if button and button.cfg and (self.nr_down or button.hold or self._hold_mode and button.active): if not (value == 127 and -1): inc = 1 cindex = button.fr_idx maxindex = len(CFG_REPEAT_FREQUENCIES) - 1 minindex = 0 self._cfg_down and inc *= 2 maxindex = cindex % 2 == 0 and maxindex or maxindex - 1 minindex = cindex % 2 new_idx = max(minindex, min(maxindex, cindex + inc)) self.canonical_parent.show_message('Note Repeat ' + CFG_REPEAT_DISPLAY[new_idx]) button.fr_idx = new_idx button.freq = CFG_REPEAT_FREQUENCIES[new_idx] self.nr_frq = CFG_REPEAT_FREQUENCIES[new_idx] self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 elif self._knob_handler: self._knob_handler.do_main(value) def _select_value(self, value, button): if value != 0: self._current_nr_button = button button.hold = True self.show_note_rates() if self._hold_mode: if self._previous_button == button: button.send_value(0, True) button.active = False self._last_active_button = button button.active = False self._note_repeat.repeat_rate = 32.0 self._previous_button = None elif self._previous_button == None or self._previous_button != button: button.send_value(1, True) button.active = True self.nr_frq = button.freq self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 if not self._note_repeat.enabled: self._note_repeat.enabled = True if self._previous_button != None: self._previous_button.active = False self._previous_button.send_value(0, True) self._previous_button = button else: button.send_value(1, True) button.active = True self.nr_frq = button.freq self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 if self._previous_button != None and self._previous_button != button: self._previous_button.active = False self._previous_button.send_value(0, True) self._previous_button = button else: button.hold = False @subject_slot('value') def _do_note_repeat(self, value): self.nr_down = value > 0 if self._hold_mode: if value > 0: self._note_repeat_button.send_value(0) self._note_repeat.enabled = False self._hold_mode = False if self._previous_button == None and self._last_active_button != None: self._previous_button = self._last_active_button self._last_active_button.send_value(1) self._last_active_button.active = True self.nr_frq = self._last_active_button.freq self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 elif self.canonical_parent.isShiftDown() and value > 0: self._note_repeat_button.send_value(1) self._note_repeat.enabled = True self._hold_mode = True self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 elif value == 0: self._note_repeat.enabled = False self._note_repeat_button.send_value(0) else: self._note_repeat_button.send_value(1) self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 self._note_repeat.enabled = True def disconnect(self): super(NoteRepeatComponent, self).disconnect()
class OP1ModeSelectorComponent(ModeSelectorComponent): __doc__ = ' SelectorComponent that assigns buttons to functions based on the shift button ' def __init__(self, parent, transport, mixer, session): ModeSelectorComponent.__init__(self) self._current_mode = -1 self._mode_index = 0 self._parent = parent self._transport = transport self._mixer = mixer self._session = session self._shift_active = False # creating buttons for the arrows keys self._left_arrow_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_LEFT_ARROW) self._right_arrow_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_RIGHT_ARROW) # creating button for the shift key self._shift_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SHIFT_BUTTON) self._shift_button.add_value_listener(self.shift_pressed) # creating buttons for the note shifted keys self.note_keys_shifted_buttons = [] self.note_keys_shifted_ccs = [ 77, 79, 81, 83, 84, 86, 88, 89, 91, 93, 95, 96, 98 ] # creating a list of shifted note keys buttons for i in range(len(self.note_keys_shifted_ccs)): self.note_keys_shifted_buttons.append( ButtonElement(True, MIDI_NOTE_TYPE, CHANNEL, self.note_keys_shifted_ccs[i])) # creating buttons for the note keys self.note_keys_buttons = [] self.note_keys_ccs = [ 53, 55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72, 74, 76 ] # creating a list of note keys buttons for i in range(len(self.note_keys_ccs)): self.note_keys_buttons.append( ButtonElement(True, MIDI_NOTE_TYPE, CHANNEL, self.note_keys_ccs[i])) def disconnect(self): ModeSelectorComponent.disconnect(self) self._transport = None return None def number_of_modes(self): return NUM_MODES def shift_pressed(self, value): # handling shift pressed (only for transport mode) if (self._current_mode == OP1_MODE_TRANSPORT): if (value == 127): self._transport.set_seek_buttons(None, None) self._left_arrow_button.add_value_listener( self.shifted_left_arrow_pressed) self._right_arrow_button.add_value_listener( self.shifted_right_arrow_pressed) else: self._left_arrow_button.remove_value_listener( self.shifted_left_arrow_pressed) self._right_arrow_button.remove_value_listener( self.shifted_right_arrow_pressed) self._transport.set_seek_buttons(self._right_arrow_button, self._left_arrow_button) def shifted_left_arrow_pressed(self, value): # handling negative loop offset behavior if (value == 127): if (self._parent.song().loop_start > 0): self._parent.song().loop_start -= 1 def shifted_right_arrow_pressed(self, value): # handling positive loop offset behavior if (value == 127): self._parent.song().loop_start += 1 def set_loop(self, index, set_loop_start): # handling set loop self._parent.song().loop = 1 if (set_loop_start): self._parent.song().loop_start = round( self._parent.song().current_song_time) if (index == 0): self._parent.song().loop_length = 1 elif (index == 1): self._parent.song().loop_length = 2 elif (index == 2): self._parent.song().loop_length = 4 elif (index == 3): self._parent.song().loop_length = 8 elif (index == 4): self._parent.song().loop_length = 16 elif (index == 5): self._parent.song().loop_length = 32 elif (index == 6): self._parent.song().loop_length = 64 elif (index == 7): self._parent.song().loop_length = 128 elif (index == 8): self._parent.song().loop_length = 256 elif (index == 9): self._parent.song().loop_length = 512 elif (index == 10): self._parent.song().loop_length = 1024 elif (index == 11): self._parent.song().loop_length = 2048 elif (index == 12): self._parent.song().loop_length = 4096 def note_key_pressed(self, value, sender): # determining index of note key button in list index = self.note_keys_buttons.index(sender) if (self._current_mode == OP1_MODE_MIXER): # if on mixer mode, use not key to select a track all_tracks = ( (self.song().visible_tracks + self.song().return_tracks)) + ( self.song().master_track, ) #modified if (index < len(all_tracks)): self.song().view.selected_track = all_tracks[index] elif (self._current_mode == OP1_MODE_TRANSPORT): # if on transport mode, use key to set loop self.set_loop(index, False) def note_key_shifted_pressed(self, value, sender): # determining index of note key button in list index = self.note_keys_shifted_buttons.index(sender) if (self._current_mode == OP1_MODE_TRANSPORT): # if on transport mode, use key to set loop with loop start change self.set_loop(index, True) def clip_color_changed(self): self._parent.log("clip color changed") def has_clip_listener(self): self._parent.log("slot has clip") def update(self): # handle current mode change if self.is_enabled(): # clearing last mappings self.clear() # updating current mode index self._current_mode = self._mode_index # based on current mode, perform necessay re mappings if (self._mode_index == OP1_MODE_PERFORM): # nothing is done for perform mode self._parent.log("PERFORM MODE") elif (self._mode_index == OP1_MODE_TRANSPORT): self._parent.log("TRANSPORT MODE") # settings arrows as seek buttons self._transport.set_seek_buttons(self._right_arrow_button, self._left_arrow_button) # adding value listeners for note keys and note shifted keys for i in range(NUM_TRACKS): self.note_keys_buttons[i].add_value_listener( self.note_key_pressed, True) for i in range(NUM_TRACKS): self.note_keys_shifted_buttons[i].add_value_listener( self.note_key_shifted_pressed, True) elif (self._mode_index == OP1_MODE_MIXER): self._parent.log("MIXER MODE") # setting arrow butons as track select buttons self._mixer.set_select_buttons(self._right_arrow_button, self._left_arrow_button) # adding value listeners for note keys for i in range(NUM_TRACKS): self.note_keys_buttons[i].add_value_listener( self.note_key_pressed, True) elif (self._mode_index == OP1_MODE_CLIP): self._parent.log("CLIP MODE") # setting arrows as track bank buttons self._session.set_track_bank_buttons(self._right_arrow_button, self._left_arrow_button) # setting last key note as stop all clip button self._session.set_stop_all_clips_button( ButtonElement(True, MIDI_NOTE_TYPE, CHANNEL, 100)) # setting track stop clip buttons self._session.set_stop_track_clip_buttons( tuple(self.note_keys_shifted_buttons)) # setting track individual clip launch button for i in range(NUM_TRACKS): self._session.scene(0).clip_slot(i).set_launch_button( self.note_keys_buttons[i]) # setting scene launch button self._session.scene(0).set_launch_button( self.note_keys_buttons[NUM_TRACKS]) return None def clear(self): if (self._current_mode == OP1_MODE_PERFORM): self._parent.log("CLEARING PERFORM MODE") elif (self._current_mode == OP1_MODE_TRANSPORT): self._parent.log("CLEARING TRANSPORT MODE") # removing value listeners for note keys for i in range(NUM_TRACKS): self.note_keys_buttons[i].remove_value_listener( self.note_key_pressed) # removing value listeners for shifted note keys for i in range(NUM_TRACKS): self.note_keys_shifted_buttons[i].remove_value_listener( self.note_key_shifted_pressed) # clearing transport seek buttons self._transport.set_seek_buttons(None, None) elif (self._current_mode == OP1_MODE_MIXER): self._parent.log("CLEARING MIXER MODE") # removing value listeners for note key press for i in range(NUM_TRACKS): self.note_keys_buttons[i].remove_value_listener( self.note_key_pressed) self._parent.clear_tracks_assigments() # clearing mixer track select buttons self._mixer.set_select_buttons(None, None) elif (self._current_mode == OP1_MODE_CLIP): self._parent.log("CLEARING CLIP MODE") # clearing session track bank buttons self._session.set_track_bank_buttons(None, None) # clearing session stop all clips button self._session.set_stop_all_clips_button(None) # clearing session track stop clip buttons self._session.set_stop_track_clip_buttons(None) # clearing individual clip launch button for i in range(NUM_TRACKS): self._session.scene(0).clip_slot(i).set_launch_button(None) # clearing session launch button self._session.scene(0).set_launch_button(None)
class OP1ModeSelectorComponent(ModeSelectorComponent): __doc__ = ' SelectorComponent that assigns buttons to functions based on the shift button ' def __init__(self, parent, transport, mixer, session): ModeSelectorComponent.__init__(self) self._current_mode = -1 self._mode_index = 0 self._parent = parent self._transport = transport self._mixer = mixer self._session = session self._shift_active = False; # creating buttons for the arrows keys self._left_arrow_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_LEFT_ARROW) self._right_arrow_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_RIGHT_ARROW) # creating button for the shift key self._shift_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SHIFT_BUTTON) self._shift_button.add_value_listener(self.shift_pressed) # creating buttons for the note shifted keys self.note_keys_shifted_buttons = [] self.note_keys_shifted_ccs = [77, 79, 81, 83, 84, 86, 88, 89, 91, 93, 95, 96, 98] # creating a list of shifted note keys buttons for i in range(len(self.note_keys_shifted_ccs)): self.note_keys_shifted_buttons.append(ButtonElement(True, MIDI_NOTE_TYPE, CHANNEL, self.note_keys_shifted_ccs[i])) # creating buttons for the note keys self.note_keys_buttons = [] self.note_keys_ccs = [53, 55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72, 74, 76] # creating a list of note keys buttons for i in range(len(self.note_keys_ccs)): self.note_keys_buttons.append(ButtonElement(True, MIDI_NOTE_TYPE, CHANNEL, self.note_keys_ccs[i])) def disconnect(self): ModeSelectorComponent.disconnect(self) self._transport = None return None def number_of_modes(self): return NUM_MODES def shift_pressed(self, value): # handling shift pressed (only for transport mode) if (self._current_mode==OP1_MODE_TRANSPORT): if (value==127): self._transport.set_seek_buttons(None, None) self._left_arrow_button.add_value_listener(self.shifted_left_arrow_pressed) self._right_arrow_button.add_value_listener(self.shifted_right_arrow_pressed) else: self._left_arrow_button.remove_value_listener(self.shifted_left_arrow_pressed) self._right_arrow_button.remove_value_listener(self.shifted_right_arrow_pressed) self._transport.set_seek_buttons(self._right_arrow_button, self._left_arrow_button) def shifted_left_arrow_pressed(self, value): # handling negative loop offset behavior if (value==127): if (self._parent.song().loop_start>0): self._parent.song().loop_start -= 1 def shifted_right_arrow_pressed(self, value): # handling positive loop offset behavior if (value==127): self._parent.song().loop_start += 1 def set_loop(self, index, set_loop_start): # handling set loop self._parent.song().loop = 1 if (set_loop_start): self._parent.song().loop_start = round(self._parent.song().current_song_time) if (index==0): self._parent.song().loop_length = 1 elif (index==1): self._parent.song().loop_length = 2 elif (index==2): self._parent.song().loop_length = 4 elif (index==3): self._parent.song().loop_length = 8 elif (index==4): self._parent.song().loop_length = 16 elif (index==5): self._parent.song().loop_length = 32 elif (index==6): self._parent.song().loop_length = 64 elif (index==7): self._parent.song().loop_length = 128 elif (index==8): self._parent.song().loop_length = 256 elif (index==9): self._parent.song().loop_length = 512 elif (index==10): self._parent.song().loop_length = 1024 elif (index==11): self._parent.song().loop_length = 2048 elif (index==12): self._parent.song().loop_length = 4096 def note_key_pressed(self, value, sender): # determining index of note key button in list index = self.note_keys_buttons.index(sender) if (self._current_mode==OP1_MODE_MIXER): # if on mixer mode, use not key to select a track all_tracks = ((self.song().visible_tracks + self.song().return_tracks)) + (self.song().master_track,) #modified if (index < len(all_tracks)): self.song().view.selected_track = all_tracks[index] elif (self._current_mode==OP1_MODE_TRANSPORT): # if on transport mode, use key to set loop self.set_loop(index, False) def note_key_shifted_pressed(self, value, sender): # determining index of note key button in list index = self.note_keys_shifted_buttons.index(sender) if (self._current_mode==OP1_MODE_TRANSPORT): # if on transport mode, use key to set loop with loop start change self.set_loop(index, True) def clip_color_changed(self): self._parent.log("clip color changed") def has_clip_listener(self): self._parent.log("slot has clip") def update(self): # handle current mode change if self.is_enabled(): # clearing last mappings self.clear() # updating current mode index self._current_mode = self._mode_index # based on current mode, perform necessay re mappings if (self._mode_index == OP1_MODE_PERFORM): # nothing is done for perform mode self._parent.log("PERFORM MODE") elif (self._mode_index == OP1_MODE_TRANSPORT): self._parent.log("TRANSPORT MODE") # settings arrows as seek buttons self._transport.set_seek_buttons(self._right_arrow_button, self._left_arrow_button) # adding value listeners for note keys and note shifted keys for i in range(NUM_TRACKS): self.note_keys_buttons[i].add_value_listener(self.note_key_pressed, True) for i in range(NUM_TRACKS): self.note_keys_shifted_buttons[i].add_value_listener(self.note_key_shifted_pressed, True) elif (self._mode_index == OP1_MODE_MIXER): self._parent.log("MIXER MODE") # setting arrow butons as track select buttons self._mixer.set_select_buttons(self._right_arrow_button, self._left_arrow_button) # adding value listeners for note keys for i in range(NUM_TRACKS): self.note_keys_buttons[i].add_value_listener(self.note_key_pressed, True) elif (self._mode_index == OP1_MODE_CLIP): self._parent.log("CLIP MODE") # setting arrows as track bank buttons self._session.set_track_bank_buttons(self._right_arrow_button, self._left_arrow_button) # setting last key note as stop all clip button self._session.set_stop_all_clips_button(ButtonElement(True, MIDI_NOTE_TYPE, CHANNEL, 100)) # setting track stop clip buttons self._session.set_stop_track_clip_buttons(tuple(self.note_keys_shifted_buttons)) # setting track individual clip launch button for i in range(NUM_TRACKS): self._session.scene(0).clip_slot(i).set_launch_button(self.note_keys_buttons[i]) # setting scene launch button self._session.scene(0).set_launch_button(self.note_keys_buttons[NUM_TRACKS]) return None def clear(self): if (self._current_mode == OP1_MODE_PERFORM): self._parent.log("CLEARING PERFORM MODE") elif (self._current_mode == OP1_MODE_TRANSPORT): self._parent.log("CLEARING TRANSPORT MODE") # removing value listeners for note keys for i in range(NUM_TRACKS): self.note_keys_buttons[i].remove_value_listener(self.note_key_pressed) # removing value listeners for shifted note keys for i in range(NUM_TRACKS): self.note_keys_shifted_buttons[i].remove_value_listener(self.note_key_shifted_pressed) # clearing transport seek buttons self._transport.set_seek_buttons(None, None) elif (self._current_mode == OP1_MODE_MIXER): self._parent.log("CLEARING MIXER MODE") # removing value listeners for note key press for i in range(NUM_TRACKS): self.note_keys_buttons[i].remove_value_listener(self.note_key_pressed) self._parent.clear_tracks_assigments() # clearing mixer track select buttons self._mixer.set_select_buttons(None, None) elif (self._current_mode == OP1_MODE_CLIP): self._parent.log("CLEARING CLIP MODE") # clearing session track bank buttons self._session.set_track_bank_buttons(None,None) # clearing session stop all clips button self._session.set_stop_all_clips_button(None) # clearing session track stop clip buttons self._session.set_stop_track_clip_buttons(None) # clearing individual clip launch button for i in range(NUM_TRACKS): self._session.scene(0).clip_slot(i).set_launch_button(None) # clearing session launch button self._session.scene(0).set_launch_button(None)
class MainKnobControl: __module__ = __name__ __doc__ = 'Mk2 Module for Controlling Parameters with Master Knob' def __init__(self, parent): self._parent = parent self.master_track = parent.song().master_track self.the_slider = SliderElement(MIDI_CC_TYPE, 1, 86) self.the_slider.add_value_listener(self._do_main_slider, True) self.volume_button = None self._set_volume_button(ButtonElement(True, MIDI_CC_TYPE, 1, 80)) self.xfade_button = None self._set_xfade_button(ButtonElement(True, MIDI_CC_TYPE, 1, 99)) self.swing_button = None self._set_swing_button(ButtonElement(True, MIDI_CC_TYPE, 1, 81)) self.mode = KN2_MODE_VOLUME self.previous_mode = -1 self.tempo_button = None self._set_tempo_button(ButtonElement(True, MIDI_CC_TYPE, 1, 82)) self.push_button = None self._set_push_button(ButtonElement(True, MIDI_CC_TYPE, 1, 87)) self.clipn_v_button = None self.clipn_h_button = None self._set_clipn_h_button(ButtonElement(True, MIDI_CC_TYPE, 1, 90)) self._set_clipn_v_button(ButtonElement(True, MIDI_CC_TYPE, 1, 91)) self.toggle_buttons = [self.volume_button, self.xfade_button, self.swing_button, self.tempo_button, self.clipn_h_button, self.clipn_v_button] self.shift_button = None self._set_shift_button(ButtonElement(True, MIDI_CC_TYPE, 1, 85)) self.shift_on = False self.scroll_mod_left_button = None self.scroll_mod_right_button = None self._set_scroll_mod_left_button(ButtonElement(True, MIDI_CC_TYPE, 0, 105)) self._set_scroll_mod_right_button(ButtonElement(True, MIDI_CC_TYPE, 0, 106)) self._prev_mode = KN2_MODE_VOLUME self.lrmode = LR_CONTROL_CLIP self.loop_div_index = 0 self.loop_incdex = 4.0 self.arrow_mode_button = ColorButton(True, MIDI_CC_TYPE, 30) self.arrow_mode_button.add_value_listener(self.toggle_arrow_mode) self.arrow_mode_button.send_color(LR_MODE_HUES[self.lrmode]) self.arrow_mode_button.send_value(1) self.navflags = 0 self.octave_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 70) self.octave_mod_button.add_value_listener(self._action_octave) self.scale_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 71) self.scale_mod_button.add_value_listener(self._action_scale) self.basenote_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 72) self.basenote_mod_button.add_value_listener(self._action_base_note) self.pad_to_mainknob_mode = 0 self.octave_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 120) self.octave_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 121) self.scale_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 118) self.scale_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 119) self.basent_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 124) self.basent_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 125) self.octave_dwn_button.add_value_listener(self._action_oct_down) self.octave_upp_button.add_value_listener(self._action_oct_up) self.scale_dwn_button.add_value_listener(self._action_scale_down) self.scale_upp_button.add_value_listener(self._action_scale_up) self.basent_dwn_button.add_value_listener(self._action_base_down) self.basent_upp_button.add_value_listener(self._action_base_up) self._measure_left_click = 0 self._measure_right_click = 0 self.mode_assign_map = {KN2_MODE_VOLUME: (self.chg_volume, 0, 'Master Knob controls MASTER Volume', KN2_MODE_CUE), KN2_MODE_CUE: (self.chg_cue, 0, 'Master Knob controls Cue Level', KN2_MODE_VOLUME), KN2_MODE_TEMPO_COARSE: (self.chg_tempo, 3, 'Master Knob controls TEMPO Coarse', KN2_MODE_TEMPO_FINE), KN2_MODE_TEMPO_FINE: (self.chg_tempo_fine, 3, 'Master Knob controls TEMPO Fine', KN2_MODE_TEMPO_COARSE), KN2_MODE_XFADE: (self.chg_xfade, 1, 'Master Knob controls Crossfader', -1), KN2_MODE_QUANT: (self.chg_quant, 2, 'Master Knob controls Recording Quantize', KN2_MODE_CLIP_QUANT), KN2_MODE_CLIP_QUANT: (self.chg_clip_q, 2, 'Master Knob controls Clip Start Quantize', KN2_MODE_QUANT), KN2_MODE_CLIPN_HOR: (self.nav_c_hor, 4, 'Master Knob Clip View horizontally', -1), KN2_MODE_CLIPN_VER: (self.nav_c_ver, 5, 'Master Knob Clip View vertically', -1), KN2_MODE_GENERAL: (self.chg_general, -1, None, -1), KN2_P_SCALES: (self.modify_pad_scaling, -1, None, -1)} def start_up(self): self._set_mode(KN2_MODE_VOLUME) self.arrow_mode_button.send_value(1) def toggle_arrow_mode(self, value): if value > 0: self.lrmode = (self.lrmode + 1) % 4 self.arrow_mode_button.send_hue(LR_MODE_HUES[self.lrmode]) self._parent.show_message('Left/Right Buttons Control: ' + L_MODE_FUNCTION[self.lrmode] + ' / ' + R_MODE_FUNCTION[self.lrmode]) def switch_to_matrix_mode(self): if self.mode != KN2_MODE_GENERAL: self.previous_mode = self.mode self._set_mode(KN2_MODE_GENERAL) def exit_matrix_mode(self): if self.mode == KN2_MODE_GENERAL: self._set_mode(self.previous_mode) self.previous_mode = -1 def update_shift(self): if self.shift_on: self.shift_button.send_value(1) else: self.shift_button.send_value(0) def _set_mode(self, mode): if not mode in range(11): raise AssertionError self.update_shift() if mode == self.mode: return self._prev_mode = mode self.mode = mode self.switch_radio_buttons(self.mode_assign_map[self.mode][1]) message = self.mode_assign_map[self.mode][2] message != None and self._parent.show_message(message) def switch_radio_buttons(self, which): for index in range(len(self.toggle_buttons)): if index == which: self.toggle_buttons[index].send_value(1) else: self.toggle_buttons[index].send_value(0) def update(self): self.switch_radio_buttons(self.mode_assign_map[self.mode][1]) self.arrow_mode_button.send_color(LR_MODE_HUES[self.lrmode]) self.arrow_mode_button.send_value(1) def _do_main_slider(self, value, encoder): if not value in range(128): raise AssertionError if not isinstance(encoder, EncoderElement): raise AssertionError if value == 1: delta = 1 else: delta = -1 if self.pad_to_mainknob_mode != 0: self.mode_assign_map[KN2_P_SCALES][0](delta) elif self.navflags == 0: self.mode_assign_map[self.mode][0](delta) if self.lrmode == LR_CONTROL_CLIP: self.navflags & LEFT_DOWN != 0 and self.nav_c_hor(delta) self.navflags & RIGHT_DOWN != 0 and self.nav_c_ver(delta) elif self.lrmode == LR_CONTROL_SEL: if self.navflags & LEFT_DOWN != 0: self.nav_track(delta) if self.navflags & RIGHT_DOWN != 0: self._parent.scroll_scene(delta) elif self.lrmode == LR_CONTROL_DEV: if self.navflags & LEFT_DOWN != 0: self.nav_track(delta) if self.navflags & RIGHT_DOWN != 0: self._parent.scroll_device(delta) elif self.lrmode == LR_CONTROL_LOOP: if self.navflags & LEFT_DOWN != 0: self.adjust_loop_start(delta) if self.navflags & RIGHT_DOWN != 0: self.adjust_loop_length(delta) def modify_pad_scaling(self, delta): if self.pad_to_mainknob_mode & PAD_KNOB_OCTAVE != 0: self._parent.inc_octave(delta) if self.pad_to_mainknob_mode & PAD_KNOB_SCALE != 0: self._parent.inc_scale(delta) if self.pad_to_mainknob_mode & PAD_KNOB_BASEN != 0: self._parent.inc_base_note(delta) def adjust_loop_start(self, delta): loopval = self._parent.song().loop_start loopval += self.loop_incdex * delta if loopval < 0: loopval = 0 elif loopval > 999: loopval = 999 self._parent.song().loop_start = loopval def adjust_loop_length(self, delta): loopval = self._parent.song().loop_length loopval += self.loop_incdex * delta if loopval < self.loop_incdex: loopval = self.loop_incdex elif loopval > 999: loopval = 999 self._parent.song().loop_length = loopval def chg_general(self, delta): self._parent._scenematrix.control_handler.mod_value(delta, self.shift_on) def nav_track(self, direction): if direction == 1: self._parent._a_trk_right(1) else: self._parent._a_trk_left(1) def nav_c_hor(self, direction): self._parent.move_view_horizontal(direction) def nav_c_ver(self, direction): if direction == 1: self._parent._session.bank_up() else: self._parent._session.bank_down() def chg_volume(self, diff): if self.shift_on: self.repeat(self.master_track.mixer_device.volume, diff) else: self.master_track.mixer_device.volume.value = self.calc_new_parm(self.master_track.mixer_device.volume, diff) def chg_xfade(self, diff): if self.shift_on: self.repeat(self.master_track.mixer_device.crossfader, diff) else: self.master_track.mixer_device.crossfader.value = self.calc_new_parm(self.master_track.mixer_device.crossfader, diff) def chg_cue(self, diff): if self.shift_on: self.repeat(self.master_track.mixer_device.cue_volume, diff) else: self.master_track.mixer_device.cue_volume.value = self.calc_new_parm(self.master_track.mixer_device.cue_volume, diff) def repeat(self, parm, delta): count = 0 while count < SHIFT_INC: parm.value = self.calc_new_parm(parm, delta) count += 1 def calc_new_parm(self, parm, delta): parm_range = parm.max - parm.min int_val = int((parm.value - parm.min) / parm_range * PARM_RANGE + 0.1) inc_val = min(PARM_RANGE, max(0, int_val + delta)) return float(inc_val) / float(PARM_RANGE) * parm_range + parm.min def chg_quant(self, diff): rec_quant = self._parent.song().midi_recording_quantization index = self.get_quant_index(rec_quant) new_index = index + diff if new_index >= 0 and new_index < len(QUANT_CONST): self._parent.song().midi_recording_quantization = QUANT_CONST[new_index] self._parent.show_message(QUANT_DESCR[new_index]) def chg_clip_q(self, diff): quant = self._parent.song().clip_trigger_quantization self._parent.song().clip_trigger_quantization = max(0, min(13, quant + diff)) self._parent.show_message('Clip Quantize ' + CLIQ_DESCR[self._parent.song().clip_trigger_quantization]) def chg_tempo_fine(self, diff): if diff < 0: amount = -0.01 else: amount = 0.01 self.chg_tempo(amount) def chg_tempo(self, diff): self._parent.song().tempo = max(20, min(999, self._parent.song().tempo + diff)) def get_quant_index(self, const): for index in range(len(QUANT_CONST)): if const == QUANT_CONST[index]: return index return -1 def _action_octave(self, value): if value != 0: self.pad_to_mainknob_mode |= PAD_KNOB_OCTAVE else: self.pad_to_mainknob_mode &= ~PAD_KNOB_OCTAVE def _action_scale(self, value): if value != 0: self.pad_to_mainknob_mode |= PAD_KNOB_SCALE else: self.pad_to_mainknob_mode &= ~PAD_KNOB_SCALE def _action_base_note(self, value): if value != 0: self.pad_to_mainknob_mode |= PAD_KNOB_BASEN else: self.pad_to_mainknob_mode &= ~PAD_KNOB_BASEN def _set_volume_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.volume_button != None: self.volume_button.remove_value_listener(self._action_volume) self.volume_button = button self.volume_button != None and self.volume_button.add_value_listener(self._action_volume) def _action_volume(self, value): if not self.volume_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.mode != KN2_MODE_VOLUME and self._set_mode(KN2_MODE_VOLUME) def _set_xfade_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.xfade_button != None: self.xfade_button.remove_value_listener(self._action_xfade) self.xfade_button = button self.xfade_button != None and self.xfade_button.add_value_listener(self._action_xfade) def _action_xfade(self, value): if not self.xfade_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.mode != KN2_MODE_XFADE and self._set_mode(KN2_MODE_XFADE) def _set_swing_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.swing_button != None: self.swing_button.remove_value_listener(self._action_swing) self.swing_button = button self.swing_button != None and self.swing_button.add_value_listener(self._action_swing) def _action_swing(self, value): if not self.swing_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.mode != KN2_MODE_QUANT and self._set_mode(KN2_MODE_QUANT) def _set_tempo_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.tempo_button != None: self.tempo_button.remove_value_listener(self._action_tempo) self.tempo_button = button self.tempo_button != None and self.tempo_button.add_value_listener(self._action_tempo) def _action_tempo(self, value): if not self.tempo_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.mode != KN2_MODE_TEMPO_COARSE and self._set_mode(KN2_MODE_TEMPO_COARSE) def _set_clipn_h_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.clipn_h_button != None: self.clipn_h_button.remove_value_listener(self._action_clipnh) self.clipn_h_button = button self.clipn_h_button != None and self.clipn_h_button.add_value_listener(self._action_clipnh) def _action_clipnh(self, value): if not self.clipn_h_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.mode != KN2_MODE_CLIPN_HOR and self._set_mode(KN2_MODE_CLIPN_HOR) def _set_clipn_v_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.clipn_v_button != None: self.clipn_v_button.remove_value_listener(self._action_clipnv) self.clipn_v_button = button self.clipn_v_button != None and self.clipn_v_button.add_value_listener(self._action_clipnv) def _action_clipnv(self, value): if not self.clipn_v_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.mode != KN2_MODE_CLIPN_VER and self._set_mode(KN2_MODE_CLIPN_VER) def _set_shift_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.shift_button != None: self.shift_button.remove_value_listener(self._action_shift) self.shift_button = button self.shift_button != None and self.shift_button.add_value_listener(self._action_shift) def _action_shift(self, value): if not self.shift_button != None: raise AssertionError raise value in range(128) or AssertionError self.shift_on = value != 0 and not self.shift_on self.update_shift() def _set_scroll_mod_left_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.scroll_mod_left_button != None: self.scroll_mod_left_button.remove_value_listener(self._action_scroll_left) self.scroll_mod_left_button = button self.scroll_mod_left_button != None and self.scroll_mod_left_button.add_value_listener(self._action_scroll_left) def _set_scroll_mod_right_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.scroll_mod_right_button != None: self.scroll_mod_right_button.remove_value_listener(self._action_scroll_right) self.scroll_mod_right_button = button self.scroll_mod_right_button != None and self.scroll_mod_right_button.add_value_listener(self._action_scroll_right) def _action_scroll_left(self, value): if not self.scroll_mod_left_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.scroll_mod_left_button.send_value(1) self.navflags |= LEFT_DOWN self._measure_left_click = int(round(time.time() * 1000)) else: self.scroll_mod_left_button.send_value(0) self.navflags &= ~LEFT_DOWN clicktime = int(round(time.time() * 1000)) - self._measure_left_click if clicktime < CLICK_TIME: if self._parent._modifier_down: self._parent.modify_track_offset(-1) elif self._parent._mode == PAD_MODE: self._do_lr_as_scale_mode(-1) elif self._parent._mode == SCENE_MODE: self._parent.modify_scene_offset(-1) elif self._parent._mode == CLIP_MODE: self._parent.move_view_horizontal(-1) elif self._parent._mode == CONTROL_MODE: self._parent.move_view_horizontal(-1) def _action_scroll_right(self, value): if not self.scroll_mod_right_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.scroll_mod_right_button.send_value(1) self.navflags |= RIGHT_DOWN self._measure_right_click = int(round(time.time() * 1000)) else: self.scroll_mod_right_button.send_value(0) self.navflags &= ~RIGHT_DOWN clicktime = int(round(time.time() * 1000)) - self._measure_right_click if clicktime < CLICK_TIME: if self._parent._modifier_down: self._parent.modify_track_offset(1) elif self._parent._mode == PAD_MODE: self._do_lr_as_scale_mode(1) elif self._parent._mode == SCENE_MODE: self._parent.modify_scene_offset(1) elif self._parent._mode == CLIP_MODE: self._parent.move_view_horizontal(1) elif self._parent._mode == CONTROL_MODE: self._parent.move_view_horizontal(1) def _do_lr_as_scale_mode(self, delta): if self.pad_to_mainknob_mode == PAD_KNOB_SCALE: self._parent.inc_scale(delta) elif self.pad_to_mainknob_mode == PAD_KNOB_BASEN: self._parent.inc_base_note(delta) else: self._parent.inc_octave(delta) def _action_oct_down(self, value): if value != 0: self._parent.inc_octave(-1) def _action_oct_up(self, value): if value != 0: self._parent.inc_octave(1) def _action_scale_down(self, value): if value != 0: self._parent.inc_scale(-1) def _action_scale_up(self, value): if value != 0: self._parent.inc_scale(1) def _action_base_down(self, value): if value != 0: self._parent.inc_base_note(-1) def _action_base_up(self, value): if value != 0: self._parent.inc_base_note(1) def _set_push_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.push_button != None: self.push_button.remove_value_listener(self._action_push) self.push_button = button self.push_button != None and self.push_button.add_value_listener(self._action_push) def _action_push(self, value): if not self.push_button != None: raise AssertionError if not value in range(128): raise AssertionError next_mode = self.mode_assign_map[self.mode][3] next_mode != -1 and self._set_mode(next_mode) self.loop_div_index = self.lrmode == LR_CONTROL_LOOP and self.navflags != 0 and (self.loop_div_index + 1) % len(LOOP_KNOB_DIVISION) self._parent.show_message('Loop Selection Granularity : ' + str(LOOP_KNOB_DIVISION[self.loop_div_index]) + ' beats ') self.loop_incdex = LOOP_KNOB_DIVISION[self.loop_div_index] def remove_listener(self, control, callback): if control != None and control.value_has_listener(callback): control.remove_value_listener(callback) control.disconnect() def disconnect(self): self.remove_listener(self.the_slider, self._do_main_slider) self.remove_listener(self.arrow_mode_button, self.toggle_arrow_mode) self.remove_listener(self.volume_button, self._action_volume) self.remove_listener(self.xfade_button, self._action_xfade) self.remove_listener(self.swing_button, self._action_swing) self.remove_listener(self.clipn_h_button, self._action_clipnh) self.remove_listener(self.clipn_v_button, self._action_clipnv) self.remove_listener(self.shift_button, self._action_shift) self.remove_listener(self.scroll_mod_left_button, self._action_scroll_left) self.remove_listener(self.scroll_mod_right_button, self._action_scroll_right) self.remove_listener(self.push_button, self._action_push) self.remove_listener(self.octave_mod_button, self._action_octave) self.remove_listener(self.scale_mod_button, self._action_scale) self.remove_listener(self.basenote_mod_button, self._action_base_note) self.remove_listener(self.octave_dwn_button, self._action_oct_down) self.remove_listener(self.octave_upp_button, self._action_oct_up) self.remove_listener(self.scale_dwn_button, self._action_scale_down) self.remove_listener(self.scale_upp_button, self._action_scale_up) self.remove_listener(self.basent_dwn_button, self._action_base_down) self.remove_listener(self.basent_upp_button, self._action_base_up) self._parent = None self.master_track = None self.the_slider = None self.mode_assign_map = None
class Launchpad(ControlSurface): """ Script for Novation's Launchpad Controller """ def __init__(self, c_instance): live = Live.Application.get_application() self._live_major_version = live.get_major_version() self._live_minor_version = live.get_minor_version() self._live_bugfix_version = live.get_bugfix_version() self._mk2 = Settings.DEVICE == 'Launchpad mk2' if self._mk2: self._skin = Skin('Launchpad mk2') self._side_notes = (89, 79, 69, 59, 49, 39, 29, 19) self._drum_notes = (20, 30, 31, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126) else: self._skin = Skin('Launchpad') self._side_notes = (8, 24, 40, 56, 72, 88, 104, 120) self._drum_notes = (41, 42, 43, 44, 45, 46, 47, 57, 58, 59, 60, 61, 62, 63, 73, 74, 75, 76, 77, 78, 79, 89, 90, 91, 92, 93, 94, 95, 105, 106, 107) ControlSurface.__init__(self, c_instance) with self.component_guard(): self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True if self._mk2: self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' else: self._suggested_input_port = 'Launchpad MK2' self._suggested_output_port = 'Launchpad MK2' self._control_is_with_automap = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._wrote_user_byte = False self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143 matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [] for column in range(8): if self._mk2: # for mk2 buttons are assigned "top to bottom" midi_note = (81 - (10 * row)) + column else: midi_note = row * 16 + column button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, midi_note, self._skin.off) button.name = str(column) + '_Clip_' + str(row) + '_Button' button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) self._config_button.add_value_listener(self._config_value) top_buttons = [ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, 104 + index, self._skin.off) for index in range(8)] side_buttons = [ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, self._side_notes[index], self._skin.off) for index in range(8)] top_buttons[0].name = 'Bank_Select_Up_Button' top_buttons[1].name = 'Bank_Select_Down_Button' top_buttons[2].name = 'Bank_Select_Left_Button' top_buttons[3].name = 'Bank_Select_Right_Button' top_buttons[4].name = 'Session_Button' top_buttons[5].name = 'User1_Button' top_buttons[6].name = 'User2_Button' top_buttons[7].name = 'Mixer_Button' side_buttons[0].name = 'Vol_Button' side_buttons[1].name = 'Pan_Button' side_buttons[2].name = 'SndA_Button' side_buttons[3].name = 'SndB_Button' side_buttons[4].name = 'Stop_Button' side_buttons[5].name = 'Trk_On_Button' side_buttons[6].name = 'Solo_Button' side_buttons[7].name = 'Arm_Button' self._osd = M4LInterface() self._osd.name = "OSD" self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self._osd, self, self._skin) self._selector.name = 'Main_Modes' self._do_combine() for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component(self._selector.session_component()) self._suppress_session_highlight = False self.log_message("LaunchPad95 Loaded !") def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._do_uncombine() self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False if self._mk2: self._send_midi((240, 0, 32, 41, 2, 24, 64, 247)) # launchpad mk2 needs disconnect string sent self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None _active_instances = [] #def highlighting_session_component(self): # " Return the session component showing the ring in Live session " # return self._selector.session_component() def _combine_active_instances(): support_devices = False for instance in Launchpad._active_instances: support_devices |= (instance._device_component != None) offset = 0 for instance in Launchpad._active_instances: instance._activate_combination_mode(offset, support_devices) offset += instance._selector._session.width() _combine_active_instances = staticmethod(_combine_active_instances) def _activate_combination_mode(self, track_offset, support_devices): if(Settings.STEPSEQ__LINK_WITH_SESSION): self._selector._stepseq.link_with_step_offset(track_offset) if(Settings.SESSION__LINK): self._selector._session.link_with_track_offset(track_offset) def _do_combine(self): if (DO_COMBINE and (self not in Launchpad._active_instances)): Launchpad._active_instances.append(self) Launchpad._combine_active_instances() def _do_uncombine(self): if self in Launchpad._active_instances: Launchpad._active_instances.remove(self) if(Settings.SESSION__LINK): self._selector._session.unlink() if(Settings.STEPSEQ__LINK_WITH_SESSION): self._selector._stepseq.unlink() Launchpad._combine_active_instances() def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if self._mk2: # mk2 has different challenge and params if len(midi_bytes) == 10: if midi_bytes[:7] == (240, 0, 32, 41, 2, 24, 64): response = long(midi_bytes[7]) response += long(midi_bytes[8]) << 8 if response == Live.Application.encrypt_challenge2(self._challenge): # self.log_message("Challenge Response ok") self._suppress_send_midi = False self.set_enabled(True) else: if len(midi_bytes) == 8: if midi_bytes[1:5] == (0, 32, 41, 6): response = long(midi_bytes[5]) response += long(midi_bytes[6]) << 8 if response == Live.Application.encrypt_challenge2(self._challenge): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector.mode_index == 1: if self._selector._sub_mode_index[self._selector._mode_index] > 0: # disable midi map rebuild for instrument mode to prevent light feedback errors new_channel = self._selector.channel_for_current_mode() # self.log_message(str(new_channel)) for note in self.drum_notes: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes, optimized=None): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): if self._mk2: challenge_bytes = tuple([ self._challenge >> 8 * index & 127 for index in xrange(4) ]) self._send_midi((240, 0, 32, 41, 2, 24, 64) + challenge_bytes + (247,)) else: for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): assert (value in range(128)) if not self._wrote_user_byte: enabled = (value == 1) self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False def _button_value(self, value): assert value in range(128) def _config_value(self, value): assert value in range(128) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class NanoKontrolLP95(ControlSurface): __module__ = __name__ __doc__ = " NanoKontrolLP95 controller script " def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) #self._suppress_session_highlight = True self._suppress_send_midi = True # Turn off rebuild MIDI map until after we're done setting up Live.Base.log(time.strftime("%d.%m.%Y %H:%M:%S", time.localtime()) + "--------------= NanoKontrolLP95 log opened =--------------") # Writes message into Live's main log file. This is a ControlSurface method. with self.component_guard(): # OBJECTS self._session = None #session object self._mixer = None #mixer object self._transport = None #transport object self._last_button_time = time.time() self._io_list_index = 0 self._setup_controls() self._setup_session_control() # Setup the session object self._setup_mixer_control() # Setup the mixer object self._session.set_mixer(self._mixer) # Bind mixer to session self._setup_transport_control() # Setup transport object self._set_mode_button() self._set_normal_mode() self._track = self.song().view.selected_track self.set_highlighting_session_component(self._session) self._flash_counter = 0 self._flash_on = True for component in self.components: component.set_enabled(True) self._suppress_send_midi = True # Turn rebuild back on, once we're done setting up Live.Base.log("NanoKontrolLP95 Loaded !") def disconnect(self): """clean things up on disconnect""" if self._cycle_button != None: self._cycle_button.remove_value_listener(self._cycle_button_value) self._clear_controls() self._transport.set_stop_button(None) self._transport.set_play_button(None) self._transport.set_rec_button(None) self._solo_buttons = None self._mute_buttons = None self._arm_buttons = None self._knobs = None self._faders = None self._ff_button = None self._rwd_button = None self._play_button = None self._stop_button = None self._rec_button = None self._track_left_button = None self._track_right_button = None self._cycle_button = None self._set_button = None self._mrk_left_button = None self._mrk_right_button = None self._session = None self._mixer = None self._transport = None ControlSurface.disconnect(self) def _setup_controls(self): self._track_left_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, track_left_btn) self._track_right_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, track_right_btn) self._cycle_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, cycle_btn) self._cycle_button_active = False self._set_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, set_btn) self._mrk_left_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, mrk_left_btn) self._mrk_right_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, mrk_right_btn) self._ff_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, ff_btn) self._rwd_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, rwd_btn) self._play_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, play_btn) self._stop_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, stop_btn) self._rec_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, rec_btn) self._solo_buttons = [ButtonElement(True, MIDI_CC_TYPE, CHANNEL, track_solo_cc[index]) for index in range(num_tracks)] self._mute_buttons = [ButtonElement(True, MIDI_CC_TYPE, CHANNEL, track_mute_cc[index]) for index in range(num_tracks)] self._arm_buttons = [ButtonElement(True, MIDI_CC_TYPE, CHANNEL, track_arm_cc[index]) for index in range(num_tracks)] self._knobs = [SliderElement(MIDI_CC_TYPE, CHANNEL, mixer_knob_cc[index]) for index in range(num_tracks)] self._faders = [SliderElement(MIDI_CC_TYPE, CHANNEL, mixer_fader_cc[index]) for index in range(num_tracks)] def _setup_session_control(self): # CREATE SESSION, SET OFFSETS, BUTTONS NAVIGATION AND BUTTON MATRIX self._session = SpecialSessionComponent(num_tracks, num_scenes) #(num_tracks, num_scenes) self._session.set_offsets(0, 0) def _setup_mixer_control(self): #CREATE MIXER, SET OFFSET (SPECIALMIXERCOMPONENT USES SPECIALCHANNELSTRIP THAT ALLOWS US TO UNFOLD TRACKS WITH TRACK SELECT BUTTON) self._mixer = SpecialMixerComponent(self, num_tracks, 0, False, False) # 8 tracks, 2 returns, no EQ, no filters self._mixer.name = 'Mixer' self._mixer.set_track_offset(0) #Sets start point for mixer strip (offset from left) self._mixer.set_select_buttons(self._track_right_button,self._track_left_button) self._mixer.set_knobs(self._knobs) def _setup_transport_control(self): # CREATE TRANSPORT DEVICE self._transport = SpecialTransportComponent(self) self._transport.set_stop_button(self._stop_button) self._transport.set_play_button(self._play_button) self._transport.set_rec_button(self._rec_button) def connect_script_instances(self, instanciated_scripts): #Live.Base.log("connect_script_instances - Start") #Live.Base.log("connect_script_instances - self._control_surfaces()=" + str(self._control_surfaces())) if(linked): for control_surface in self._control_surfaces(): control_surface_type = str(control_surface) for sync_master in SYNC_TO_LIST: if(control_surface_type.count(sync_master)>0): control_surface_session = control_surface.highlighting_session_component() if control_surface_session: self._session.sync_to(control_surface_session) self._on_track_list_changed() break def _clear_controls(self): if (self._ff_button != None): self._ff_button.remove_value_listener(self._out_value) self._ff_button.turn_off() if (self._rwd_button != None): self._rwd_button.remove_value_listener(self._in_value) self._rwd_button.turn_off() if (self._set_button != None): self._set_button.remove_value_listener(self._monitor_value) self._set_button.remove_value_listener(self._dup_track_value) if (self._mrk_left_button != None): self._mrk_left_button.remove_value_listener(self._sub_in_value) self._mrk_left_button.remove_value_listener(self._new_midi_value) if (self._mrk_right_button != None): self._mrk_right_button.remove_value_listener(self._sub_out_value) self._mrk_right_button.remove_value_listener(self._new_audio_value) # SESSION resetsend_controls = [] self._mixer.send_controls = [] self._session.set_stop_track_clip_buttons(None) # MIXER self._mixer._set_send_nav(None, None) for track_index in range(num_tracks): strip = self._mixer.channel_strip(track_index) strip.set_solo_button(None) strip.set_mute_button(None) strip.set_arm_button(None) resetsend_controls.append(None) strip.set_select_button(None) for i in range(12): self._mixer.send_controls.append(None) strip.set_send_controls(tuple(self._mixer.send_controls)) self._mixer.set_resetsend_buttons(tuple(resetsend_controls)) self.log_message("Controls Cleared") def _set_mode_button(self): if self._cycle_button != None: self._cycle_button.remove_value_listener(self._cycle_button_value) self._cycle_button.add_value_listener(self._cycle_button_value) self._cycle_button.set_light(self._cycle_button_active) def _cycle_button_value(self, value): assert (value in range(128)) if self._cycle_button != None: if value is not 0: self._cycle_button_active = not self._cycle_button_active self._clear_controls() if self._cycle_button_active: self._set_alt_mode() else: self._set_normal_mode() self.update() def _set_normal_mode(self): for index in range(num_tracks): strip = self._mixer.channel_strip(index) strip.set_solo_button(self._solo_buttons[index]) strip.set_mute_button(self._mute_buttons[index]) strip.set_arm_button(self._arm_buttons[index]) strip.set_pan_control(self._knobs[index]) strip.set_volume_control(self._faders[index]) self._set_in_out_nav_listeners() self.show_message("IN/OUT SETUP - MUTE SOLO ARM") def _set_alt_mode(self): self._mixer._set_send_nav(self._ff_button, self._rwd_button) stop_track_controls = [] resetsend_controls = [] # SET SESSION TRACKSTOP, TRACK SELECT, RESET SEND KNOB for index in range(num_tracks): strip = self._mixer.channel_strip(index) strip.set_select_button(self._solo_buttons[index]) stop_track_controls.append(self._arm_buttons[index]) resetsend_controls.append(self._mute_buttons[index]) self._session.set_stop_track_clip_buttons(tuple(stop_track_controls)) self._mixer.set_resetsend_buttons(tuple(resetsend_controls)) self._mixer._update_send_index() self._set_create_track_listeners() self.show_message("TRACK CREATE DEL DUPE - SEL STOP RESET SEND") def _set_in_out_nav_listeners(self): if (self._ff_button != None): self._ff_button.add_value_listener(self._out_value) if (self._rwd_button != None): self._rwd_button.add_value_listener(self._in_value) if (self._set_button != None): self._set_button.add_value_listener(self._monitor_value) if (self._mrk_left_button != None): self._mrk_left_button.add_value_listener(self._sub_in_value) if (self._mrk_right_button != None): self._mrk_right_button.add_value_listener(self._sub_out_value) self.update() def _monitor_value(self, value): now = time.time() if(value is not 0): self._last_button_time = now else: song = self.song() if self._track in song.tracks: if now - self._last_button_time < LONG_PRESS: if not self._track.is_foldable: self._track.current_monitoring_state = (self._track.current_monitoring_state + 1) % 3 else: self._set_default_io() def _set_default_io(self): if self._track.has_midi_input: self._track.input_routing_type = list(self._track.available_input_routing_types)[0] if self._track.has_audio_output: if self._track.is_grouped: self._track.output_routing_type = list(self._track.available_output_routing_types)[2] else: self._track.output_routing_type = list(self._track.available_output_routing_types)[1] else: self._track.output_routing_type = list(self._track.available_output_routing_types)[-1] else: self._track.input_routing_type = list(self._track.available_input_routing_types)[-1] if self._track.is_grouped: self._track.output_routing_type = list(self._track.available_output_routing_types)[2] else: self._track.output_routing_type = list(self._track.available_output_routing_types)[1] self._track.input_routing_channel = list(self._track.available_input_routing_channels)[0] self._track.output_routing_channel = list(self._track.available_output_routing_channels)[0] self.show_message("TRACK: " + str(self._track.name) + ' INPUT - OUTPUT RESET ') def _in_value(self, value): if(value is not 0): routings = list(self._track.available_input_routing_types) current_routing = self._track.input_routing_type if current_routing in routings: new_index = (routings.index(current_routing) + 1) % len(routings) self._track.input_routing_type = routings[new_index] route = ' INPUT: ' + str(self._track.input_routing_type.display_name) self.show_message("TRACK: " + str(self._track.name) + route) self.update() def _out_value(self, value): if(value is not 0): routings = list(self._track.available_output_routing_types) current_routing = self._track.output_routing_type if current_routing in routings: new_index = (routings.index(current_routing) + 1) % len(routings) self._track.output_routing_type = routings[new_index] route = ' OUTPUT: ' + str(self._track.output_routing_type.display_name) self.show_message("TRACK: " + str(self._track.name) + route) self.update() def _sub_in_value(self, value): if(value is not 0): routings = list(self._track.available_input_routing_channels) current_routing = self._track.input_routing_channel if current_routing in routings: new_index = (routings.index(current_routing) + 1) % len(routings) self._track.input_routing_channel = routings[new_index] route = ' SUB_INPUT: ' + str(self._track.input_routing_channel.display_name) self.show_message("TRACK: " + str(self._track.name) + route) self.update() def _sub_out_value(self, value): if(value is not 0): routings = list(self._track.available_output_routing_channels) current_routing = self._track.output_routing_channel if current_routing in routings: new_index = (routings.index(current_routing) + 1) % len(routings) self._track.output_routing_channel = routings[new_index] route = ' SUB_OUTPUT: ' + str(self._track.output_routing_channel.display_name) self.show_message("TRACK: " + str(self._track.name) + route) self.update() def _on_selected_track_changed(self): # ALLOWS TO GRAB THE FIRST DEVICE OF A SELECTED TRACK IF THERE'S ANY ControlSurface._on_selected_track_changed(self) self._track = self.song().view.selected_track def update(self): ControlSurface.update(self) self._cycle_button.set_light(self._cycle_button_active) def _set_create_track_listeners(self): if (self._set_button != None): self._set_button.add_value_listener(self._dup_track_value) if (self._mrk_left_button != None): self._mrk_left_button.add_value_listener(self._new_midi_value) if (self._mrk_right_button != None): self._mrk_right_button.add_value_listener(self._new_audio_value) self.update() def _dup_track_value(self, value): now = time.time() if(value is not 0): self._last_button_time = now else: song = self.song() if self._track in song.tracks: if now - self._last_button_time < LONG_PRESS: song.duplicate_track(list(song.tracks).index(self._track)) else: song.delete_track(list(song.tracks).index(self._track)) def _new_audio_value(self, value): if(value is not 0): self._add_track(self.song().create_audio_track) def _new_midi_value(self, value): if(value is not 0): self._add_track(self.song().create_midi_track) def _add_track(self, func): song = self.song() index = list(song.tracks).index(self._track) + 1 if index < len(song.tracks) and index >0: track = song.tracks[index] if track.is_foldable or track.is_grouped: while index < len(song.tracks) and song.tracks[index].is_grouped: index += 1 func(index) @profile def update_display(self): super(NanoKontrolLP95, self).update_display() self._flash_counter = self._flash_counter + 1 if self._cycle_button_active == True: if(self._flash_counter % 2 == 0): if (self._flash_on == True): self._cycle_button.send_value(127) else: self._cycle_button.send_value(0) self._flash_on = not self._flash_on self._flash_counter = self._flash_counter % 4
class MaschineMk2(ControlSurface): __module__ = __name__ __doc__ = 'Control Script for Maschine Mk2 and Maschine Mikro Mk2' def __init__(self, c_instance): ControlSurface.__init__(self, c_instance, False) with self.component_guard(): self._suppress_send_midi = True self.togglecolor = (10, 30, 50, 70, 90) self.toggleindex = 0 self._c_ref = c_instance self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143 self._c_inst = c_instance is_momentary = True self._active = True self._modifier_down = False self._return_mode = 0 self._returntopad = False self._mode = CLIP_MODE self.init_slot = 0 self.init_done = False self._midi_pause_count = 0 self.nav_index = 0 self._base_note = 0 self._octave = 0.55 self._scale_select_mode = MODE_PRESS_NONE self.send_slider_index = 0 self._pad_mode = PM_OFF self._note_display_mode = ND_KEYBOARD1 self._set_suppress_rebuild_requests(True) self._scenematrix = SceneMatrix(self) self._master_knob = Mk2KnobControl(self) self._device = self._set_up_device_control() self.show_message(str('')) self.request_rebuild_midi_map() self._set_global_buttons() self._set_mode_buttons() self._setup_transport() self._set_modecontrol() self._set_up_session() self._set_up_mixer() self._set_up_timer() self._set_up_machine_knobs() self.current_scale_index = 0 self.assign_transpose(SCALES[self.current_scale_index]) self.set_highlighting_session_component(self._session) self._navigate_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 127) self._navigate_button.add_value_listener(self._do_focus_navigate) self.display_update_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 86) self.display_update_button.add_value_listener(self._a_display_update) self._set_suppress_rebuild_requests(False) self.song().view.add_detail_clip_listener(self.clip_handle) self.song().add_visible_tracks_listener(self.clip_handle) self.song().add_scenes_listener(self.clip_handle) self.application().view.add_view_focus_changed_listener(self.focus_changed) self.log_message('########## LIVE 9 MASCHINE MK2 V 1.02 #############') self._suppress_send_midi = False def _set_up_mixer(self): is_momentary = True self._mixer = MixerComponent(8) self.send_sliders = [] for track in range(8): self.send_sliders.append(SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, SEND_CC_OFF + track)) for track in range(8): strip = self._mixer.channel_strip(track) strip.set_arm_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, ARM_CC_OFF + track)) strip.set_solo_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, SOLO_CC_OFF + track)) strip.set_mute_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, MUTE_CC_OFF + track)) strip.set_volume_control(SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, LEVEL_CC_OFF + track)) strip.set_pan_control(SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, PAN_CC_OFF + track)) strip.set_select_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, SELECT_CC_OFF + track)) st = tuple([self.send_sliders[track]]) strip.set_send_controls(st) self.send_slider_toggle_button = StateButton(False, MIDI_CC_TYPE, 0, 90) self.send_slider_toggle_button.add_value_listener(self._do_toggle_send) self._session.set_mixer(self._mixer) def _set_global_buttons(self): is_momentary = True self._undo_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 85) self._undo_button.add_value_listener(self._do_undo) self._redo_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 87) self._redo_button.add_value_listener(self._do_redo) self._armsolomode_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 89) self._armsolomode_button.add_value_listener(self._do_armsolo_mode) self._fire_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 9) self._fire_button.add_value_listener(self._do_fire_button) self.track_left_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 120) self.track_right_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 121) self.track_left_button.add_value_listener(self._a_trk_left) self.track_right_button.add_value_listener(self._a_trk_right) self.test_button = StateButton(True, MIDI_CC_TYPE, 5, 60) self.note_repeat_button = StateButton(True, MIDI_CC_TYPE, 5, 61) self.test_button.add_value_listener(self.do_test) self.note_repeat_button.add_value_listener(self.do_note_repeat) self.reset_test_button = StateButton(True, MIDI_CC_TYPE, 5, 62) self.reset_test_button.add_value_listener(self.do_reset) def _set_mode_buttons(self): self.xfade_assign_button = StateButton(True, MIDI_CC_TYPE, 0, 116) self._pad_select_button = StateButton(False, MIDI_CC_TYPE, 0, 117) self._pad_solo_button = StateButton(False, MIDI_CC_TYPE, 0, 118) self._mute_button = StateButton(False, MIDI_CC_TYPE, 0, 119) self._pad_scale_up = GatedColorButton(True, MIDI_CC_TYPE, 83, 0) self._pad_scale_down = GatedColorButton(True, MIDI_CC_TYPE, 94, 16) self._pad_select_button.add_value_listener(self._do_pad_select_multi) self._mute_button.add_value_listener(self._do_mute_button) self._pad_solo_button.add_value_listener(self._do_pad_solo_multi) self.xfade_assign_button.add_value_listener(self._do_xfade_assign) self._pad_scale_up.add_value_listener(self._do_pad_note_up) self._pad_scale_down.add_value_listener(self._do_pad_note_down) def set_appointed_device(self, device): with self.component_guard(): self._device_component.set_device(device) def _set_up_device_control(self): is_momentary = True device = MaschineDeviceComponent(self) device.set_device_changed_listener(self._handle_device_changed) device.set_device_parm_listener(self._hande_device_parm_changed) param_controls = [] for index in range(8): param_controls.append(SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_CC_OFF + index)) device.set_parameter_controls(tuple(param_controls)) device.set_on_off_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_BUTTON_CC_OFF)) device.set_bank_nav_buttons(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_BUTTON_CC_OFF + 4), ButtonElement(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_BUTTON_CC_OFF + 5)) self._device_nav_button_left = StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_BUTTON_CC_OFF + 6) self._device_nav_button_right = StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_BUTTON_CC_OFF + 7) self._device_nav_button_left.add_value_listener(self._nav_value_left) self._device_nav_button_right.add_value_listener(self._nav_value_right) device.name = 'Device_Component' self.set_device_component(device) return device def _setup_transport(self): is_momentary = True transport = TransportComponent() playButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 108) stopButton = StateButton(not is_momentary, MIDI_CC_TYPE, 0, 110) recordButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 109) overdubButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 107) metrononmeButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 104) playButton.name = 'Play' stopButton.name = 'Stop' recordButton.name = 'Record' overdubButton.name = 'Overdub' metrononmeButton.name = 'Metronome' transport.set_play_button(playButton) transport.set_stop_button(stopButton) transport.set_record_button(recordButton) transport.set_overdub_button(overdubButton) transport.set_metronome_button(metrononmeButton) transport.set_nudge_buttons(StateButton(is_momentary, MIDI_CC_TYPE, 1, 51), StateButton(is_momentary, MIDI_CC_TYPE, 1, 50)) punchinbutton = ToggleButton(MIDI_CC_TYPE, 1, 52) punchoutbutton = ToggleButton(MIDI_CC_TYPE, 1, 53) punchinbutton.name = 'Punch In' punchoutbutton.name = 'Punch Out' transport.set_punch_buttons(punchinbutton, punchoutbutton) transport.set_loop_button(StateButton(is_momentary, MIDI_CC_TYPE, 1, 54)) self.transp_ff_button = ButtonElement(True, MIDI_CC_TYPE, 1, 59) self.transp_rw_button = ButtonElement(True, MIDI_CC_TYPE, 1, 58) transport.set_seek_buttons(self.transp_ff_button, self.transp_rw_button) self.xfadeKnob = SliderElement(MIDI_CC_TYPE, 1, 100) self.xfadeKnob.connect_to(self.song().master_track.mixer_device.crossfader) self.tap_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 88) self.tap_button.add_value_listener(self._do_tap_tempo) self.cue_add_delete_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 55) self.cue_prev_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 56) self.cue_next_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 57) self.cue_add_delete_button.add_value_listener(self._do_toggle_cue) self.cue_prev_button.add_value_listener(self._do_toggle_prev_cue) self.cue_next_button.add_value_listener(self._do_toggle_next_cue) def _set_up_machine_knobs(self): master_track = self.song().master_track self.master_volume = SliderElement(MIDI_CC_TYPE, 0, 40) self.prehear = SliderElement(MIDI_CC_TYPE, 0, 41) self.master_volume.connect_to(master_track.mixer_device.volume) self.prehear.connect_to(master_track.mixer_device.cue_volume) def _set_up_session(self): is_momentary = True self._session = MaschineSessionComponent() self._session.add_offset_listener(self.notify_track_scroll) nhue = COLOR_HUE_NAV self.nav_buttons = (GatedColorButton(True, MIDI_CC_TYPE, 92, nhue), GatedColorButton(True, MIDI_CC_TYPE, 81, nhue), GatedColorButton(True, MIDI_CC_TYPE, 93, nhue), GatedColorButton(True, MIDI_CC_TYPE, 91, nhue)) self._session.set_scene_bank_buttons(self.nav_buttons[0], self.nav_buttons[1]) self._session.set_track_bank_buttons(self.nav_buttons[2], self.nav_buttons[3]) self._session.set_stop_all_clips_button(StateButton(is_momentary, MIDI_CC_TYPE, 0, 111)) track_stop_buttons = [ StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, index + STOP_CC_OFF) for index in range(4) ] self._session.set_stop_track_clip_buttons(tuple(track_stop_buttons)) self._init_matrix() self._set_up_buttons() self._session._link() self._session.set_advance(STEP4) def _set_up_buttons(self): self._bmatrix = ButtonMatrixElement() for scene_index in range(4): button_row = [] scene = self._session.scene(scene_index) for track_index in range(4): button = self._matrix[scene_index][track_index] clip_slot = scene.clip_slot(track_index) clip_slot.set_launch_button(button) clip_slot.set_triggered_to_play_value(1) clip_slot.set_triggered_to_record_value(1) clip_slot.set_started_value(1) clip_slot.set_recording_value(1) clip_slot.set_stopped_value(1) self._bmatrix.add_row(tuple(button_row)) def _init_matrix(self): is_momentary = True self._button_sequence = [] self._matrix = [] for scene_index in range(4): button_row = [] for track_index in range(4): button = VarButtonElement(is_momentary, 0, scene_index, track_index, self) partner = TwinButton(is_momentary, 1, button) partner.add_value_listener(self.ox, True) button_row.append(button) self._matrix.append(tuple(button_row)) for scene_index in [3, 2, 1, 0]: for track_index in range(4): self._button_sequence.append(self._matrix[scene_index][track_index]) self._session.set_matrix(self._matrix) def set_pad_translations(self, pad_translations): ControlSurface.set_pad_translations(pad_translations) def refresh_state(self): ControlSurface.refresh_state(self) self._update_hardware() def ox(self, value, button): if not isinstance(button, TwinButton): raise AssertionError self._mode == PAD_MODE and button.fire(value) def _update_hardware(self): self._session.update() self._set_suppress_rebuild_requests(True) self._set_mode() self._master_knob.update() if self._scenematrix.soloexclusive: self._armsolomode_button.send_value(1, True) else: self._armsolomode_button.send_value(0, True) self._master_knob.start_up() self._pad_scale_up.activate() self._pad_scale_down.activate() self.current_scale_to_display() self.send_to_display(KEY_COLOR_MODES_STRINGS[self._note_display_mode], 1) for scene_index in range(4): scene = self._session.scene(scene_index) for track_index in range(4): button = self._matrix[scene_index][track_index] button.refresh() self._set_suppress_rebuild_requests(False) def get_color(self, value, track_index, scene_index): if not self._active: return if self._mode == SCENE_MODE or self._mode == CONTROL_MODE or self._pad_mode == PM_ON: element = self._scenematrix.get_element(scene_index, track_index) return element.get_color(value) elif self._mode == CLIP_MODE: scene = self._session.scene(scene_index) clip_slot = scene.clip_slot(track_index)._clip_slot cindex = 0 if value == 0: cindex = 1 if clip_slot != None: if clip_slot.has_clip: if clip_slot.clip.is_recording or clip_slot.clip.will_record_on_start: return PColor.CLIP_RECORD[cindex] if clip_slot.clip.is_playing: return PColor.CLIP_PLAY[cindex] elif clip_slot.clip.is_triggered: return PColor.CLIP_PLAY[cindex] else: return PColor.CLIP_STOPPED[cindex] elif clip_slot.will_record_on_start: return PColor.CLIP_RECORD[cindex] elif clip_slot.is_playing: return PColor.CLIP_GROUP_PLAY[cindex] elif clip_slot.controls_other_clips: return PColor.CLIP_GROUP_CONTROL[cindex] elif clip_slot.is_triggered: return PColor.CLIP_GROUP_TRIGGER[cindex] elif self._mode == PAD_MODE: button = self._matrix[scene_index][track_index] return self.get_color_by_note_mode(button.get_identifier(), value > 0) def step_key_color_mode(self): self._note_display_mode = (self._note_display_mode + 1) % len(KEY_COLOR_MODES_STRINGS) self.show_message('Pad Mode Key Color = ' + KEY_COLOR_MODES_STRINGS[self._note_display_mode]) self.send_to_display('Colors: ' + KEY_COLOR_MODES_STRINGS[self._note_display_mode], 1) if self._mode == PAD_MODE: for note_index in range(16): button = self._button_sequence[note_index] button.send_color_direct(self.get_color_by_note_mode(button.get_identifier(), False)) def get_color_by_note_mode(self, midi_note, on): if self._note_display_mode == ND_BASE_OTHER: interval = (midi_note + 12 - self._base_note) % 12 if on: return INTERVAL_COLOR_MAP[interval][0] else: return INTERVAL_COLOR_MAP[interval][1] elif on: return KEY_COLOR_MAP[midi_note % 12][0] else: return KEY_COLOR_MAP[midi_note % 12][1] def _send_midi(self, midi_bytes, **keys): self._c_ref.send_midi(midi_bytes) if self._midi_pause_count == 2: time.sleep(0.002) self._midi_pause_count = 0 else: self._midi_pause_count = self._midi_pause_count + 1 return True def clip_handle(self): if self._mode == SCENE_MODE or self._mode == CONTROL_MODE or self._modifier_down: self._scenematrix.update() def _a_display_update(self, value): if not self.display_update_button != None: raise AssertionError raise value in range(128) or AssertionError (value != 0 or not self.display_update_button.is_momentary()) and self._update_hardware() self.show_message('Maschine Display Updated') def _set_up_timer(self): self.blink_state = 1 def update_display(self): with self.component_guard(): with self._is_sending_scheduled_messages(): self._task_group.update(0.1) if self._mode == CLIP_MODE and not self._modifier_down: if self.blink_state == 0: self._session.notify(1, 0) elif self.blink_state == 1: self._session.notify(1, 1) elif self.blink_state == 3: self._session.notify(2, 0) elif self.blink_state == 4: self._session.notify(2, 1) elif self._mode == PAD_MODE: pass elif self.blink_state == 0: self._scenematrix.notify_scene_mode(1) elif self.blink_state == 2: self._scenematrix.notify_scene_mode(0) self.blink_state = (self.blink_state + 1) % 4 self.init_slot += 1 def _invoke_track_edit(self, mode): self._deassign_matrix() self._scenematrix.assign() self._scenematrix.set_mode(mode) self._pad_mode = PM_ON self.request_rebuild_midi_map() self._scenematrix.update() def _set_modecontrol(self): is_momentary = True self.scene_mode_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 112) self.clip_mode_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 113) self.pad_mode_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 114) self.control_mode_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 115) self.xfade_assign_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 116) self.scene_mode_button.add_value_listener(self._a_mode_scene) self.clip_mode_button.add_value_listener(self._a_mode_clip) self.pad_mode_button.add_value_listener(self._a_mode_pad) self.control_mode_button.add_value_listener(self._a_mode_control) def _set_mode(self, mode = None): if mode == None: mode = self._mode if mode == SCENE_MODE: self.clip_mode_button.send_value(OFF_VALUE, True) self.pad_mode_button.send_value(OFF_VALUE, True) self.control_mode_button.send_value(OFF_VALUE, True) self.scene_mode_button.send_value(ON_VALUE, True) elif mode == CLIP_MODE: self.scene_mode_button.send_value(OFF_VALUE, True) self.pad_mode_button.send_value(OFF_VALUE, True) self.control_mode_button.send_value(OFF_VALUE, True) self.clip_mode_button.send_value(ON_VALUE, True) elif mode == PAD_MODE: self.scene_mode_button.send_value(OFF_VALUE, True) self.clip_mode_button.send_value(OFF_VALUE, True) self.control_mode_button.send_value(OFF_VALUE, True) self.pad_mode_button.send_value(ON_VALUE, True) elif mode == CONTROL_MODE: self.scene_mode_button.send_value(OFF_VALUE, True) self.clip_mode_button.send_value(OFF_VALUE, True) self.pad_mode_button.send_value(OFF_VALUE, True) self.control_mode_button.send_value(ON_VALUE, True) def _reset_matrix(self): for scene_index in range(4): scene = self._session.scene(scene_index) for track_index in range(4): button = self._matrix[scene_index][track_index] clip_slot = scene.clip_slot(track_index) clip_slot.set_launch_button(button) def update_button_matrix(self): self._session.update() for scene_index in range(4): scene = self._session.scene(scene_index) for track_index in range(4): button = self._matrix[scene_index][track_index] clip_slot = scene.clip_slot(track_index) if clip_slot._clip_slot != None and clip_slot._clip_slot.clip != None: button.send_value(1, True) else: button.send_value(0, True) def _deassign_matrix(self): for scene_index in range(4): scene = self._session.scene(scene_index) for track_index in range(4): clip_slot = scene.clip_slot(track_index) clip_slot.set_launch_button(None) def _from_pad_mode(self, matrix_mode): self._mode = SCENE_MODE self._register_buttons() self._scenematrix.assign() self._scenematrix.set_mode(matrix_mode) self._set_suppress_rebuild_requests(True) self.request_rebuild_midi_map() self._scenematrix.update() self._set_suppress_rebuild_requests(False) def _enter_pad_mode(self): self._set_mode(PAD_MODE) if self._mode == CLIP_MODE: self._deassign_matrix() elif self._mode == SCENE_MODE: self._scenematrix.deassign() elif self._mode == CONTROL_MODE: self._scenematrix.deassign() self._master_knob.exit_matrix_mode() self._mode = PAD_MODE self._set_suppress_rebuild_requests(True) for row in range(4): for column in range(4): button = self._matrix[row][column] button.send_value(0, True) button.set_to_notemode(True) self._forwarding_registry[MIDI_NOTE_ON_STATUS, button.get_identifier()] = button self._forwarding_registry[MIDI_NOTE_OFF_STATUS, button.get_identifier()] = button self._set_suppress_rebuild_requests(False) def _register_buttons(self, update = False): self._set_suppress_rebuild_requests(True) for row in range(4): for column in range(4): button = self._matrix[row][column] button.set_to_notemode(False) if update: button.send_value(127, True) fwkey = [MIDI_NOTE_ON_STATUS] fwkey.append(button.get_identifier()) self._forwarding_registry[tuple(fwkey)] = button self._forwarding_registry[MIDI_NOTE_OFF_STATUS, button.get_identifier()] = button self._set_suppress_rebuild_requests(False) def _back_to_clip_mode(self): self._pad_mode = PM_OFF self._scenematrix.set_mode(SCENE_MODE_NORMAL) self._scenematrix.deassign() self._set_up_clip_matrix() def _set_up_clip_matrix(self): for row in range(4): for column in range(4): button = self._matrix[row][column] button.set_to_notemode(False) self._set_suppress_rebuild_requests(True) self.request_rebuild_midi_map() self._reset_matrix() self.update_button_matrix() self._set_suppress_rebuild_requests(False) def _enter_scene_mode(self): self._set_mode(SCENE_MODE) if self._mode == CLIP_MODE: self._deassign_matrix() elif self._mode == CONTROL_MODE: self._master_knob.exit_matrix_mode() elif self._mode == PAD_MODE: self._register_buttons() self._mode = SCENE_MODE self._scenematrix.assign() self._scenematrix.set_mode(SCENE_MODE_NORMAL) self._return_mode = SCENE_MODE_NORMAL self.request_rebuild_midi_map() def _enter_clip_mode(self): self._set_suppress_rebuild_requests(True) self._set_mode(CLIP_MODE) if self._mode == SCENE_MODE: self._scenematrix.deassign() elif self._mode == CONTROL_MODE: self._master_knob.exit_matrix_mode() self._mode = CLIP_MODE self._set_up_clip_matrix() self.request_rebuild_midi_map() self._set_suppress_rebuild_requests(False) def _enter_control_mode(self): self._set_mode(CONTROL_MODE) if self._mode == CLIP_MODE: self._deassign_matrix() elif self._mode == PAD_MODE: self._mode = CONTROL_MODE self._register_buttons() self._mode = CONTROL_MODE self._set_suppress_rebuild_requests(True) self._scenematrix.set_mode(SCENE_MODE_CONTROL) self._return_mode = SCENE_MODE_CONTROL self._scenematrix.assign() self._master_knob.switch_to_matrix_mode() self._set_suppress_rebuild_requests(False) self.request_rebuild_midi_map() self._scenematrix.update() def _a_mode_scene(self, value): if not self.scene_mode_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.show_message('SCENE MODE') self._enter_scene_mode() def _a_mode_clip(self, value): if not self.clip_mode_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.show_message('CLIP MODE') self._enter_clip_mode() def _a_mode_pad(self, value): if not self.pad_mode_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.show_message('PAD MODE') self._enter_pad_mode() def _a_mode_control(self, value): if not self.control_mode_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.show_message('CONTROL MODE') self._enter_control_mode() def _do_pad_select_multi(self, value): if not value in range(128): raise AssertionError self._modifier_down = value != 0 if self._mode == PAD_MODE or self._returntopad: value != 0 and self._from_pad_mode(SCENE_MODE_SELECT) self._returntopad = True else: self._returntopad = False self._enter_pad_mode() elif self._mode == CLIP_MODE: if value != 0: self._invoke_track_edit(SCENE_MODE_SELECT) else: self._back_to_clip_mode() elif self._mode != PAD_MODE: if value == 0: self._scenematrix.set_mode(self._return_mode) else: if self._scenematrix.in_main_mode(): self._return_mode = self._scenematrix.mode self._scenematrix.set_mode(SCENE_MODE_SELECT) def _do_mute_button(self, value): if not self._mute_button != None: raise AssertionError if not value in range(128): raise AssertionError self._modifier_down = value != 0 (self._mode == PAD_MODE or self._returntopad) and value != 0 and self._from_pad_mode(SCENE_MODE_MUTE) self._returntopad = True else: self._returntopad = False self._enter_pad_mode() elif self._mode == SCENE_MODE or self._mode == CONTROL_MODE: if value == 0: self._scenematrix.set_mode(self._return_mode) self._pad_mode = PM_OFF else: if self._scenematrix.in_main_mode(): self._return_mode = self._scenematrix.mode self._scenematrix.set_mode(SCENE_MODE_MUTE) self._pad_mode = PM_ON elif self._mode == CLIP_MODE: if value > 0: self._invoke_track_edit(SCENE_MODE_MUTE) else: self._back_to_clip_mode() self._pad_mode = PM_OFF def _do_pad_solo_multi(self, value): if not value in range(128): raise AssertionError self._modifier_down = value != 0 if self._mode == PAD_MODE or self._returntopad: value != 0 and self._from_pad_mode(SCENE_MODE_SOLO) self._returntopad = True else: self._returntopad = False self._enter_pad_mode() elif self._mode == CLIP_MODE: if value != 0: self._invoke_track_edit(SCENE_MODE_SOLO) else: self._back_to_clip_mode() elif self._mode != PAD_MODE: if value == 0: self._scenematrix.set_mode(self._return_mode) else: if self._scenematrix.in_main_mode(): self._return_mode = self._scenematrix.mode self._scenematrix.set_mode(SCENE_MODE_SOLO) def _do_xfade_assign(self, value): if not self.xfade_assign_button != None: raise AssertionError if not value in range(128): raise AssertionError (self._mode == PAD_MODE or self._returntopad) and value != 0 and self._from_pad_mode(SCENE_MODE_XFADE) self._returntopad = True else: self._returntopad = False self._enter_pad_mode() elif self._mode == CLIP_MODE: if value != 0: self._invoke_track_edit(SCENE_MODE_XFADE) else: self._back_to_clip_mode() elif self._mode == SCENE_MODE or self._mode == CONTROL_MODE: if value == 0: self._scenematrix.set_mode(self._return_mode) else: if self._scenematrix.in_main_mode(): self._return_mode = self._scenematrix.mode self._scenematrix.set_mode(SCENE_MODE_XFADE) def _do_pad_note_up(self, value): if not self._pad_scale_up != None: raise AssertionError if not value in range(128): raise AssertionError self._pad_scale_up.send_value(value, True) self._modifier_down = value != 0 (self._mode == PAD_MODE or self._returntopad) and value != 0 and self._from_pad_mode(SCENE_MODE_ARM) self._returntopad = True else: self._returntopad = False self._enter_pad_mode() elif self._mode == CLIP_MODE: self.show_message('Arm tracks with pads') if value != 0: self._invoke_track_edit(SCENE_MODE_ARM) else: self._back_to_clip_mode() elif self._mode == SCENE_MODE or self._mode == CONTROL_MODE: self.show_message('Arm tracks with pads') if value == 0: self._scenematrix.set_mode(self._return_mode) else: if self._scenematrix.in_main_mode(): self._return_mode = self._scenematrix.mode self._scenematrix.set_mode(SCENE_MODE_ARM) def _do_pad_note_down(self, value): if not self._pad_scale_down != None: raise AssertionError if not value in range(128): raise AssertionError self._pad_scale_down.send_value(value, True) self._modifier_down = value != 0 (self._mode == PAD_MODE or self._returntopad) and value != 0 and self._from_pad_mode(SCENE_MODE_STOP) self._returntopad = True else: self._returntopad = False self._enter_pad_mode() elif self._mode == CLIP_MODE: self.show_message('Stop tracks with pads') if value != 0: self._invoke_track_edit(SCENE_MODE_STOP) else: self._back_to_clip_mode() elif self._mode == SCENE_MODE or self._mode == CONTROL_MODE: self.show_message('Stop tracks with pads') if value == 0: self._scenematrix.set_mode(self._return_mode) else: if self._scenematrix.in_main_mode(): self._return_mode = self._scenematrix.mode self._scenematrix.set_mode(SCENE_MODE_STOP) def modify_track_offset(self, delta): self._scenematrix.mod_track_offset(delta) def modify_scene_offset(self, delta): self._scenematrix.mod_scene_offset(delta) def move_view_horizontal(self, delta): if delta == 1: self._session.bank_right() else: self._session.bank_left() if self._mode == CONTROL_MODE: self._scenematrix.update() def inc_octave(self, inc): scale = SCALES[self.current_scale_index] octave = scale.to_octave(self._octave) newoctave = octave + inc if newoctave < 0: newoctave = 0 elif newoctave > scale.octave_range: newoctave = scale.octave_range self._octave = scale.to_relative(newoctave, self._octave) scale = SCALES[self.current_scale_index] self.show_message(' OCTAVE ' + BASE_NOTE[self._base_note] + str(newoctave - 2) + ' to ' + scale.name) self.current_scale_to_display() def inc_base_note(self, inc): newbase = self._base_note + inc if newbase < 0: self._base_note = 0 elif newbase > 11: self._base_note = 11 else: self._base_note = newbase scale = SCALES[self.current_scale_index] self.show_message(' Base Note ' + BASE_NOTE[self._base_note] + ' to ' + scale.name) self.current_scale_to_display() def current_scale_to_display(self): scale = SCALES[self.current_scale_index] text = scale.name + ' ' + BASE_NOTE[self._base_note] + str(scale.to_octave(self._octave)) self.send_to_display(text) def inc_scale(self, inc): nr_of_scales = len(SCALES) newindex = self.current_scale_index + inc if newindex < 0: newindex = 0 elif newindex >= nr_of_scales: newindex = nr_of_scales - 1 else: self.current_scale_index += inc newscale = SCALES[self.current_scale_index] self.show_message(' PAD Scale ' + newscale.name + ' ' + BASE_NOTE[self._base_note] + str(newscale.to_octave(self._octave) - 2)) self.current_scale_to_display() def update_transpose(self): self.assign_transpose(SCALES[self.current_scale_index]) self._set_suppress_rebuild_requests(True) self.request_rebuild_midi_map() self._set_suppress_rebuild_requests(False) def set_scale(self, scale): raise isinstance(scale, PadScale) or AssertionError scale_len = len(scale.notevalues) octave = scale.to_octave(self._octave) def assign_transpose(self, scale): raise isinstance(scale, PadScale) or AssertionError scale_len = len(scale.notevalues) octave = scale.to_octave(self._octave) last_note_val = None for note_index in range(16): button = self._button_sequence[note_index] scale_index = note_index % scale_len octave_offset = note_index / scale_len note_value = scale.notevalues[scale_index] + self._base_note + octave * 12 + octave_offset * 12 if note_value < 128: last_note_val = note_value elif last_note_val != None: note_value = last_note_val button.set_send_note(note_value) if self._mode == PAD_MODE: button.send_color_direct(self.get_color_by_note_mode(note_value, False)) def do_reset(self, value): if value == 0: return for row in range(4): for column in range(4): button = self._matrix[row][column] data_byte1 = button._original_identifier button.send_midi((MIDI_CC_STATUS + 2, data_byte1, 0)) def do_test(self, value): color = self.togglecolor[self.toggleindex] self.toggleindex = (self.toggleindex + 1) % len(self.togglecolor) if value == 0: return for row in range(4): for column in range(4): button = self._matrix[row][column] self.schedule_message(1, self.dosend, (color, 127, 127, row, column)) def dosend(self, parm = None): button = self._matrix[parm[3]][parm[4]] data_byte1 = button._original_identifier button.send_midi((MIDI_CC_STATUS + 0, data_byte1, parm[0])) button.send_midi((MIDI_CC_STATUS + 1, data_byte1, parm[1])) button.send_midi((MIDI_CC_STATUS + 2, data_byte1, parm[2])) def do_note_repeat(self, value): nrvalue = 0 if value != 0: nrvalue = 1 self._c_ref.set_note_repeat_state(nrvalue) def _do_toggle_send(self, value): nr_of_tracks = len(self.song().return_tracks) if value == 0 or nr_of_tracks < 1: return prev = self.send_slider_index self.send_slider_index += 1 if self.send_slider_index >= nr_of_tracks: self.send_slider_index = 0 self.show_message(' Set Send ' + str(SENDS[self.send_slider_index])) if prev != self.send_slider_index: for track in range(8): strip = self._mixer.channel_strip(track) slider_list = [] for index in range(self.send_slider_index + 1): if index < self.send_slider_index - 1: slider_list.append(None) else: slider_list.append(self.send_sliders[track]) strip.set_send_controls(tuple(slider_list)) def _do_armsolo_mode(self, value): if not self._armsolomode_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self._scenematrix.set_armsolo_exclusive(self._armsolomode_button) def _do_fire_button(self, value): if not self._fire_button != None: raise AssertionError if not value in range(128): raise AssertionError clip_slot = (value != 0 or not self._mute_button.is_momentary()) and self.song().view.highlighted_clip_slot clip_slot and clip_slot.fire() def _do_undo(self, value): if not self._undo_button != None: raise AssertionError if not value in range(128): raise AssertionError (value != 0 or not self._undo_button.is_momentary()) and self.song().can_undo == 1 and self.song().undo() self.show_message(str('UNDO')) def _do_redo(self, value): if not self._redo_button != None: raise AssertionError if not value in range(128): raise AssertionError (value != 0 or not self._redo_button.is_momentary()) and self.song().can_redo == 1 and self.song().redo() self.show_message(str('REDO')) def _a_trk_left(self, value): if not value in range(128): raise AssertionError if value != 0: direction = self.application().view.is_view_visible('Session') and Live.Application.Application.View.NavDirection.left self.application().view.scroll_view(direction, 'Session', True) def _a_trk_right(self, value): if not value in range(128): raise AssertionError if value != 0: direction = self.application().view.is_view_visible('Session') and Live.Application.Application.View.NavDirection.right self.application().view.scroll_view(direction, 'Session', True) def _nav_value_left(self, value): if not self._device_nav_button_left != None: raise AssertionError if not value in range(128): raise AssertionError modifier_pressed = True value > 0 and (not self.application().view.is_view_visible('Detail') or not self.application().view.is_view_visible('Detail/DeviceChain')) and self.application().view.show_view('Detail') self.application().view.show_view('Detail/DeviceChain') else: direction = Live.Application.Application.View.NavDirection.left self.application().view.scroll_view(direction, 'Detail/DeviceChain', not modifier_pressed) def _nav_value_right(self, value): if not self._device_nav_button_right != None: raise AssertionError if not value in range(128): raise AssertionError modifier_pressed = value > 0 and True (not self.application().view.is_view_visible('Detail') or not self.application().view.is_view_visible('Detail/DeviceChain')) and self.application().view.show_view('Detail') self.application().view.show_view('Detail/DeviceChain') else: direction = Live.Application.Application.View.NavDirection.right self.application().view.scroll_view(direction, 'Detail/DeviceChain', not modifier_pressed) def _do_tap_tempo(self, value): if not value in range(128): raise AssertionError value > 0 and self.song().tap_tempo() def _do_toggle_cue(self, value): if not value in range(128): raise AssertionError value > 0 and self.song().set_or_delete_cue() def _do_toggle_prev_cue(self, value): if not value in range(128): raise AssertionError value > 0 and self.song().jump_to_prev_cue() def _do_toggle_next_cue(self, value): if not value in range(128): raise AssertionError value > 0 and self.song().jump_to_next_cue() def focus_changed(self): pass def _handle_device_changed(self, device): pass def _hande_device_parm_changed(self): pass def _do_focus_navigate(self, value): if not self._navigate_button != None: raise AssertionError raise value in range(128) or AssertionError self.nav_index = value != 0 and (self.nav_index + 1) % len(VIEWS_ALL) self.application().view.focus_view(VIEWS_ALL[self.nav_index]) self.show_message('Focus on : ' + str(VIEWS_ALL[self.nav_index])) def scroll_focus(self, delta): if delta == 1: self.nav_index = (self.nav_index + 1) % len(VIEWS_ALL) elif self.nav_index == 0: self.nav_index = len(VIEWS_ALL) - 1 else: self.nav_index -= 1 self.show_message('Focus on : ' + str(VIEWS_ALL[self.nav_index])) self.application().view.focus_view(VIEWS_ALL[self.nav_index]) def scroll_device(self, delta): if not (delta == 1 or delta == -1): raise AssertionError direction = delta == 1 and Live.Application.Application.View.NavDirection.right else: direction = Live.Application.Application.View.NavDirection.left self.application().view.scroll_view(direction, 'Detail/DeviceChain', True) def scroll_scene(self, delta): if not self.track_left_button != None: raise AssertionError raise delta == 1 or delta == -1 or AssertionError direction = delta == 1 and Live.Application.Application.View.NavDirection.down else: direction = Live.Application.Application.View.NavDirection.up self.application().view.scroll_view(direction, 'Session', True) def index_in_strip(self, track): for ind in range(len(self._mixer._channel_strips)): strack = self._mixer._channel_strips[ind]._track if strack == track: return ind return -1 def notify_track_scroll(self): self._scenematrix.update_control_selection() if self._mode == CONTROL_MODE: self._scenematrix.eval_matrix() self._scenematrix.fire_values() def send_to_display(self, text, grid = 0): if USE_DISPLAY == False: return if len(text) > 28: text = text[:27] msgsysex = [240, 0, 0, 102, 23, 18, min(grid, 3) * 28] filled = text.ljust(28) for c in filled: msgsysex.append(ord(c)) msgsysex.append(247) self._send_midi(tuple(msgsysex)) def send_color(self, button, hue, sat, bright): raise isinstance(button, ButtonElement) or AssertionError raise hue in range(128) or AssertionError raise sat in range(128) or AssertionError raise bright in range(128) or AssertionError data_byte1 = button._original_identifier button.send_midi((MIDI_CC_STATUS + 2, data_byte1, bright)) button.send_midi((MIDI_CC_STATUS + 1, data_byte1, sat)) button.send_midi((MIDI_CC_STATUS + 0, data_byte1, hue)) def turn_off_matrix(self): for row in range(4): for column in range(4): button = self._matrix[row][column] self.send_color(button, 2, 0, 0) button.set_to_notemode(False) def remove_listener(self, control, callback): if control != None and control.value_has_listener(callback): control.remove_value_listener(callback) control.disconnect() def disconnect(self): self.turn_off_matrix() self.scene_mode_button.send_value(0, True) self.clip_mode_button.send_value(0, True) self.pad_mode_button.send_value(0, True) self.control_mode_button.send_value(0, True) time.sleep(0.2) self._active = False self._suppress_send_midi = True self.remove_listener(self.scene_mode_button, self._a_mode_scene) self.remove_listener(self.clip_mode_button, self._a_mode_clip) self.remove_listener(self.pad_mode_button, self._a_mode_pad) self.remove_listener(self.control_mode_button, self._a_mode_control) self.remove_listener(self._undo_button, self._do_undo) self.remove_listener(self._redo_button, self._do_redo) self.remove_listener(self._armsolomode_button, self._do_armsolo_mode) self.remove_listener(self.xfade_assign_button, self._do_xfade_assign) self.remove_listener(self._fire_button, self._do_fire_button) self._session.remove_offset_listener(self.notify_track_scroll) self._mixer.disconnect() ControlSurface.disconnect(self)
class LaunchMod(ControlSurface): __module__ = __name__ __doc__ = " Script for Novation's Launchpad Controller " def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self._monomod_version = 'b994' self._host_name = 'LaunchMod' self._color_type = 'Launchpad' self.hosts = [] self._timer = 0 self.set_suppress_rebuild_requests(True) self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._wrote_user_byte = False self._control_is_with_automap = False self._challenge = (Live.Application.get_random_int(0, 400000000) & 2139062143) matrix = ButtonMatrixElement() matrix.name = 'ButtonMatrix' for row in range(8): #button_row = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column)) for column in range(8) ] button_row = [ FlashingButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column), 'Button_'+str(row)+'_'+str(column), self) for column in range(8) ] matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0) self._config_button.add_value_listener(self._config_value) top_buttons = [ FlashingButtonElement(is_momentary, MIDI_CC_TYPE, 0, (104 + index), 'Top_Button'+str(index), self) for index in range(8) ] side_buttons = [ FlashingButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index], 'Side_Button'+str(index), self) for index in range(8) ] self._setup_monobridge() self._setup_monomod() self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self) self._suppress_session_highlight = False self._suppress_send_midi = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._suppress_send_midi = True self.set_suppress_rebuild_requests(False) self.log_message("--------------= LaunchMod log opened =--------------") #Create entry in log file self.refresh_state() def _setup_monobridge(self): self._monobridge = MonoBridgeElement(self) self._monobridge.name = 'MonoBridge' def _setup_monomod(self): self._host = MonomodComponent(self) self._host.name = 'Monomod_Host' self.hosts = [self._host] def disconnect(self): self._suppress_send_midi = True self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None self.log_message("--------------= LaunchMod log closed =--------------") #Create entry in log file def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if (len(midi_bytes) == 8): if (midi_bytes[1:5] == (0, 32, 41, 6)): response = long(midi_bytes[5]) response += (long(midi_bytes[6]) << 8) if (response == Live.Application.encrypt_challenge2(self._challenge)): self._suppress_send_midi = False self.set_enabled(True) #self.refresh_state() def _send_midi(self, midi_bytes): if (not self._suppress_send_midi): ControlSurface._send_midi(self, midi_bytes) def _update_hardware(self): self._suppress_send_midi = False self._config_button.send_value(40) self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = ((self._challenge >> (8 * index)) & 127) self._send_midi((176, (17 + index), challenge_byte)) def _user_byte_value(self, value): assert (value in range(128)) enabled = (value == 1) if enabled: self._config_button.send_value(40) self._control_is_with_automap = (not enabled) for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() if (not self._wrote_user_byte): self._selector.set_mode(0) self.set_enabled(enabled) else: self._wrote_user_byte = False self.request_rebuild_midi_map() def _config_value(self, value): assert (value in range(128)) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_returns = False): if (not self._suppress_session_highlight): ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_returns) def _install_forwarding(self, control): result = False if ((not self._control_is_with_automap) or (control == self._user_byte_write_button)): result = ControlSurface._install_forwarding(self, control) return result def _translate_message(self, type, from_identifier, from_channel, to_identifier, to_channel): if (not self._control_is_with_automap): ControlSurface._translate_message(self, type, from_identifier, from_channel, to_identifier, to_channel) def update_display(self): """ Live -> Script Aka on_timer. Called every 100 ms and should be used to update display relevant parts of the controller """ for message in self._scheduled_messages: message['Delay'] -= 1 if (message['Delay'] == 0): if (message['Parameter'] != None): message['Message'](message['Parameter']) else: message['Message']() del self._scheduled_messages[self._scheduled_messages.index(message)] for callback in self._timer_callbacks: callback() self._timer = (self._timer + 1) % 256 self.flash() def flash(self): #if(self.flash_status > 0): for row in range(8): if(self._selector._side_buttons[row]._flash_state > 0): self._selector._side_buttons[row].flash(self._timer) for column in range(8): button = self._selector._matrix.get_button(column, row) if(button._flash_state > 0): button.flash(self._timer) for index in range(4): if(self._selector._nav_buttons[index]._flash_state > 0): self._selector._nav_buttons[index].flash(self._timer) if(self._selector._modes_buttons[index]._flash_state > 0): self._selector._modes_buttons[index].flash(self._timer) def allow_updates(self, allow_updates): for component in self.components: component.set_allow_update(int(allow_updates!=0))
class APCmini(ControlSurface): """ Script for Novation's Launchpad Controller """ def __init__(self, c_instance): live = Live.Application.get_application() self._live_major_version = live.get_major_version() self._live_minor_version = live.get_minor_version() self._live_bugfix_version = live.get_bugfix_version() ControlSurface.__init__(self, c_instance) #self._device_selection_follows_track_selection = True with self.component_guard(): self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._control_is_with_automap = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) # Apparently this CC is used to enable feedback, ie lighting up buttons that you pressed self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._wrote_user_byte = False self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143 matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [] for column in range(8): button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, (7-row) * 8 + column) # APCmini rows are in reverse order from Launchpad button.name = str(column) + '_Clip_' + str(row) + '_Button' button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) # Control goes through here on LP, no real equivalent in APCmini (I don't think) self._config_button.add_value_listener(self._config_value) top_buttons = [ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, 64 + index) for index in range(8)] side_buttons = [ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index]) for index in range(8)] top_buttons[0].name = 'Bank_Select_Up_Button' top_buttons[1].name = 'Bank_Select_Down_Button' top_buttons[2].name = 'Bank_Select_Left_Button' top_buttons[3].name = 'Bank_Select_Right_Button' top_buttons[4].name = 'Session_Button' top_buttons[5].name = 'User1_Button' top_buttons[6].name = 'User2_Button' top_buttons[7].name = 'Mixer_Button' side_buttons[0].name = 'Vol_Button' side_buttons[1].name = 'Pan_Button' side_buttons[2].name = 'SndA_Button' side_buttons[3].name = 'SndB_Button' side_buttons[4].name = 'Stop_Button' side_buttons[5].name = 'Trk_On_Button' side_buttons[6].name = 'Solo_Button' side_buttons[7].name = 'Arm_Button' self._osd = M4LInterface() self._osd.name = "OSD" self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self._osd, self) self._selector.name = 'Main_Modes' self._do_combine() for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component(self._selector.session_component()) self._suppress_session_highlight = False self.log_message("LaunchPad95 Loaded !") def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._do_uncombine() self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None _active_instances = [] #def highlighting_session_component(self): # " Return the session component showing the ring in Live session " # return self._selector.session_component() def _combine_active_instances(): support_devices = False for instance in APCmini._active_instances: support_devices |= (instance._device_component != None) offset = 0 for instance in APCmini._active_instances: instance._activate_combination_mode(offset, support_devices) offset += instance._selector._session.width() _combine_active_instances = staticmethod(_combine_active_instances) def _activate_combination_mode(self, track_offset, support_devices): if(Settings.STEPSEQ__LINK_WITH_SESSION): self._selector._stepseq.link_with_step_offset(track_offset) if(Settings.SESSION__LINK): self._selector._session.link_with_track_offset(track_offset) def _do_combine(self): if (DO_COMBINE and (self not in APCmini._active_instances)): APCmini._active_instances.append(self) APCmini._combine_active_instances() def _do_uncombine(self): if self in APCmini._active_instances: APCmini._active_instances.remove(self) if(Settings.SESSION__LINK): self._selector._session.unlink() if(Settings.STEPSEQ__LINK_WITH_SESSION): self._selector._stepseq.unlink() APCmini._combine_active_instances() def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): # if len(midi_bytes) == 8: # if midi_bytes[1:5] == (0, 32, 41, 6): # response = long(midi_bytes[5]) # response += long(midi_bytes[6]) << 8 # if response == Live.Application.encrypt_challenge2(self._challenge): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector.mode_index == 1: if self._selector._sub_mode_index[self._selector._mode_index] > 0: # disable midi map rebuild for instrument mode to prevent light feedback errors new_channel = self._selector.channel_for_current_mode() # self.log_message(str(new_channel)) # for note in DRUM_NOTES: # self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes, optimized=None): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False # self._send_challenge() self.set_enabled(True) # Enable anyways, without checking def _send_challenge(self): for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): assert (value in range(128)) if not self._wrote_user_byte: enabled = (value == 1) self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False def _button_value(self, value): assert value in range(128) def _config_value(self, value): assert value in range(128) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class BlockPad(ControlSurface): __module__ = __name__ __doc__ = " Script for Novation's Launchpad Controller adapted to Livid's Block controller and Monomodular by amounra " def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self._monomod_version = 'b994' self.hosts = [] self._host_name = 'BlockPad' self._color_type = 'Monochrome' self._timer = 0 is_momentary = True self._suggested_input_port = 'block (Controls)' self._suggested_output_port = 'block (Controls)' self._wrote_user_byte = False matrix = ButtonMatrixElement() for row in range(8): button_row = [ FlashingButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((column * 8) + row), 'Button_'+str(column)+'_'+str(row), self) for column in range(8) ] matrix.add_row(tuple(button_row)) knobs = [ EncoderElement(MIDI_CC_TYPE, 0, KNOB_CC[index], Live.MidiMap.MapMode.absolute) for index in range(8) ] sliders = [ EncoderElement(MIDI_CC_TYPE, 0, SLIDER_CC[index], Live.MidiMap.MapMode.absolute) for index in range(2) ] self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0) self._config_button.add_value_listener(self._config_value) top_buttons = [ FlashingButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, TOP_NOTES[index], 'Top_Button'+str(index), self) for index in range(8) ] side_buttons = [ FlashingButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index], 'Side_Button'+str(index), self) for index in range(8) ] self._setup_monobridge() self._setup_monomod() self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, tuple(knobs), tuple(sliders), self) self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self.set_enabled(True) self.log_message("--------------= BlockPad log opened =--------------") #Create entry in log file self.refresh_state() def _setup_monobridge(self): self._monobridge = MonoBridgeElement(self) self._monobridge.name = 'MonoBridge' def _setup_monomod(self): self._host = MonomodComponent(self) self._host.name = 'Monomod_Host' self.hosts = [self._host] def disconnect(self): self._suppress_send_midi = True self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None self.log_message("--------------= BlockPad log closed =--------------") #Create entry in log file def _user_byte_value(self, value): assert (value in range(128)) enabled = (value == 1) if enabled: self._config_button.send_value(40) self._control_is_with_automap = (not enabled) for control in self.controls: if isinstance(control, FlashingButtonElement): control.set_force_next_value() if (not self._wrote_user_byte): self._selector.set_mode(0) self.set_enabled(enabled) else: self._wrote_user_byte = False self.request_rebuild_midi_map() def _config_value(self, value): assert (value in range(128)) def update_display(self): """ Live -> Script Aka on_timer. Called every 100 ms and should be used to update display relevant parts of the controller """ for message in self._scheduled_messages: message['Delay'] -= 1 if (message['Delay'] == 0): if (message['Parameter'] != None): message['Message'](message['Parameter']) else: message['Message']() del self._scheduled_messages[self._scheduled_messages.index(message)] for callback in self._timer_callbacks: callback() self._timer = (self._timer + 1) % 256 if(self._timer == 0): self._selector._shift_pressed_timer = -12 self.flash() def flash(self): for row in range(8): for column in range(8): button = self._selector._matrix.get_button(column, row) if(button._flash_state > 0): button.flash(self._timer)
class MainKnobControl: __module__ = __name__ __doc__ = 'Mk2 Module for Controlling Parameters with Master Knob' def __init__(self, parent): self._parent = parent self.master_track = parent.song().master_track self.the_slider = SliderElement(MIDI_CC_TYPE, 1, 86) self.the_slider.add_value_listener(self._do_main_slider, True) self.volume_button = None self._set_volume_button(StateButton(True, MIDI_CC_TYPE, 1, 80)) self.xfade_button = None self._set_xfade_button(StateButton(True, MIDI_CC_TYPE, 1, 99)) self.swing_button = None self._set_swing_button(StateButton(True, MIDI_CC_TYPE, 1, 81)) self.mode = KN2_MODE_VOLUME self.previous_mode = -1 self.tempo_button = None self._set_tempo_button(StateButton(True, MIDI_CC_TYPE, 1, 82)) self.push_button = None self._set_push_button(StateButton(True, MIDI_CC_TYPE, 1, 87)) self.clipn_v_button = None self.clipn_h_button = None self._set_clipn_h_button(StateButton(True, MIDI_CC_TYPE, 1, 90)) self._set_clipn_v_button(StateButton(True, MIDI_CC_TYPE, 1, 91)) self.toggle_buttons = [self.volume_button, self.xfade_button, self.swing_button, self.tempo_button, self.clipn_h_button, self.clipn_v_button] self.shift_button = None self._set_shift_button(StateButton(True, MIDI_CC_TYPE, 1, 85)) self.shift_on = False self.scroll_mod_left_button = None self.scroll_mod_right_button = None self._set_scroll_mod_left_button(ButtonElement(True, MIDI_CC_TYPE, 0, 105)) self._set_scroll_mod_right_button(ButtonElement(True, MIDI_CC_TYPE, 0, 106)) self._prev_mode = KN2_MODE_VOLUME self.lrmode = LR_CONTROL_CLIP self.loop_div_index = 0 self.loop_incdex = 4.0 self.arrow_mode_button = ColorButton(True, MIDI_CC_TYPE, 30) self.arrow_mode_button.add_value_listener(self.toggle_arrow_mode) self.arrow_mode_button.send_value(1, True) self.navflags = 0 self.octave_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 70) self.octave_mod_button.add_value_listener(self._action_octave) self.scale_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 71) self.scale_mod_button.add_value_listener(self._action_scale) self.basenote_mod_button = ButtonElement(True, MIDI_CC_TYPE, 1, 72) self.basenote_mod_button.add_value_listener(self._action_base_note) self.pad_to_mainknob_mode = 0 self.octave_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 120) self.octave_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 121) self.scale_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 118) self.scale_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 119) self.basent_dwn_button = ButtonElement(True, MIDI_CC_TYPE, 3, 124) self.basent_upp_button = ButtonElement(True, MIDI_CC_TYPE, 3, 125) self.octave_dwn_button.add_value_listener(self._action_oct_down) self.octave_upp_button.add_value_listener(self._action_oct_up) self.scale_dwn_button.add_value_listener(self._action_scale_down) self.scale_upp_button.add_value_listener(self._action_scale_up) self.basent_dwn_button.add_value_listener(self._action_base_down) self.basent_upp_button.add_value_listener(self._action_base_up) self._measure_left_click = 0 self._measure_right_click = 0 self.mode_assign_map = {KN2_MODE_VOLUME: (self.chg_volume, 0, 'Master Knob controls MASTER Volume', KN2_MODE_CUE), KN2_MODE_CUE: (self.chg_cue, 0, 'Master Knob controls Cue Level', KN2_MODE_VOLUME), KN2_MODE_TEMPO_COARSE: (self.chg_tempo, 3, 'Master Knob controls TEMPO Coarse', KN2_MODE_TEMPO_FINE), KN2_MODE_TEMPO_FINE: (self.chg_tempo_fine, 3, 'Master Knob controls TEMPO Fine', KN2_MODE_TEMPO_COARSE), KN2_MODE_XFADE: (self.chg_xfade, 1, 'Master Knob controls Crossfader', -1), KN2_MODE_QUANT: (self.chg_quant, 2, 'Master Knob controls Recording Quantize', KN2_MODE_CLIP_QUANT), KN2_MODE_CLIP_QUANT: (self.chg_clip_q, 2, 'Master Knob controls Clip Start Quantize', KN2_MODE_QUANT), KN2_MODE_CLIPN_HOR: (self.nav_c_hor, 4, 'Master Knob Clip View horizontally', -1), KN2_MODE_CLIPN_VER: (self.nav_c_ver, 5, 'Master Knob Clip View vertically', -1), KN2_MODE_GENERAL: (self.chg_general, -1, None, -1), KN2_P_SCALES: (self.modify_pad_scaling, -1, None, -1)} def start_up(self): self._set_mode(KN2_MODE_VOLUME) self.arrow_mode_button.send_value(127, True) def toggle_arrow_mode(self, value): if value > 0: self.lrmode = (self.lrmode + 1) % 4 self.arrow_mode_button.send_hue(LR_MODE_HUES[self.lrmode]) self._parent.show_message('Left/Right Buttons Control: ' + L_MODE_FUNCTION[self.lrmode] + ' / ' + R_MODE_FUNCTION[self.lrmode]) def switch_to_matrix_mode(self): if self.mode != KN2_MODE_GENERAL: self.previous_mode = self.mode self._set_mode(KN2_MODE_GENERAL) def exit_matrix_mode(self): if self.mode == KN2_MODE_GENERAL: self._set_mode(self.previous_mode) self.previous_mode = -1 def update_shift(self): if self.shift_on: self.shift_button.send_value(127, True) else: self.shift_button.send_value(0, True) def _set_mode(self, mode): if not mode in range(11): raise AssertionError self.update_shift() if mode == self.mode: return self._prev_mode = mode self.mode = mode self.switch_radio_buttons(self.mode_assign_map[self.mode][1]) message = self.mode_assign_map[self.mode][2] message != None and self._parent.show_message(message) def switch_radio_buttons(self, which): for index in range(len(self.toggle_buttons)): if index == which: self.toggle_buttons[index].send_value(127, True) else: self.toggle_buttons[index].send_value(0, True) def update(self): self.switch_radio_buttons(self.mode_assign_map[self.mode][1]) self.arrow_mode_button.send_color(LR_MODE_HUES[self.lrmode]) self.arrow_mode_button.send_value(127, True) def _do_main_slider(self, value, encoder): if not value in range(128): raise AssertionError if not isinstance(encoder, EncoderElement): raise AssertionError if value == 1: delta = 1 else: delta = -1 if self.pad_to_mainknob_mode != 0: self.mode_assign_map[KN2_P_SCALES][0](delta) elif self.navflags == 0: self.mode_assign_map[self.mode][0](delta) if self.lrmode == LR_CONTROL_CLIP: self.navflags & LEFT_DOWN != 0 and self.nav_c_hor(delta) self.navflags & RIGHT_DOWN != 0 and self.nav_c_ver(delta) elif self.lrmode == LR_CONTROL_SEL: if self.navflags & LEFT_DOWN != 0: self.nav_track(delta) if self.navflags & RIGHT_DOWN != 0: self._parent.scroll_scene(delta) elif self.lrmode == LR_CONTROL_DEV: if self.navflags & LEFT_DOWN != 0: self.nav_track(delta) if self.navflags & RIGHT_DOWN != 0: self._parent.scroll_device(delta) elif self.lrmode == LR_CONTROL_LOOP: if self.navflags & LEFT_DOWN != 0: self.adjust_loop_start(delta) if self.navflags & RIGHT_DOWN != 0: self.adjust_loop_length(delta) def modify_pad_scaling(self, delta): if self.pad_to_mainknob_mode & PAD_KNOB_OCTAVE != 0: self._parent.inc_octave(delta) if self.pad_to_mainknob_mode & PAD_KNOB_SCALE != 0: self._parent.inc_scale(delta) if self.pad_to_mainknob_mode & PAD_KNOB_BASEN != 0: self._parent.inc_base_note(delta) def adjust_loop_start(self, delta): loopval = self._parent.song().loop_start loopval += self.loop_incdex * delta if loopval < 0: loopval = 0 elif loopval > 999: loopval = 999 self._parent.song().loop_start = loopval def adjust_loop_length(self, delta): loopval = self._parent.song().loop_length loopval += self.loop_incdex * delta if loopval < self.loop_incdex: loopval = self.loop_incdex elif loopval > 999: loopval = 999 self._parent.song().loop_length = loopval def chg_general(self, delta): self._parent._scenematrix.control_handler.mod_value(delta, self.shift_on) def nav_track(self, direction): if direction == 1: self._parent._a_trk_right(1) else: self._parent._a_trk_left(1) def nav_c_hor(self, direction): self._parent.move_view_horizontal(direction) def nav_c_ver(self, direction): if direction == 1: self._parent._session.bank_up() else: self._parent._session.bank_down() def chg_volume(self, diff): if self.shift_on: self.repeat(self.master_track.mixer_device.volume, diff) else: self.master_track.mixer_device.volume.value = self.calc_new_parm(self.master_track.mixer_device.volume, diff) def chg_xfade(self, diff): if self.shift_on: self.repeat(self.master_track.mixer_device.crossfader, diff) else: self.master_track.mixer_device.crossfader.value = self.calc_new_parm(self.master_track.mixer_device.crossfader, diff) def chg_cue(self, diff): if self.shift_on: self.repeat(self.master_track.mixer_device.cue_volume, diff) else: self.master_track.mixer_device.cue_volume.value = self.calc_new_parm(self.master_track.mixer_device.cue_volume, diff) def repeat(self, parm, delta): count = 0 while count < SHIFT_INC: parm.value = self.calc_new_parm(parm, delta) count += 1 def calc_new_parm(self, parm, delta): parm_range = parm.max - parm.min int_val = int((parm.value - parm.min) / parm_range * PARM_RANGE + 0.1) inc_val = min(PARM_RANGE, max(0, int_val + delta)) return float(inc_val) / float(PARM_RANGE) * parm_range + parm.min def chg_quant(self, diff): rec_quant = self._parent.song().midi_recording_quantization index = self.get_quant_index(rec_quant) new_index = index + diff if new_index >= 0 and new_index < len(QUANT_CONST): self._parent.song().midi_recording_quantization = QUANT_CONST[new_index] self._parent.show_message(QUANT_DESCR[new_index]) def chg_clip_q(self, diff): quant = self._parent.song().clip_trigger_quantization self._parent.song().clip_trigger_quantization = max(0, min(13, quant + diff)) self._parent.show_message('Clip Quantize ' + CLIQ_DESCR[self._parent.song().clip_trigger_quantization]) def chg_tempo_fine(self, diff): if diff < 0: amount = -0.01 else: amount = 0.01 self.chg_tempo(amount) def chg_tempo(self, diff): self._parent.song().tempo = max(20, min(999, self._parent.song().tempo + diff)) def get_quant_index(self, const): for index in range(len(QUANT_CONST)): if const == QUANT_CONST[index]: return index return -1 def _action_octave(self, value): if value != 0: self.pad_to_mainknob_mode |= PAD_KNOB_OCTAVE else: self.pad_to_mainknob_mode &= ~PAD_KNOB_OCTAVE def _action_scale(self, value): if value != 0: self.pad_to_mainknob_mode |= PAD_KNOB_SCALE else: self.pad_to_mainknob_mode &= ~PAD_KNOB_SCALE def _action_base_note(self, value): if value != 0: self.pad_to_mainknob_mode |= PAD_KNOB_BASEN else: self.pad_to_mainknob_mode &= ~PAD_KNOB_BASEN def _set_volume_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.volume_button != None: self.volume_button.remove_value_listener(self._action_volume) self.volume_button = button self.volume_button != None and self.volume_button.add_value_listener(self._action_volume) def _action_volume(self, value): if not self.volume_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.mode != KN2_MODE_VOLUME and self._set_mode(KN2_MODE_VOLUME) def _set_xfade_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.xfade_button != None: self.xfade_button.remove_value_listener(self._action_xfade) self.xfade_button = button self.xfade_button != None and self.xfade_button.add_value_listener(self._action_xfade) def _action_xfade(self, value): if not self.xfade_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.mode != KN2_MODE_XFADE and self._set_mode(KN2_MODE_XFADE) def _set_swing_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.swing_button != None: self.swing_button.remove_value_listener(self._action_swing) self.swing_button = button self.swing_button != None and self.swing_button.add_value_listener(self._action_swing) def _action_swing(self, value): if not self.swing_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.mode != KN2_MODE_QUANT and self._set_mode(KN2_MODE_QUANT) def _set_tempo_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.tempo_button != None: self.tempo_button.remove_value_listener(self._action_tempo) self.tempo_button = button self.tempo_button != None and self.tempo_button.add_value_listener(self._action_tempo) def _action_tempo(self, value): if not self.tempo_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.mode != KN2_MODE_TEMPO_COARSE and self._set_mode(KN2_MODE_TEMPO_COARSE) def _set_clipn_h_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.clipn_h_button != None: self.clipn_h_button.remove_value_listener(self._action_clipnh) self.clipn_h_button = button self.clipn_h_button != None and self.clipn_h_button.add_value_listener(self._action_clipnh) def _action_clipnh(self, value): if not self.clipn_h_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.mode != KN2_MODE_CLIPN_HOR and self._set_mode(KN2_MODE_CLIPN_HOR) def _set_clipn_v_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.clipn_v_button != None: self.clipn_v_button.remove_value_listener(self._action_clipnv) self.clipn_v_button = button self.clipn_v_button != None and self.clipn_v_button.add_value_listener(self._action_clipnv) def _action_clipnv(self, value): if not self.clipn_v_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.mode != KN2_MODE_CLIPN_VER and self._set_mode(KN2_MODE_CLIPN_VER) def _set_shift_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.shift_button != None: self.shift_button.remove_value_listener(self._action_shift) self.shift_button = button self.shift_button != None and self.shift_button.add_value_listener(self._action_shift) def _action_shift(self, value): if not self.shift_button != None: raise AssertionError raise value in range(128) or AssertionError self.shift_on = value != 0 and not self.shift_on self.update_shift() def _set_scroll_mod_left_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.scroll_mod_left_button != None: self.scroll_mod_left_button.remove_value_listener(self._action_scroll_left) self.scroll_mod_left_button = button self.scroll_mod_left_button != None and self.scroll_mod_left_button.add_value_listener(self._action_scroll_left) def _set_scroll_mod_right_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.scroll_mod_right_button != None: self.scroll_mod_right_button.remove_value_listener(self._action_scroll_right) self.scroll_mod_right_button = button self.scroll_mod_right_button != None and self.scroll_mod_right_button.add_value_listener(self._action_scroll_right) def _action_scroll_left(self, value): if not self.scroll_mod_left_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.scroll_mod_left_button.send_value(127, True) self.navflags |= LEFT_DOWN self._measure_left_click = int(round(time.time() * 1000)) else: self.scroll_mod_left_button.send_value(0, True) self.navflags &= ~LEFT_DOWN clicktime = int(round(time.time() * 1000)) - self._measure_left_click if clicktime < CLICK_TIME: if self._parent._modifier_down: self._parent.modify_track_offset(-1) elif self._parent._mode == PAD_MODE: self._do_lr_as_scale_mode(-1) elif self._parent._mode == SCENE_MODE: self._parent.modify_scene_offset(-1) elif self._parent._mode == CLIP_MODE: self._parent.move_view_horizontal(-1) elif self._parent._mode == CONTROL_MODE: self._parent.move_view_horizontal(-1) def _action_scroll_right(self, value): if not self.scroll_mod_right_button != None: raise AssertionError raise value in range(128) or AssertionError value != 0 and self.scroll_mod_right_button.send_value(1) self.navflags |= RIGHT_DOWN self._measure_right_click = int(round(time.time() * 1000)) else: self.scroll_mod_right_button.send_value(0, True) self.navflags &= ~RIGHT_DOWN clicktime = int(round(time.time() * 1000)) - self._measure_right_click if clicktime < CLICK_TIME: if self._parent._modifier_down: self._parent.modify_track_offset(1) elif self._parent._mode == PAD_MODE: self._do_lr_as_scale_mode(1) elif self._parent._mode == SCENE_MODE: self._parent.modify_scene_offset(1) elif self._parent._mode == CLIP_MODE: self._parent.move_view_horizontal(1) elif self._parent._mode == CONTROL_MODE: self._parent.move_view_horizontal(1) def _do_lr_as_scale_mode(self, delta): if self.pad_to_mainknob_mode == PAD_KNOB_SCALE: self._parent.inc_scale(delta) elif self.pad_to_mainknob_mode == PAD_KNOB_BASEN: self._parent.inc_base_note(delta) else: self._parent.inc_octave(delta) def _action_oct_down(self, value): if value != 0: self._parent.inc_octave(-1) def _action_oct_up(self, value): if value != 0: self._parent.inc_octave(1) def _action_scale_down(self, value): if value != 0: self._parent.inc_scale(-1) def _action_scale_up(self, value): if value != 0: self._parent.inc_scale(1) def _action_base_down(self, value): if value != 0: self._parent.inc_base_note(-1) def _action_base_up(self, value): if value != 0: self._parent.inc_base_note(1) def _set_push_button(self, button): if not (button == None or isinstance(button, ButtonElement)): raise AssertionError if self.push_button != None: self.push_button.remove_value_listener(self._action_push) self.push_button = button self.push_button != None and self.push_button.add_value_listener(self._action_push) def _action_push(self, value): if not self.push_button != None: raise AssertionError if not value in range(128): raise AssertionError next_mode = self.mode_assign_map[self.mode][3] next_mode != -1 and self._set_mode(next_mode) self.loop_div_index = self.lrmode == LR_CONTROL_LOOP and self.navflags != 0 and (self.loop_div_index + 1) % len(LOOP_KNOB_DIVISION) self._parent.show_message('Loop Selection Granularity : ' + str(LOOP_KNOB_DIVISION[self.loop_div_index]) + ' beats ') self.loop_incdex = LOOP_KNOB_DIVISION[self.loop_div_index] def remove_listener(self, control, callback): if control != None and control.value_has_listener(callback): control.remove_value_listener(callback) control.disconnect() def disconnect(self): self.remove_listener(self.the_slider, self._do_main_slider) self.remove_listener(self.arrow_mode_button, self.toggle_arrow_mode) self.remove_listener(self.volume_button, self._action_volume) self.remove_listener(self.xfade_button, self._action_xfade) self.remove_listener(self.swing_button, self._action_swing) self.remove_listener(self.clipn_h_button, self._action_clipnh) self.remove_listener(self.clipn_v_button, self._action_clipnv) self.remove_listener(self.shift_button, self._action_shift) self.remove_listener(self.scroll_mod_left_button, self._action_scroll_left) self.remove_listener(self.scroll_mod_right_button, self._action_scroll_right) self.remove_listener(self.push_button, self._action_push) self.remove_listener(self.octave_mod_button, self._action_octave) self.remove_listener(self.scale_mod_button, self._action_scale) self.remove_listener(self.basenote_mod_button, self._action_base_note) self.remove_listener(self.octave_dwn_button, self._action_oct_down) self.remove_listener(self.octave_upp_button, self._action_oct_up) self.remove_listener(self.scale_dwn_button, self._action_scale_down) self.remove_listener(self.scale_upp_button, self._action_scale_up) self.remove_listener(self.basent_dwn_button, self._action_base_down) self.remove_listener(self.basent_upp_button, self._action_base_up) self._parent = None self.master_track = None self.the_slider = None self.mode_assign_map = None
class Launchpad(ControlSurface): """ Script for Novation's Launchpad Controller """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self.set_suppress_rebuild_requests(True) self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._control_is_with_automap = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._wrote_user_byte = False self._challenge = Live.Application.get_random_int( 0, 400000000) & 2139062143 matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [] for column in range(8): button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, row * 16 + column) button.name = str(column) + '_Clip_' + str(row) + '_Button' button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0) self._config_button.add_value_listener(self._config_value) top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, 104 + index) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index]) for index in range(8) ] top_buttons[0].name = 'Bank_Select_Up_Button' top_buttons[1].name = 'Bank_Select_Down_Button' top_buttons[2].name = 'Bank_Select_Left_Button' top_buttons[3].name = 'Bank_Select_Right_Button' top_buttons[4].name = 'Session_Button' top_buttons[5].name = 'User1_Button' top_buttons[6].name = 'User2_Button' top_buttons[7].name = 'Mixer_Button' side_buttons[0].name = 'Vol_Button' side_buttons[1].name = 'Pan_Button' side_buttons[2].name = 'SndA_Button' side_buttons[3].name = 'SndB_Button' side_buttons[4].name = 'Stop_Button' side_buttons[5].name = 'Trk_On_Button' side_buttons[6].name = 'Solo_Button' side_buttons[7].name = 'Arm_Button' self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self) self._selector.name = 'Main_Modes' self._do_combine() for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self._suppress_session_highlight = False self.set_suppress_rebuild_requests(False) self.log_message("LaunchPad85 Loaded !") def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._do_uncombine() self._selector = None self._user_byte_write_button.remove_value_listener( self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None _active_instances = [] def highlighting_session_component(self): " Return the session component showing the ring in Live session " return self._selector.session_component() def _combine_active_instances(): support_devices = False for instance in Launchpad._active_instances: support_devices |= (instance._device_component != None) offset = 0 for instance in Launchpad._active_instances: instance._activate_combination_mode(offset, support_devices) offset += instance._selector._session.width() _combine_active_instances = staticmethod(_combine_active_instances) def _activate_combination_mode(self, track_offset, support_devices): if (LINK_STEPSEQ): self._selector._stepseq.link_with_step_offset(track_offset) if (LINK_SESSION): self._selector._session.link_with_track_offset(track_offset) def _do_combine(self): if (DO_COMBINE and (self not in Launchpad._active_instances)): Launchpad._active_instances.append(self) Launchpad._combine_active_instances() def _do_uncombine(self): if self in Launchpad._active_instances: Launchpad._active_instances.remove(self) if (LINK_SESSION): self._selector._session.unlink() if (LINK_STEPSEQ): self._selector._stepseq.unlink() Launchpad._combine_active_instances() def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if len(midi_bytes) == 8: if midi_bytes[1:5] == (0, 32, 41, 6): response = long(midi_bytes[5]) response += long(midi_bytes[6]) << 8 if response == Live.Application.encrypt_challenge2( self._challenge): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector.mode_index == 1: new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): assert (value in range(128)) if (not self._wrote_user_byte): enabled = (value == 1) self._control_is_with_automap = (not enabled) self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False def _button_value(self, value): assert (value in range(128)) def _config_value(self, value): assert (value in range(128)) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class MidiFighterTwister(ControlSurface): __module__ = __name__ __doc__ = "MidiFighterTwister class" def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): self.__c_instance = c_instance self.show_message('Script initiated') self.init() def init(self): # initialize listeners, caches and colors self.flush_all() self.mf_disable_bank_buttons() self.mf_init_page_config() self.mf_init_light_pages() # Start listeners to call dispatcher self.song().view.add_detail_clip_listener( self.dispatch_detail_clip_listener) self.song().add_current_song_time_listener( self.dispatch_current_song_time_listener) self.song().view.add_selected_track_listener( self.dispatch_selected_track_listener) self.device_listener_wrapper() # Initialize the sequencer buttons self.sequencer_init_buttons() self.sequencer_init_rotaries() self.init_clip_page() self.init_pad_page() self.init_device_params() def flush_all(self): for poti in range(64): for channel in range(4): self._send_midi((175 + channel, poti, 0)) def mf_init_page_config(self): # Initialize configuration parameters # Sequencer configuration self.sequencer_page_default_color = 1 self.sequencer_current_page_color_index = self.sequencer_page_default_color self.sequencer_base_default_note = 36 self.sequencer_current_selected_note = self.sequencer_base_default_note self.sequencer_clip_position_16th = None # Midi Channels self.rotary_midi_channel = 175 + 1 self.ring_midi_channel = 175 + 1 self.switch_midi_channel = 175 + 2 self.light_midi_channel = 175 + 2 # Pages cc map self.clip_page_cc = range(0, 16) self.sequencer_page_cc = range(16, 32) self.note_page_cc = range(32, 48) self.control_page_cc = range(48, 64) # Pages init color self.clip_page_colors = [1] * 16 self.sequencer_page_colors = [self.sequencer_page_default_color] * 16 self.note_page_colors = range(1, 127, 16) * 2 #self.note_page_colors = [0] * 16 self.control_page_colors = [1] * 16 # Status cache for sequencer self.switch_encoder_status_cache = [False] * 64 # List to store ButtonElements in self.switch_encoder_buttons = [False] * 64 # Status cache for rotaries self.rotary_encoder_potis = [False] * 64 def mf_init_light_pages(self): sequencer_page_map = zip(self.sequencer_page_cc, self.sequencer_page_colors) for light_encoder_cc, light_color_cc in sequencer_page_map: self._mf_set_light(light_encoder_cc, light_color_cc, False) note_page_map = zip(self.note_page_cc, self.note_page_colors) for light_encoder_cc, light_color_cc in note_page_map: self._mf_set_light(light_encoder_cc, light_color_cc, False) def _mf_set_light(self, light_encoder_cc, light_color_cc, status): # Sets color on midi channel 2 (177) end updates status cache # for sequencer to remember statuses self._send_midi((self.light_midi_channel, light_encoder_cc, light_color_cc)) self.switch_encoder_status_cache[light_encoder_cc] = status def mf_disable_bank_buttons(self): # Workaround for not sending values to track when pressing bank buttons self.padm0 = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 4, 0) self.padm1 = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 4, 1) self.padm2 = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 4, 2) self.padm3 = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 4, 3) self.padm0.add_value_listener(self.bank_buttons_dummy, identify_sender=True) self.padm1.add_value_listener(self.bank_buttons_dummy, identify_sender=True) self.padm2.add_value_listener(self.bank_buttons_dummy, identify_sender=True) self.padm3.add_value_listener(self.bank_buttons_dummy, identify_sender=True) def bank_buttons_dummy(self): pass def dispatch_detail_clip_listener(self): self.current_clip = self.song().view.highlighted_clip_slot.clip self.init_sequencer() try: if self.current_clip.is_midi_clip and not self.current_clip.notes_has_listener: # Update leds when notes are added or removed self.current_clip.add_notes_listener( self._sequencer_update_notes_to_light) self.init_sequencer() else: self.sequencer_reset_colors() except AttributeError: pass def dispatch_current_song_time_listener(self): self.sequencer_light_follows_beat() def dispatch_selected_track_listener(self): self.device_auto_select() self.device_listener_wrapper() def device_listener_wrapper(self): selected_track = self.song().view.selected_track if not selected_track.devices_has_listener(self.device_auto_select): self.song().view.selected_track.add_devices_listener( self.device_auto_select) # Sequencer def init_sequencer(self): self.sequencer_current_selected_note = self.sequencer_base_default_note self.sequencer_current_page_color_index = self.sequencer_page_default_color self._sequencer_update_notes_to_light() def sequencer_init_buttons(self): for switch_encoder_cc in self.sequencer_page_cc: self.switch_encoder_buttons[switch_encoder_cc] = ButtonElement( IS_MOMENTARY, MIDI_CC_TYPE, 1, switch_encoder_cc) self.switch_encoder_buttons[switch_encoder_cc].add_value_listener( self.sequencer_button_press, identify_sender=True) self.padl14 = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 3, 14) self.padr17 = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 3, 17) self.padl16 = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 3, 16) self.padr19 = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 3, 19) self.padl14.add_value_listener(self.sequencer_side_button_press, identify_sender=True) self.padr17.add_value_listener(self.sequencer_side_button_press, identify_sender=True) self.padl16.add_value_listener(self.sequencer_side_button_press, identify_sender=True) self.padr19.add_value_listener(self.sequencer_side_button_press, identify_sender=True) def sequencer_init_rotaries(self): for rotary_encoder_cc in self.sequencer_page_cc: self.rotary_encoder_potis[rotary_encoder_cc] = EncoderElement( MIDI_CC_TYPE, 0, rotary_encoder_cc, Live.MidiMap.MapMode.absolute) self.rotary_encoder_potis[rotary_encoder_cc].add_value_listener(self.sequencer_rotary_change, identify_sender=True) def sequencer_rotary_change(self, value, sender): try: self.current_clip except AttributeError: return False if self.current_clip.is_midi_clip: cc_value = sender._msg_identifier if value > 0: if not self.switch_encoder_status_cache[cc_value]: self._send_midi((self.light_midi_channel, cc_value, self.sequencer_current_light_on_color)) self._send_midi(( self.ring_midi_channel, cc_value, 100)) self.switch_encoder_status_cache[cc_value] = True self.current_clip.set_notes(((self.sequencer_current_selected_note, cc_value % 16 * 0.25, 0.25, value, False),)) elif value == 0: self._send_midi((self.light_midi_channel, cc_value, self.sequencer_current_page_color)) self.current_clip.remove_notes( cc_value % 16 * 0.25, self.sequencer_current_selected_note, 0.25, 1) self.switch_encoder_status_cache[cc_value] = False def sequencer_button_press(self, value, sender): try: self.current_clip except AttributeError: return False if self.current_clip.is_midi_clip: cc_value = sender._msg_identifier if value > 0: if not self.switch_encoder_status_cache[cc_value]: self._send_midi((self.light_midi_channel, cc_value, self.sequencer_current_light_on_color)) self.current_clip.set_notes((( self.sequencer_current_selected_note, cc_value % 16 * 0.25, 0.25, 100, False),)) self.switch_encoder_status_cache[cc_value] = True self._send_midi((self.ring_midi_channel, cc_value, 100)) else: self._send_midi((self.light_midi_channel, cc_value, self.sequencer_current_page_color)) self._send_midi((self.ring_midi_channel, cc_value, 0)) self.current_clip.remove_notes( cc_value % 16 * 0.25, self.sequencer_current_selected_note, 0.25, 1) self.switch_encoder_status_cache[cc_value] = False def sequencer_side_button_press(self, value, sender): try: cc_value = sender._msg_identifier if value > 0: # Note/clolor up/down if cc_value == 14 and self.sequencer_current_selected_note > 0: self.sequencer_current_selected_note = self.sequencer_current_selected_note - 1 self.sequencer_current_page_color_index = self.sequencer_current_page_color_index - 16 self._sequencer_update_notes_to_light() self.sequencer_clip_position_16th = None self.show_message("Selected Midi Note: "+str(self.sequencer_current_selected_note)) if cc_value == 17 and self.sequencer_current_selected_note < 127: self.sequencer_current_selected_note = self.sequencer_current_selected_note + 1 self.sequencer_current_page_color_index = self.sequencer_current_page_color_index + 16 self._sequencer_update_notes_to_light() self.sequencer_clip_position_16th = None self.show_message("Selected Midi Note: "+str(self.sequencer_current_selected_note)) # New/duplicate clip if cc_value == 16 and self.sequencer_current_selected_note > 0: self.duplicate_clip() if cc_value == 19 and self.sequencer_current_selected_note > 0: self.session_record() except AttributeError: pass def sequencer_light_follows_beat(self): try: if self.current_clip.is_midi_clip: if self.sequencer_clip_position_16th == None: self.sequencer_clip_position_16th = int(self.current_clip.playing_position / 0.25) if self.switch_encoder_status_cache[self.sequencer_page_cc[self.sequencer_clip_position_16th]]: self._send_midi((177, self.sequencer_page_cc[self.sequencer_clip_position_16th], self.sequencer_current_light_on_color)) else: self._send_midi((177, self.sequencer_page_cc[self.sequencer_clip_position_16th], self.sequencer_current_page_color)) elif self.sequencer_clip_position_16th != int(self.current_clip.playing_position/0.25): if self.switch_encoder_status_cache[self.sequencer_page_cc[self.sequencer_clip_position_16th]]: self._send_midi((177, self.sequencer_page_cc[self.sequencer_clip_position_16th], self.sequencer_current_light_on_color)) else: self._send_midi((177, self.sequencer_page_cc[self.sequencer_clip_position_16th], self.sequencer_current_page_color)) self.sequencer_clip_position_16th = int(self.current_clip.playing_position/0.25) self._send_midi((177, self.sequencer_page_cc[self.sequencer_clip_position_16th], self.sequencer_current_light_beat_color)) except IndexError: pass @property def sequencer_current_light_on_color(self): # light on color to be relative to page color return self.sequencer_current_page_color + 32 % 128 @property def sequencer_current_light_beat_color(self): # light on color to be relative to page color return self.sequencer_current_page_color + 64 % 128 @property def sequencer_current_page_color(self): return self.sequencer_current_page_color_index % 128 def _sequencer_get_midi_notes(self, note): # self.current_clip.get_notes(start, self.sequencer_current_selected_note, # selection_length, hight) try: return self.current_clip.get_notes(0, note, 4, 1) except AttributeError: return [] def _sequencer_update_notes_to_light(self): self.sequencer_reset_colors() notes_for_current_selected_note = self._sequencer_get_midi_notes( self.sequencer_current_selected_note) for note in notes_for_current_selected_note: light_encoder_cc = int(note[1]*4+self.sequencer_page_cc[0]) self._send_midi((self.ring_midi_channel, light_encoder_cc, note[3])) self._mf_set_light(light_encoder_cc, self.sequencer_current_light_on_color, True) def sequencer_reset_colors(self): for light_encoder_cc in self.sequencer_page_cc: self._mf_set_light(light_encoder_cc, self.sequencer_current_page_color, False) self._send_midi((self.ring_midi_channel, light_encoder_cc, 0)) def duplicate_clip(self): self.log_message("duplicate clip") #if self._clip_slot and self._clip_slot.has_clip: # slot_name = self._clip_slot.clip.name # track = self._clip_slot.canonical_parent current_track = self.song().view.selected_track current_clip_slot = self.song().view.highlighted_clip_slot self.song().duplicate_scene(list(current_track.clip_slots).index(current_clip_slot)) #new_clip = current_track.duplicate_clip_slot( # list(current_track.clip_slots).index(current_clip_slot)+1) #self.log_message(new_clip) #selected_track = self.song().view.selected_track #selected_track.duplicate_clip_slot(selected_track) def session_record(self): self.log_message("session record") #self.song().trigger_session_record() # Clip page setion def init_clip_page(self): num_tracks = 4 num_scenes = 3 self.flash_status = 1 self.Mixer = MixerComponent(4, 3) # Volencoder self.volencoders = [None for index in range(num_tracks)] for index in range(num_tracks): self.volencoders[index] = EncoderElement( MIDI_CC_TYPE, 0, index, Live.MidiMap.MapMode.absolute) self.Mixer.channel_strip(index).set_volume_control( self.volencoders[index]) # Sendencoder for index in range(num_tracks): encoder_cc_send_1 = index + num_tracks encoder_cc_send_2 = index + num_tracks * 2 send1 = EncoderElement(MIDI_CC_TYPE, 0, encoder_cc_send_1, Live.MidiMap.MapMode.absolute) send2 = EncoderElement(MIDI_CC_TYPE, 0, encoder_cc_send_2, Live.MidiMap.MapMode.absolute) self.Mixer.channel_strip(index).set_send_controls((send1, send2)) # Panencoder for index in range(num_tracks): encoder_cc_pan = index + num_tracks * 3 pan = EncoderElement(MIDI_CC_TYPE, 0, encoder_cc_pan, Live.MidiMap.MapMode.absolute) self.Mixer.channel_strip(index).set_pan_control(pan) # Arm-/selectbuttons for index in range(num_tracks): armbutton = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 1, index + 12) self.Mixer.channel_strip(index).set_arm_button(armbutton) self.Mixer.channel_strip(index).set_select_button(armbutton) # Navigation buttons self.padl11 = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 3, 11) self.padr8 = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 3, 8) self.padl10 = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 3, 10) self.padr13 = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 3, 13) self.grid = [None for index in range(num_tracks * 3)] for index in range(num_tracks * 3): self.grid[index] = ButtonElement( IS_MOMENTARY, MIDI_CC_TYPE, 1, index) self.matrix = ButtonMatrixElement() for row in range(num_scenes): button_row = [] for column in range(num_tracks): button_row.append(self.grid[column+(row*4)]) self.log_message(str(column+(row*4))) self.matrix.add_row(tuple(button_row)) self.Session = SessionComponent(num_tracks, num_scenes) self.Session.name = "Session" self.Session.set_offsets(0, 0) self.Session.set_mixer(self.Mixer) self.Session._do_show_highlight() self.set_highlighting_session_component(self.Session) self.Session.set_track_bank_buttons(self.padl11, self.padr8) self.Session.set_scene_bank_buttons(self.padr13, self.padl10) self.scene = [None for index in range(num_scenes)] for row in range(num_scenes): self.scene[row] = self.Session.scene(row) self.scene[row].name = 'Scene_'+str(row) for column in range(num_tracks): clip_slot = self.scene[row].clip_slot(column) clip_slot.name = str(column)+'_Clip_Slot'+str(row) self.scene[row].clip_slot(column).set_triggered_to_play_value(35) self.scene[row].clip_slot(column).set_stopped_value(68) self.scene[row].clip_slot(column).set_started_value(45) self.scene[row].clip_slot(column).set_triggered_to_record_value(100) self.scene[row].clip_slot(column).set_recording_value(80) for column in range(num_tracks): for row in range(num_scenes): self.scene[row].clip_slot(column).set_launch_button(self.grid[column+(row*4)]) for index in range(num_tracks*num_scenes): self.grid[index].clear_send_cache() def update_display(self): # Called every 100 ms ControlSurface.update_display(self) self.refresh_state() self.Session.set_enabled(True) self.Session.update() # Sequencer hack try: for light_encoder_cc in self.sequencer_page_cc: self._send_midi((self.ring_midi_channel, light_encoder_cc, 0)) notes_for_current_selected_note = self._sequencer_get_midi_notes( self.sequencer_current_selected_note) for note in notes_for_current_selected_note: light_encoder_cc = int(note[1]*4+self.sequencer_page_cc[0]) self._send_midi((self.ring_midi_channel, light_encoder_cc, note[3])) except AttributeError: pass # Pad Section def init_pad_page(self): self.pad_device_params() PAD_TRANSLATION = ( (0, 0, 32, 1), (1, 0, 33, 1), (2, 0, 34, 1), (3, 0, 35, 1), (0, 1, 36, 1), (1, 1, 37, 1), (2, 1, 38, 1), (3, 1, 39, 1), (0, 2, 40, 1), (1, 2, 41, 1), (2, 2, 42, 1), (3, 2, 43, 1), (0, 3, 44, 1), (1, 3, 45, 1), (2, 3, 46, 1), (3, 3, 47, 1)) self.set_pad_translations(PAD_TRANSLATION) self._device_selection_follows_track_selection = True def pad_device_params(self): device_param_controls = [] for param in self.note_page_cc[:8]: self.rotary_encoder_potis[param] = EncoderElement( MIDI_CC_TYPE, 0, param, Live.MidiMap.MapMode.absolute) self.rotary_encoder_potis[param].release_parameter() self.rotary_encoder_potis[param].send_value(0, True) self.rotary_encoder_potis[param].clear_send_cache() device_param_controls.append(self.rotary_encoder_potis[param]) device = DeviceComponent() device.name = 'Device_Component pad' device.set_parameter_controls(tuple(device_param_controls)) self.set_device_component(device) #def scrolling(self): # self.application().view.scroll_view(ndir, 'Detail/DeviceChain', True) # Live.Song.Song.View.selected_track #Live.Song.Song.View.select_device()[0] # Device parameter section def init_device_params(self): device_param_controls = [] device_bank_buttons = [] for param in self.control_page_cc[:8]: self.rotary_encoder_potis[param] = EncoderElement( MIDI_CC_TYPE, 0, param, Live.MidiMap.MapMode.absolute) self.switch_encoder_buttons[param] = ButtonElement( IS_MOMENTARY, MIDI_CC_TYPE, 1, param) self.rotary_encoder_potis[param].release_parameter() self.rotary_encoder_potis[param].send_value(0, True) self.rotary_encoder_potis[param].clear_send_cache() device_param_controls.append(self.rotary_encoder_potis[param]) device_bank_buttons.append(self.switch_encoder_buttons[param]) device = DeviceComponent() device.name = 'Device_Component' device.set_parameter_controls(tuple(device_param_controls)) device.set_bank_buttons(tuple(device_bank_buttons)) device.set_on_off_button(ButtonElement( IS_MOMENTARY, MIDI_CC_TYPE, 1, 56)) self.set_device_component(device) def device_auto_select(self): # Iterates through devices within a track and assigns the first # DrumPad device to activate the individual drum's device. # Use first device in case none is a DrumPad device. selected_track = self.song().view.selected_track devices = selected_track.devices for device in devices: if device.can_have_drum_pads: self.current_drum_device = device pad_device = self.current_drum_device.view.selected_chain.devices[0] self.song().view.select_device(pad_device) if not self.current_drum_device.view.selected_drum_pad_has_listener( self.device_update_current_note): self.current_drum_device.view.add_selected_drum_pad_listener( self.device_update_current_note) break else: self.song().view.select_device(devices[0]) def device_update_current_note(self): current_note = self.current_drum_device.view.selected_drum_pad.note self.sequencer_current_selected_note = current_note # Update light of active pad #self._send_midi((self.light_midi_channel, light_encoder_cc, 63)) try: self.current_clip = self.song().view.highlighted_clip_slot.clip self.current_clip.is_midi_clip self._sequencer_update_notes_to_light() except AttributeError: pass
class Novation_Impulse2(ControlSurface): """ Script for Novation's Impulse keyboards """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self.c_instance = c_instance with self.component_guard(): self.set_pad_translations(PAD_TRANSLATIONS) self._device_selection_follows_track_selection = True self._suggested_input_port = 'Impulse' self._suggested_output_port = 'Impulse' self._has_sliders = True self._current_midi_map = None self._display_reset_delay = -1 self._string_to_display = None self.shift_pressed = False # special alternative buttons mode. for now only mixer buttons become record buttons. later we will add something more self.alternative_buttons_mode = False self._shift_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 39) self._preview_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 41) self._master_slider = SliderElement(MIDI_CC_TYPE, 0, 8) self._shift_button.name = 'Shift_Button' self._master_slider.name = 'Master_Volume_Control' self._master_slider.add_value_listener(self._slider_value, identify_sender=True) self._preview_button.add_value_listener(self._preview_value) self._setup_mixer() self._setup_session() self._setup_transport() self._setup_device() self._setup_name_display() device_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 1, 10) mixer_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 1, 9) device_button.name = 'Encoder_Device_Mode' mixer_button.name = 'Encoder_Mixer_Mode' self._encoder_modes = EncoderModeSelector(self._device_component, self._mixer, self._next_bank_button, self._prev_bank_button, self._encoders) self._encoder_modes.set_device_mixer_buttons(device_button, mixer_button) self._shift_button.add_value_listener(self._shift_button_handler) for component in self.components: component.set_enabled(False) # attributes def alternative_buttons_mode(self): return self.alternative_buttons_mode def alternative_buttons_mode(self,value): self.log ('alternative_buttons_mode_value ' + str(value)) self.alternative_buttons_mode = value def shift_pressed(self): return self.shift_pressed def shift_pressed(self,value): self.log ('shift_pressed value ' + str(value)) self.shift_pressed = value def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(3, self._send_midi, SYSEX_START + (6, 1, 1, 1, 247)) def handle_sysex(self, midi_bytes): if midi_bytes[0:-2] == SYSEX_START + (7,) and midi_bytes[-2] != 0: self._has_sliders = midi_bytes[-2] != 25 self.schedule_message(1, self._show_startup_message) for control in self.controls: if isinstance(control, InputControlElement): control.clear_send_cache() for component in self.components: component.set_enabled(True) if self._has_sliders: self._mixer.master_strip().set_volume_control(self._master_slider) self._mixer.update() else: self._mixer.master_strip().set_volume_control(None) self._mixer.selected_strip().set_volume_control(self._master_slider) for index in range(len(self._sliders)): self._mixer.channel_strip(index).set_volume_control(None) slider = self._sliders[index] slider.release_parameter() if slider.value_has_listener(self._slider_value): slider.remove_value_listener(self._slider_value) self._encoder_modes.set_provide_volume_mode(not self._has_sliders) self.request_rebuild_midi_map() def disconnect(self): self.log('starting disconnect 1') self._name_display_data_source.set_display_string(' ') for encoder in self._encoders: encoder.remove_value_listener(self._encoder_value) self._master_slider.remove_value_listener(self._slider_value) if self._has_sliders: for slider in tuple(self._sliders): slider.remove_value_listener(self._slider_value) for button in self._strip_buttons: button.remove_value_listener(self._mixer_button_value) self._preview_button.remove_value_listener(self._preview_value) self.log('starting disconnect 3') ControlSurface.disconnect(self) self.log('starting disconnect 3') self._encoders = None self._sliders = None self._strip_buttons = None self._master_slider = None self._current_midi_map = None self._name_display = None self._prev_bank_button = None self._next_bank_button = None self._encoder_modes = None self._transport_view_modes = None self.log('starting disconnect 4') self._send_midi(SYSEX_START + (6, 0, 0, 0, 247)) self.log('starting disconnect 5') if self._shift_button != None: self._shift_button.remove_value_listener(self._shift_button_handler) self._shift_button = None self.log('starting disconnect 6') def build_midi_map(self, midi_map_handle): self._current_midi_map = midi_map_handle ControlSurface.build_midi_map(self, midi_map_handle) def update_display(self): ControlSurface.update_display(self) if self._string_to_display != None: self._name_display_data_source.set_display_string(self._string_to_display) self._string_to_display = None if self._display_reset_delay >= 0: self._display_reset_delay -= 1 if self._display_reset_delay == -1: self._show_current_track_name() def _setup_mixer(self): self.log('setup mixer') mute_solo_flip_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 0, 34) self._next_nav_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 37) self._prev_nav_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 38) self._strip_buttons = [] mute_solo_flip_button.name = 'Mute_Solo_Flip_Button' self._next_nav_button.name = 'Next_Track_Button' self._prev_nav_button.name = 'Prev_Track_Button' self._mixer = SpecialMixerComponent(self, 8, self.c_instance) self._mixer.name = 'Mixer' self._mixer.set_select_buttons(self._next_nav_button, self._prev_nav_button) self._mixer.selected_strip().name = 'Selected_Channel_Strip' self._mixer.master_strip().name = 'Master_Channel_Strip' self._mixer.master_strip().set_volume_control(self._master_slider) self._sliders = [] for index in range(8): strip = self._mixer.channel_strip(index) strip.name = 'Channel_Strip_' + str(index) strip.set_invert_mute_feedback(True) self._sliders.append(SliderElement(MIDI_CC_TYPE, 0, index)) self._sliders[-1].name = str(index) + '_Volume_Control' self._sliders[-1].set_feedback_delay(-1) self._sliders[-1].add_value_listener(self._slider_value, identify_sender=True) strip.set_volume_control(self._sliders[-1]) self._strip_buttons.append(ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 9 + index)) self._strip_buttons[-1].name = str(index) + '_Mute_Button' self._strip_buttons[-1].add_value_listener(self._mixer_button_value, identify_sender=True) self._mixer.master_strip().set_mute_button(ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 1, 17)) self._mixer.set_strip_mute_solo_buttons(tuple(self._strip_buttons), mute_solo_flip_button) #self._mixer.set_shift_button(self._shift_button) self._mixer.updateMixerButtons() self._button9 = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 9 + 8) def _setup_session(self): num_pads = len(PAD_TRANSLATIONS) self._session = SessionComponent(8, 0) self._session.name = 'Session_Control' self._session.selected_scene().name = 'Selected_Scene' self._session.set_mixer(self._mixer) # for ableton 9.1.1 and lower #self._session.set_track_banking_increment(num_pads) #self._session.set_track_bank_buttons(ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 0, 35), ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 0, 36)) # for ableton 9.1.1 and higher self._track_left_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 0, 36) self._track_right_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 0, 35) self._session.set_page_left_button(self._track_left_button) self._session.set_page_right_button(self._track_right_button) pads = [] for index in range(num_pads): pads.append(ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 60 + index)) pads[-1].name = 'Pad_' + str(index) clip_slot = self._session.selected_scene().clip_slot(index) clip_slot.set_triggered_to_play_value(GREEN_BLINK) clip_slot.set_triggered_to_record_value(RED_BLINK) clip_slot.set_stopped_value(AMBER_FULL) clip_slot.set_started_value(GREEN_FULL) clip_slot.set_recording_value(RED_FULL) clip_slot.set_launch_button(pads[-1]) clip_slot.name = str(index) + '_Selected_Clip_Slot' def _setup_transport(self): rwd_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 27) ffwd_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 28) stop_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 29) play_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 30) loop_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 31) rec_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 32) ffwd_button.name = 'FFwd_Button' rwd_button.name = 'Rwd_Button' loop_button.name = 'Loop_Button' play_button.name = 'Play_Button' stop_button.name = 'Stop_Button' rec_button.name = 'Record_Button' self._transport = ShiftableTransportComponent(self.c_instance,self._session, self, ffwd_button, rwd_button) self._transport.name = 'Transport' self._transport.set_stop_buttonOnInit(stop_button) self._transport.set_play_button(play_button) self._transport.set_record_buttonOnInit(rec_button) # self._transport.set_shift_button(self._shift_button) self._transport.set_mixer9_button(self._button9) self._transport_view_modes = TransportViewModeSelector(self,self.c_instance,self._transport, self._session, ffwd_button, rwd_button, loop_button) self._transport_view_modes.name = 'Transport_View_Modes' def _setup_device(self): encoders = [] for index in range(8): encoders.append(PeekableEncoderElement(MIDI_CC_TYPE, 1, index, Live.MidiMap.MapMode.relative_binary_offset)) encoders[-1].set_feedback_delay(-1) encoders[-1].add_value_listener(self._encoder_value, identify_sender=True) encoders[-1].name = 'Device_Control_' + str(index) self._encoders = tuple(encoders) self._prev_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 1, 12) self._next_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 1, 11) self._prev_bank_button.name = 'Device_Bank_Down_Button' self._next_bank_button.name = 'Device_Bank_Up_Button' device = DeviceComponent() device.name = 'Device_Component' self.set_device_component(device) device.set_parameter_controls(self._encoders) device.set_bank_nav_buttons(self._prev_bank_button, self._next_bank_button) def _setup_name_display(self): self._name_display = PhysicalDisplayElement(16, 1) self._name_display.name = 'Display' self._name_display.set_message_parts(SYSEX_START + (8,), (247,)) self._name_display_data_source = DisplayDataSource() self._name_display.segment(0).set_data_source(self._name_display_data_source) def _encoder_value(self, value, sender): if not sender in self._encoders: raise AssertionError if not value in range(128): raise AssertionError # display_string = self._device_component.is_enabled() and ' - ' # display_string = sender.mapped_parameter() != None and sender.mapped_parameter().name display_string = '' if self._device_component.is_enabled(): # display_string = sender.name # track = self.song().view.selected_track # display_string = str(list(tracks).index(track) + 1) pass if (sender.mapped_parameter() != None): # display_string = display_string + '-' display_string = display_string + sender.mapped_parameter().name self._set_string_to_display(display_string) def _slider_value(self, value, sender): self.log ('_slider_value ' + str(value) + ' ' +str(sender)) if not sender in tuple(self._sliders) + (self._master_slider,): raise AssertionError if not value in range(128): raise AssertionError if self._mixer.is_enabled(): display_string = ' - ' master = self.song().master_track tracks = self.song().tracks returns = self.song().return_tracks track = None if sender.mapped_parameter() != None: self.log ('1') if sender == self._master_slider: self.log ('2') # track = self._has_sliders and master if self._has_sliders: track = master else: self.log ('2.1') track = self.song().view.selected_track else: self.log ('3') track = self._mixer.channel_strip(self._sliders.index(sender))._track else: self.log ('4') track = self.song().view.selected_track self.log('track='+str(track)) if track == master: display_string = 'Master' elif track in tracks: display_string = str(list(tracks).index(track) + 1) elif track in returns: display_string = str(chr(ord('A') + list(returns).index(track))) else: # raise False or AssertionError raise AssertionError display_string += ' Volume' self._set_string_to_display(display_string) def _mixer_button_value(self, value, sender): self.log ('__mixer_button_value ' + str(value) + ' ' +str(sender)) if not value in range(128): raise AssertionError #if self._mixer.is_enabled() and value > 0: if self._mixer.is_enabled(): strip = self._mixer.channel_strip(self._strip_buttons.index(sender)) #self._string_to_display = strip != None and None self._name_display.segment(0).set_data_source(strip.track_name_data_source()) self._name_display.update() self._display_reset_delay = STANDARD_DISPLAY_DELAY else: self._set_string_to_display(' - ') # if shift_pressed XOR alternative_mode if self.shift_pressed <> self.alternative_buttons_mode: self.log("_mixer_button_value") self.log(str(value)) if (value == 0): self.select_armed_track_if_only_one() def select_armed_track_if_only_one(self): self.log("select_armed_track_if_only_one") song = self.song() armed_tracks = [] tracks = song.tracks self.log("select_armed_track_if_only_one 2") for track in tracks: if track.can_be_armed and track.arm: armed_tracks.append(track) self.log(str(len(armed_tracks))) if (len(armed_tracks) == 1): self.log("selecting the track") sel_track = armed_tracks[0] self.song().view.selected_track = sel_track self._mixer._selected_tracks = [] self._mixer._selected_tracks.append(sel_track) self._mixer.on_selected_track_changed() def _preview_value(self, value): if not value in range(128): raise AssertionError for encoder in self._encoders: encoder.set_peek_mode(value > 0) def _show_current_track_name(self): if self._name_display != None and self._mixer != None: self._string_to_display = None self._name_display.segment(0).set_data_source(self._mixer.selected_strip().track_name_data_source()) self._name_display.update() def _show_startup_message(self): self._name_display.display_message('LIVE') self._display_reset_delay = INITIAL_DISPLAY_DELAY def _set_string_to_display(self, string_to_display): if not isinstance(string_to_display, (str, unicode)): raise AssertionError self._name_display.segment(0).set_data_source(self._name_display_data_source) self._string_to_display = string_to_display self._display_reset_delay = STANDARD_DISPLAY_DELAY def _on_selected_track_changed(self): self.log('_on_selected_track_changed') ControlSurface._on_selected_track_changed(self) self._show_current_track_name() #all_tracks = self._has_sliders or self._session.tracks_to_use() all_tracks2 = self._session.tracks_to_use() selected_track = self.song().view.selected_track num_strips = self._session.width() if selected_track in all_tracks2: track_index = list(all_tracks2).index(selected_track) self.log('track_index '+ str(track_index)) new_offset = track_index - track_index % num_strips self.log('new_offset '+ str(new_offset)) if not new_offset / num_strips == int(new_offset / num_strips): raise AssertionError self._session.set_offsets(new_offset, self._session.scene_offset()) def _shift_button_handler(self, value): self.log("root shift handler : "+ str(value)) if not self._shift_button != None: raise AssertionError if not value in range(128): raise AssertionError self.log("root shift handler 2") self.shift_pressed = value > 0 # calling other handlers self._mixer._shift_button_handler(value) self._transport._shift_button_handler(value) self._transport_view_modes._shift_button_handler(value) #clip stop self.log("root shift handler 3") num_pads = len(PAD_TRANSLATIONS) pads = [] for index in range(num_pads): pads.append(ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 60 + index)) pads[-1].name = 'Pad_' + str(index) clip_slot = self._session.selected_scene().clip_slot(index) if self.shift_pressed: clip_slot.set_launch_button(None) else: clip_slot.set_launch_button(pads[index]) if self.shift_pressed: self._session.set_stop_track_clip_buttons(tuple(pads)) else: self._session.set_stop_track_clip_buttons(None) self.log("root shift handler 4") def flipAlternativeButtonMode(self): self.alternative_buttons_mode = not self.alternative_buttons_mode self.updateAlternativeButtonMode() def updateAlternativeButtonMode(self): self._mixer.updateMixerButtons() self._transport_view_modes.update() def log(self, message): pass
class TriggerFingerMX(ControlSurface): def __init__(self, *a, **k): super(TriggerFingerMX, self).__init__(*a, **k) self.show_message( "-----------------------= TriggerFingerMX v0.5 LOADING - maxcloutier13 says hi =----------------------------------------------------------" ) self.log_message( "-----------------------= TriggerFingerMX LOADING - maxcloutier13 says hi =----------------------------------------------------------" ) with self.component_guard(): self._init_controls() def _init_controls(self): #Define the controls in here for clarity #self.show_message("TFMX Debug: initControls") #Using channel 13 for some reason #Pads are set from C4 to D6 self._Pad0 = ButtonElement(True, MIDI_NOTE_TYPE, 12, 72, name='Pad0') self._Pad1 = ButtonElement(True, MIDI_NOTE_TYPE, 12, 74, name='Pad1') self._Pad2 = ButtonElement(True, MIDI_NOTE_TYPE, 12, 76, name='Pad2') self._Pad3 = ButtonElement(True, MIDI_NOTE_TYPE, 12, 77, name='Pad3') # self._Pad4 = ButtonElement(True, MIDI_NOTE_TYPE, 12, 79, name='Pad4') self._Pad5 = ButtonElement(True, MIDI_NOTE_TYPE, 12, 81, name='Pad5') self._Pad6 = ButtonElement(True, MIDI_NOTE_TYPE, 12, 83, name='Pad6') self._Pad7 = ButtonElement(True, MIDI_NOTE_TYPE, 12, 84, name='Pad7') # self._Pad8 = ButtonElement(True, MIDI_NOTE_TYPE, 12, 86, name='Pad8') self._Pad9 = ButtonElement(True, MIDI_NOTE_TYPE, 12, 88, name='Pad9') self._Pad10 = ButtonElement(True, MIDI_NOTE_TYPE, 12, 89, name='Pad10') self._Pad11 = ButtonElement(True, MIDI_NOTE_TYPE, 12, 91, name='Pad11') # self._Pad12 = ButtonElement(True, MIDI_NOTE_TYPE, 12, 93, name='Pad12') self._Pad13 = ButtonElement(True, MIDI_NOTE_TYPE, 12, 95, name='Pad13') self._Pad14 = ButtonElement(True, MIDI_NOTE_TYPE, 12, 96, name='Pad14') self._Pad15 = ButtonElement(True, MIDI_NOTE_TYPE, 12, 98, name='Pad15') #Listeners on individual pads self._Pad0.add_value_listener(self._trig_pad0, False) self._Pad1.add_value_listener(self._trig_pad1, False) self._Pad2.add_value_listener(self._trig_pad2, False) self._Pad3.add_value_listener(self._trig_pad3, False) # self._Pad4.add_value_listener(self._trig_pad4, False) self._Pad5.add_value_listener(self._trig_pad5, False) self._Pad6.add_value_listener(self._trig_pad6, False) self._Pad7.add_value_listener(self._trig_pad7, False) # self._Pad8.add_value_listener(self._trig_pad8, False) self._Pad9.add_value_listener(self._trig_pad9, False) self._Pad10.add_value_listener(self._trig_pad10, False) self._Pad11.add_value_listener(self._trig_pad11, False) # self._Pad12.add_value_listener(self._trig_pad12, False) self._Pad13.add_value_listener(self._trig_pad13, False) self._Pad14.add_value_listener(self._trig_pad14, False) self._Pad15.add_value_listener(self._trig_pad15, False) self._Pads = ButtonMatrixElement( rows=[[self._Pad0, self._Pad1, self._Pad2, self._Pad3], [self._Pad4, self._Pad5, self._Pad6, self._Pad7], [self._Pad8, self._Pad9, self._Pad10, self._Pad11], [self._Pad12, self._Pad13, self._Pad14, self._Pad15]]) #Encoders to auto-map to devices ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- self._Encoder0 = EncoderElement(MIDI_CC_TYPE, 12, 22, Live.MidiMap.MapMode.absolute, name='Encoder0') self._Encoder1 = EncoderElement(MIDI_CC_TYPE, 12, 23, Live.MidiMap.MapMode.absolute, name='Encoder1') self._Encoder2 = EncoderElement(MIDI_CC_TYPE, 12, 24, Live.MidiMap.MapMode.absolute, name='Encoder2') self._Encoder3 = EncoderElement(MIDI_CC_TYPE, 12, 25, Live.MidiMap.MapMode.absolute, name='Encoder3') self._Encoder4 = EncoderElement(MIDI_CC_TYPE, 12, 26, Live.MidiMap.MapMode.absolute, name='Encoder4') self._Encoder5 = EncoderElement(MIDI_CC_TYPE, 12, 27, Live.MidiMap.MapMode.absolute, name='Encoder5') self._Encoder6 = EncoderElement(MIDI_CC_TYPE, 12, 28, Live.MidiMap.MapMode.absolute, name='Encoder6') self._Encoder7 = EncoderElement(MIDI_CC_TYPE, 12, 29, Live.MidiMap.MapMode.absolute, name='Encoder7') self._Encoders = ButtonMatrixElement(rows=[[ self._Encoder0, self._Encoder1, self._Encoder2, self._Encoder3, self._Encoder4, self._Encoder5, self._Encoder6, self._Encoder7 ]]) #Device ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- self.log_message("Device component set") self._device = DeviceComponent( name='Device', is_enabled=False, layer=Layer(parameter_controls=self._Encoders), device_selection_follows_track_selection=True) self._device.set_enabled(True) self.set_device_component(self._device) self.show_message("TFMX Debug: initControls: Done") #--- Bottom-row #Record New def _trig_pad0(self, value): if value > 0: self._record_new() #Play/Pause def _trig_pad1(self, value): if value > 0: self._play_pause() #Stop def _trig_pad2(self, value): if value > 0: self._stop_reset() #Launch clip def _trig_pad3(self, value): if value > 0: self._launch_clip() #-- 2nd row #Set new def _trig_pad4(self, value): if value > 0: self._set_new() #-- Move left def _trig_pad5(self, value): if value > 0: self._move_track(1) #-- Move down def _trig_pad6(self, value): if value > 0: self._move_clipslot(0) #-- Move right def _trig_pad7(self, value): if value > 0: self._move_track(0) #-- 3rd-row #Stop new def _trig_pad8(self, value): if value > 0: self._stop_new() def _trig_pad9(self, value): if value > 0: self.show_message("TFMX Debug: Pad9 triggered") #-- Move Up def _trig_pad10(self, value): if value > 0: self._move_clipslot(1) def _trig_pad11(self, value): if value > 0: self.show_message("TFMX Debug: Pad11 triggered") #-- Top-row def _trig_pad12(self, value): if value > 0: self.show_message("TFMX Debug: Pad12 triggered") def _trig_pad13(self, value): if value > 0: self.show_message("TFMX Debug: Pad13 triggered") def _trig_pad14(self, value): if value > 0: self.show_message("TFMX Debug: Pad14 triggered") def _trig_pad15(self, value): if value > 0: self.show_message("TFMX Debug: Pad15 triggered") def _stop_reset(self): global play_flag if play_flag == 0: #Not playing, reset playing position to start self.song().stop_playing() play_flag = 1 else: #Playing, stops, but tells play button to start from position next time self.song().stop_playing() play_flag = 2 return def _play_pause(self): #Play/pause functionality global play_flag if self.song().is_playing == 0: #Song not playing if play_flag == 0: #Song is paused: unpause self.song().continue_playing() elif play_flag == 1: #Song is Stopped, play from selection start self.song().play_selection() play_flag = 0 else: #Song is stopped and reset, play from start self.song().start_playing() play_flag = 0 else: #Playing: pause self.song().stop_playing() def _move_clipslot(self, up): scene = self.song().view.selected_scene scenes = self.song().scenes max_scenes = len(scenes) for i in range(max_scenes): if scene == scenes[i]: #Found our guy if up == 1: self.song().view.selected_scene = scenes[i - 1] self.show_message("TFMX Debug: Up!") else: if scene == scenes[max_scenes - 1]: self.song().view.selected_scene = scenes[0] else: self.song().view.selected_scene = scenes[i + 1] self.show_message("TFMX Debug: Down!") def _move_track(self, left): #Get track and tracks track = self.song().view.selected_track tracks = self.get_all_tracks(only_visible=True) max_tracks = len(tracks) #Iterate to find current track's index. for i in range(max_tracks): if track == tracks[i]: #Found our track if left == 1: self.song().view.selected_track = tracks[i - 1] else: if track == tracks[max_tracks - 1]: self.song().view.selected_track = tracks[0] else: self.song().view.selected_track = tracks[i + 1] def get_all_tracks(self, only_visible=False): tracks = [] for track in self.song().tracks: if not only_visible or track.is_visible: tracks.append(track) #Include the master track? #NOPE tracks.append(self.song().master_track) return tracks def _launch_clip(self): global overdub_flag #self.log_message("--> Track launch! -----") #self.song().view.highlighted_clip_slot.set_fire_button_state(True) _current_slot = self.song().view.highlighted_clip_slot if _current_slot.is_playing == 0 and _current_slot.is_recording == 0: self.song().view.highlighted_clip_slot.set_fire_button_state(True) elif _current_slot.is_playing == 1 and _current_slot.is_recording == 0: self.song().overdub = 1 overdub_flag = 1 elif _current_slot.is_playing == 1 and _current_slot.is_recording == 1 and overdub_flag == 1: self.song().overdub = 0 overdub_flag = 0 else: self.song().view.highlighted_clip_slot.set_fire_button_state(True) #Position on next valid clipslot def _set_new(self): self.show_message("TFMX Debug: SetNew") #Find the first empty available clipslot and set it as highlighed self._find_empty_slot() #Stop clips then Position on next valid clipslot def _stop_new(self): self.show_message("TFMX Debug: StopNew") self.song().view.selected_track.stop_all_clips() #Position on first empty cell self._find_empty_slot() #Stop clips, position on next valid clipslot and fire record def _record_new(self): self.show_message("TFMX Debug: RecordNew") self.song().view.selected_track.stop_all_clips() #Position on first empty cell self._find_empty_slot() self.song().view.highlighted_clip_slot.set_fire_button_state(True) def _find_empty_slot(self): #Find the first empty available clipslot and set it as highlighed for clipslot in self.song().view.selected_track.clip_slots: if clipslot.has_clip == 0: self.song().view.highlighted_clip_slot = clipslot return
class MPK261MXLOOP(ControlSurface): def __init__(self, *a, **k): super(MPK261MXLOOP, self).__init__(*a, **k) self.show_message("-----------------------= MPK261MXLOOP LOADING - maxcloutier13 says hi =----------------------------------------------------------") self.log_message("-----------------------= MPK261MXLOOP LOADING - maxcloutier13 says hi =----------------------------------------------------------") with self.component_guard(): midimap = MidiMap() #Sustain pedal 1 = Play/Record/Overdub switch for live looping self._LoopRecordButton = ButtonElement(True, MIDI_CC_TYPE, 0, 64) self._LoopRecordButton.add_value_listener(self._launch_clip, False) #Sustain pedal 2 = Sustain pour l'instant ;o) #self._LoopRecordButton = ButtonElement(True, MIDI_CC_TYPE, 0, 65) #Control up/down/left/right using the daw controls self._UpButton = ButtonElement(False, MIDI_CC_TYPE, 0, 88, name='UpButton') self._DownButton = ButtonElement(False, MIDI_CC_TYPE, 0, 89, name='DownButton') self._LeftButton = ButtonElement(False, MIDI_CC_TYPE, 0, 20, name='LeftButton') self._RightButton = ButtonElement(False, MIDI_CC_TYPE, 0, 21, name='RightButton') #Listeners for the functions self._UpButton.add_value_listener(self._move_up, False) self._DownButton.add_value_listener(self._move_down, False) self._LeftButton.add_value_listener(self._move_left, False) self._RightButton.add_value_listener(self._move_right, False) #Super crude manual init for the custom buttons and faders #Control Bank A - Channel 1 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- self._Encoder0 = EncoderElement(MIDI_CC_TYPE,1,22, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder0') self._Encoder1 = EncoderElement(MIDI_CC_TYPE,1,23, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder1') self._Encoder2 = EncoderElement(MIDI_CC_TYPE,1,24, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder2') self._Encoder3 = EncoderElement(MIDI_CC_TYPE,1,25, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder3') self._Encoder4 = EncoderElement(MIDI_CC_TYPE,1,26, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder4') self._Encoder5 = EncoderElement(MIDI_CC_TYPE,1,27, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder5') self._Encoder6 = EncoderElement(MIDI_CC_TYPE,1,28, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder6') self._Encoder7 = EncoderElement(MIDI_CC_TYPE,1,29, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder7') self._ArmButton0 = ButtonElement(False, MIDI_CC_TYPE, 0, 32, name='ArmButton0') self._ArmButton1 = ButtonElement(False, MIDI_CC_TYPE, 0, 33, name='ArmButton1') self._ArmButton2 = ButtonElement(False, MIDI_CC_TYPE, 0, 34, name='ArmButton2') self._ArmButton3 = ButtonElement(False, MIDI_CC_TYPE, 0, 35, name='ArmButton3') self._ArmButton4 = ButtonElement(False, MIDI_CC_TYPE, 0, 36, name='ArmButton4') self._ArmButton5 = ButtonElement(False, MIDI_CC_TYPE, 0, 37, name='ArmButton5') self._ArmButton6 = ButtonElement(False, MIDI_CC_TYPE, 0, 38, name='ArmButton6') self._ArmButton7 = ButtonElement(False, MIDI_CC_TYPE, 0, 39, name='ArmButton7') self._VolumeSlider0 = SliderElement(MIDI_CC_TYPE, 0, 12, name='VolumeSlider0') self._VolumeSlider1 = SliderElement(MIDI_CC_TYPE, 0, 13, name='VolumeSlider1') self._VolumeSlider2 = SliderElement(MIDI_CC_TYPE, 0, 14, name='VolumeSlider2') self._VolumeSlider3 = SliderElement(MIDI_CC_TYPE, 0, 15, name='VolumeSlider3') self._VolumeSlider4 = SliderElement(MIDI_CC_TYPE, 0, 16, name='VolumeSlider4') self._VolumeSlider5 = SliderElement(MIDI_CC_TYPE, 0, 17, name='VolumeSlider5') self._VolumeSlider6 = SliderElement(MIDI_CC_TYPE, 0, 18, name='VolumeSlider6') self._VolumeSlider7 = SliderElement(MIDI_CC_TYPE, 0, 19, name='VolumeSlider7') #Control Bank B - Channel 2 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- self._Encoder8 = EncoderElement(MIDI_CC_TYPE,2,22, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder8') self._Encoder9 = EncoderElement(MIDI_CC_TYPE,2,23, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder9') self._Encoder10 = EncoderElement(MIDI_CC_TYPE,2,24, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder10') self._Encoder11 = EncoderElement(MIDI_CC_TYPE,2,25, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder11') self._Encoder12 = EncoderElement(MIDI_CC_TYPE,2,26, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder12') self._Encoder13 = EncoderElement(MIDI_CC_TYPE,2,27, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder13') self._Encoder14 = EncoderElement(MIDI_CC_TYPE,2,28, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder14') self._Encoder15 = EncoderElement(MIDI_CC_TYPE,2,29, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder15') self._ArmButton8 = ButtonElement(False, MIDI_CC_TYPE, 1, 32, name='ArmButton8') self._ArmButton9 = ButtonElement(False, MIDI_CC_TYPE, 1, 33, name='ArmButton9') self._ArmButton10 = ButtonElement(False, MIDI_CC_TYPE, 1, 34, name='ArmButton10') self._ArmButton11 = ButtonElement(False, MIDI_CC_TYPE, 1, 35, name='ArmButton11') self._ArmButton12 = ButtonElement(False, MIDI_CC_TYPE, 1, 36, name='ArmButton12') self._ArmButton13 = ButtonElement(False, MIDI_CC_TYPE, 1, 37, name='ArmButton13') self._ArmButton14 = ButtonElement(False, MIDI_CC_TYPE, 1, 38, name='ArmButton14') self._ArmButton15 = ButtonElement(False, MIDI_CC_TYPE, 1, 39, name='ArmButton15') self._VolumeSlider8 = SliderElement(MIDI_CC_TYPE, 1, 12, name='VolumeSlider8') self._VolumeSlider9 = SliderElement(MIDI_CC_TYPE, 1, 13, name='VolumeSlider9') self._VolumeSlider10 = SliderElement(MIDI_CC_TYPE, 1, 14, name='VolumeSlider10') self._VolumeSlider11 = SliderElement(MIDI_CC_TYPE, 1, 15, name='VolumeSlider11') self._VolumeSlider12 = SliderElement(MIDI_CC_TYPE, 1, 16, name='VolumeSlider12') self._VolumeSlider13 = SliderElement(MIDI_CC_TYPE, 1, 17, name='VolumeSlider13') self._VolumeSlider14 = SliderElement(MIDI_CC_TYPE, 1, 18, name='VolumeSlider14') self._VolumeSlider15 = SliderElement(MIDI_CC_TYPE, 1, 19, name='VolumeSlider15') #Control Bank C - Channel 3 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- self._Encoder16 = EncoderElement(MIDI_CC_TYPE,3,22, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder16') self._Encoder17 = EncoderElement(MIDI_CC_TYPE,3,23, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder17') self._Encoder18 = EncoderElement(MIDI_CC_TYPE,3,24, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder18') self._Encoder19 = EncoderElement(MIDI_CC_TYPE,3,25, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder19') self._Encoder20 = EncoderElement(MIDI_CC_TYPE,3,26, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder20') self._Encoder21 = EncoderElement(MIDI_CC_TYPE,3,27, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder21') self._Encoder22 = EncoderElement(MIDI_CC_TYPE,3,28, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder22') self._Encoder23 = EncoderElement(MIDI_CC_TYPE,3,29, Live.MidiMap.MapMode.relative_two_compliment, name='Encoder23') self._ArmButton16 = ButtonElement(False, MIDI_CC_TYPE, 2, 32, name='ArmButton16') self._ArmButton17 = ButtonElement(False, MIDI_CC_TYPE, 2, 33, name='ArmButton17') self._ArmButton18 = ButtonElement(False, MIDI_CC_TYPE, 2, 34, name='ArmButton18') self._ArmButton19 = ButtonElement(False, MIDI_CC_TYPE, 2, 35, name='ArmButton19') self._ArmButton20 = ButtonElement(False, MIDI_CC_TYPE, 2, 36, name='ArmButton20') self._ArmButton21 = ButtonElement(False, MIDI_CC_TYPE, 2, 37, name='ArmButton21') self._ArmButton22 = ButtonElement(False, MIDI_CC_TYPE, 2, 38, name='ArmButton22') self._ArmButton23 = ButtonElement(False, MIDI_CC_TYPE, 2, 39, name='ArmButton23') self._VolumeSlider16 = SliderElement(MIDI_CC_TYPE, 2, 12, name='VolumeSlider16') self._VolumeSlider17 = SliderElement(MIDI_CC_TYPE, 2, 13, name='VolumeSlider17') self._VolumeSlider18 = SliderElement(MIDI_CC_TYPE, 2, 14, name='VolumeSlider18') self._VolumeSlider19 = SliderElement(MIDI_CC_TYPE, 2, 15, name='VolumeSlider19') self._VolumeSlider20 = SliderElement(MIDI_CC_TYPE, 2, 16, name='VolumeSlider20') self._VolumeSlider21 = SliderElement(MIDI_CC_TYPE, 2, 17, name='VolumeSlider21') self._VolumeSlider22 = SliderElement(MIDI_CC_TYPE, 2, 18, name='VolumeSlider22') self._VolumeSlider23 = SliderElement(MIDI_CC_TYPE, 2, 19, name='VolumeSlider23') #Drum Bank A - Channel 4-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- self._Pad0 = ButtonElement(True, MIDI_NOTE_TYPE, 3, 81, name='Pad0') self._Pad1 = ButtonElement(True, MIDI_NOTE_TYPE, 3, 83, name='Pad1') self._Pad2 = ButtonElement(True, MIDI_NOTE_TYPE, 3, 84, name='Pad2') self._Pad3 = ButtonElement(True, MIDI_NOTE_TYPE, 3, 86, name='Pad3') self._Pad4 = ButtonElement(True, MIDI_NOTE_TYPE, 3, 74, name='Pad4') self._Pad5 = ButtonElement(True, MIDI_NOTE_TYPE, 3, 76, name='Pad5') self._Pad6 = ButtonElement(True, MIDI_NOTE_TYPE, 3, 77, name='Pad6') self._Pad7 = ButtonElement(True, MIDI_NOTE_TYPE, 3, 79, name='Pad7') self._Pad8 = ButtonElement(True, MIDI_NOTE_TYPE, 3, 67, name='Pad8') self._Pad9 = ButtonElement(True, MIDI_NOTE_TYPE, 3, 69, name='Pad9') self._Pad10 = ButtonElement(True, MIDI_NOTE_TYPE, 3, 71, name='Pad10') self._Pad11 = ButtonElement(True, MIDI_NOTE_TYPE, 3, 72, name='Pad11') self._Pad12 = ButtonElement(True, MIDI_NOTE_TYPE, 3, 60, name='Pad12') self._Pad13 = ButtonElement(True, MIDI_NOTE_TYPE, 3, 62, name='Pad13') self._Pad14 = ButtonElement(True, MIDI_NOTE_TYPE, 3, 64, name='Pad14') self._Pad15 = ButtonElement(True, MIDI_NOTE_TYPE, 3, 65, name='Pad15') self._Pads = ButtonMatrixElement(rows=[[self._Pad0, self._Pad1, self._Pad2, self._Pad3], [self._Pad4, self._Pad5, self._Pad6, self._Pad7], [self._Pad8, self._Pad9, self._Pad10, self._Pad11], [self._Pad12, self._Pad13, self._Pad14, self._Pad15]]) #Drum Bank B ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Drum Bank C ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #Drum Bank D ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- # #Drum rack ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- drum_rack = DrumRackComponent(name='Drum_Rack', is_enabled=False, layer=Layer(pads=self._Pads)) drum_rack.set_enabled(True) #Transport ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- transport = TransportComponent(name='Transport', is_enabled=False, layer=Layer(play_button=midimap['Play'], record_button=midimap['Record'], stop_button=midimap['Stop'], loop_button=midimap['Loop'])) #, seek_forward_button=midimap['Forward'], seek_backward_button=midimap['Backward'] transport.set_enabled(True) #Make the Back/Fwd button just normal mapable CC senders self._BackButton = ButtonElement(False, MIDI_CC_TYPE, 0, 116, name='BackButton') self._FwdButton = ButtonElement(False, MIDI_CC_TYPE, 0, 115, name='FwdButton') #Device ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- self._device = DeviceComponent(name='Device', is_enabled=False, layer=Layer(parameter_controls=midimap['Encoders']), device_selection_follows_track_selection=True) self._device.set_enabled(True) self.set_device_component(self._device) #Mixer ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- mixer_size = 24 self._mixer = MixerComponent(mixer_size, name='Mixer', is_enabled=False) #Super crude and repetitive mapping because after all this shit I'm not spending time learning how to loop this crap hehe #Bank A self._mixer.channel_strip(0).layer = Layer(volume_control = self._VolumeSlider0, arm_button=self._ArmButton0, pan_control=self._Encoder0) self._mixer.channel_strip(1).layer = Layer(volume_control = self._VolumeSlider1, arm_button=self._ArmButton1, pan_control=self._Encoder1) self._mixer.channel_strip(2).layer = Layer(volume_control = self._VolumeSlider2, arm_button=self._ArmButton2, pan_control=self._Encoder2) self._mixer.channel_strip(3).layer = Layer(volume_control = self._VolumeSlider3, arm_button=self._ArmButton3, pan_control=self._Encoder3) self._mixer.channel_strip(4).layer = Layer(volume_control = self._VolumeSlider4, arm_button=self._ArmButton4, pan_control=self._Encoder4) self._mixer.channel_strip(5).layer = Layer(volume_control = self._VolumeSlider5, arm_button=self._ArmButton5, pan_control=self._Encoder5) self._mixer.channel_strip(6).layer = Layer(volume_control = self._VolumeSlider6, arm_button=self._ArmButton6, pan_control=self._Encoder6) self._mixer.channel_strip(7).layer = Layer(volume_control = self._VolumeSlider7, arm_button=self._ArmButton7, pan_control=self._Encoder7) #Bank B self._mixer.channel_strip(8).layer = Layer(volume_control = self._VolumeSlider8, arm_button=self._ArmButton8, pan_control=self._Encoder8) self._mixer.channel_strip(9).layer = Layer(volume_control = self._VolumeSlider9, arm_button=self._ArmButton9, pan_control=self._Encoder9) self._mixer.channel_strip(10).layer = Layer(volume_control = self._VolumeSlider10, arm_button=self._ArmButton10, pan_control=self._Encoder10) self._mixer.channel_strip(11).layer = Layer(volume_control = self._VolumeSlider11, arm_button=self._ArmButton11, pan_control=self._Encoder11) self._mixer.channel_strip(12).layer = Layer(volume_control = self._VolumeSlider12, arm_button=self._ArmButton12, pan_control=self._Encoder12) self._mixer.channel_strip(13).layer = Layer(volume_control = self._VolumeSlider13, arm_button=self._ArmButton13, pan_control=self._Encoder13) self._mixer.channel_strip(14).layer = Layer(volume_control = self._VolumeSlider14, arm_button=self._ArmButton14, pan_control=self._Encoder14) self._mixer.channel_strip(15).layer = Layer(volume_control = self._VolumeSlider15, arm_button=self._ArmButton15, pan_control=self._Encoder15) #Bank C self._mixer.channel_strip(16).layer = Layer(volume_control = self._VolumeSlider16, arm_button=self._ArmButton16, pan_control=self._Encoder16) self._mixer.channel_strip(17).layer = Layer(volume_control = self._VolumeSlider17, arm_button=self._ArmButton17, pan_control=self._Encoder17) self._mixer.channel_strip(18).layer = Layer(volume_control = self._VolumeSlider18, arm_button=self._ArmButton18, pan_control=self._Encoder18) self._mixer.channel_strip(19).layer = Layer(volume_control = self._VolumeSlider19, arm_button=self._ArmButton19, pan_control=self._Encoder19) self._mixer.channel_strip(20).layer = Layer(volume_control = self._VolumeSlider20, arm_button=self._ArmButton20, pan_control=self._Encoder20) self._mixer.channel_strip(21).layer = Layer(volume_control = self._VolumeSlider21, arm_button=self._ArmButton21, pan_control=self._Encoder21) self._mixer.channel_strip(22).layer = Layer(volume_control = self._VolumeSlider22, arm_button=self._ArmButton22, pan_control=self._Encoder22) self._mixer.channel_strip(23).layer = Layer(volume_control = self._VolumeSlider23, arm_button=self._ArmButton23, pan_control=self._Encoder23) self._mixer.set_enabled(True) #Track change listener self.song().view.add_selected_track_listener(self._update_selected_device) #Track changed def _update_selected_device(self): track = self.song().view.selected_track #self.show_message("----- Track changed! -----") #Launch/Record/Overdub def _launch_clip(self, value): global _overdub_flag #self.log_message("--> Track launch! -----") #self.song().view.highlighted_clip_slot.set_fire_button_state(True) _current_slot = self.song().view.highlighted_clip_slot if _current_slot.is_playing == 0 and _current_slot.is_recording == 0: self.song().view.highlighted_clip_slot.set_fire_button_state(True) elif _current_slot.is_playing == 1 and _current_slot.is_recording == 0: self.song().overdub = 1 _overdub_flag = 1 elif _current_slot.is_playing == 1 and _current_slot.is_recording == 1 and _overdub_flag == 1: self.song().overdub = 0 _overdub_flag = 0 else: self.song().view.highlighted_clip_slot.set_fire_button_state(True) #Move up/down def _move_clipslot(self, up): scene = self.song().view.selected_scene scenes = self.song().scenes max_scenes = len(scenes) for i in range(max_scenes): if scene == scenes[i]: #Found our guy if up == 1: self.song().view.selected_scene = scenes[i-1] self.show_message("TFMX Debug: Up!") else: if scene == scenes[max_scenes-1]: self.song().view.selected_scene = scenes[0] else: self.song().view.selected_scene = scenes[i+1] self.show_message("TFMX Debug: Down!") #Move left/right def _move_track(self, left): #Get track and tracks track = self.song().view.selected_track tracks = self.get_all_tracks(only_visible = True) max_tracks = len(tracks) #Iterate to find current track's index. for i in range(max_tracks): if track == tracks[i]: #Found our track if left == 1: self.song().view.selected_track = tracks[i-1] else: if track == tracks[max_tracks-1]: self.song().view.selected_track = tracks[0] else: self.song().view.selected_track = tracks[i+1] def get_all_tracks(self, only_visible = False): tracks = [] for track in self.song().tracks: if not only_visible or track.is_visible: tracks.append(track) #Include the master track? #NOPE tracks.append(self.song().master_track) return tracks def _move_up(self, value): if value > 0: self._move_clipslot(1) def _move_down(self, value): if value > 0: self._move_clipslot(0) def _move_left(self, value): if value > 0: self._move_track(1) def _move_right(self, value): if value > 0: self._move_track(0) #-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
class MaschineChannelStripComponent(ChannelStripComponent): def __init__(self): ChannelStripComponent.__init__(self) self.deleted = {} self.clear_mode = False self.touch_mode = False self.send_control = None self.clear_vol_button = None self.clear_pan_button = None self.clear_send_button = None return def set_touch_mode(self, touchchannel): self.touch_mode = True id_vol = self._volume_control.message_identifier() id_pan = self._pan_control.message_identifier() id_send = None for send in self._send_controls: if send: id_send = send.message_identifier() self.clear_vol_button = ButtonElement(False, MIDI_CC_TYPE, touchchannel, id_vol) self.clear_vol_button.add_value_listener(self._do_clear_vol) self.clear_pan_button = ButtonElement(False, MIDI_CC_TYPE, touchchannel, id_pan) self.clear_pan_button.add_value_listener(self._do_clear_pan) self.clear_send_button = ButtonElement(False, MIDI_CC_TYPE, touchchannel, id_send) self.clear_send_button.add_value_listener(self._do_clear_send) for send in self._send_controls: if send: self.send_control = send return def enter_clear(self): self.clear_mode = True self.deleted = {} if not self.touch_mode: self.set_enabled(False) self._volume_control.add_value_listener(self._do_clear_vol) self._pan_control.add_value_listener(self._do_clear_pan) for send in self._send_controls: if send: self.send_control = send send.add_value_listener(self._do_clear_send) def exit_clear(self): self.clear_mode = False if not self.touch_mode: self._volume_control.remove_value_listener(self._do_clear_vol) self._pan_control.remove_value_listener(self._do_clear_pan) for send in self._send_controls: if send: send.remove_value_listener(self._do_clear_send) self.set_enabled(True) def _do_clear_vol(self, value): key = self._volume_control.message_identifier() if self.clear_mode and key not in self.deleted: self.deleted[key] = True playing_clip = self._get_playing_clip() if playing_clip: playing_clip.clear_envelope(self._track.mixer_device.volume) def _do_clear_pan(self, value): key = self._pan_control.message_identifier() if self.clear_mode and key not in self.deleted: self.deleted[key] = True playing_clip = self._get_playing_clip() if playing_clip: playing_clip.clear_envelope(self._track.mixer_device.panning) def _do_clear_send(self, value): key = self.send_control.message_identifier() if self.clear_mode and key not in self.deleted: send_index = len(self._send_controls) - 1 self.deleted[key] = True playing_clip = self._get_playing_clip() if playing_clip and send_index in range(len(self._track.mixer_device.sends)): playing_clip.clear_envelope(self._track.mixer_device.sends[send_index]) def _mute_value(self, value): super(MaschineChannelStripComponent, self)._mute_value(value) key = self._mute_button.message_identifier() if self.clear_mode and key not in self.deleted: self.deleted[key] = True playing_clip = self._get_playing_clip() if playing_clip: playing_clip.clear_envelope(self._track.mixer_device.track_activator) def _get_playing_clip(self): if self._track == None: return clips_slots = self._track.clip_slots for cs in clips_slots: if cs.has_clip and cs.is_playing: return cs.clip return def disconnect(self): self.clear_pan_button = None self.clear_send_button = None if self.clear_vol_button != None: self.clear_vol_button.remove_value_listener(self._do_clear_vol) self.clear_vol_button = None if self.clear_pan_button != None: self.clear_pan_button.remove_value_listener(self._do_clear_pan) self.clear_pan_button = None if self.clear_send_button != None: self.clear_send_button.remove_value_listener(self._do_clear_send) self.clear_send_button = None if not self.touch_mode and self.clear_mode: if self.send_control != None: self.send_control.remove_value_listener(self._do_clear_send) self.send_control = None if self._volume_control != None: self._volume_control.remove_value_listener(self._do_clear_vol) self._volume_control = None if self._pan_control != None: self._pan_control.remove_value_listener(self._do_clear_pan) self._pan_control = None super(MaschineChannelStripComponent, self).disconnect() return
class HyperionChan(CompoundComponent): def __init__(self, hyperion, mod_num, *a, **kw): super(HyperionChan, self).__init__(*a, **kw) self.hyperion = hyperion self.mod_num = mod_num self._track_selector_encoder = EncoderElement(MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 0x15, Live.MidiMap.MapMode.relative_smooth_binary_offset) # Right encoder on Hyperion self._track_selector_encoder.add_value_listener(self._on_track_selector_encoder) self._fader = SliderElement(MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 0x25) self._pots = [ EncoderElement(MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 0x1E + num, Live.MidiMap.MapMode.absolute, name='Pot{}'.format(num)) for num in range(8) ] self._btns = [ ButtonElement(True, MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 1 + num, name='Btn{}'.format(num)) for num in range(8) ] self._enc_right_btn = ButtonElement(True, MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 10, name='EncRightBtn') self._enc_right_btn.add_value_listener(self._on_enc_right_btn) #self._cs = ChannelStripComponent() #self._cs.set_volume_control(self._fader) self._vu_slider = SliderElement(MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 60) self._vu = VUMeter(self) self._track = None tracks = self._get_all_tracks(self.hyperion.song().tracks) if len(tracks) > self.mod_num: self._bind_to_track(tracks[self.mod_num]) else: self._bind_to_track(None) def log(self, msg, *args): self.hyperion.log_message(('HyperionChan({}): ' + msg).format(self.mod_num, *args)) def disconnect(self): super(HyperionChan, self).disconnect() self._enc_right_btn.remove_value_listener(self._on_enc_right_btn) def _get_parent_by_type(self, obj, parent_type): if not obj.canonical_parent: return if isinstance(obj.canonical_parent, parent_type): return obj.canonical_parent return self._get_parent_by_type(obj.canonical_parent, parent_type) def _on_enc_right_btn(self, value): if value and self._track: self.log('type {}',type(self._track)) song = self.hyperion.song() if isinstance(self._track, Live.Track.Track): song.view.selected_track = self._track elif isinstance(self._track, Live.DrumChain.DrumChain): parent_track = self._get_parent_by_type(self._track, Live.Track.Track) song.view.selected_track = parent_track try: song.view.selected_chain = self._track except: try: song.view.selected_track = parent_track song.view.selected_chain = self._track.canonical_parent.canonical_parent self._track.canonical_parent.view.selected_chain = self._track except: pass def _get_track_mapper_device(self, track): for device in track.devices: if device.name == 'MultiMapper16 V2.0': return device def _get_all_tracks(self, all_tracks): got_tracks = [] for cur_track in all_tracks: if isinstance(cur_track, (Live.Track.Track, Live.DrumChain.DrumChain)): got_tracks.append(cur_track) devices = list(cur_track.devices) if len(devices) and isinstance(devices[0], Live.RackDevice.RackDevice): got_tracks.extend(self._get_all_tracks(devices[0].chains)) return [track for track in got_tracks if self._get_track_mapper_device(track)] def _on_track_selector_encoder(self, value): direction = 1 if value > 64 else -1 tracks = self._get_all_tracks(self.hyperion.song().tracks) # for t in tracks: # self.log('AAAAA {}', t.name) try: cur_track_idx = tracks.index(self._track) except ValueError: self.log('track disappeared :(') self._bind_to_track(tracks[0]) else: cur_track_idx += direction if cur_track_idx == len(tracks): cur_track_idx = 0 if cur_track_idx == -1: cur_track_idx = len(tracks) - 1 self._bind_to_track(tracks[cur_track_idx]) def _bind_to_track(self, track): if self._track: #self._cs.set_track(None) self._fader.release_parameter() [pot.release_parameter() for pot in self._pots] [btn.release_parameter() for btn in self._btns] self._track.remove_name_listener(self._on_name_changed) self._track = None if not track: return self.log('binding to {}', track.name) self._track = track self._fader.connect_to(track.mixer_device.volume) mapper_dev = self._get_track_mapper_device(track) for num in range(8): self._pots[num].connect_to(mapper_dev.parameters[3 + num]) # MacroA0 MacroA1 etc self._btns[num].connect_to(mapper_dev.parameters[11 + num]) # MacroB0 MacroB1 etc # self._cs.set_track(track) if getattr(self._track, 'has_audio_output', False) and hasattr(self._track, 'add_output_meter_left_listener'): self._vu.set_vu_meter(track, self._vu_slider) else: self._vu.set_vu_meter(None, None) self._track.add_name_listener(self._on_name_changed) self._on_name_changed() def _on_name_changed(self): self.hyperion.sysex.set_title(self.mod_num, self._track.name if self._track else '-')
class Oxygen_3rd_Gen(ControlSurface): """ Script for the 3rd generation of M-Audio's Oxygen controllers """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): is_momentary = True self._suggested_input_port = 'Oxygen' self._suggested_output_port = 'Oxygen' self._has_slider_section = True self._device_selection_follows_track_selection = True self._shift_button = ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 57) self._shift_button.add_value_listener(self._shift_value) self._mixer = SpecialMixerComponent(NUM_TRACKS) self._mute_solo_buttons = [] self._track_up_button = ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 111) self._track_down_button = ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 110) self._master_slider = SliderElement(MIDI_CC_TYPE, GLOBAL_CHANNEL, 41) for index in range(NUM_TRACKS): self._mute_solo_buttons.append( ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 49 + index)) self._mixer.channel_strip(index).set_volume_control( SliderElement(MIDI_CC_TYPE, GLOBAL_CHANNEL, 33 + index)) self._shift_value(0) self._mixer.master_strip().set_volume_control(self._master_slider) self._mixer.selected_strip().set_volume_control(None) device = DeviceComponent() device.set_parameter_controls( tuple([ EncoderElement(MIDI_CC_TYPE, GLOBAL_CHANNEL, 17 + index, Live.MidiMap.MapMode.absolute) for index in range(8) ])) self.set_device_component(device) ffwd_button = ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 115) rwd_button = ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 114) loop_button = ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 113) transport = TransportComponent() transport.set_stop_button( ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 116)) transport.set_play_button( ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 117)) transport.set_record_button( ButtonElement(is_momentary, MIDI_CC_TYPE, GLOBAL_CHANNEL, 118)) session = SessionComponent(0, 0) transport_view_modes = TransportViewModeSelector( transport, session, ffwd_button, rwd_button, loop_button) def disconnect(self): self._shift_button.remove_value_listener(self._shift_value) self._shift_button = None ControlSurface.disconnect(self) def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._send_midi, IDENTITY_REQUEST) def handle_sysex(self, midi_bytes): if midi_bytes[0:5] == IDENTITY_RESPONSE: if midi_bytes[10] == 38: self._mixer.master_strip().set_volume_control(None) self._mixer.selected_strip().set_volume_control( self._master_slider) def _shift_value(self, value): raise value in range(128) or AssertionError for index in range(NUM_TRACKS): if value == 0: self._mixer.channel_strip(index).set_solo_button(None) self._mixer.channel_strip(index).set_mute_button( self._mute_solo_buttons[index]) self._mixer.set_bank_buttons(None, None) self._mixer.set_select_buttons(self._track_up_button, self._track_down_button) else: self._mixer.channel_strip(index).set_mute_button(None) self._mixer.channel_strip(index).set_solo_button( self._mute_solo_buttons[index]) self._mixer.set_select_buttons(None, None) self._mixer.set_bank_buttons(self._track_up_button, self._track_down_button)
class LaunchMod(ControlSurface): """ Script for Novation's Launchpad Controller """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): self._monomod_version = 'b995' self._host_name = 'LaunchMod' self._color_type = 'Launchpad' self.hosts = [] self._timer = 0 self._suppress_send_midi = True self._suppress_session_highlight = True self._suppress_highlight = False is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._control_is_with_automap = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._wrote_user_byte = False self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143 matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column), str(column) + '_Clip_' + str(row) + '_Button', self) for column in range(8) ] matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) self._config_button.add_value_listener(self._config_value) top_button_names = ['Bank_Select_Up_Button', 'Bank_Select_Down_Button', 'Bank_Select_Left_Button', 'Bank_Select_Right_Button', 'Session_Button', 'User1_Button', 'User2_Button', 'Mixer_Button'] side_button_names = ['Vol_Button', 'Pan_Button', 'SndA_Button', 'SndB_Button', 'Stop_Button', 'Trk_On_Button', 'Solo_Button', 'Arm_Button'] top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, (104 + index), top_button_names[index], self) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index], side_button_names[index], self) for index in range(8) ] self._setup_monobridge() self._setup_monomod() self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self) self._selector.name = 'Main_Modes' for control in self.controls: if isinstance(control, MonoButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component(self._selector.session_component()) self._suppress_session_highlight = False self.log_message("--------------= " + str(self._monomod_version) + " log opened =--------------") #Create entry in log file def allow_updates(self, allow_updates): for component in self.components: component.set_allow_update(int(allow_updates!=0)) def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if len(midi_bytes) == 8: if midi_bytes[1:5] == (0, 32, 41, 6): response = long(midi_bytes[5]) response += long(midi_bytes[6]) << 8 if response == Live.Application.encrypt_challenge2(self._challenge): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector.mode_index == 1: new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes, optimized = None): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): assert value in range(128) enabled = self._wrote_user_byte or value == 1 self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, MonoButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False def _button_value(self, value): assert value in range(128) def _config_value(self, value): assert value in range(128) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks) def _setup_m4l_interface(self): self._m4l_interface = M4LInterfaceComponent(controls=self.controls, component_guard=self.component_guard) self.get_control_names = self._m4l_interface.get_control_names self.get_control = self._m4l_interface.get_control self.grab_control = self._m4l_interface.grab_control self.release_control = self._m4l_interface.release_control """Mono overrides and additions""" def _setup_monobridge(self): self._monobridge = MonoBridgeElement(self) self._monobridge.name = 'MonoBridge' def _setup_monomod(self): self._host = MonomodComponent(self) self._host.name = 'Monomod_Host' self.hosts = [self._host] def update_display(self): ControlSurface.update_display(self) self._timer = (self._timer + 1) % 256 self.flash() def flash(self): if self._host.is_enabled(): for control in self.controls: if isinstance(control, MonoButtonElement): control.flash(self._timer)
class Launchpad(ControlSurface): u""" Script for Novation's Launchpad Controller """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._control_is_with_automap = False self._user_byte_write_button = ButtonElement( is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener( self._user_byte_value) self._wrote_user_byte = False self._challenge = Live.Application.get_random_int( 0, 400000000) & 2139062143 matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [] for column in range(8): button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, row * 16 + column) button.name = str(column) + '_Clip_' + str(row) + '_Button' button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) self._config_button.add_value_listener(self._config_value) top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, 104 + index) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index]) for index in range(8) ] top_buttons[0].name = 'Bank_Select_Up_Button' top_buttons[1].name = 'Bank_Select_Down_Button' top_buttons[2].name = 'Bank_Select_Left_Button' top_buttons[3].name = 'Bank_Select_Right_Button' top_buttons[4].name = 'Session_Button' top_buttons[5].name = 'User1_Button' top_buttons[6].name = 'User2_Button' top_buttons[7].name = 'Mixer_Button' side_buttons[0].name = 'Vol_Button' side_buttons[1].name = 'Pan_Button' side_buttons[2].name = 'SndA_Button' side_buttons[3].name = 'SndB_Button' side_buttons[4].name = 'Stop_Button' side_buttons[5].name = 'Trk_On_Button' side_buttons[6].name = 'Solo_Button' side_buttons[7].name = 'Arm_Button' self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button) self._selector.name = 'Main_Modes' for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component( self._selector.session_component()) self._suppress_session_highlight = False def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._selector = None self._user_byte_write_button.remove_value_listener( self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None return def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if len(midi_bytes) == 8: if midi_bytes[1:5] == (0, 32, 41, 6): response = long(midi_bytes[5]) response += long(midi_bytes[6]) << 8 if response == Live.Application.encrypt_challenge2( self._challenge): self._on_handshake_successful() def _on_handshake_successful(self): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector.mode_index == 1: new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes, optimized=None): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): assert value in range(128) if not self._wrote_user_byte: enabled = value == 1 self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False def _button_value(self, value): assert value in range(128) def _config_value(self, value): assert value in range(128) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class Launchpad(ControlSurface): _active_instances = [] def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) live = Live.Application.get_application() self._live_major_version = live.get_major_version() self._live_minor_version = live.get_minor_version() self._live_bugfix_version = live.get_bugfix_version() self._selector = None #needed because update hardware is called. self._mk2_rgb = False with self.component_guard(): self._suppress_send_midi = True self._suppress_session_highlight = True self._suggested_input_port = ("Launchpad", "Launchpad Mini", "Launchpad S", "Launchpad MK2") self._suggested_output_port = ("Launchpad", "Launchpad Mini", "Launchpad S", "Launchpad MK2") self._control_is_with_automap = False self._user_byte_write_button = None self._config_button = None self._wrote_user_byte = False self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143 self._init_done = False # caller will send challenge and we will continue as challenge is received. def init(self): #skip init if already done. if self._init_done: return self._init_done = True # second part of the __init__ after model has been identified using its challenge response if self._mk2_rgb: from SkinMK2 import make_skin self._skin = make_skin() self._side_notes = (89, 79, 69, 59, 49, 39, 29, 19) #self._drum_notes = (20, 30, 31, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126) self._drum_notes = (20, 30, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126) else: from SkinMK1 import make_skin self._skin = make_skin() self._side_notes = (8, 24, 40, 56, 72, 88, 104, 120) self._drum_notes = (41, 42, 43, 44, 45, 46, 47, 57, 58, 59, 60, 61, 62, 63, 73, 74, 75, 76, 77, 78, 79, 89, 90, 91, 92, 93, 94, 95, 105, 106, 107) with self.component_guard(): is_momentary = True self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) self._config_button.add_value_listener(self._config_value) self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [] for column in range(8): if self._mk2_rgb: # for mk2 buttons are assigned "top to bottom" midi_note = (81 - (10 * row)) + column else: midi_note = row * 16 + column button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, midi_note, skin = self._skin, control_surface = self) button.name = str(column) + '_Clip_' + str(row) + '_Button' button_row.append(button) matrix.add_row(tuple(button_row)) top_buttons = [ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, 104 + index, skin = self._skin) for index in range(8)] side_buttons = [ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, self._side_notes[index], skin = self._skin) for index in range(8)] top_buttons[0].name = 'Bank_Select_Up_Button' top_buttons[1].name = 'Bank_Select_Down_Button' top_buttons[2].name = 'Bank_Select_Left_Button' top_buttons[3].name = 'Bank_Select_Right_Button' top_buttons[4].name = 'Session_Button' top_buttons[5].name = 'User1_Button' top_buttons[6].name = 'User2_Button' top_buttons[7].name = 'Mixer_Button' side_buttons[0].name = 'Vol_Button' side_buttons[1].name = 'Pan_Button' side_buttons[2].name = 'SndA_Button' side_buttons[3].name = 'SndB_Button' side_buttons[4].name = 'Stop_Button' side_buttons[5].name = 'Trk_On_Button' side_buttons[6].name = 'Solo_Button' side_buttons[7].name = 'Arm_Button' self._osd = M4LInterface() self._osd.name = "OSD" self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self._osd, self) self._selector.name = 'Main_Modes' self._do_combine() for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component(self._selector.session_component()) self._suppress_session_highlight = False # due to our 2 stage init, we need to rebuild midi map self.request_rebuild_midi_map() # and request update self._selector.update() if self._mk2_rgb: self.log_message("LaunchPad95 (mk2) Loaded !") else: self.log_message("LaunchPad95 Loaded !") def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._do_uncombine() if self._selector != None: self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False if self._mk2_rgb: # launchpad mk2 needs disconnect string sent self._send_midi((240, 0, 32, 41, 2, 24, 64, 247)) if self._config_button != None: self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None def _combine_active_instances(): support_devices = False for instance in Launchpad._active_instances: support_devices |= (instance._device_component != None) offset = 0 for instance in Launchpad._active_instances: instance._activate_combination_mode(offset, support_devices) offset += instance._selector._session.width() _combine_active_instances = staticmethod(_combine_active_instances) def _activate_combination_mode(self, track_offset, support_devices): if(Settings.STEPSEQ__LINK_WITH_SESSION): self._selector._stepseq.link_with_step_offset(track_offset) if(Settings.SESSION__LINK): self._selector._session.link_with_track_offset(track_offset) def _do_combine(self): if (DO_COMBINE and (self not in Launchpad._active_instances)): Launchpad._active_instances.append(self) Launchpad._combine_active_instances() def _do_uncombine(self): if self in Launchpad._active_instances: Launchpad._active_instances.remove(self) if(Settings.SESSION__LINK): self._selector._session.unlink() if(Settings.STEPSEQ__LINK_WITH_SESSION): self._selector._stepseq.unlink() Launchpad._combine_active_instances() def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): # MK2 has different challenge and params if len(midi_bytes) == 10 and midi_bytes[:7] == (240, 0, 32, 41, 2, 24, 64): response = long(midi_bytes[7]) response += long(midi_bytes[8]) << 8 if response == Live.Application.encrypt_challenge2(self._challenge): self._mk2_rgb = True self.log_message("Challenge Response ok (mk2)") self._suppress_send_midi = False self.set_enabled(True) self.init() #MK1 Challenge elif len(midi_bytes) == 8 and midi_bytes[1:5] == (0, 32, 41, 6): response = long(midi_bytes[5]) response += long(midi_bytes[6]) << 8 if response == Live.Application.encrypt_challenge2(self._challenge): self.log_message("Challenge Response ok (mk1)") self._mk2_rgb = False self.init() self._suppress_send_midi = False self.set_enabled(True) else: ControlSurface.handle_sysex(self,midi_bytes) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector!=None: if self._selector._main_mode_index==2 or self._selector._main_mode_index==1: mode = Settings.USER_MODES[ (self._selector._main_mode_index-1) * 3 + self._selector._sub_mode_index[self._selector._main_mode_index] ] #self._selector.mode_index == 1: #if self._selector._sub_mode_index[self._selector._mode_index] > 0: # disable midi map rebuild for instrument mode to prevent light feedback errors if mode != "instrument": new_channel = self._selector.channel_for_current_mode() for note in self._drum_notes: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes, optimized=None): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False if self._user_byte_write_button != None: self._user_byte_write_button.send_value(1) self._wrote_user_byte = True self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): # send challenge for all models to allow to detect which one is actually plugged # mk2 challenge_bytes = tuple([ self._challenge >> 8 * index & 127 for index in xrange(4) ]) self._send_midi((240, 0, 32, 41, 2, 24, 64) + challenge_bytes + (247,)) # mk1's for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): assert (value in range(128)) if not self._wrote_user_byte: enabled = (value == 1) self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.force_next_send() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False def _button_value(self, value): assert value in range(128) def _config_value(self, value): assert value in range(128) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class Novation_Impulse(ControlSurface): """ Script for Novation's Impulse keyboards """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): self.set_pad_translations(PAD_TRANSLATIONS) self._device_selection_follows_track_selection = True self._suggested_input_port = 'Impulse' self._suggested_output_port = 'Impulse' self._has_sliders = True self._current_midi_map = None self._display_reset_delay = -1 self._shift_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 39) self._preview_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 41) self._master_slider = SliderElement(MIDI_CC_TYPE, 0, 8) self._shift_button.name = 'Shift_Button' self._master_slider.name = 'Master_Volume_Control' self._master_slider.add_value_listener(self._slider_value, identify_sender=True) self._preview_button.add_value_listener(self._preview_value) self._setup_mixer() self._setup_session() self._setup_transport() self._setup_device() self._setup_name_display() device_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 1, 10) mixer_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 1, 9) device_button.name = 'Encoder_Device_Mode' mixer_button.name = 'Encoder_Mixer_Mode' self._encoder_modes = EncoderModeSelector(self._device_component, self._mixer, self._next_bank_button, self._prev_bank_button, self._encoders) self._encoder_modes.set_device_mixer_buttons(device_button, mixer_button) self._string_to_display = None for component in self.components: component.set_enabled(False) def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(3, self._send_midi, SYSEX_START + (6, 1, 1, 1, 247)) def handle_sysex(self, midi_bytes): if midi_bytes[0:-2] == SYSEX_START + (7,) and midi_bytes[-2] != 0: self._has_sliders = midi_bytes[-2] != 25 self.schedule_message(1, self._show_startup_message) for control in self.controls: if isinstance(control, InputControlElement): control.clear_send_cache() for component in self.components: component.set_enabled(True) if self._has_sliders: self._mixer.master_strip().set_volume_control(self._master_slider) self._mixer.update() else: self._mixer.master_strip().set_volume_control(None) self._mixer.selected_strip().set_volume_control(self._master_slider) for index in range(len(self._sliders)): self._mixer.channel_strip(index).set_volume_control(None) slider = self._sliders[index] slider.release_parameter() if slider.value_has_listener(self._slider_value): slider.remove_value_listener(self._slider_value) self._encoder_modes.set_provide_volume_mode(not self._has_sliders) self.request_rebuild_midi_map() def disconnect(self): self._name_display_data_source.set_display_string(' ') for encoder in self._encoders: encoder.remove_value_listener(self._encoder_value) self._master_slider.remove_value_listener(self._slider_value) if self._has_sliders: for slider in tuple(self._sliders): slider.remove_value_listener(self._slider_value) for button in self._strip_buttons: button.remove_value_listener(self._mixer_button_value) self._preview_button.remove_value_listener(self._preview_value) ControlSurface.disconnect(self) self._encoders = None self._sliders = None self._strip_buttons = None self._master_slider = None self._current_midi_map = None self._shift_button = None self._name_display = None self._prev_bank_button = None self._next_bank_button = None self._encoder_modes = None self._transport_view_modes = None self._send_midi(SYSEX_START + (6, 0, 0, 0, 247)) def build_midi_map(self, midi_map_handle): self._current_midi_map = midi_map_handle ControlSurface.build_midi_map(self, midi_map_handle) def update_display(self): ControlSurface.update_display(self) if self._string_to_display != None: self._name_display_data_source.set_display_string(self._string_to_display) self._string_to_display = None if self._display_reset_delay >= 0: self._display_reset_delay -= 1 if self._display_reset_delay == -1: self._show_current_track_name() def _setup_mixer(self): mute_solo_flip_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 0, 34) self._next_nav_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 37) self._prev_nav_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 38) self._strip_buttons = [] mute_solo_flip_button.name = 'Mute_Solo_Flip_Button' self._next_nav_button.name = 'Next_Track_Button' self._prev_nav_button.name = 'Prev_Track_Button' self._mixer = SpecialMixerComponent(8) self._mixer.name = 'Mixer' self._mixer.set_select_buttons(self._next_nav_button, self._prev_nav_button) self._mixer.selected_strip().name = 'Selected_Channel_Strip' self._mixer.master_strip().name = 'Master_Channel_Strip' self._mixer.master_strip().set_volume_control(self._master_slider) self._sliders = [] for index in range(8): strip = self._mixer.channel_strip(index) strip.name = 'Channel_Strip_' + str(index) strip.set_invert_mute_feedback(True) self._sliders.append(SliderElement(MIDI_CC_TYPE, 0, index)) self._sliders[-1].name = str(index) + '_Volume_Control' self._sliders[-1].set_feedback_delay(-1) self._sliders[-1].add_value_listener(self._slider_value, identify_sender=True) strip.set_volume_control(self._sliders[-1]) self._strip_buttons.append(ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 9 + index)) self._strip_buttons[-1].name = str(index) + '_Mute_Button' self._strip_buttons[-1].add_value_listener(self._mixer_button_value, identify_sender=True) self._mixer.master_strip().set_mute_button(ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 1, 17)) self._mixer.set_strip_mute_solo_buttons(tuple(self._strip_buttons), mute_solo_flip_button) def _setup_session(self): num_pads = len(PAD_TRANSLATIONS) self._track_left_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 0, 36) self._track_right_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 0, 35) self._session = SessionComponent(8, 0) self._session.name = 'Session_Control' self._session.selected_scene().name = 'Selected_Scene' self._session.set_mixer(self._mixer) self._session.set_page_left_button(self._track_left_button) self._session.set_page_right_button(self._track_right_button) pads = [] for index in range(num_pads): pads.append(ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 60 + index)) pads[-1].name = 'Pad_' + str(index) clip_slot = self._session.selected_scene().clip_slot(index) clip_slot.set_triggered_to_play_value(GREEN_BLINK) clip_slot.set_triggered_to_record_value(RED_BLINK) clip_slot.set_stopped_value(AMBER_FULL) clip_slot.set_started_value(GREEN_FULL) clip_slot.set_recording_value(RED_FULL) clip_slot.set_launch_button(pads[-1]) clip_slot.name = str(index) + '_Selected_Clip_Slot' def _setup_transport(self): rwd_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 27) ffwd_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 28) stop_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 29) play_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 30) loop_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 31) rec_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 32) ffwd_button.name = 'FFwd_Button' rwd_button.name = 'Rwd_Button' loop_button.name = 'Loop_Button' play_button.name = 'Play_Button' stop_button.name = 'Stop_Button' rec_button.name = 'Record_Button' transport = ShiftableTransportComponent() transport.name = 'Transport' transport.set_stop_button(stop_button) transport.set_play_button(play_button) transport.set_record_button(rec_button) transport.set_shift_button(self._shift_button) self._transport_view_modes = TransportViewModeSelector(transport, self._session, ffwd_button, rwd_button, loop_button) self._transport_view_modes.name = 'Transport_View_Modes' def _setup_device(self): encoders = [] for index in range(8): encoders.append(PeekableEncoderElement(MIDI_CC_TYPE, 1, index, Live.MidiMap.MapMode.relative_binary_offset)) encoders[-1].set_feedback_delay(-1) encoders[-1].add_value_listener(self._encoder_value, identify_sender=True) encoders[-1].name = 'Device_Control_' + str(index) self._encoders = tuple(encoders) self._prev_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 1, 12) self._next_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 1, 11) self._prev_bank_button.name = 'Device_Bank_Down_Button' self._next_bank_button.name = 'Device_Bank_Up_Button' device = DeviceComponent() device.name = 'Device_Component' self.set_device_component(device) device.set_parameter_controls(self._encoders) device.set_bank_nav_buttons(self._prev_bank_button, self._next_bank_button) def _setup_name_display(self): self._name_display = PhysicalDisplayElement(16, 1) self._name_display.name = 'Display' self._name_display.set_message_parts(SYSEX_START + (8,), (247,)) self._name_display_data_source = DisplayDataSource() self._name_display.segment(0).set_data_source(self._name_display_data_source) def _encoder_value(self, value, sender): if not sender in self._encoders: raise AssertionError if not value in range(128): raise AssertionError display_string = self._device_component.is_enabled() and ' - ' display_string = sender.mapped_parameter() != None and sender.mapped_parameter().name self._set_string_to_display(display_string) def _slider_value(self, value, sender): if not sender in tuple(self._sliders) + (self._master_slider,): raise AssertionError if not value in range(128): raise AssertionError if self._mixer.is_enabled(): display_string = ' - ' if sender.mapped_parameter() != None: master = self.song().master_track tracks = self.song().tracks returns = self.song().return_tracks track = None if sender == self._master_slider: track = self._has_sliders and master else: track = self.song().view.selected_track else: track = self._mixer.channel_strip(self._sliders.index(sender))._track display_string = track == master and 'Master' elif track in tracks: display_string = str(list(tracks).index(track) + 1) elif track in returns: display_string = str(chr(ord('A') + list(returns).index(track))) else: raise False or AssertionError display_string += ' Volume' self._set_string_to_display(display_string) def _mixer_button_value(self, value, sender): if not value in range(128): raise AssertionError if self._mixer.is_enabled() and value > 0: strip = self._mixer.channel_strip(self._strip_buttons.index(sender)) self._string_to_display = strip != None and None self._name_display.segment(0).set_data_source(strip.track_name_data_source()) self._name_display.update() self._display_reset_delay = STANDARD_DISPLAY_DELAY else: self._set_string_to_display(' - ') def _preview_value(self, value): raise value in range(128) or AssertionError for encoder in self._encoders: encoder.set_peek_mode(value > 0) def _show_current_track_name(self): if self._name_display != None and self._mixer != None: self._string_to_display = None self._name_display.segment(0).set_data_source(self._mixer.selected_strip().track_name_data_source()) self._name_display.update() def _show_startup_message(self): self._name_display.display_message('LIVE') self._display_reset_delay = INITIAL_DISPLAY_DELAY def _set_string_to_display(self, string_to_display): raise isinstance(string_to_display, (str, unicode)) or AssertionError self._name_display.segment(0).set_data_source(self._name_display_data_source) self._string_to_display = string_to_display self._display_reset_delay = STANDARD_DISPLAY_DELAY def _on_selected_track_changed(self): ControlSurface._on_selected_track_changed(self) self._show_current_track_name() all_tracks = self._has_sliders or self._session.tracks_to_use() selected_track = self.song().view.selected_track num_strips = self._session.width() if selected_track in all_tracks: track_index = list(all_tracks).index(selected_track) new_offset = track_index - track_index % num_strips if not new_offset / num_strips == int(new_offset / num_strips): raise AssertionError self._session.set_offsets(new_offset, self._session.scene_offset())
class Launchpad(ControlSurface): " SCRIPT FOR NOVATION'S LAUNCHPAD CONTROLLER " " INITALIZE " def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): #self.set_suppress_rebuild_requests(True) self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = "Launchpad" self._suggested_output_port = "Launchpad" self._control_is_with_automap = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = "User_Byte_Button" self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._wrote_user_byte = False self._challenge = (Live.Application.get_random_int(0, 400000000) & 2139062143) matrix = ButtonMatrixElement() matrix.name = "Button_Matrix" """ TRACKFINDER TEST track_index = 0 for track in self.song().tracks: if track_index < 8: button_row = [] if track.is_foldable: for column in range(8): log("right one: " + str(track_index)) button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((track_index * 16) + column)) #@UndefinedVariable button.name = (((str(column) + "_Clip_") + str(track_index)) + "_Button") button_row.append(button) track_index = track_index + 1 else: for column in range(8): log("wrong one: " + str(track_index)) button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, 99, True) #@UndefinedVariable button.name = (str(column) + "_Clip_Button-DUMMY") button_row.append(button) matrix.add_row(tuple(button_row)) log("done")""" """ ORIGINAL CODE """ for row in range(8): button_row = [] for column in range(8): button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column)) button.name = (((str(column) + "_Clip_") + str(row)) + "_Button") button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) self._config_button.add_value_listener(self._config_value) top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, (104 + index)) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index]) for index in range(8) ] top_buttons[0].name = "Bank_Select_Up_Button" top_buttons[1].name = "Bank_Select_Down_Button" top_buttons[2].name = "Bank_Select_Left_Button" top_buttons[3].name = "Bank_Select_Right_Button" top_buttons[4].name = "Session_Button" top_buttons[5].name = "User1_Button" top_buttons[6].name = "User2_Button" top_buttons[7].name = "Mixer_Button" side_buttons[0].name = "Vol_Button" side_buttons[1].name = "Pan_Button" side_buttons[2].name = "SndA_Button" side_buttons[3].name = "SndB_Button" side_buttons[4].name = "Stop_Button" side_buttons[5].name = "Trk_On_Button" side_buttons[6].name = "Solo_Button" side_buttons[7].name = "Arm_Button" self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button) self._selector.name = "Main_Modes" for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component(self._selector.session_component()) self._suppress_session_highlight = False #self.set_suppress_rebuild_requests(False) " DISCONNECTOR " def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None " RETURN THE SESSION COMPONENT SHOWING THE RING IN LIVE SESSION " def highlighting_session_component(self): return self._selector.session_component() " REFRESH " def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) " SYSEX HANDLING " def handle_sysex(self, midi_bytes): if (len(midi_bytes) == 8): if (midi_bytes[1:5] == (0, 32, 41, 6)): response = long(midi_bytes[5]) response += (long(midi_bytes[6]) << 8) if (response == Live.Application.encrypt_challenge2(self._challenge)): self._suppress_send_midi = False self.set_enabled(True) " MIDI MAP " def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if (self._selector.mode_index == 1): new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) " SEND THE MIDI STUFF " def _send_midi(self, midi_bytes, optimized = None): sent_successfully = False if (not self._suppress_send_midi): sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully " UPDATE THE HARDWARE " def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() " CHALLANGE SEND " def _send_challenge(self): for index in range(4): challenge_byte = ((self._challenge >> (8 * index)) & 127) self._send_midi((176, (17 + index), challenge_byte)) " USER BYTE STUFF " def _user_byte_value(self, value): if not value in range(128): raise AssertionError enabled = self._wrote_user_byte or value == 1 self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False " BUTTON VALUE " def _button_value(self, value): assert (value in range(128)) " CONFIG VALUE " def _config_value(self, value): assert (value in range(128)) " SET THE SESSION HIGHLIGHT " def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if (not self._suppress_session_highlight): ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class NanoKontrolShift(ControlSurface): __module__ = __name__ __doc__ = " NanoKontrolShift controller script " def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self._suppress_session_highlight = True self._suppress_send_midi = True # Turn off rebuild MIDI map until after we're done setting up self.log_message(time.strftime("%d.%m.%Y %H:%M:%S", time.localtime()) + "--------------= NanoKontrolShift log opened =--------------") # Writes message into Live's main log file. This is a ControlSurface method. with self.component_guard(): # OBJECTS self.session = None #session object self.mixer = None #mixer object self.view = None #clip/device view object self.device = None #device object self.transport = None #transport object # MODES self._shift_button = None self._shift_button_pressed = False self._alt_button = None self._alt_button_pressed = False self._ctrl_button = None self._ctrl_button_pressed = False # INITIALIZE MIXER, SESSIONS self._setup_session_control() # Setup the session object self._setup_mixer_control() # Setup the mixer object self._setup_view_control() # Setup the clip/view toggler self.session.set_mixer(self.mixer) # Bind mixer to session self._setup_device_control() # Setup the device object self._setup_transport_control() # Setup transport object self.set_device_component(self.device) # Bind device to control surface # SET INITIAL SESSION/MIXER AND MODIFIERS BUTTONS self._set_modifiers_buttons() self.__update_matrix() # self.set_highlighting_session_component(self.session) for component in self.components: component.set_enabled(True) # self._suppress_session_highlight = True self._suppress_send_midi = True # Turn rebuild back on, once we're done setting up def _setup_session_control(self): self.show_message("#################### SESSION: ON ##############################") is_momentary = True # CREATE SESSION, SET OFFSETS, BUTTONS NAVIGATION AND BUTTON MATRIX self.session = SessionComponent(num_tracks, num_scenes) #(num_tracks, num_scenes) self.session.set_offsets(0, 0) # self.session.set_scene_bank_buttons(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, session_down), ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, session_up)) # self.session.set_track_bank_buttons(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, session_right), ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, session_left)) # (right_button, left_button) This moves the "red box" selection set left & right. We'll use the mixer track selection instead... def _setup_mixer_control(self): is_momentary = True #CREATE MIXER, SET OFFSET (SPECIALMIXERCOMPONENT USES SPECIALCHANNELSTRIP THAT ALLOWS US TO UNFOLD TRACKS WITH TRACK SELECT BUTTON) self.mixer = SpecialMixerComponent(num_tracks, 0, False, False) # 4 tracks, 2 returns, no EQ, no filters self.mixer.name = 'Mixer' self.mixer.set_track_offset(0) #Sets start point for mixer strip (offset from left) def _setup_view_control(self): # CREATES OBJECT SO WE CAN TOGGLE DEVICE/CLIP, LOCK DEVICE self.view = ViewTogglerComponent(num_tracks, self) def _setup_device_control(self): # CREATE DEVICE TO ASSIGN PARAMETERS BANK self.device = DeviceComponent() self.device.name = 'Device_Component' def _setup_transport_control(self): # CREATE TRANSPORT DEVICE self.transport = SpecialTransportComponent(self) def _on_selected_scene_changed(self): # ALLOWS TO GRAB THE FIRST DEVICE OF A SELECTED TRACK IF THERE'S ANY ControlSurface._on_selected_track_changed(self) track = self.song().view.selected_track if (track.devices is not None): self._ignore_track_selection = True device_to_select = track.devices[0] self.song().view.select_device(device_to_select) self._device_component.set_device(device_to_select) self._ignore_track_selection = False """ LED ON, OFF, FLASH WITH SESSION CLIPS """ # UPDATING BUTTONS FROM CLIP MATRIX IN NK AS SESSION MOVES def __update_matrix(self): for scene_index in range(num_scenes): scene = self.session.scene(scene_index) for track_index in range(num_tracks): clip_slot = scene.clip_slot(track_index) button = clip_slot._launch_button_value.subject value_to_send = -1 if clip_slot._clip_slot != None: if clip_slot.has_clip(): value_to_send = 127 if clip_slot._clip_slot.clip.is_triggered: if clip_slot._clip_slot.clip.will_record_on_start: value_to_send = clip_slot._triggered_to_record_value else: value_to_send = clip_slot._triggered_to_play_value ''' elif clip_slot._clip_slot.clip.is_playing: if clip_slot._clip_slot.clip.is_recording: value_to_send = 127 ######### CLIPS PLAYING WILL FLASH for i in range(2000): if i % 50 == 0: button.send_value(127) else: button.send_value(0) else: for i in range(2000): if i % 50 == 0: button.send_value(127) else: button.send_value(0) ''' elif clip_slot._clip_slot.is_triggered: if clip_slot._clip_slot.will_record_on_start: value_to_send = clip_slot._triggered_to_record_value else: value_to_send = clip_slot._triggered_to_play_value elif clip_slot._clip_slot.is_playing: if clip_slot._clip_slot.is_recording: value_to_send = clip_slot._recording_value else: value_to_send = clip_slot._started_value elif clip_slot._clip_slot.controls_other_clips: value_to_send = 0 ''' if value_to_send in range(128): button.send_value(value_to_send) else: button.turn_off() ''' """ MODIFIERS, MODES, KEYS CONFIG """ #MODES ARE HERE: INITIALIZATIONS, DISCONNECTS BUTTONS, SLIDERS, ENCODERS def _clear_controls(self): # TURNING OFF ALL LEDS IN MATRIX self._turn_off_matrix() # SESSION resetsend_controls = [] self.mixer.send_controls = [] for scene_index in range(num_scenes): scene = self.session.scene(scene_index) scene.set_launch_button(None) for track_index in range(num_tracks): clip_slot = scene.clip_slot(track_index) clip_slot.set_launch_button(None) self.session.set_stop_track_clip_buttons(None) self.session.set_stop_all_clips_button(None) # REMOVE LISTENER TO SESSION MOVES if self.session.offset_has_listener(self.__update_matrix): self.session.remove_offset_listener(self.__update_matrix) self.session.set_stop_all_clips_button(None) # MIXER self.mixer._set_send_nav(None, None) for track_index in range(num_tracks): strip = self.mixer.channel_strip(track_index) strip.set_solo_button(None) strip.set_mute_button(None) strip.set_arm_button(None) resetsend_controls.append(None) strip.set_select_button(None) for i in range(12): self.mixer.send_controls.append(None) strip.set_send_controls(tuple(self.mixer.send_controls)) self.mixer.set_resetsend_buttons(tuple(resetsend_controls)) # VIEW self.view.set_device_nav_buttons(None, None) detailclip_view_controls = [] for track_index in range(num_tracks): detailclip_view_controls.append(None) self.view.set_buttons(tuple(detailclip_view_controls)) # DEVICE PARAMETERS device_param_controls = [] self.device.set_parameter_controls(tuple(device_param_controls)) self.device.set_on_off_button(None) self.device.set_lock_button(None) # TRANSPORT self.transport.set_stop_button(None) self.transport.set_play_button(None) self.transport.set_record_button(None) self.transport._set_quant_toggle_button(None) self.transport.set_metronome_button(None) self.transport._set_tempo_buttons(None, None) self.log_message("Controls Cleared") def _set_normal_mode(self): is_momentary = True self.mixer._set_send_nav(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, send_up), ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, send_down)) for index in range(num_tracks): strip = self.mixer.channel_strip(index) strip.name = 'Mixer_ChannelStrip_' + str(index) self.mixer._update_send_index(self.mixer.sends_index) strip.set_volume_control(SliderElement(MIDI_CC_TYPE, CHANNEL, mixer_volumefader_cc[index])) self.mixer.send_controls[self.mixer.sends_index] = EncoderElement(MIDI_CC_TYPE, CHANNEL, mixer_sendknob_cc[index], Live.MidiMap.MapMode.absolute) strip.set_send_controls(tuple(self.mixer.send_controls)) strip._invert_mute_feedback = True ### SET ARM, SOLO, MUTE for index in range(num_tracks): strip = self.mixer.channel_strip(index) strip.set_send_controls(tuple(self.mixer.send_controls)) strip.set_solo_button(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, track_solo_cc[index])) strip.set_mute_button(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, track_mute_cc[index])) strip.set_arm_button(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, track_arm_cc[index])) # self.transport.set_stop_button(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, transport_stop_cc)) for i in range(12): self.mixer.send_controls.append(None) self.mixer.send_controls[self.mixer.sends_index] = EncoderElement(MIDI_CC_TYPE, CHANNEL, mixer_sendknob_cc[index], Live.MidiMap.MapMode.absolute) strip.set_send_controls(tuple(self.mixer.send_controls)) strip._invert_mute_feedback = True self.mixer._update_send_index(self.mixer.sends_index) def _set_alt_mode(self): is_momentary = True self.mixer._set_send_nav(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, send_up), ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, send_down)) stop_track_controls = [] resetsend_controls = [] button = None buttons = None # SET SESSION TRACKSTOP, TRACK SELECT, RESET SEND KNOB for index in range(num_tracks): strip = self.mixer.channel_strip(index) strip.set_select_button(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, track_select_cc[index])) button = ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, stop_track_cc[index]) stop_track_controls.append(button) buttons = ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, track_resetsend_cc[index]) resetsend_controls.append(buttons) self.session.set_stop_track_clip_buttons(tuple(stop_track_controls)) self.mixer.set_resetsend_buttons(tuple(resetsend_controls)) self.mixer._update_send_index(self.mixer.sends_index) def _set_ctrl_mode(self): # CLIP/DEVICE VIEW TOGGLE is_momentary = True self.view.set_device_nav_buttons(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, device_left_cc), ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, device_right_cc)) button = None detailclip_view_controls = [] for index in range(num_tracks): button = ButtonElement(not is_momentary, MIDI_CC_TYPE, CHANNEL, detailclip_view_cc[index]) detailclip_view_controls.append(button) self.view.set_buttons(tuple(detailclip_view_controls)) # DEVICE ON/OFF, LOCK AND PARAMETERS device_param_controls = [] onoff_control = ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, onoff_device_cc) setlock_control = ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, lock_device_cc) for index in range(num_tracks): knob = None knob = EncoderElement(MIDI_CC_TYPE, CHANNEL, device_param_cc[index], Live.MidiMap.MapMode.absolute) device_param_controls.append(knob) if None not in device_param_controls: self.device.set_parameter_controls(tuple(device_param_controls)) self.device.set_on_off_button(onoff_control) self.device.set_lock_button(setlock_control) # TRANSPORT # self.transport.set_stop_button(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, transport_stop_cc)) # self.transport.set_play_button(ButtonElement(not is_momentary, MIDI_CC_TYPE, CHANNEL, transport_play_cc)) # self.transport.set_record_button(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, transport_record_cc)) self.transport._set_quant_toggle_button(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, transport_quantization_cc)) self.transport.set_metronome_button(ButtonElement(not is_momentary, MIDI_CC_TYPE, CHANNEL, transport_metronome_cc)) self.transport._set_tempo_buttons(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, transport_tempodown_cc), ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, transport_tempoup_cc)) # SESSION STOP ALL CLIPS AND SCENE LAUNCH self.session.set_stop_all_clips_button(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, session_stopall_cc)) for index in range(num_scenes): self.session.scene(index).set_launch_button(ButtonElement(is_momentary, MIDI_CC_TYPE, CHANNEL, session_scenelaunch_cc[index])) def _set_modifiers_buttons(self): is_momentary = True self._shift_button = ButtonElement(not is_momentary, MIDI_CC_TYPE, CHANNEL, shift_mod) self._alt_button = ButtonElement(not is_momentary, MIDI_CC_TYPE, CHANNEL, alt_mod) self._ctrl_button = ButtonElement(not is_momentary, MIDI_CC_TYPE, CHANNEL, ctrl_mod) if (self._shift_button != None): self._shift_button.add_value_listener(self._shift_value) if (self._alt_button != None): self._alt_button.add_value_listener(self._alt_value) if (self._ctrl_button != None): self._ctrl_button.add_value_listener(self._ctrl_value) #INIT NORMAL MODE self._manage_modes(0) # MODIFIERS LISTENERS FUNCS ARE HERE def _shift_value(self, value): assert isinstance(value, int) assert isinstance(self._shift_button, ButtonElement) if value == 127: if self._shift_button_pressed is False: self._unpress_modes() self._shift_button_pressed = True self._manage_modes(0) def _alt_value(self, value): assert isinstance(value, int) assert isinstance(self._alt_button, ButtonElement) if value == 127: if self._alt_button_pressed is False: self._unpress_modes() self._alt_button_pressed = True self._manage_modes(2) def _ctrl_value(self, value): assert isinstance(value, int) assert isinstance(self._ctrl_button, ButtonElement) if value == 127: if self._ctrl_button_pressed is False: self._unpress_modes() self._ctrl_button_pressed = True self._manage_modes(3) def _manage_modes(self, mode_index): if mode_index == 0: self._clear_controls() self._set_normal_mode() # self._shift_button.turn_on() self._alt_button.turn_on() self._ctrl_button.turn_on() self.log_message("NORMAL ON") elif mode_index == 2: self._clear_controls() self._set_alt_mode() self._alt_button.turn_on() self._shift_button.turn_off() self._ctrl_button.turn_off() self.log_message("ALT ON") elif mode_index == 3: self._clear_controls() self._set_ctrl_mode() self._ctrl_button.turn_on() self._shift_button.turn_off() self._alt_button.turn_off() self.log_message("CTRL ON") def _unpress_modes(self): self._shift_button_pressed = False self._alt_button_pressed = False self._ctrl_button_pressed = False def _turn_off_matrix(self): for index in range(24): self._send_midi(tuple([176,index+16,0])) def disconnect(self): """clean things up on disconnect""" self.log_message(time.strftime("%d.%m.%Y %H:%M:%S", time.localtime()) + "--------------= NanoKontrolShift log closed =--------------") #Create entry in log file self._clear_controls() self.session = None self.mixer = None self.view = None self.device = None self.transport = None # MODES self._shift_button = None self._alt_button = None self._ctrl_button = None # SENDS self.send_button_up = None self.send_button_down = None self.send_controls = [] self.send_reset = [] self.set_device_component(None) ControlSurface.disconnect(self) return None
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 Launchpad(ControlSurface): """ Script for Novation's Launchpad Controller """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self.set_suppress_rebuild_requests(True) self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._control_is_with_automap = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._wrote_user_byte = False self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143 matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [] for column in range(8): button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, row * 16 + column) button.name = str(column) + '_Clip_' + str(row) + '_Button' button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0) self._config_button.add_value_listener(self._config_value) top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, 104 + index) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index]) for index in range(8) ] top_buttons[0].name = 'Bank_Select_Up_Button' top_buttons[1].name = 'Bank_Select_Down_Button' top_buttons[2].name = 'Bank_Select_Left_Button' top_buttons[3].name = 'Bank_Select_Right_Button' top_buttons[4].name = 'Session_Button' top_buttons[5].name = 'User1_Button' top_buttons[6].name = 'User2_Button' top_buttons[7].name = 'Mixer_Button' side_buttons[0].name = 'Vol_Button' side_buttons[1].name = 'Pan_Button' side_buttons[2].name = 'SndA_Button' side_buttons[3].name = 'SndB_Button' side_buttons[4].name = 'Stop_Button' side_buttons[5].name = 'Trk_On_Button' side_buttons[6].name = 'Solo_Button' side_buttons[7].name = 'Arm_Button' self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self) self._selector.name = 'Main_Modes' self._do_combine() for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self._suppress_session_highlight = False self.set_suppress_rebuild_requests(False) self.log_message("LaunchPad85 Loaded !") def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._do_uncombine() self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None _active_instances = [] def highlighting_session_component(self): " Return the session component showing the ring in Live session " return self._selector.session_component() def _combine_active_instances(): support_devices = False for instance in Launchpad._active_instances: support_devices |= (instance._device_component != None) offset = 0 for instance in Launchpad._active_instances: instance._activate_combination_mode(offset, support_devices) offset += instance._selector._session.width() _combine_active_instances = staticmethod(_combine_active_instances) def _activate_combination_mode(self, track_offset, support_devices): if(LINK_STEPSEQ): self._selector._stepseq.link_with_step_offset(track_offset) if(LINK_SESSION): self._selector._session.link_with_track_offset(track_offset) def _do_combine(self): if (DO_COMBINE and (self not in Launchpad._active_instances)): Launchpad._active_instances.append(self) Launchpad._combine_active_instances() def _do_uncombine(self): if self in Launchpad._active_instances: Launchpad._active_instances.remove(self) if(LINK_SESSION): self._selector._session.unlink() if(LINK_STEPSEQ): self._selector._stepseq.unlink() Launchpad._combine_active_instances() def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if len(midi_bytes) == 8: if midi_bytes[1:5] == (0, 32, 41, 6): response = long(midi_bytes[5]) response += long(midi_bytes[6]) << 8 if response == Live.Application.encrypt_challenge2(self._challenge): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector.mode_index == 1: new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): assert (value in range(128)) if (not self._wrote_user_byte): enabled = (value == 1) self._control_is_with_automap = (not enabled) self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False def _button_value(self, value): assert (value in range(128)) def _config_value(self, value): assert (value in range(128)) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class Novation_Impulse2(ControlSurface): """ Script for Novation's Impulse keyboards """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self.c_instance = c_instance with self.component_guard(): self.set_pad_translations(PAD_TRANSLATIONS) self._device_selection_follows_track_selection = True self._suggested_input_port = 'Impulse' self._suggested_output_port = 'Impulse' self._has_sliders = True self._current_midi_map = None self._display_reset_delay = -1 self._string_to_display = None self.shift_pressed = False # special alternative buttons mode. for now only mixer buttons become record buttons. later we will add something more self.alternative_buttons_mode = False self._shift_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 39) self._preview_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 41) self._master_slider = SliderElement(MIDI_CC_TYPE, 0, 8) self._shift_button.name = 'Shift_Button' self._master_slider.name = 'Master_Volume_Control' self._master_slider.add_value_listener(self._slider_value, identify_sender=True) self._preview_button.add_value_listener(self._preview_value) self._setup_mixer() self._setup_session() self._setup_transport() self._setup_device() self._setup_name_display() device_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 1, 10) mixer_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 1, 9) device_button.name = 'Encoder_Device_Mode' mixer_button.name = 'Encoder_Mixer_Mode' self._encoder_modes = EncoderModeSelector(self._device_component, self._mixer, self._next_bank_button, self._prev_bank_button, self._encoders) self._encoder_modes.set_device_mixer_buttons( device_button, mixer_button) self._shift_button.add_value_listener(self._shift_button_handler) for component in self.components: component.set_enabled(False) # attributes def alternative_buttons_mode(self): return self.alternative_buttons_mode def alternative_buttons_mode(self, value): self.log('alternative_buttons_mode_value ' + str(value)) self.alternative_buttons_mode = value def shift_pressed(self): return self.shift_pressed def shift_pressed(self, value): self.log('shift_pressed value ' + str(value)) self.shift_pressed = value def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(3, self._send_midi, SYSEX_START + (6, 1, 1, 1, 247)) def handle_sysex(self, midi_bytes): if midi_bytes[0:-2] == SYSEX_START + (7, ) and midi_bytes[-2] != 0: self._has_sliders = midi_bytes[-2] != 25 self.schedule_message(1, self._show_startup_message) for control in self.controls: if isinstance(control, InputControlElement): control.clear_send_cache() for component in self.components: component.set_enabled(True) if self._has_sliders: self._mixer.master_strip().set_volume_control( self._master_slider) self._mixer.update() else: self._mixer.master_strip().set_volume_control(None) self._mixer.selected_strip().set_volume_control( self._master_slider) for index in range(len(self._sliders)): self._mixer.channel_strip(index).set_volume_control(None) slider = self._sliders[index] slider.release_parameter() if slider.value_has_listener(self._slider_value): slider.remove_value_listener(self._slider_value) self._encoder_modes.set_provide_volume_mode(not self._has_sliders) self.request_rebuild_midi_map() def disconnect(self): self.log('starting disconnect 1') self._name_display_data_source.set_display_string(' ') for encoder in self._encoders: encoder.remove_value_listener(self._encoder_value) self._master_slider.remove_value_listener(self._slider_value) if self._has_sliders: for slider in tuple(self._sliders): slider.remove_value_listener(self._slider_value) for button in self._strip_buttons: button.remove_value_listener(self._mixer_button_value) self._preview_button.remove_value_listener(self._preview_value) self.log('starting disconnect 3') ControlSurface.disconnect(self) self.log('starting disconnect 3') self._encoders = None self._sliders = None self._strip_buttons = None self._master_slider = None self._current_midi_map = None self._name_display = None self._prev_bank_button = None self._next_bank_button = None self._encoder_modes = None self._transport_view_modes = None self.log('starting disconnect 4') self._send_midi(SYSEX_START + (6, 0, 0, 0, 247)) self.log('starting disconnect 5') if self._shift_button != None: self._shift_button.remove_value_listener( self._shift_button_handler) self._shift_button = None self.log('starting disconnect 6') def build_midi_map(self, midi_map_handle): self._current_midi_map = midi_map_handle ControlSurface.build_midi_map(self, midi_map_handle) def update_display(self): ControlSurface.update_display(self) if self._string_to_display != None: self._name_display_data_source.set_display_string( self._string_to_display) self._string_to_display = None if self._display_reset_delay >= 0: self._display_reset_delay -= 1 if self._display_reset_delay == -1: self._show_current_track_name() def _setup_mixer(self): self.log('setup mixer') mute_solo_flip_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 0, 34) self._next_nav_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 37) self._prev_nav_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 38) self._strip_buttons = [] mute_solo_flip_button.name = 'Mute_Solo_Flip_Button' self._next_nav_button.name = 'Next_Track_Button' self._prev_nav_button.name = 'Prev_Track_Button' self._mixer = SpecialMixerComponent(self, 8, self.c_instance) self._mixer.name = 'Mixer' self._mixer.set_select_buttons(self._next_nav_button, self._prev_nav_button) self._mixer.selected_strip().name = 'Selected_Channel_Strip' self._mixer.master_strip().name = 'Master_Channel_Strip' self._mixer.master_strip().set_volume_control(self._master_slider) self._sliders = [] for index in range(8): strip = self._mixer.channel_strip(index) strip.name = 'Channel_Strip_' + str(index) strip.set_invert_mute_feedback(True) self._sliders.append(SliderElement(MIDI_CC_TYPE, 0, index)) self._sliders[-1].name = str(index) + '_Volume_Control' self._sliders[-1].set_feedback_delay(-1) self._sliders[-1].add_value_listener(self._slider_value, identify_sender=True) strip.set_volume_control(self._sliders[-1]) self._strip_buttons.append( ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 9 + index)) self._strip_buttons[-1].name = str(index) + '_Mute_Button' self._strip_buttons[-1].add_value_listener( self._mixer_button_value, identify_sender=True) self._mixer.master_strip().set_mute_button( ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 1, 17)) self._mixer.set_strip_mute_solo_buttons(tuple(self._strip_buttons), mute_solo_flip_button) #self._mixer.set_shift_button(self._shift_button) self._mixer.updateMixerButtons() self._button9 = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 9 + 8) def _setup_session(self): num_pads = len(PAD_TRANSLATIONS) self._session = SessionComponent(8, 0) self._session.name = 'Session_Control' self._session.selected_scene().name = 'Selected_Scene' self._session.set_mixer(self._mixer) # for ableton 9.1.1 and lower #self._session.set_track_banking_increment(num_pads) #self._session.set_track_bank_buttons(ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 0, 35), ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 0, 36)) # for ableton 9.1.1 and higher self._track_left_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 0, 36) self._track_right_button = ButtonElement(not IS_MOMENTARY, MIDI_CC_TYPE, 0, 35) self._session.set_page_left_button(self._track_left_button) self._session.set_page_right_button(self._track_right_button) pads = [] for index in range(num_pads): pads.append( ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 60 + index)) pads[-1].name = 'Pad_' + str(index) clip_slot = self._session.selected_scene().clip_slot(index) clip_slot.set_triggered_to_play_value(GREEN_BLINK) clip_slot.set_triggered_to_record_value(RED_BLINK) clip_slot.set_stopped_value(AMBER_FULL) clip_slot.set_started_value(GREEN_FULL) clip_slot.set_recording_value(RED_FULL) clip_slot.set_launch_button(pads[-1]) clip_slot.name = str(index) + '_Selected_Clip_Slot' def _setup_transport(self): rwd_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 27) ffwd_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 28) stop_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 29) play_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 30) loop_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 31) rec_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 32) ffwd_button.name = 'FFwd_Button' rwd_button.name = 'Rwd_Button' loop_button.name = 'Loop_Button' play_button.name = 'Play_Button' stop_button.name = 'Stop_Button' rec_button.name = 'Record_Button' self._transport = ShiftableTransportComponent(self.c_instance, self._session, self, ffwd_button, rwd_button) self._transport.name = 'Transport' self._transport.set_stop_buttonOnInit(stop_button) self._transport.set_play_button(play_button) self._transport.set_record_buttonOnInit(rec_button) # self._transport.set_shift_button(self._shift_button) self._transport.set_mixer9_button(self._button9) self._transport_view_modes = TransportViewModeSelector( self, self.c_instance, self._transport, self._session, ffwd_button, rwd_button, loop_button) self._transport_view_modes.name = 'Transport_View_Modes' def _setup_device(self): encoders = [] for index in range(8): encoders.append( PeekableEncoderElement( MIDI_CC_TYPE, 1, index, Live.MidiMap.MapMode.relative_binary_offset)) encoders[-1].set_feedback_delay(-1) encoders[-1].add_value_listener(self._encoder_value, identify_sender=True) encoders[-1].name = 'Device_Control_' + str(index) self._encoders = tuple(encoders) self._prev_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 1, 12) self._next_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 1, 11) self._prev_bank_button.name = 'Device_Bank_Down_Button' self._next_bank_button.name = 'Device_Bank_Up_Button' device = DeviceComponent() device.name = 'Device_Component' self.set_device_component(device) device.set_parameter_controls(self._encoders) device.set_bank_nav_buttons(self._prev_bank_button, self._next_bank_button) def _setup_name_display(self): self._name_display = PhysicalDisplayElement(16, 1) self._name_display.name = 'Display' self._name_display.set_message_parts(SYSEX_START + (8, ), (247, )) self._name_display_data_source = DisplayDataSource() self._name_display.segment(0).set_data_source( self._name_display_data_source) def _encoder_value(self, value, sender): if not sender in self._encoders: raise AssertionError if not value in range(128): raise AssertionError # display_string = self._device_component.is_enabled() and ' - ' # display_string = sender.mapped_parameter() != None and sender.mapped_parameter().name display_string = '' if self._device_component.is_enabled(): # display_string = sender.name # track = self.song().view.selected_track # display_string = str(list(tracks).index(track) + 1) pass if (sender.mapped_parameter() != None): # display_string = display_string + '-' display_string = display_string + sender.mapped_parameter().name self._set_string_to_display(display_string) def _slider_value(self, value, sender): self.log('_slider_value ' + str(value) + ' ' + str(sender)) if not sender in tuple(self._sliders) + (self._master_slider, ): raise AssertionError if not value in range(128): raise AssertionError if self._mixer.is_enabled(): display_string = ' - ' master = self.song().master_track tracks = self.song().tracks returns = self.song().return_tracks track = None if sender.mapped_parameter() != None: self.log('1') if sender == self._master_slider: self.log('2') # track = self._has_sliders and master if self._has_sliders: track = master else: self.log('2.1') track = self.song().view.selected_track else: self.log('3') track = self._mixer.channel_strip( self._sliders.index(sender))._track else: self.log('4') track = self.song().view.selected_track self.log('track=' + str(track)) if track == master: display_string = 'Master' elif track in tracks: display_string = str(list(tracks).index(track) + 1) elif track in returns: display_string = str( chr(ord('A') + list(returns).index(track))) else: # raise False or AssertionError raise AssertionError display_string += ' Volume' self._set_string_to_display(display_string) def _mixer_button_value(self, value, sender): self.log('__mixer_button_value ' + str(value) + ' ' + str(sender)) if not value in range(128): raise AssertionError #if self._mixer.is_enabled() and value > 0: if self._mixer.is_enabled(): strip = self._mixer.channel_strip( self._strip_buttons.index(sender)) #self._string_to_display = strip != None and None self._name_display.segment(0).set_data_source( strip.track_name_data_source()) self._name_display.update() self._display_reset_delay = STANDARD_DISPLAY_DELAY else: self._set_string_to_display(' - ') # if shift_pressed XOR alternative_mode if self.shift_pressed <> self.alternative_buttons_mode: self.log("_mixer_button_value") self.log(str(value)) if (value == 0): self.select_armed_track_if_only_one() def select_armed_track_if_only_one(self): self.log("select_armed_track_if_only_one") song = self.song() armed_tracks = [] tracks = song.tracks self.log("select_armed_track_if_only_one 2") for track in tracks: if track.can_be_armed and track.arm: armed_tracks.append(track) self.log(str(len(armed_tracks))) if (len(armed_tracks) == 1): self.log("selecting the track") sel_track = armed_tracks[0] self.song().view.selected_track = sel_track self._mixer._selected_tracks = [] self._mixer._selected_tracks.append(sel_track) self._mixer.on_selected_track_changed() def _preview_value(self, value): if not value in range(128): raise AssertionError for encoder in self._encoders: encoder.set_peek_mode(value > 0) def _show_current_track_name(self): if self._name_display != None and self._mixer != None: self._string_to_display = None self._name_display.segment(0).set_data_source( self._mixer.selected_strip().track_name_data_source()) self._name_display.update() def _show_startup_message(self): self._name_display.display_message('LIVE') self._display_reset_delay = INITIAL_DISPLAY_DELAY def _set_string_to_display(self, string_to_display): if not isinstance(string_to_display, (str, unicode)): raise AssertionError self._name_display.segment(0).set_data_source( self._name_display_data_source) self._string_to_display = string_to_display self._display_reset_delay = STANDARD_DISPLAY_DELAY def _on_selected_track_changed(self): self.log('_on_selected_track_changed') ControlSurface._on_selected_track_changed(self) self._show_current_track_name() #all_tracks = self._has_sliders or self._session.tracks_to_use() all_tracks2 = self._session.tracks_to_use() selected_track = self.song().view.selected_track num_strips = self._session.width() if selected_track in all_tracks2: track_index = list(all_tracks2).index(selected_track) self.log('track_index ' + str(track_index)) new_offset = track_index - track_index % num_strips self.log('new_offset ' + str(new_offset)) if not new_offset / num_strips == int(new_offset / num_strips): raise AssertionError self._session.set_offsets(new_offset, self._session.scene_offset()) def _shift_button_handler(self, value): self.log("root shift handler : " + str(value)) if not self._shift_button != None: raise AssertionError if not value in range(128): raise AssertionError self.log("root shift handler 2") self.shift_pressed = value > 0 # calling other handlers self._mixer._shift_button_handler(value) self._transport._shift_button_handler(value) self._transport_view_modes._shift_button_handler(value) #clip stop self.log("root shift handler 3") num_pads = len(PAD_TRANSLATIONS) pads = [] for index in range(num_pads): pads.append( ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 0, 60 + index)) pads[-1].name = 'Pad_' + str(index) clip_slot = self._session.selected_scene().clip_slot(index) if self.shift_pressed: clip_slot.set_launch_button(None) else: clip_slot.set_launch_button(pads[index]) if self.shift_pressed: self._session.set_stop_track_clip_buttons(tuple(pads)) else: self._session.set_stop_track_clip_buttons(None) self.log("root shift handler 4") def flipAlternativeButtonMode(self): self.alternative_buttons_mode = not self.alternative_buttons_mode self.updateAlternativeButtonMode() def updateAlternativeButtonMode(self): self._mixer.updateMixerButtons() self._transport_view_modes.update() def log(self, message): pass
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 add_value_listener(self, callback, identify_sender=False): if not self._is_notifying: ButtonElement.add_value_listener(self, callback, identify_sender) else: self._pending_listeners.append((callback, identify_sender))
def add_value_listener(self, callback, identify_sender = False): if (not self._is_notifying): ButtonElement.add_value_listener(self, callback, identify_sender) else: self._pending_listeners.append((callback, identify_sender))
class Launchpad(ControlSurface): " SCRIPT FOR NOVATION'S LAUNCHPAD CONTROLLER " " INITALIZE " def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): #self.set_suppress_rebuild_requests(True) self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = "Launchpad" self._suggested_output_port = "Launchpad" self._control_is_with_automap = False self._user_byte_write_button = ButtonElement( is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = "User_Byte_Button" self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener( self._user_byte_value) self._wrote_user_byte = False self._challenge = (Live.Application.get_random_int(0, 400000000) & 2139062143) matrix = ButtonMatrixElement() matrix.name = "Button_Matrix" """ TRACKFINDER TEST track_index = 0 for track in self.song().tracks: if track_index < 8: button_row = [] if track.is_foldable: for column in range(8): log("right one: " + str(track_index)) button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((track_index * 16) + column)) #@UndefinedVariable button.name = (((str(column) + "_Clip_") + str(track_index)) + "_Button") button_row.append(button) track_index = track_index + 1 else: for column in range(8): log("wrong one: " + str(track_index)) button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, 99, True) #@UndefinedVariable button.name = (str(column) + "_Clip_Button-DUMMY") button_row.append(button) matrix.add_row(tuple(button_row)) log("done")""" """ ORIGINAL CODE """ for row in range(8): button_row = [] for column in range(8): button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column)) button.name = (((str(column) + "_Clip_") + str(row)) + "_Button") button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) self._config_button.add_value_listener(self._config_value) top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, (104 + index)) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index]) for index in range(8) ] top_buttons[0].name = "Bank_Select_Up_Button" top_buttons[1].name = "Bank_Select_Down_Button" top_buttons[2].name = "Bank_Select_Left_Button" top_buttons[3].name = "Bank_Select_Right_Button" top_buttons[4].name = "Session_Button" top_buttons[5].name = "User1_Button" top_buttons[6].name = "User2_Button" top_buttons[7].name = "Mixer_Button" side_buttons[0].name = "Vol_Button" side_buttons[1].name = "Pan_Button" side_buttons[2].name = "SndA_Button" side_buttons[3].name = "SndB_Button" side_buttons[4].name = "Stop_Button" side_buttons[5].name = "Trk_On_Button" side_buttons[6].name = "Solo_Button" side_buttons[7].name = "Arm_Button" self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button) self._selector.name = "Main_Modes" for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component( self._selector.session_component()) self._suppress_session_highlight = False #self.set_suppress_rebuild_requests(False) " DISCONNECTOR " def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._selector = None self._user_byte_write_button.remove_value_listener( self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None " RETURN THE SESSION COMPONENT SHOWING THE RING IN LIVE SESSION " def highlighting_session_component(self): return self._selector.session_component() " REFRESH " def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) " SYSEX HANDLING " def handle_sysex(self, midi_bytes): if (len(midi_bytes) == 8): if (midi_bytes[1:5] == (0, 32, 41, 6)): response = long(midi_bytes[5]) response += (long(midi_bytes[6]) << 8) if (response == Live.Application.encrypt_challenge2( self._challenge)): self._suppress_send_midi = False self.set_enabled(True) " MIDI MAP " def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if (self._selector.mode_index == 1): new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) " SEND THE MIDI STUFF " def _send_midi(self, midi_bytes, optimized=None): sent_successfully = False if (not self._suppress_send_midi): sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully " UPDATE THE HARDWARE " def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() " CHALLANGE SEND " def _send_challenge(self): for index in range(4): challenge_byte = ((self._challenge >> (8 * index)) & 127) self._send_midi((176, (17 + index), challenge_byte)) " USER BYTE STUFF " def _user_byte_value(self, value): if not value in range(128): raise AssertionError enabled = self._wrote_user_byte or value == 1 self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False " BUTTON VALUE " def _button_value(self, value): assert (value in range(128)) " CONFIG VALUE " def _config_value(self, value): assert (value in range(128)) " SET THE SESSION HIGHLIGHT " def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if (not self._suppress_session_highlight): ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class NoteRepeatComponent(CompoundComponent): """ Noter Repeat Handler""" __module__ = __name__ _knob_handler = None def __init__(self, note_repeat=None, *a, **k): super(NoteRepeatComponent, self).__init__(*a, **k) self._note_repeat = note_repeat self._adjust_cfg_value.subject = SliderElement(MIDI_CC_TYPE, 2, 105) self._note_repeat_button = ButtonElement(True, MIDI_CC_TYPE, 0, 102) self._do_note_repeat.subject = self._note_repeat_button self._cfg_adjust_button = ButtonElement(True, MIDI_CC_TYPE, 2, 106) self._cfg_adjust_button.add_value_listener(self._do_cfg_button) self._cfg_down = False self._hold_mode = False self.nr_down = False self._current_nr_button = None self._do_change_nr_1.subject = SliderElement(MIDI_CC_TYPE, 1, 76) self._do_change_nr_2.subject = SliderElement(MIDI_CC_TYPE, 1, 77) self._do_change_nr_3.subject = SliderElement(MIDI_CC_TYPE, 1, 78) self._do_change_nr_4.subject = SliderElement(MIDI_CC_TYPE, 1, 79) def createButton(ccindenfier, nr_freq): button = ButtonElement(True, MIDI_CC_TYPE, 1, ccindenfier) button.add_value_listener(self._select_value, True) button.active = False button.freq = nr_freq button.cfg = False button.hold = False return button def createCfgButton(ccindenfier, nr_freq_idx): button = ButtonElement(True, MIDI_CC_TYPE, 2, ccindenfier) button.add_value_listener(self._select_value, True) button.active = False button.fr_idx = nr_freq_idx button.freq = CFG_REPEAT_FREQUENCIES[nr_freq_idx] button.cfg = True button.hold = False return button self._cfg_buttons = [ createCfgButton(assign[0], assign[1]) for assign in CTRL_CFG_TO_FREQ ] for button in self._cfg_buttons: button.send_value(button.active and 1 or 0, True) self.nr_frq = CTRL_TO_FREQ[4][1] self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 self.buttons = [ createButton(assign[0], assign[1]) for assign in CTRL_TO_FREQ ] self.buttons[4].active = True self._previous_button = self.buttons[4] self._last_active_button = self.buttons[4] for button in self.buttons: button.send_value(button.active and 1 or 0, True) return def update(self): pass def store_values(self, dict): for button, idx in zip(self._cfg_buttons, range(len(self._cfg_buttons))): dict['cofig-nr-val' + str(idx)] = button.fr_idx def recall_values(self, dict): for button, idx in zip(self._cfg_buttons, range(len(self._cfg_buttons))): key = 'cofig-nr-val' + str(idx) if key in dict: fqidx = dict[key] button.fr_idx = fqidx button.freq = CFG_REPEAT_FREQUENCIES[fqidx] def registerKnobHandler(self, handler): self._knob_handler = handler def show_note_rates(self): rates = '' for button, idx in zip(self._cfg_buttons, range(len(self._cfg_buttons))): rates += ' ' + CFG_REPEAT_DISPLAY[button.fr_idx].ljust(5) if idx < 3: rates += '|' self.canonical_parent.timed_message(2, rates) def mod_button(self, button, inc, which): cindex = button.fr_idx maxindex = len(CFG_REPEAT_FREQUENCIES) - 1 minindex = 0 if self.canonical_parent.isShiftDown(): inc *= 2 maxindex = cindex % 2 == 0 and maxindex or maxindex - 1 minindex = cindex % 2 new_idx = max(minindex, min(maxindex, cindex + inc)) if new_idx != cindex: self.canonical_parent.show_message('Note Repeat Button ' + str(which) + ' : ' + CFG_REPEAT_DISPLAY[new_idx]) button.fr_idx = new_idx button.freq = CFG_REPEAT_FREQUENCIES[new_idx] if button.active: self.nr_frq = CFG_REPEAT_FREQUENCIES[new_idx] self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 @subject_slot('value') def _do_change_nr_1(self, value): self.mod_button(self._cfg_buttons[0], value == REL_KNOB_DOWN and -1 or 1, 1) self.show_note_rates() @subject_slot('value') def _do_change_nr_2(self, value): self.mod_button(self._cfg_buttons[1], value == REL_KNOB_DOWN and -1 or 1, 2) self.show_note_rates() @subject_slot('value') def _do_change_nr_3(self, value): self.mod_button(self._cfg_buttons[2], value == REL_KNOB_DOWN and -1 or 1, 3) self.show_note_rates() @subject_slot('value') def _do_change_nr_4(self, value): self.mod_button(self._cfg_buttons[3], value == REL_KNOB_DOWN and -1 or 1, 4) self.show_note_rates() def _do_cfg_button(self, value): if value != 0: self._cfg_down = True button = self._current_nr_button if button and button.cfg and button.fr_idx >= 0: self.canonical_parent.show_message( 'Note Repeat ' + CFG_REPEAT_DISPLAY[button.fr_idx]) else: self._cfg_down = False if self._knob_handler: self._knob_handler.do_main_push(value) @subject_slot('value') def _adjust_cfg_value(self, value): button = self._current_nr_button if button and button.cfg and (self.nr_down or button.hold or self._hold_mode and button.active): inc = value == 127 and -1 or 1 cindex = button.fr_idx maxindex = len(CFG_REPEAT_FREQUENCIES) - 1 minindex = 0 if self._cfg_down: inc *= 2 maxindex = cindex % 2 == 0 and maxindex or maxindex - 1 minindex = cindex % 2 new_idx = max(minindex, min(maxindex, cindex + inc)) self.canonical_parent.show_message('Note Repeat ' + CFG_REPEAT_DISPLAY[new_idx]) button.fr_idx = new_idx button.freq = CFG_REPEAT_FREQUENCIES[new_idx] self.nr_frq = CFG_REPEAT_FREQUENCIES[new_idx] self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 else: if self._knob_handler: self._knob_handler.do_main(value) def _select_value(self, value, button): if value != 0: self._current_nr_button = button button.hold = True self.show_note_rates() if self._hold_mode: if self._previous_button == button: button.send_value(0, True) button.active = False self._last_active_button = button button.active = False self._note_repeat.repeat_rate = 32.0 self._previous_button = None elif self._previous_button == None or self._previous_button != button: button.send_value(1, True) button.active = True self.nr_frq = button.freq self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 if not self._note_repeat.enabled: self._note_repeat.enabled = True if self._previous_button != None: self._previous_button.active = False self._previous_button.send_value(0, True) self._previous_button = button else: button.send_value(1, True) button.active = True self.nr_frq = button.freq self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 if self._previous_button != None and self._previous_button != button: self._previous_button.active = False self._previous_button.send_value(0, True) self._previous_button = button else: button.hold = False return @subject_slot('value') def _do_note_repeat(self, value): self.nr_down = value > 0 if self._hold_mode: if value > 0: self._note_repeat_button.send_value(0) self._note_repeat.enabled = False self._hold_mode = False if self._previous_button == None and self._last_active_button != None: self._previous_button = self._last_active_button self._last_active_button.send_value(1) self._last_active_button.active = True self.nr_frq = self._last_active_button.freq self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 else: if self.canonical_parent.isShiftDown() and value > 0: self._note_repeat_button.send_value(1) self._note_repeat.enabled = True self._hold_mode = True self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 else: if value == 0: self._note_repeat.enabled = False self._note_repeat_button.send_value(0) else: self._note_repeat_button.send_value(1) self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 self._note_repeat.enabled = True return def disconnect(self): super(NoteRepeatComponent, 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 Axiom_DirectLink(ControlSurface): """ Script for the M-Audio Axiom DirectLink """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): self.set_pad_translations(PAD_TRANSLATIONS) self._device_selection_follows_track_selection = True self._suggested_input_port = 'DirectLink' self._suggested_output_port = 'DirectLink' self._waiting_for_first_response = True self._has_sliders = True self._current_midi_map = None self._display_reset_delay = -1 self._shift_pressed = False self._shift_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 13) self._master_slider = SliderElement(MIDI_CC_TYPE, 15, 41) self._next_nav_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 111) self._prev_nav_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 110) self._device_bank_buttons = None self._device_navigation = None self._shift_button.name = 'Shift_Button' self._master_slider.name = 'Master_Volume_Control' self._next_nav_button.name = 'Next_Track_Button' self._prev_nav_button.name = 'Prev_Track_Button' self._master_slider.add_value_listener(self._slider_value, identify_sender=True) self._shift_button.add_value_listener(self._shift_value) self._setup_mixer() self._setup_transport_and_session() self._setup_device() self._setup_display() for component in self.components: component.set_enabled(False) def refresh_state(self): ControlSurface.refresh_state(self) self._waiting_for_first_response = True self.schedule_message(3, self._send_midi, SYSEX_START + (32, 46, 247)) def handle_sysex(self, midi_bytes): if midi_bytes[0:-2] == SYSEX_START + (32, ) and midi_bytes[-2] != 0: self._has_sliders = midi_bytes[-2] & 8 != 0 if self._waiting_for_first_response: self._waiting_for_first_response = False self.schedule_message(1, self._show_startup_message) for component in self.components: component.set_enabled(True) if self._has_sliders: self._mixer.master_strip().set_volume_control( self._master_slider) self._mixer.update() else: self._mixer.master_strip().set_volume_control(None) self._mixer.selected_strip().set_volume_control( self._master_slider) self.request_rebuild_midi_map() def disconnect(self): self._display_data_source.set_display_string(' ') self._shift_button.remove_value_listener(self._shift_value) self._inst_button.remove_value_listener(self._inst_value) for encoder in self._encoders: encoder.remove_value_listener(self._encoder_value) for slider in tuple(self._sliders) + (self._master_slider, ): slider.remove_value_listener(self._slider_value) for button in tuple( self._strip_buttons) + (self._selected_mute_solo_button, ): button.remove_value_listener(self._mixer_button_value) for button in self._device_bank_buttons: button.remove_value_listener(self._device_bank_value) self._encoders = None self._sliders = None self._strip_buttons = None self._master_slider = None self._current_midi_map = None self._selected_mute_solo_button = None self._inst_button = None self._shift_button = None self._device_navigation = None self._display = None ControlSurface.disconnect(self) self._send_midi(SYSEX_START + (32, 0, 247)) def build_midi_map(self, midi_map_handle): self._current_midi_map = midi_map_handle ControlSurface.build_midi_map(self, midi_map_handle) def update_display(self): ControlSurface.update_display(self) if self._display_reset_delay >= 0: self._display_reset_delay -= 1 if self._display_reset_delay == -1: self._show_current_track_name() def _setup_mixer(self): self._selected_mute_solo_button = ButtonElement( IS_MOMENTARY, MIDI_CC_TYPE, 15, 12) mute_solo_flip_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 57) self._strip_buttons = [] self._selected_mute_solo_button.name = 'Selected_Mute_Button' mute_solo_flip_button.name = 'Mute_Solo_Flip_Button' self._selected_mute_solo_button.add_value_listener( self._mixer_button_value, identify_sender=True) self._mixer = ShiftableMixerComponent(8) self._mixer.name = 'Mixer' self._mixer.set_shift_button(self._shift_button) self._mixer.set_selected_mute_solo_button( self._selected_mute_solo_button) self._mixer.set_select_buttons(self._next_nav_button, self._prev_nav_button) self._mixer.selected_strip().name = 'Selected_Channel_Strip' self._mixer.master_strip().name = 'Master_Channel_Strip' self._mixer.master_strip().set_volume_control(self._master_slider) self._sliders = [] for index in range(8): strip = self._mixer.channel_strip(index) strip.name = 'Channel_Strip_' + str(index) strip.set_invert_mute_feedback(True) self._sliders.append(SliderElement(MIDI_CC_TYPE, 15, 33 + index)) self._sliders[-1].name = str(index) + '_Volume_Control' self._sliders[-1].set_feedback_delay(-1) self._sliders[-1].add_value_listener(self._slider_value, identify_sender=True) strip.set_volume_control(self._sliders[-1]) self._strip_buttons.append( ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 49 + index)) self._strip_buttons[-1].name = str(index) + '_Mute_Button' self._strip_buttons[-1].add_value_listener( self._mixer_button_value, identify_sender=True) self._mixer.set_strip_mute_solo_buttons(tuple(self._strip_buttons), mute_solo_flip_button) def _setup_transport_and_session(self): ffwd_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 115) rwd_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 114) loop_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 113) play_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 117) stop_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 116) rec_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 118) ffwd_button.name = 'FFwd_Button' rwd_button.name = 'Rwd_Button' loop_button.name = 'Loop_Button' play_button.name = 'Play_Button' stop_button.name = 'Stop_Button' rec_button.name = 'Record_Button' transport = ShiftableTransportComponent() transport.name = 'Transport' transport.set_shift_button(self._shift_button) transport.set_stop_button(stop_button) transport.set_play_button(play_button) transport.set_record_button(rec_button) pads = [] for index in range(len(PAD_TRANSLATIONS)): pads.append( ButtonElement(IS_MOMENTARY, MIDI_NOTE_TYPE, 15, PAD_TRANSLATIONS[index][2])) pads[-1].name = 'Pad_' + str(index) self._session = ShiftableSessionComponent(8, 0) self._session.name = 'Session_Control' self._session.selected_scene().name = 'Selected_Scene' self._session.set_mixer(self._mixer) self._session.set_shift_button(self._shift_button) self._session.set_clip_slot_buttons(tuple(pads)) transport_view_modes = TransportViewModeSelector( transport, self._session, ffwd_button, rwd_button, loop_button) transport_view_modes.name = 'Transport_View_Modes' def _setup_device(self): self._encoders = [] for offset in range(8): self._encoders.append( PeekableEncoderElement( MIDI_CC_TYPE, 15, 17 + offset, Live.MidiMap.MapMode.relative_smooth_two_compliment)) self._encoders[-1].set_feedback_delay(-1) self._encoders[-1].add_value_listener(self._encoder_value, identify_sender=True) self._encoders[-1].name = 'Device_Control_' + str(offset) prev_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 14) next_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 15) prev_bank_button.name = 'Device_Bank_Down_Button' next_bank_button.name = 'Device_Bank_Up_Button' device = BestBankDeviceComponent() device.name = 'Device_Component' self.set_device_component(device) device.set_parameter_controls(tuple(self._encoders)) device.set_bank_nav_buttons(prev_bank_button, next_bank_button) self._device_bank_buttons = (prev_bank_button, next_bank_button) prev_bank_button.add_value_listener(self._device_bank_value) next_bank_button.add_value_listener(self._device_bank_value) self._inst_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 109) self._inst_button.name = 'Inst_Button' self._inst_button.add_value_listener(self._inst_value) self._device_navigation = DetailViewCntrlComponent() self._device_navigation.name = 'Device_Navigation_Component' def _setup_display(self): self._display = PhysicalDisplayElement(5, 1) self._display.name = 'Display' self._display.set_message_parts(SYSEX_START + (17, 1, 0, 0), (247, )) self._display_data_source = DisplayDataSource() self._display.segment(0).set_data_source(self._display_data_source) def _on_selected_track_changed(self): ControlSurface._on_selected_track_changed(self) self._show_current_track_name() def _shift_value(self, value): if not value in range(128): raise AssertionError self._shift_pressed = value > 0 for encoder in self._encoders: encoder.set_peek_mode(self._shift_pressed) self._shift_pressed and self._mixer.set_select_buttons(None, None) self._session.set_track_bank_buttons(self._next_nav_button, self._prev_nav_button) self._device_component.set_bank_nav_buttons(None, None) self._device_navigation.set_device_nav_buttons( self._device_bank_buttons[0], self._device_bank_buttons[1]) else: self._session.set_track_bank_buttons(None, None) self._mixer.set_select_buttons(self._next_nav_button, self._prev_nav_button) self._device_navigation.set_device_nav_buttons(None, None) self._device_component.set_bank_nav_buttons( self._device_bank_buttons[0], self._device_bank_buttons[1]) self.request_rebuild_midi_map() def _encoder_value(self, value, sender): if not sender in self._encoders: raise AssertionError if not value in range(128): raise AssertionError display_string = self._device_component.is_enabled() and ' - ' display_string = sender.mapped_parameter( ) != None and sender.mapped_parameter().name self._display_data_source.set_display_string(display_string) self._set_display_data_source(self._display_data_source) self._display_reset_delay = STANDARD_DISPLAY_DELAY def _slider_value(self, value, sender): if not sender in tuple(self._sliders) + (self._master_slider, ): raise AssertionError if not value in range(128): raise AssertionError if self._mixer.is_enabled(): display_string = ' - ' if sender.mapped_parameter() != None: master = self.song().master_track tracks = self.song().tracks returns = self.song().return_tracks track = None if sender == self._master_slider: track = self._has_sliders and master else: track = self.song().view.selected_track else: track = self._mixer.channel_strip( self._sliders.index(sender))._track display_string = track == master and 'Ma' elif track in tracks: display_string = str(list(tracks).index(track) + 1) elif track in returns: display_string = str( chr(ord('A') + list(returns).index(track))) else: raise False or AssertionError display_string += ' Vol' self._display_data_source.set_display_string(display_string) self._set_display_data_source(self._display_data_source) self._display_reset_delay = STANDARD_DISPLAY_DELAY def _mixer_button_value(self, value, sender): if not sender in tuple( self._strip_buttons) + (self._selected_mute_solo_button, ): raise AssertionError if not value in range(128): raise AssertionError if self._mixer.is_enabled() and value > 0: strip = None strip = sender == self._selected_mute_solo_button and self._mixer.selected_strip( ) else: strip = self._mixer.channel_strip( self._strip_buttons.index(sender)) strip != None and self._set_display_data_source( strip.track_name_data_source()) else: self._display_data_source.set_display_string(' - ') self._set_display_data_source(self._display_data_source) self._display_reset_delay = STANDARD_DISPLAY_DELAY def _device_bank_value(self, value): if not value in range(128): raise AssertionError if self._device_component.is_enabled() and value > 0: data_source = self._device_component.bank_name_data_source() data_source = self._shift_pressed and self._device_component.device_name_data_source( ) self._set_display_data_source(data_source) self._display_reset_delay = STANDARD_DISPLAY_DELAY def _inst_value(self, value): if not value in range(128): raise AssertionError value > 0 and self._device_component.is_enabled() and self.song( ).view.selected_track.view.select_instrument( ) and self._set_display_data_source( self._device_component.device_name_data_source()) self._display_reset_delay = STANDARD_DISPLAY_DELAY def _show_current_track_name(self): if self._display != None and self._mixer != None: self._set_display_data_source( self._mixer.selected_strip().track_name_data_source()) def _show_startup_message(self): self._display.display_message('LIVE') self._display_reset_delay = INITIAL_DISPLAY_DELAY def _set_display_data_source(self, data_source): raise isinstance(data_source, DisplayDataSource) or AssertionError self._display.segment(0).set_data_source(data_source) data_source.update()
class Launchpad(ControlSurface): " Script for Novation's Launchpad Controller " def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self.set_suppress_rebuild_requests(True) self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = "Launchpad" self._suggested_output_port = "Launchpad" self._control_is_with_automap = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = "User_Byte_Button" self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._wrote_user_byte = False self._challenge = (Live.Application.get_random_int(0, 400000000) & 2139062143) matrix = ButtonMatrixElement() matrix.name = "Button_Matrix" for row in range(8): button_row = [] for column in range(8): button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column)) button.name = (((str(column) + "_Clip_") + str(row)) + "_Button") button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0) self._config_button.add_value_listener(self._config_value) top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, (104 + index)) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index]) for index in range(8) ] top_buttons[0].name = "Bank_Select_Up_Button" top_buttons[1].name = "Bank_Select_Down_Button" top_buttons[2].name = "Bank_Select_Left_Button" top_buttons[3].name = "Bank_Select_Right_Button" top_buttons[4].name = "Session_Button" top_buttons[5].name = "User1_Button" top_buttons[6].name = "User2_Button" top_buttons[7].name = "Mixer_Button" side_buttons[0].name = "Vol_Button" side_buttons[1].name = "Pan_Button" side_buttons[2].name = "SndA_Button" side_buttons[3].name = "SndB_Button" side_buttons[4].name = "Stop_Button" side_buttons[5].name = "Trk_On_Button" side_buttons[6].name = "Solo_Button" side_buttons[7].name = "Arm_Button" self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self) self._selector.name = "Main_Modes" for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self._suppress_session_highlight = False self.set_suppress_rebuild_requests(False) self.log_message("LaunchPad85 Loaded !") def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None def highlighting_session_component(self): " Return the session component showing the ring in Live session " return self._selector.session_component() def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if (len(midi_bytes) == 8): if (midi_bytes[1:5] == (0, 32, 41, 6)): response = long(midi_bytes[5]) response += (long(midi_bytes[6]) << 8) if (response == Live.Application.encrypt_challenge2(self._challenge)): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if (self._selector.mode_index == 1): new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes): sent_successfully = False if (not self._suppress_send_midi): sent_successfully = ControlSurface._send_midi(self, midi_bytes) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = ((self._challenge >> (8 * index)) & 127) self._send_midi((176, (17 + index), challenge_byte)) def _user_byte_value(self, value): assert (value in range(128)) if (not self._wrote_user_byte): enabled = (value == 1) self._control_is_with_automap = (not enabled) self._suppress_send_midi = self._control_is_with_automap if (not self._control_is_with_automap): for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False def _button_value(self, value): assert (value in range(128)) def _config_value(self, value): assert (value in range(128)) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if (not self._suppress_session_highlight): ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)