class Launchpad_Pro(IdentifiableControlSurface, OptimizedControlSurface): def __init__(self, c_instance, *a, **k): product_id_bytes = consts.MANUFACTURER_ID + consts.DEVICE_CODE super(Launchpad_Pro, self).__init__(c_instance=c_instance, product_id_bytes=product_id_bytes, *a, **k) self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143 with self.component_guard(): self._skin = make_default_skin() with inject(skin=const(self._skin)).everywhere(): self._midimap = MidiMap() self._target_track_component = TargetTrackComponent(name='Target_Track') self._create_background() self._create_global_component() self._last_sent_mode_byte = None with inject(layout_setup=const(self._layout_setup), should_arm=const(self._should_arm_track)).everywhere(): self._create_session() self._create_recording() self._create_actions() self._create_drums() self._create_mixer() self._create_device() self._create_modes() self._create_m4l_interface() self._on_session_record_changed.subject = self.song() self.set_highlighting_session_component(self._session) self.set_device_component(self._device) self._device_selection_follows_track_selection = True self._on_session_record_changed() def disconnect(self): self._send_midi(consts.TURN_OFF_LEDS) self._send_midi(consts.QUIT_MESSAGE) super(Launchpad_Pro, self).disconnect() def _create_background(self): self._modifier_background_component = ModifierBackgroundComponent(name='Background_Component', is_enabled=False, layer=Layer(shift_button=self._midimap['Shift_Button'])) self._shifted_background = BackgroundComponent(name='No_Op_Shifted_Buttons', is_enabled=False, layer=Layer(click_bitton=self._midimap.with_shift('Click_Button'), delete_button=self._midimap.with_shift('Delete_Button'), duplicate_button=self._midimap.with_shift('Duplicate_Button'), double_button=self._midimap.with_shift('Double_Loop_Button'), session_record_button=self._midimap.with_shift('Session_Record_Button'))) def _create_global_component(self): self._actions_component = ActionsComponent(name='Global_Actions', is_enabled=False, layer=Layer(undo_button=self._midimap['Undo_Button'], redo_button=self._midimap.with_shift('Undo_Button'), metronome_button=self._midimap['Click_Button'], quantization_on_button=self._midimap.with_shift('Quantize_Button'))) def _create_session(self): self._session = SessionComponent(NUM_TRACKS, NUM_SCENES, auto_name=True, is_enabled=False, enable_skinning=True, layer=Layer(track_bank_left_button=self._midimap['Arrow_Left_Button'], track_bank_right_button=self._midimap['Arrow_Right_Button'], scene_bank_up_button=self._midimap['Arrow_Up_Button'], scene_bank_down_button=self._midimap['Arrow_Down_Button'])) self._session.set_enabled(True) self._session.set_rgb_mode(CLIP_COLOR_TABLE, RGB_COLOR_TABLE) SpecialClipSlotComponent.quantization_component = self._actions_component for scene_index in xrange(NUM_SCENES): scene = self._session.scene(scene_index) scene.layer = Layer(select_button=self._midimap['Shift_Button'], delete_button=self._midimap['Delete_Button'], duplicate_button=self._midimap['Duplicate_Button']) for track_index in xrange(NUM_TRACKS): slot = scene.clip_slot(track_index) slot.layer = Layer(select_button=self._midimap['Shift_Button'], delete_button=self._midimap['Delete_Button'], duplicate_button=self._midimap['Duplicate_Button'], double_loop_button=self._midimap['Double_Loop_Button'], quantize_button=self._midimap['Quantize_Button']) self._session_zoom = SessionZoomingComponent(self._session, name='Session_Overview', is_enabled=True, enable_skinning=True) def _create_recording(self): self._session_record = SpecialSessionRecordingComponent(self._target_track_component, name='Session_Recording', is_enabled=False, layer=Layer(record_button=self._midimap['Session_Record_Button'])) def _create_actions(self): self._clip_actions_component = ClipActionsComponent(self._target_track_component, name='Clip_Actions', is_enabled=False, layer=Layer(duplicate_button=self._midimap['Duplicate_Button'], double_button=self._midimap['Double_Loop_Button'], quantize_button=self._midimap['Quantize_Button'])) ClipActionsComponent.quantization_component = self._actions_component def _create_drums(self): self._drum_group_finder = DrumGroupFinderComponent(self._target_track_component, name='Drum_Group_Finder', is_enabled=False, layer=None) self._on_drum_group_changed.subject = self._drum_group_finder self._drum_group_finder.set_enabled(True) self._drum_group = DrumGroupComponent(self._clip_actions_component, name='Drum_Group_Control', translation_channel=consts.DR_MAP_CHANNEL) self._drum_group.set_enabled(True) def _create_mixer(self): self._mixer = SpecialMixerComponent(NUM_TRACKS, auto_name=True, is_enabled=True, invert_mute_feedback=True) self._mixer.name = 'Mixer_Control' self._session.set_mixer(self._mixer) def _create_device(self): self._device = SpecialDeviceComponent(name='Device_Control', is_enabled=False) self._device_navigation = DeviceNavigationComponent(name='Device_Navigation') self._device_background = BackgroundComponent(name='Device_Background_Component') def _setup_drum_group(self): self._drum_group.set_drum_group_device(self._drum_group_finder.drum_group) def _create_translation(self, comp_name, channel, button_layer, should_enable = True, should_reset = True): translation_component = TranslationComponent(name=comp_name, translated_channel=channel, should_enable=should_enable, should_reset=should_reset, is_enabled=False, layer=button_layer) setattr(self, '_' + comp_name.lower(), translation_component) return translation_component def _create_modes(self): self._modes = ModesComponent(name='Launchpad_Modes', is_enabled=False) self._session_layer_mode = AddLayerMode(self._session, Layer(scene_launch_buttons=self._midimap['Scene_Launch_Button_Matrix'], clip_launch_buttons=self._midimap['Main_Button_Matrix'], delete_button=self._midimap['Delete_Button'], duplicate_button=self._midimap['Duplicate_Button'], double_button=self._midimap['Double_Loop_Button'], quantize_button=self._midimap['Quantize_Button'])) action_button_background = BackgroundComponent(name='No_Op_Buttons') self._action_button_background_layer_mode = LayerMode(action_button_background, Layer(delete_button=self._midimap['Delete_Button'], quantize_button=self._midimap['Quantize_Button'], duplicate_button=self._midimap['Duplicate_Button'], double_button=self._midimap['Double_Loop_Button'])) self._clip_delete_layer_mode = AddLayerMode(self._clip_actions_component, layer=Layer(delete_button=self._midimap['Delete_Button'])) self._create_session_zooming_modes() self._create_session_mode() self._create_note_modes() self._create_device_mode() self._create_user_mode() self._create_record_arm_mode() self._create_track_select_mode() self._create_mute_mode() self._create_solo_mode() self._create_volume_mode() self._create_pan_mode() self._create_sends_mode() self._create_stop_clips_mode() self._modes.layer = Layer(session_mode_button=self._midimap['Session_Mode_Button'], note_mode_button=self._midimap['Note_Mode_Button'], device_mode_button=self._midimap['Device_Mode_Button'], user_mode_button=self._midimap['User_Mode_Button'], record_arm_mode_button=self._midimap['Record_Arm_Mode_Button'], track_select_mode_button=self._midimap['Track_Select_Mode_Button'], mute_mode_button=self._midimap['Mute_Mode_Button'], solo_mode_button=self._midimap['Solo_Mode_Button'], volume_mode_button=self._midimap['Volume_Mode_Button'], pan_mode_button=self._midimap['Pan_Mode_Button'], sends_mode_button=self._midimap['Sends_Mode_Button'], stop_clip_mode_button=self._midimap['Stop_Clip_Mode_Button']) self._modes.selected_mode = 'session_mode' self._on_layout_changed.subject = self._modes def _create_session_zooming_modes(self): session_zoom_layer = Layer(button_matrix=self._midimap['Main_Button_Matrix'], nav_left_button=self._midimap['Arrow_Left_Button'], nav_right_button=self._midimap['Arrow_Right_Button'], nav_up_button=self._midimap['Arrow_Up_Button'], nav_down_button=self._midimap['Arrow_Down_Button']) session_zooming_layer_mode = LayerMode(self._session_zoom, session_zoom_layer) self._session_zooming_manager = SessionZoomingManagerComponent(self._modes, is_enabled=False) session_zooming_button_layer_mode = LayerMode(self._session_zooming_manager, Layer(session_zooming_button=self._midimap['Session_Mode_Button'])) self._prioritized_session_zooming_button_layer_mode = LayerMode(self._session_zooming_manager, Layer(session_zooming_button=self._midimap['Session_Mode_Button'], priority=1)) self._session_zooming_background = BackgroundComponent(name='Session_Zooming_Background') session_zooming_background_layer_mode = LayerMode(self._session_zooming_background, Layer(scene_launch_buttons=self._midimap['Scene_Launch_Button_Matrix'], delete_button=self._midimap['Delete_Button'], quantize_button=self._midimap['Quantize_Button'], duplicate_button=self._midimap['Duplicate_Button'], double_loop_button=self._midimap['Double_Loop_Button'])) self._modes.add_mode('session_zooming_mode', [self._session_zooming_manager, session_zooming_button_layer_mode, session_zooming_layer_mode, session_zooming_background_layer_mode]) self._modes.add_mode('prioritized_session_zooming_mode', [partial(self._layout_switch, consts.SESSION_LAYOUT_SYSEX_BYTE), self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, session_zooming_layer_mode, session_zooming_background_layer_mode, self.update]) def _create_session_mode(self): self._modes.add_mode('session_mode', [partial(self._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), self._session_layer_mode, self._session.update_navigation_buttons], behaviour=CancelingReenterBehaviour('session_zooming_mode')) def _create_note_modes(self): note_mode_matrix_translation = self._create_translation('Note_Mode_Matrix_Translation', consts.CHROM_MAP_CHANNEL, Layer(button_matrix=self._midimap['Main_Button_Matrix'], note_button_matrix=self._midimap['Note_Button_Matrix'], drum_matrix=self._midimap['Drum_Button_Matrix'], mixer_button_matrix=self._midimap['Mixer_Button_Matrix']), should_enable=False) note_mode_scene_launch_translation = self._create_translation('Note_Mode_Scene_Launch_Translation', consts.CHROM_MAP_CHANNEL, Layer(scene_launch_buttons=self._midimap['Scene_Launch_Button_Matrix'])) drum_mode_note_matrix_translation = self._create_translation('Drum_Mode_Note_Button_Translation', 0, Layer(note_button_matrix=self._midimap['Note_Button_Matrix']), should_enable=False, should_reset=False) drum_group_layer_mode = LayerMode(self._drum_group, layer=Layer(scroll_up_button=self._midimap['Arrow_Left_Button'], scroll_down_button=self._midimap['Arrow_Right_Button'], scroll_page_up_button=self._midimap['Arrow_Up_Button'], scroll_page_down_button=self._midimap['Arrow_Down_Button'], drum_matrix=self._midimap['Drum_Button_Matrix'], select_button=self._midimap['Shift_Button'], delete_button=self._midimap['Delete_Button'])) self._note_modes = SpecialModesComponent(name='Note_Modes') self._note_modes.add_mode('chromatic_mode', [partial(self._layout_setup, consts.NOTE_LAYOUT_SYSEX_BYTE), self._clip_delete_layer_mode, note_mode_matrix_translation]) self._note_modes.add_mode('drum_mode', [partial(self._layout_setup, consts.DRUM_LAYOUT_SYSEX_BYTE), self._setup_drum_group, drum_group_layer_mode, drum_mode_note_matrix_translation]) self._note_modes.add_mode('audio_mode', [partial(self._layout_setup, consts.AUDIO_LAYOUT_SYSEX_BYTE), self._clip_delete_layer_mode]) self._note_modes.set_enabled(False) self._modes.add_mode('note_mode', [note_mode_scene_launch_translation, self._note_modes, self._select_note_mode, self._select_target_track, self._clip_actions_component, self._show_playing_clip, self._set_clip_actions_type], behaviour=ReenterBehaviour(self.toggle_detail_view)) self._session_record.set_modes_component(self._modes) self._session_record.set_note_mode_name('note_mode') def _create_device_mode(self): device_mode_scene_launch_translation = self._create_translation('Device_Mode_Scene_Launch_Translation', consts.DEVICE_MAP_CHANNEL, Layer(scene_launch_buttons=self._midimap['Scene_Launch_Button_Matrix'])) device_layer_mode = LayerMode(self._device, layer=Layer(parameter_controls=self._midimap['Slider_Button_Matrix'])) device_nav_layer_mode = LayerMode(self._device_navigation, layer=Layer(device_nav_left_button=self._midimap['Arrow_Left_Button'], device_nav_right_button=self._midimap['Arrow_Right_Button'])) device_background_layer_mode = LayerMode(self._device_background, layer=Layer(arrow_up_button=self._midimap['Arrow_Up_Button'], arrow_down_button=self._midimap['Arrow_Down_Button'])) self._modes.add_mode('device_mode', [partial(self._layout_setup, consts.FADER_LAYOUT_SYSEX_BYTE), self._device, device_layer_mode, device_nav_layer_mode, device_background_layer_mode, self._clip_actions_component, self._clip_delete_layer_mode, device_mode_scene_launch_translation, self._show_playing_clip, self._set_clip_actions_type], behaviour=ReenterBehaviour(self.toggle_detail_view)) def _create_user_mode(self): self._modes.add_mode('user_mode', [partial(self._layout_setup, consts.USER_LAYOUT_SYSEX_BYTE)]) def _create_record_arm_mode(self): arm_layer_mode = LayerMode(self._mixer, layer=Layer(arm_buttons=self._midimap['Mixer_Button_Matrix'])) self._modes.add_mode('record_arm_mode', [partial(self._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), self._session_layer_mode, arm_layer_mode, self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, self._session.update_navigation_buttons], behaviour=SpecialReenterBehaviour('session_mode')) def _create_track_select_mode(self): track_select_layer_mode = LayerMode(self._mixer, layer=Layer(track_select_buttons=self._midimap['Mixer_Button_Matrix'])) self._modes.add_mode('track_select_mode', [partial(self._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), self._session_layer_mode, track_select_layer_mode, self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, self._session.update_navigation_buttons], behaviour=SpecialReenterBehaviour('session_mode')) def _create_mute_mode(self): mute_layer_mode = LayerMode(self._mixer, layer=Layer(mute_buttons=self._midimap['Mixer_Button_Matrix'])) self._modes.add_mode('mute_mode', [partial(self._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), self._session_layer_mode, mute_layer_mode, self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, self._session.update_navigation_buttons], behaviour=SpecialReenterBehaviour('session_mode')) def _create_solo_mode(self): solo_layer_mode = LayerMode(self._mixer, layer=Layer(solo_buttons=self._midimap['Mixer_Button_Matrix'])) self._modes.add_mode('solo_mode', [partial(self._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), self._session_layer_mode, solo_layer_mode, self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, self._session.update_navigation_buttons], behaviour=SpecialReenterBehaviour('session_mode')) def _create_volume_mode(self): volume_mode_scene_launch_translation = self._create_translation('Volume_Mode_Scene_Launch_Translation', consts.VOLUME_MAP_CHANNEL, Layer(scene_launch_buttons=self._midimap['Scene_Launch_Button_Matrix'])) volume_layer_mode = LayerMode(self._mixer, layer=Layer(volume_controls=self._midimap['Slider_Button_Matrix'])) self._modes.add_mode('volume_mode', [partial(self._layout_setup, consts.FADER_LAYOUT_SYSEX_BYTE), volume_layer_mode, self._action_button_background_layer_mode, self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, volume_mode_scene_launch_translation, self._session.update_navigation_buttons], behaviour=SpecialReenterBehaviour('session_mode')) def _create_pan_mode(self): pan_mode_scene_launch_translation = self._create_translation('Pan_Mode_Scene_Launch_Translation', consts.PAN_MAP_CHANNEL, Layer(scene_launch_buttons=self._midimap['Scene_Launch_Button_Matrix'])) pan_layer_mode = LayerMode(self._mixer, layer=Layer(pan_controls=self._midimap['Slider_Button_Matrix'])) self._modes.add_mode('pan_mode', [partial(self._layout_setup, consts.FADER_LAYOUT_SYSEX_BYTE), pan_layer_mode, self._action_button_background_layer_mode, self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, pan_mode_scene_launch_translation, self._session.update_navigation_buttons], behaviour=SpecialReenterBehaviour('session_mode')) def _create_sends_mode(self): send_layer_mode = LayerMode(self._mixer, layer=Layer(send_controls=self._midimap['Slider_Button_Matrix'], send_select_buttons=self._midimap['Scene_Launch_Button_Matrix'])) self._modes.add_mode('sends_mode', [partial(self._layout_setup, consts.FADER_LAYOUT_SYSEX_BYTE), send_layer_mode, self._action_button_background_layer_mode, self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, self._session.update_navigation_buttons], behaviour=SpecialReenterBehaviour('session_mode')) def _create_stop_clips_mode(self): stop_layer_mode = AddLayerMode(self._session, Layer(stop_track_clip_buttons=self._midimap['Mixer_Button_Matrix'], stop_scene_clip_buttons=self._midimap['Scene_Stop_Button_Matrix'], stop_all_clips_button=self._midimap['Stop_All_Clips_Button'])) self._modes.add_mode('stop_clip_mode', [partial(self._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), self._session_layer_mode, stop_layer_mode, self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, self._session.update_navigation_buttons], behaviour=SpecialReenterBehaviour('session_mode')) def _create_m4l_interface(self): self._m4l_interface = M4LInterfaceComponent(controls=self.controls, component_guard=self.component_guard, priority=1) 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 def toggle_detail_view(self): view = self.application().view if view.is_view_visible('Detail'): if view.is_view_visible('Detail/DeviceChain'): view.show_view('Detail/Clip') else: view.show_view('Detail/DeviceChain') @subject_slot('drum_group') def _on_drum_group_changed(self): if self._note_modes.selected_mode == 'drum_mode': self._drum_group.set_drum_group_device(self._drum_group_finder.drum_group) if self._modes.selected_mode == 'note_mode': self._select_note_mode() else: self.release_controlled_track() self._update_note_mode_button(self._drum_group_finder.drum_group is not None) def _select_note_mode(self): """ Selects which note mode to use depending on the kind of current target track and its device chain. Will also select the target if specified. """ track = self._target_track_component.target_track drum_device = self._drum_group_finder.drum_group if track is None or track.is_foldable or track in self.song().return_tracks or track == self.song().master_track or track.is_frozen or track.has_audio_input: self._note_modes.selected_mode = 'audio_mode' elif drum_device: self._note_modes.selected_mode = 'drum_mode' else: self._note_modes.selected_mode = 'chromatic_mode' self._modes.update() if self._note_modes.selected_mode == 'audio_mode': self.release_controlled_track() else: self.set_controlled_track(self._target_track_component.target_track) def _select_target_track(self): track = self._target_track_component.target_track if track != self.song().view.selected_track: self.song().view.selected_track = track def _update_note_mode_button(self, focused_track_is_drum_track): button = self._midimap['Note_Mode_Button'] if focused_track_is_drum_track: button.default_states = {True: 'Mode.Drum.On', False: 'Mode.Drum.Off'} else: button.default_states = {True: 'Mode.Chromatic.On', False: 'Mode.Chromatic.Off'} button.reset_state() self._modes.update() def _show_playing_clip(self): track = None if self._use_sel_track(): track = self.song().view.selected_track else: track = self._target_track_component.target_track if track in self.song().tracks: slot_index = track.fired_slot_index if slot_index < 0: slot_index = track.playing_slot_index if slot_index >= 0: clip_slot = track.clip_slots[slot_index] self.song().view.highlighted_clip_slot = clip_slot def _set_clip_actions_type(self): self._clip_actions_component.use_selected_track(self._use_sel_track()) self._clip_actions_component.update() def _use_sel_track(self): return self._modes.selected_mode == 'device_mode' def _should_arm_track(self): return self._modes.selected_mode == 'record_arm_mode' @subject_slot('selected_mode') def _on_layout_changed(self, mode): if mode == 'note_mode': self.set_controlled_track(self._target_track_component.target_track) else: self.release_controlled_track() self._session_record.set_enabled(mode != 'user_mode') @subject_slot('session_record') def _on_session_record_changed(self): status = self.song().session_record feedback_color = int(self._skin['Instrument.FeedbackRecord'] if status else self._skin['Instrument.Feedback']) self._c_instance.set_feedback_velocity(feedback_color) def _clear_send_cache(self): with self.component_guard(): for control in self.controls: control.clear_send_cache() def _update_global_components(self): self._actions_component.update() self._session_record.update() self._modifier_background_component.update() def _layout_setup(self, mode): self._layout_switch(mode) self._clear_send_cache() self._update_global_components() def _layout_switch(self, mode): prefix = consts.SYSEX_STANDARD_PREFIX + consts.SYSEX_PARAM_BYTE_LAYOUT suffix = consts.SYSEX_STANDARD_SUFFIX self._send_midi(prefix + mode + suffix) self._last_sent_mode_byte = mode def _send_identity_request(self): self._send_midi(consts.SYSEX_IDENTITY_REQUEST) def on_identified(self): self._send_challenge() def _send_challenge(self): challenge_bytes = [] for index in range(4): challenge_bytes.append(self._challenge >> 8 * index & 127) challenge = consts.CHALLENGE_PREFIX + tuple(challenge_bytes) + (247,) self._send_midi(challenge) def _on_handshake_successful(self): self._do_send_midi(consts.LIVE_MODE_SWITCH_REQUEST) with self.component_guard(): self._modes.set_enabled(True) self._actions_component.set_enabled(True) self._session_record.set_enabled(True) self._modifier_background_component.set_enabled(True) self._shifted_background.set_enabled(True) self.release_controlled_track() self.set_feedback_channels(consts.FEEDBACK_CHANNELS) if self._last_sent_mode_byte is not None: self._layout_setup(self._last_sent_mode_byte) self.update() def _is_challenge_response(self, midi_bytes): return len(midi_bytes) == 10 and midi_bytes[:7] == consts.SYSEX_STANDARD_PREFIX + consts.SYSEX_CHALLENGE_RESPONSE_BYTE def _is_response_valid(self, midi_bytes): response = long(midi_bytes[7]) response += long(midi_bytes[8] << 8) return response == Live.Application.encrypt_challenge2(self._challenge) def handle_sysex(self, midi_bytes): if len(midi_bytes) < 7: pass if self._is_challenge_response(midi_bytes) and self._is_response_valid(midi_bytes): self._on_handshake_successful() elif midi_bytes[6] in (consts.SYSEX_STATUS_BYTE_MODE, consts.SYSEX_STATUS_BYTE_LAYOUT): pass else: super(Launchpad_Pro, self).handle_sysex(midi_bytes)
class InstrumentComponent(ControlSurfaceComponent): """ handles notes mode and switches between note, drum and dummy audio mode. """ def __init__(self, control_surface, drum_component=None, *a, **k): self._control_surface = control_surface self._drum_component = drum_component self._implicit_arm = True self._modes = ModesComponent(name='Instrument_Modes', is_enabled=False) self._modes.set_enabled = self.set_enabled self._feedback_channels = [ consts.DR_MAP_CHANNEL, consts.DR_MAP_CHANNEL + 1, consts.DR_MAP_CHANNEL + 2, consts.DR_MAP_CHANNEL + 3, consts.DR_MAP_CHANNEL + 4 ] self._non_feedback_channel = consts.DR_MAP_CHANNEL + 5 self._common_component = CommonModeComponent( instrument_component=self, control_surface=self._control_surface) self._scale_component = ScaleComponent( control_surface=self._control_surface, enabled=True) self._note_component = NoteComponent( control_surface=self._control_surface, feedback_channels=self._feedback_channels, non_feedback_channel=self._non_feedback_channel, get_pattern=self._scale_component.get_pattern) super(InstrumentComponent, self).__init__(*a, **k) def set_layers(self, midimap): common_layer_mode = LayerMode( self._common_component, layer=Layer( arrow_left_button=midimap['Arrow_Left_Button'], arrow_right_button=midimap['Arrow_Right_Button'], arrow_up_button=midimap['Arrow_Up_Button'], arrow_down_button=midimap['Arrow_Down_Button'], scale_button=midimap['Scene_Launch_Button_Matrix_Raw'][0][0], scale_up_button=midimap['Scene_Launch_Button_Matrix_Raw'][0] [1], scale_down_button=midimap['Scene_Launch_Button_Matrix_Raw'][0] [2], dummy_button=midimap['Scene_Launch_Button_Matrix_Raw'][0][3], play_button=midimap['Scene_Launch_Button_Matrix_Raw'][0][6], stop_button=midimap['Scene_Launch_Button_Matrix_Raw'][0][7])) drum_group_layer_mode = LayerMode( self._control_surface._drum_group, layer=Layer( #scroll_up_button = midimap['Scene_Launch_Button_Matrix_Raw'][0][1], #scroll_down_button = midimap['Scene_Launch_Button_Matrix_Raw'][0][2], mute_button=midimap['Scene_Launch_Button_Matrix_Raw'][0][4], solo_button=midimap['Scene_Launch_Button_Matrix_Raw'][0][5], #scroll_up_button=midimap['Arrow_Left_Button'], #scroll_down_button=midimap['Arrow_Right_Button'], #scroll_page_up_button=midimap['Arrow_Up_Button'], #scroll_page_down_button=midimap['Arrow_Down_Button'], #drum_matrix = midimap['Drum_Button_Matrix']#, drum_matrix=midimap['Main_Button_Matrix'] #select_button = midimap['Shift_Button'], #delete_button = midimap['Delete_Button'] )) self._modes.add_mode( 'drum_mode', [ partial(self._control_surface._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), partial(self._control_surface._layout_setup, consts.USER_LAYOUT_SYSEX_BYTE, consts.SYSEX_PARAM_BYTE_STANDALONE_LAYOUT), #partial(self._control_surface._layout_setup, consts.DRUM_LAYOUT_SYSEX_BYTE), self._control_surface._setup_drum_group(), drum_group_layer_mode, common_layer_mode #, #drum_mode_note_matrix_translation ]) scale_layer_mode = LayerMode( self._scale_component, layer=Layer(matrix=midimap['Main_Button_Matrix'])) self._modes.add_mode( 'scale_mode', [ partial(self._control_surface._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), partial(self._control_surface._layout_setup, consts.USER_LAYOUT_SYSEX_BYTE, consts.SYSEX_PARAM_BYTE_STANDALONE_LAYOUT), scale_layer_mode, common_layer_mode #, #drum_mode_note_matrix_translation ]) note_layer_mode = LayerMode( self._note_component, layer=Layer( matrix=midimap['Main_Button_Matrix'] #select_button = midimap['Shift_Button'], #delete_button = midimap['Delete_Button'] )) self._modes.add_mode( 'note_mode', [ partial(self._control_surface._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), partial(self._control_surface._layout_setup, consts.USER_LAYOUT_SYSEX_BYTE, consts.SYSEX_PARAM_BYTE_STANDALONE_LAYOUT), note_layer_mode, common_layer_mode #, #drum_mode_note_matrix_translation ]) #Audio mode audio_layer_mode = LayerMode( AudioModeComponent(), layer=Layer(matrix=midimap['Main_Button_Matrix'])) self._modes.add_mode( 'audio_mode', [ partial(self._control_surface._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), partial(self._control_surface._layout_setup, consts.DRUM_LAYOUT_SYSEX_BYTE, consts.SYSEX_PARAM_BYTE_STANDALONE_LAYOUT), #partial(self._control_surface._layout_setup, consts.DRUM_LAYOUT_SYSEX_BYTE),#consts.AUDIO_LAYOUT_SYSEX_BYTE), self._control_surface._clip_delete_layer_mode, common_layer_mode, audio_layer_mode ]) def set_enabled(self, enabled): if not enabled: self._do_implicit_arm(enabled) ControlSurfaceComponent.set_enabled(self, enabled) ModesComponent.set_enabled(self._modes, enabled) if enabled: self._do_implicit_arm(enabled) def _enter_scale_mode(self): self._previous_mode = self._modes.selected_mode self.set_mode('scale_mode') def _leave_scale_mode(self): self.set_mode(self._previous_mode) def _detect_mode(self): track = self._control_surface._target_track_component.target_track #drum_device = self._control_surface._drum_group_finder.drum_group self._control_surface._setup_drum_group() if track is None or track.is_foldable or track in self.song( ).return_tracks or track == self.song( ).master_track or track.is_frozen or track.has_audio_input: self.set_mode('audio_mode') self._scale_component._is_drumrack = False elif self._control_surface._drum_group._drum_group_device: self._scale_component._is_drumrack = True self.set_mode('drum_mode') else: self._scale_component._is_drumrack = False self.set_mode('note_mode') def get_pattern(self): return self._scale_component.get_pattern() def on_selected_track_changed(self): if self._implicit_arm: #self._control_surface._setup_drum_group() self._detect_mode() self._do_implicit_arm() def update(self): self._modes.update() def on_selected_scene_changed(self): #self._do_implicit_arm() self.update() def set_mode(self, mode): self._modes.selected_mode = mode self._modes.update() def get_mode(self): return self._modes.selected_mode def can_implicit_arm_track(self, track): #todo return track.can_be_armed and track.has_midi_input def _do_implicit_arm(self, arm=True): if self._is_enabled: if self._implicit_arm and arm: self._control_surface.set_feedback_channels( self._feedback_channels) self._control_surface.set_controlled_track( self._control_surface._target_track_component.target_track) else: self._control_surface.release_controlled_track() for track in self.song().tracks: if self.can_implicit_arm_track(track): track.implicit_arm = self._is_enabled and self._implicit_arm and arm and self._control_surface._target_track_component.target_track == track
class InstrumentComponent(ControlSurfaceComponent): """ handles notes mode and switches between note, drum and dummy audio mode. """ def __init__(self, control_surface, drum_component=None, *a, **k): self._control_surface = control_surface self._drum_component = drum_component self._implicit_arm = True self._modes = ModesComponent(name="Instrument_Modes", is_enabled=False) self._modes.set_enabled = self.set_enabled self._feedback_channels = [ consts.DR_MAP_CHANNEL, consts.DR_MAP_CHANNEL + 1, consts.DR_MAP_CHANNEL + 2, consts.DR_MAP_CHANNEL + 3, consts.DR_MAP_CHANNEL + 4, ] self._non_feedback_channel = consts.DR_MAP_CHANNEL + 5 self._common_component = CommonModeComponent(instrument_component=self, control_surface=self._control_surface) self._scale_component = ScaleComponent(control_surface=self._control_surface, enabled=True) self._note_component = NoteComponent( control_surface=self._control_surface, feedback_channels=self._feedback_channels, non_feedback_channel=self._non_feedback_channel, get_pattern=self._scale_component.get_pattern, ) super(InstrumentComponent, self).__init__(*a, **k) def set_layers(self, midimap): common_layer_mode = LayerMode( self._common_component, layer=Layer( arrow_left_button=midimap["Arrow_Left_Button"], arrow_right_button=midimap["Arrow_Right_Button"], arrow_up_button=midimap["Arrow_Up_Button"], arrow_down_button=midimap["Arrow_Down_Button"], scale_button=midimap["Scene_Launch_Button_Matrix_Raw"][0][0], scale_up_button=midimap["Scene_Launch_Button_Matrix_Raw"][0][1], scale_down_button=midimap["Scene_Launch_Button_Matrix_Raw"][0][2], dummy_button=midimap["Scene_Launch_Button_Matrix_Raw"][0][3], play_button=midimap["Scene_Launch_Button_Matrix_Raw"][0][6], stop_button=midimap["Scene_Launch_Button_Matrix_Raw"][0][7], ), ) drum_group_layer_mode = LayerMode( self._control_surface._drum_group, layer=Layer( # scroll_up_button = midimap['Scene_Launch_Button_Matrix_Raw'][0][1], # scroll_down_button = midimap['Scene_Launch_Button_Matrix_Raw'][0][2], mute_button=midimap["Scene_Launch_Button_Matrix_Raw"][0][4], solo_button=midimap["Scene_Launch_Button_Matrix_Raw"][0][5], # scroll_up_button=midimap['Arrow_Left_Button'], # scroll_down_button=midimap['Arrow_Right_Button'], # scroll_page_up_button=midimap['Arrow_Up_Button'], # scroll_page_down_button=midimap['Arrow_Down_Button'], # drum_matrix = midimap['Drum_Button_Matrix']#, drum_matrix=midimap["Main_Button_Matrix"] # select_button = midimap['Shift_Button'], # delete_button = midimap['Delete_Button'] ), ) self._modes.add_mode( "drum_mode", [ partial(self._control_surface._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), partial( self._control_surface._layout_setup, consts.USER_LAYOUT_SYSEX_BYTE, consts.SYSEX_PARAM_BYTE_STANDALONE_LAYOUT, ), # partial(self._control_surface._layout_setup, consts.DRUM_LAYOUT_SYSEX_BYTE), self._control_surface._setup_drum_group(), drum_group_layer_mode, common_layer_mode # , # drum_mode_note_matrix_translation ], ) scale_layer_mode = LayerMode(self._scale_component, layer=Layer(matrix=midimap["Main_Button_Matrix"])) self._modes.add_mode( "scale_mode", [ partial(self._control_surface._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), partial( self._control_surface._layout_setup, consts.USER_LAYOUT_SYSEX_BYTE, consts.SYSEX_PARAM_BYTE_STANDALONE_LAYOUT, ), scale_layer_mode, common_layer_mode # , # drum_mode_note_matrix_translation ], ) note_layer_mode = LayerMode( self._note_component, layer=Layer( matrix=midimap["Main_Button_Matrix"] # select_button = midimap['Shift_Button'], # delete_button = midimap['Delete_Button'] ), ) self._modes.add_mode( "note_mode", [ partial(self._control_surface._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), partial( self._control_surface._layout_setup, consts.USER_LAYOUT_SYSEX_BYTE, consts.SYSEX_PARAM_BYTE_STANDALONE_LAYOUT, ), note_layer_mode, common_layer_mode # , # drum_mode_note_matrix_translation ], ) # Audio mode audio_layer_mode = LayerMode(AudioModeComponent(), layer=Layer(matrix=midimap["Main_Button_Matrix"])) self._modes.add_mode( "audio_mode", [ partial(self._control_surface._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), partial( self._control_surface._layout_setup, consts.DRUM_LAYOUT_SYSEX_BYTE, consts.SYSEX_PARAM_BYTE_STANDALONE_LAYOUT, ), # partial(self._control_surface._layout_setup, consts.DRUM_LAYOUT_SYSEX_BYTE),#consts.AUDIO_LAYOUT_SYSEX_BYTE), self._control_surface._clip_delete_layer_mode, common_layer_mode, audio_layer_mode, ], ) def set_enabled(self, enabled): if not enabled: self._do_implicit_arm(enabled) ControlSurfaceComponent.set_enabled(self, enabled) ModesComponent.set_enabled(self._modes, enabled) if enabled: self._do_implicit_arm(enabled) def _enter_scale_mode(self): self._previous_mode = self._modes.selected_mode self.set_mode("scale_mode") def _leave_scale_mode(self): self.set_mode(self._previous_mode) def _detect_mode(self): track = self._control_surface._target_track_component.target_track # drum_device = self._control_surface._drum_group_finder.drum_group self._control_surface._setup_drum_group() if ( track is None or track.is_foldable or track in self.song().return_tracks or track == self.song().master_track or track.is_frozen or track.has_audio_input ): self.set_mode("audio_mode") self._scale_component._is_drumrack = False elif self._control_surface._drum_group._drum_group_device: self._scale_component._is_drumrack = True self.set_mode("drum_mode") else: self._scale_component._is_drumrack = False self.set_mode("note_mode") def get_pattern(self): return self._scale_component.get_pattern() def on_selected_track_changed(self): if self._implicit_arm: # self._control_surface._setup_drum_group() self._detect_mode() self._do_implicit_arm() def update(self): self._modes.update() def on_selected_scene_changed(self): # self._do_implicit_arm() self.update() def set_mode(self, mode): self._modes.selected_mode = mode self._modes.update() def get_mode(self): return self._modes.selected_mode def can_implicit_arm_track(self, track): # todo return track.can_be_armed and track.has_midi_input def _do_implicit_arm(self, arm=True): if self._is_enabled: if self._implicit_arm and arm: self._control_surface.set_feedback_channels(self._feedback_channels) self._control_surface.set_controlled_track(self._control_surface._target_track_component.target_track) else: self._control_surface.release_controlled_track() for track in self.song().tracks: if self.can_implicit_arm_track(track): track.implicit_arm = ( self._is_enabled and self._implicit_arm and arm and self._control_surface._target_track_component.target_track == track )
class Launchpad_Pro(IdentifiableControlSurface, OptimizedControlSurface): identity_request = consts.SYSEX_IDENTITY_REQUEST def __init__(self, c_instance, *a, **k): product_id_bytes = consts.MANUFACTURER_ID + consts.DEVICE_CODE super(Launchpad_Pro, self).__init__(c_instance=c_instance, product_id_bytes=product_id_bytes, *a, **k) self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143 with self.component_guard(): self._skin = make_default_skin() with inject(skin=const(self._skin)).everywhere(): self._midimap = MidiMap() self._target_track_component = TargetTrackComponent(name='Target_Track') self._create_background() self._create_global_component() self._last_sent_mode_byte = None with inject(layout_setup=const(self._layout_setup), should_arm=const(self._should_arm_track)).everywhere(): self._create_session() self._create_recording() self._create_actions() self._create_drums() self._create_mixer() self._create_device() self._create_modes() self._create_user() self._on_session_record_changed.subject = self.song() self.set_device_component(self._device) self._on_session_record_changed() return def disconnect(self): self._send_midi(consts.TURN_OFF_LEDS) self._send_midi(consts.QUIT_MESSAGE) super(Launchpad_Pro, self).disconnect() def _create_background(self): self._modifier_background_component = ModifierBackgroundComponent(name='Background_Component', is_enabled=False, layer=Layer(shift_button=self._midimap['Shift_Button'])) self._shifted_background = BackgroundComponent(name='No_Op_Shifted_Buttons', is_enabled=False, layer=Layer(click_bitton=self._midimap.with_shift('Click_Button'), delete_button=self._midimap.with_shift('Delete_Button'), duplicate_button=self._midimap.with_shift('Duplicate_Button'), double_button=self._midimap.with_shift('Double_Loop_Button'), session_record_button=self._midimap.with_shift('Session_Record_Button'))) def _create_global_component(self): self._actions_component = ActionsComponent(name='Global_Actions', is_enabled=False, layer=Layer(undo_button=self._midimap['Undo_Button'], redo_button=self._midimap.with_shift('Undo_Button'), metronome_button=self._midimap['Click_Button'], quantization_on_button=self._midimap.with_shift('Quantize_Button'))) def _create_session(self): self._session = SessionComponent(NUM_TRACKS, NUM_SCENES, auto_name=True, is_enabled=False, enable_skinning=True, layer=Layer(track_bank_left_button=self._midimap['Arrow_Left_Button'], track_bank_right_button=self._midimap['Arrow_Right_Button'], scene_bank_up_button=self._midimap['Arrow_Up_Button'], scene_bank_down_button=self._midimap['Arrow_Down_Button'])) self._session.set_enabled(True) self._session.set_rgb_mode(LIVE_COLORS_TO_MIDI_VALUES, RGB_COLOR_TABLE) SpecialClipSlotComponent.quantization_component = self._actions_component for scene_index in xrange(NUM_SCENES): scene = self._session.scene(scene_index) scene.layer = Layer(select_button=self._midimap['Shift_Button'], delete_button=self._midimap['Delete_Button'], duplicate_button=self._midimap['Duplicate_Button']) for track_index in xrange(NUM_TRACKS): slot = scene.clip_slot(track_index) slot.layer = Layer(select_button=self._midimap['Shift_Button'], delete_button=self._midimap['Delete_Button'], duplicate_button=self._midimap['Duplicate_Button'], double_loop_button=self._midimap['Double_Loop_Button'], quantize_button=self._midimap['Quantize_Button']) self._session_zoom = SessionZoomingComponent(self._session, name='Session_Overview', is_enabled=True, enable_skinning=True) def _create_recording(self): self._session_record = SpecialSessionRecordingComponent(self._target_track_component, name='Session_Recording', is_enabled=False, layer=Layer(record_button=self._midimap['Session_Record_Button'])) def _create_actions(self): self._clip_actions_component = ClipActionsComponent(self._target_track_component, name='Clip_Actions', is_enabled=False, layer=Layer(duplicate_button=self._midimap['Duplicate_Button'], double_button=self._midimap['Double_Loop_Button'], quantize_button=self._midimap['Quantize_Button'])) ClipActionsComponent.quantization_component = self._actions_component def _create_drums(self): self._drum_group_finder = DrumGroupFinderComponent(self._target_track_component, name='Drum_Group_Finder', is_enabled=False, layer=None) self._on_drum_group_changed.subject = self._drum_group_finder self._drum_group_finder.set_enabled(True) self._drum_group = DrumGroupComponent(self._clip_actions_component, name='Drum_Group_Control', translation_channel=consts.DR_MAP_CHANNEL) self._drum_group.set_enabled(True) return def _create_mixer(self): self._mixer = SpecialMixerComponent(NUM_TRACKS, auto_name=True, is_enabled=True, invert_mute_feedback=True) self._mixer.name = 'Mixer_Control' self._session.set_mixer(self._mixer) def _create_device(self): self._device = SpecialDeviceComponent(name='Device_Control', is_enabled=False, device_selection_follows_track_selection=True) self._device_navigation = DeviceNavigationComponent(name='Device_Navigation') self._device_background = BackgroundComponent(name='Device_Background_Component') def _setup_drum_group(self): self._drum_group.set_drum_group_device(self._drum_group_finder.drum_group) def _create_translation(self, comp_name, channel, button_layer, should_enable=True, should_reset=True): translation_component = TranslationComponent(name=comp_name, translated_channel=channel, should_enable=should_enable, should_reset=should_reset, is_enabled=False, layer=button_layer) setattr(self, '_' + comp_name.lower(), translation_component) return translation_component def _create_modes(self): self._modes = ModesComponent(name='Launchpad_Modes', is_enabled=False) self._session_layer_mode = AddLayerMode(self._session, Layer(scene_launch_buttons=self._midimap['Scene_Launch_Button_Matrix'], clip_launch_buttons=self._midimap['Main_Button_Matrix'], delete_button=self._midimap['Delete_Button'], duplicate_button=self._midimap['Duplicate_Button'], double_button=self._midimap['Double_Loop_Button'], quantize_button=self._midimap['Quantize_Button'])) action_button_background = BackgroundComponent(name='No_Op_Buttons') self._action_button_background_layer_mode = LayerMode(action_button_background, Layer(delete_button=self._midimap['Delete_Button'], quantize_button=self._midimap['Quantize_Button'], duplicate_button=self._midimap['Duplicate_Button'], double_button=self._midimap['Double_Loop_Button'])) self._clip_delete_layer_mode = AddLayerMode(self._clip_actions_component, layer=Layer(delete_button=self._midimap['Delete_Button'])) self._create_session_zooming_modes() self._create_session_mode() self._create_note_modes() self._create_device_mode() self._create_user_mode() self._create_record_arm_mode() self._create_track_select_mode() self._create_mute_mode() self._create_solo_mode() self._create_volume_mode() self._create_pan_mode() self._create_sends_mode() self._create_stop_clips_mode() self._modes.layer = Layer(session_mode_button=self._midimap['Session_Mode_Button'], note_mode_button=self._midimap['Note_Mode_Button'], device_mode_button=self._midimap['Device_Mode_Button'], user_mode_button=self._midimap['User_Mode_Button'], record_arm_mode_button=self._midimap['Record_Arm_Mode_Button'], track_select_mode_button=self._midimap['Track_Select_Mode_Button'], mute_mode_button=self._midimap['Mute_Mode_Button'], solo_mode_button=self._midimap['Solo_Mode_Button'], volume_mode_button=self._midimap['Volume_Mode_Button'], pan_mode_button=self._midimap['Pan_Mode_Button'], sends_mode_button=self._midimap['Sends_Mode_Button'], stop_clip_mode_button=self._midimap['Stop_Clip_Mode_Button']) self._modes.selected_mode = 'session_mode' self._on_layout_changed.subject = self._modes def _create_session_zooming_modes(self): session_zoom_layer = Layer(button_matrix=self._midimap['Main_Button_Matrix'], nav_left_button=self._midimap['Arrow_Left_Button'], nav_right_button=self._midimap['Arrow_Right_Button'], nav_up_button=self._midimap['Arrow_Up_Button'], nav_down_button=self._midimap['Arrow_Down_Button']) session_zooming_layer_mode = LayerMode(self._session_zoom, session_zoom_layer) self._session_zooming_manager = SessionZoomingManagerComponent(self._modes, is_enabled=False) session_zooming_button_layer_mode = LayerMode(self._session_zooming_manager, Layer(session_zooming_button=self._midimap['Session_Mode_Button'])) self._prioritized_session_zooming_button_layer_mode = LayerMode(self._session_zooming_manager, Layer(session_zooming_button=self._midimap['Session_Mode_Button'], priority=1)) self._session_zooming_background = BackgroundComponent(name='Session_Zooming_Background') session_zooming_background_layer_mode = LayerMode(self._session_zooming_background, Layer(scene_launch_buttons=self._midimap['Scene_Launch_Button_Matrix'], delete_button=self._midimap['Delete_Button'], quantize_button=self._midimap['Quantize_Button'], duplicate_button=self._midimap['Duplicate_Button'], double_loop_button=self._midimap['Double_Loop_Button'])) self._modes.add_mode('session_zooming_mode', [ self._session_zooming_manager, session_zooming_button_layer_mode, session_zooming_layer_mode, session_zooming_background_layer_mode]) self._modes.add_mode('prioritized_session_zooming_mode', [ partial(self._layout_switch, consts.SESSION_LAYOUT_SYSEX_BYTE), self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, session_zooming_layer_mode, session_zooming_background_layer_mode, self.update]) def _create_session_mode(self): self._modes.add_mode('session_mode', [ partial(self._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), self._session_layer_mode, self._session.update_navigation_buttons], behaviour=CancelingReenterBehaviour('session_zooming_mode')) def _create_note_modes(self): note_mode_matrix_translation = self._create_translation('Note_Mode_Matrix_Translation', consts.CHROM_MAP_CHANNEL, Layer(button_matrix=self._midimap['Main_Button_Matrix'], note_button_matrix=self._midimap['Note_Button_Matrix'], drum_matrix=self._midimap['Drum_Button_Matrix'], mixer_button_matrix=self._midimap['Mixer_Button_Matrix']), should_enable=False) note_mode_scene_launch_translation = self._create_translation('Note_Mode_Scene_Launch_Translation', consts.CHROM_MAP_CHANNEL, Layer(scene_launch_buttons=self._midimap['Scene_Launch_Button_Matrix'])) scale_setup_mode_button_lighting = LedLightingComponent(name='LED_Lighting_Component', is_enabled=False, layer=Layer(button=self._midimap.with_shift('Note_Mode_Button'))) drum_mode_note_matrix_translation = self._create_translation('Drum_Mode_Note_Button_Translation', 0, Layer(note_button_matrix=self._midimap['Note_Button_Matrix']), should_enable=False, should_reset=False) drum_group_layer_mode = LayerMode(self._drum_group, layer=Layer(scroll_up_button=self._midimap['Arrow_Left_Button'], scroll_down_button=self._midimap['Arrow_Right_Button'], scroll_page_up_button=self._midimap['Arrow_Up_Button'], scroll_page_down_button=self._midimap['Arrow_Down_Button'], drum_matrix=self._midimap['Drum_Button_Matrix'], select_button=self._midimap['Shift_Button'], delete_button=self._midimap['Delete_Button'])) self._note_modes = SpecialModesComponent(name='Note_Modes') self._note_modes.add_mode('chromatic_mode', [ partial(self._layout_setup, consts.NOTE_LAYOUT_SYSEX_BYTE), self._clip_delete_layer_mode, note_mode_matrix_translation, scale_setup_mode_button_lighting]) self._note_modes.add_mode('drum_mode', [ partial(self._layout_setup, consts.DRUM_LAYOUT_SYSEX_BYTE), self._setup_drum_group, drum_group_layer_mode, drum_mode_note_matrix_translation]) self._note_modes.add_mode('audio_mode', [ partial(self._layout_setup, consts.AUDIO_LAYOUT_SYSEX_BYTE), self._clip_delete_layer_mode]) self._note_modes.set_enabled(False) self._modes.add_mode('note_mode', [ note_mode_scene_launch_translation, self._note_modes, self._select_note_mode, self._select_target_track, self._clip_actions_component, self._show_playing_clip, self._set_clip_actions_type], behaviour=ReenterBehaviour(self.toggle_detail_view)) self._session_record.set_modes_component(self._modes) self._session_record.set_note_mode_name('note_mode') def _create_device_mode(self): device_mode_scene_launch_translation = self._create_translation('Device_Mode_Scene_Launch_Translation', consts.DEVICE_MAP_CHANNEL, Layer(scene_launch_buttons=self._midimap['Scene_Launch_Button_Matrix'])) device_layer_mode = LayerMode(self._device, layer=Layer(parameter_controls=self._midimap['Slider_Button_Matrix'])) device_nav_layer_mode = LayerMode(self._device_navigation, layer=Layer(device_nav_left_button=self._midimap['Arrow_Left_Button'], device_nav_right_button=self._midimap['Arrow_Right_Button'])) device_background_layer_mode = LayerMode(self._device_background, layer=Layer(arrow_up_button=self._midimap['Arrow_Up_Button'], arrow_down_button=self._midimap['Arrow_Down_Button'])) self._modes.add_mode('device_mode', [ partial(self._layout_setup, consts.FADER_LAYOUT_SYSEX_BYTE), self._device, device_layer_mode, device_nav_layer_mode, device_background_layer_mode, self._clip_actions_component, self._clip_delete_layer_mode, device_mode_scene_launch_translation, self._show_playing_clip, self._set_clip_actions_type], behaviour=ReenterBehaviour(self.toggle_detail_view)) def _create_user_mode(self): self._modes.add_mode('user_mode', [ partial(self._layout_setup, consts.USER_LAYOUT_SYSEX_BYTE)]) def _create_record_arm_mode(self): arm_layer_mode = LayerMode(self._mixer, layer=Layer(arm_buttons=self._midimap['Mixer_Button_Matrix'])) self._modes.add_mode('record_arm_mode', [ partial(self._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), self._session_layer_mode, arm_layer_mode, self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, self._session.update_navigation_buttons], behaviour=SpecialReenterBehaviour('session_mode')) def _create_track_select_mode(self): track_select_layer_mode = LayerMode(self._mixer, layer=Layer(track_select_buttons=self._midimap['Mixer_Button_Matrix'])) self._modes.add_mode('track_select_mode', [ partial(self._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), self._session_layer_mode, track_select_layer_mode, self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, self._session.update_navigation_buttons], behaviour=SpecialReenterBehaviour('session_mode')) def _create_mute_mode(self): mute_layer_mode = LayerMode(self._mixer, layer=Layer(mute_buttons=self._midimap['Mixer_Button_Matrix'])) self._modes.add_mode('mute_mode', [ partial(self._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), self._session_layer_mode, mute_layer_mode, self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, self._session.update_navigation_buttons], behaviour=SpecialReenterBehaviour('session_mode')) def _create_solo_mode(self): solo_layer_mode = LayerMode(self._mixer, layer=Layer(solo_buttons=self._midimap['Mixer_Button_Matrix'])) self._modes.add_mode('solo_mode', [ partial(self._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), self._session_layer_mode, solo_layer_mode, self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, self._session.update_navigation_buttons], behaviour=SpecialReenterBehaviour('session_mode')) def _create_volume_mode(self): volume_mode_scene_launch_translation = self._create_translation('Volume_Mode_Scene_Launch_Translation', consts.VOLUME_MAP_CHANNEL, Layer(scene_launch_buttons=self._midimap['Scene_Launch_Button_Matrix'])) volume_layer_mode = LayerMode(self._mixer, layer=Layer(volume_controls=self._midimap['Slider_Button_Matrix'])) self._modes.add_mode('volume_mode', [ partial(self._layout_setup, consts.FADER_LAYOUT_SYSEX_BYTE), volume_layer_mode, self._action_button_background_layer_mode, self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, volume_mode_scene_launch_translation, self._session.update_navigation_buttons], behaviour=SpecialReenterBehaviour('session_mode')) def _create_pan_mode(self): pan_mode_scene_launch_translation = self._create_translation('Pan_Mode_Scene_Launch_Translation', consts.PAN_MAP_CHANNEL, Layer(scene_launch_buttons=self._midimap['Scene_Launch_Button_Matrix'])) pan_layer_mode = LayerMode(self._mixer, layer=Layer(pan_controls=self._midimap['Slider_Button_Matrix'])) self._modes.add_mode('pan_mode', [ partial(self._layout_setup, consts.FADER_LAYOUT_SYSEX_BYTE), pan_layer_mode, self._action_button_background_layer_mode, self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, pan_mode_scene_launch_translation, self._session.update_navigation_buttons], behaviour=SpecialReenterBehaviour('session_mode')) def _create_sends_mode(self): send_layer_mode = LayerMode(self._mixer, layer=Layer(send_controls=self._midimap['Slider_Button_Matrix'], send_select_buttons=self._midimap['Scene_Launch_Button_Matrix'])) self._modes.add_mode('sends_mode', [ partial(self._layout_setup, consts.FADER_LAYOUT_SYSEX_BYTE), send_layer_mode, self._action_button_background_layer_mode, self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, self._session.update_navigation_buttons], behaviour=SpecialReenterBehaviour('session_mode')) def _create_stop_clips_mode(self): stop_layer_mode = AddLayerMode(self._session, Layer(stop_track_clip_buttons=self._midimap['Mixer_Button_Matrix'], stop_scene_clip_buttons=self._midimap['Scene_Stop_Button_Matrix'], stop_all_clips_button=self._midimap['Stop_All_Clips_Button'])) self._modes.add_mode('stop_clip_mode', [ partial(self._layout_setup, consts.SESSION_LAYOUT_SYSEX_BYTE), self._session_layer_mode, stop_layer_mode, self._session_zooming_manager, self._prioritized_session_zooming_button_layer_mode, self._session.update_navigation_buttons], behaviour=SpecialReenterBehaviour('session_mode')) def toggle_detail_view(self): view = self.application().view if view.is_view_visible('Detail'): if view.is_view_visible('Detail/DeviceChain'): view.show_view('Detail/Clip') else: view.show_view('Detail/DeviceChain') def _create_user(self): self._user_matrix_component = UserMatrixComponent(name='User_Matrix_Component', is_enabled=False, layer=Layer(user_button_matrix_ch_6=self._midimap['User_Button_Matrix_Ch_6'], user_button_matrix_ch_7=self._midimap['User_Button_Matrix_Ch_7'], user_button_matrix_ch_8=self._midimap['User_Button_Matrix_Ch_8'], user_button_matrix_ch_14=self._midimap['User_Button_Matrix_Ch_14'], user_button_matrix_ch_15=self._midimap['User_Button_Matrix_Ch_15'], user_button_matrix_ch_16=self._midimap['User_Button_Matrix_Ch_16'], user_left_side_button_matrix_ch_6=self._midimap['User_Left_Side_Button_Matrix_Ch_6'], user_left_side_button_matrix_ch_7=self._midimap['User_Left_Side_Button_Matrix_Ch_7'], user_left_side_button_matrix_ch_8=self._midimap['User_Left_Side_Button_Matrix_Ch_8'], user_left_side_button_matrix_ch_14=self._midimap['User_Left_Side_Button_Matrix_Ch_14'], user_left_side_button_matrix_ch_15=self._midimap['User_Left_Side_Button_Matrix_Ch_15'], user_left_side_button_matrix_ch_16=self._midimap['User_Left_Side_Button_Matrix_Ch_16'], user_right_side_button_matrix_ch_6=self._midimap['User_Right_Side_Button_Matrix_Ch_6'], user_right_side_button_matrix_ch_7=self._midimap['User_Right_Side_Button_Matrix_Ch_7'], user_right_side_button_matrix_ch_8=self._midimap['User_Right_Side_Button_Matrix_Ch_8'], user_right_side_button_matrix_ch_14=self._midimap['User_Right_Side_Button_Matrix_Ch_14'], user_right_side_button_matrix_ch_15=self._midimap['User_Right_Side_Button_Matrix_Ch_15'], user_right_side_button_matrix_ch_16=self._midimap['User_Right_Side_Button_Matrix_Ch_16'], user_bottom_button_matrix_ch_6=self._midimap['User_Bottom_Button_Matrix_Ch_6'], user_bottom_button_matrix_ch_7=self._midimap['User_Bottom_Button_Matrix_Ch_7'], user_bottom_button_matrix_ch_8=self._midimap['User_Bottom_Button_Matrix_Ch_8'], user_bottom_button_matrix_ch_14=self._midimap['User_Bottom_Button_Matrix_Ch_14'], user_bottom_button_matrix_ch_15=self._midimap['User_Bottom_Button_Matrix_Ch_15'], user_bottom_button_matrix_ch_16=self._midimap['User_Bottom_Button_Matrix_Ch_16'], user_arrow_button_matrix_ch_6=self._midimap['User_Arrow_Button_Matrix_Ch_6'], user_arrow_button_matrix_ch_7=self._midimap['User_Arrow_Button_Matrix_Ch_7'], user_arrow_button_matrix_ch_8=self._midimap['User_Arrow_Button_Matrix_Ch_8'], user_arrow_button_matrix_ch_14=self._midimap['User_Arrow_Button_Matrix_Ch_14'], user_arrow_button_matrix_ch_15=self._midimap['User_Arrow_Button_Matrix_Ch_15'], user_arrow_button_matrix_ch_16=self._midimap['User_Arrow_Button_Matrix_Ch_16'])) self._user_matrix_component.set_enabled(True) @subject_slot('drum_group') def _on_drum_group_changed(self): if self._note_modes.selected_mode == 'drum_mode': self._drum_group.set_drum_group_device(self._drum_group_finder.drum_group) if self._modes.selected_mode == 'note_mode': self._select_note_mode() else: self.release_controlled_track() self._update_note_mode_button(self._drum_group_finder.drum_group is not None) return def _select_note_mode(self): u""" Selects which note mode to use depending on the kind of current target track and its device chain. Will also select the target if specified. """ track = self._target_track_component.target_track drum_device = self._drum_group_finder.drum_group if track is None or track.is_foldable or track in self.song().return_tracks or track == self.song().master_track or track.is_frozen or track.has_audio_input: self._note_modes.selected_mode = 'audio_mode' else: if drum_device: self._note_modes.selected_mode = 'drum_mode' else: self._note_modes.selected_mode = 'chromatic_mode' self._modes.update() if self._note_modes.selected_mode == 'audio_mode': self.release_controlled_track() else: self.set_controlled_track(self._target_track_component.target_track) return def _select_target_track(self): track = self._target_track_component.target_track if track != self.song().view.selected_track: self.song().view.selected_track = track def _update_note_mode_button(self, focused_track_is_drum_track): button = self._midimap['Note_Mode_Button'] if focused_track_is_drum_track: button.default_states = {True: 'Mode.Drum.On', False: 'Mode.Drum.Off'} else: button.default_states = {True: 'Mode.Chromatic.On', False: 'Mode.Chromatic.Off'} button.reset_state() self._modes.update() def _show_playing_clip(self): track = None if self._use_sel_track(): track = self.song().view.selected_track else: track = self._target_track_component.target_track if track in self.song().tracks: slot_index = track.fired_slot_index if slot_index < 0: slot_index = track.playing_slot_index if slot_index >= 0: clip_slot = track.clip_slots[slot_index] self.song().view.highlighted_clip_slot = clip_slot return def _set_clip_actions_type(self): self._clip_actions_component.use_selected_track(self._use_sel_track()) self._clip_actions_component.update() def _use_sel_track(self): return self._modes.selected_mode == 'device_mode' def _should_arm_track(self): return self._modes.selected_mode == 'record_arm_mode' @subject_slot('selected_mode') def _on_layout_changed(self, mode): if mode == 'note_mode': self.set_controlled_track(self._target_track_component.target_track) else: self.release_controlled_track() self._session_record.set_enabled(mode != 'user_mode') @subject_slot('session_record') def _on_session_record_changed(self): status = self.song().session_record feedback_color = int(self._skin['Instrument.FeedbackRecord'] if status else self._skin['Instrument.Feedback']) self._c_instance.set_feedback_velocity(feedback_color) def _clear_send_cache(self): with self.component_guard(): for control in self.controls: control.clear_send_cache() def _update_hardware(self): self._clear_send_cache() self.update() def _update_global_components(self): self._actions_component.update() self._session_record.update() self._modifier_background_component.update() def _layout_setup(self, mode): self._layout_switch(mode) self._clear_send_cache() self._update_global_components() def _layout_switch(self, mode): prefix = consts.SYSEX_STANDARD_PREFIX + consts.SYSEX_PARAM_BYTE_LAYOUT suffix = consts.SYSEX_STANDARD_SUFFIX self._send_midi(prefix + mode + suffix) self._last_sent_mode_byte = mode def port_settings_changed(self): self.set_highlighting_session_component(None) super(Launchpad_Pro, self).port_settings_changed() return def on_identified(self): self._send_challenge() def _send_challenge(self): challenge_bytes = [] for index in range(4): challenge_bytes.append(self._challenge >> 8 * index & 127) challenge = consts.CHALLENGE_PREFIX + tuple(challenge_bytes) + (247, ) self._send_midi(challenge) def _on_handshake_successful(self): self._do_send_midi(consts.LIVE_MODE_SWITCH_REQUEST) with self.component_guard(): self._modes.set_enabled(True) self._actions_component.set_enabled(True) self._session_record.set_enabled(True) self._modifier_background_component.set_enabled(True) self._shifted_background.set_enabled(True) self.release_controlled_track() self.set_feedback_channels(consts.FEEDBACK_CHANNELS) if self._last_sent_mode_byte is not None: self._layout_setup(self._last_sent_mode_byte) self.set_highlighting_session_component(self._session) self.update() return def _is_challenge_response(self, midi_bytes): return len(midi_bytes) == 10 and midi_bytes[:7] == consts.SYSEX_STANDARD_PREFIX + consts.SYSEX_CHALLENGE_RESPONSE_BYTE def _is_response_valid(self, midi_bytes): response = long(midi_bytes[7]) response += long(midi_bytes[8] << 8) return response == Live.Application.encrypt_challenge2(self._challenge) def handle_sysex(self, midi_bytes): if len(midi_bytes) < 7: pass else: if self._is_challenge_response(midi_bytes) and self._is_response_valid(midi_bytes): self._on_handshake_successful() else: if midi_bytes[6] == consts.SYSEX_STATUS_BYTE_LAYOUT and midi_bytes[7] == consts.NOTE_LAYOUT_SYSEX_BYTE[0]: self._update_hardware() else: if midi_bytes[6] in (consts.SYSEX_STATUS_BYTE_MODE, consts.SYSEX_STATUS_BYTE_LAYOUT): pass else: super(Launchpad_Pro, self).handle_sysex(midi_bytes)