def _create_view_control(self):
     self._view_control = NotifyingViewControlComponent(
         name='Track_Scroller',
         is_enabled=False,
         track_provider=(self._session_ring),
         layer=Layer(prev_track_button='left_button',
                     next_track_button='right_button'))
     self._view_control.set_enabled(True)
     self._session_ring_selection_linking = self.register_disconnectable(
         SessionRingSelectionLinking(
             session_ring=(self._session_ring),
             selection_changed_notifier=(self._view_control)))
Example #2
0
 def _create_view_control(self):
     self._view_control = NotifyingViewControlComponent(
         name=b'View_Control',
         is_enabled=False,
         track_provider=self._session_ring,
         enable_skinning=False,
         layer=Layer(next_track_button=b'right_button',
                     prev_track_button=b'left_button',
                     next_scene_button=b'down_button',
                     prev_scene_button=b'up_button'))
     self._view_control.set_enabled(True)
     self._session_ring_selection_linking = self.register_disconnectable(
         SessionRingSelectionLinking(
             session_ring=self._session_ring,
             selection_changed_notifier=self._view_control))
Example #3
0
 def _create_track_navigation(self):
     self._view_control = NotifyingViewControlComponent(
         name='view_control',
         is_enabled=False,
         track_provider=(self._session_ring),
         layer=Layer(prev_track_button='track_left_button',
                     next_track_button='track_right_button',
                     prev_track_page_button='track_left_button_with_shift',
                     next_track_page_button='track_right_button_with_shift',
                     prev_scene_button='up_button_with_shift',
                     next_scene_button='down_button_with_shift'))
     self._view_control.set_enabled(True)
     self._session_ring_selection_linking = self.register_disconnectable(
         SessionRingSelectionLinking(
             session_ring=(self._session_ring),
             selection_changed_notifier=(self._view_control)))
class SLMkIII(IdentifiableControlSurface):
    _sysex_message_cache = MidiMessageCache()

    def __init__(self, *a, **k):
        super(SLMkIII, self).__init__(
            product_id_bytes=(sysex.NOVATION_MANUFACTURER_ID +
                              sysex.DEVICE_FAMILY_CODE +
                              sysex.DEVICE_FAMILY_MEMBER_CODE),
            *a,
            **k)
        self._main_modes = NullModes()
        self._element_injector = inject(
            element_container=const(None)).everywhere()
        self._message_injector = inject(message=const(None)).everywhere()
        with self.component_guard():
            with inject(skin=const(skin),
                        message_cache=const(
                            self._sysex_message_cache)).everywhere():
                self._elements = Elements()
        self._element_injector = inject(
            element_container=const(self._elements)).everywhere()
        with self.component_guard():
            self._create_message()
        self._message_injector = inject(
            message=const(self._message)).everywhere()
        self._switch_display_layout(sysex.KNOB_SCREEN_LAYOUT_BYTE)
        self._device_bank_registry = DeviceBankRegistry()
        with self.component_guard():
            self._create_session()
            self._create_mixer()
            self._create_transport()
            self._create_session_recording()
            self._create_auto_arm()
            self._create_track_navigation()
            self._create_drums()
            self._create_device()
            self._create_device_navigation()
            self._create_actions()
            self._create_clip_actions()
            self._create_background()
            self._create_modes()
        self._drum_group_finder = self.register_disconnectable(
            PercussionInstrumentFinder(
                device_parent=self.song.view.selected_track))
        self.__on_drum_group_found.subject = self._drum_group_finder
        self.__on_drum_group_found()
        self.__on_selected_track_changed.subject = self.song.view
        self.__on_selected_track_changed()
        self.__on_session_record_changed.subject = self.song
        self.__on_record_mode_changed.subject = self.song
        self.set_feedback_channels([DRUM_FEEDBACK_CHANNEL])
        self._set_feedback_velocity()

    def on_identified(self, midi_bytes):
        self._switch_display_layout(sysex.KNOB_SCREEN_LAYOUT_BYTE, force=True)
        self._main_modes.selected_mode = u'device_control'
        self._auto_arm.set_enabled(True)
        self._session_ring.set_enabled(True)
        self.set_feedback_channels([DRUM_FEEDBACK_CHANNEL])
        super(SLMkIII, self).on_identified(midi_bytes)

    def disconnect(self):
        self._auto_arm.set_enabled(False)
        super(SLMkIII, self).disconnect()

    def port_settings_changed(self):
        self._auto_arm.set_enabled(False)
        self._session_ring.set_enabled(False)
        super(SLMkIII, self).port_settings_changed()

    @contextmanager
    def _component_guard(self):
        with super(SLMkIII, self)._component_guard():
            with self._element_injector:
                with self._message_injector:
                    yield
        self._format_and_send_sysex()

    def _format_and_send_sysex(self):
        messages_to_send = sysex.make_sysex_from_segments(
            self._sysex_message_cache.messages)
        for msg in messages_to_send:
            self._send_midi(msg)

        self._sysex_message_cache.clear()

    def _install_mapping(self, midi_map_handle, control, parameter,
                         feedback_delay, feedback_map):
        success = False
        if control.message_type() == MIDI_CC_TYPE:
            feedback_rule = Live.MidiMap.CCFeedbackRule()
            feedback_rule.cc_no = control.message_identifier()
            feedback_rule.cc_value_map = feedback_map
            feedback_rule.channel = getattr(control, u'feedback_channel',
                                            control.message_channel())
            feedback_rule.delay_in_ms = feedback_delay
            success = Live.MidiMap.map_midi_cc_with_feedback_map(
                midi_map_handle, parameter, control.message_channel(),
                control.message_identifier(), control.message_map_mode(),
                feedback_rule, not control.needs_takeover(),
                control.mapping_sensitivity)
            if success:
                Live.MidiMap.send_feedback_for_parameter(
                    midi_map_handle, parameter)
        return success

    def _create_message(self):
        self._message = MessageComponent(
            name=u'Message',
            is_enabled=False,
            layer=Layer(display=u'message_display'))
        self._message.set_enabled(True)

    def _switch_display_layout(self, layout_byte, force=False):
        display_layout_switch = self._elements.display_layout_switch
        if force:
            display_layout_switch.clear_send_cache()
        display_layout_switch.send_value(layout_byte)
        self._clear_display_send_cache()

    def _clear_display_send_cache(self):
        for display in self._elements.text_display_lines:
            display.clear_send_cache()

    def _create_session(self):
        self._session_ring = SessionRingComponent(
            is_enabled=False,
            num_tracks=SESSION_WIDTH,
            num_scenes=SESSION_HEIGHT,
            tracks_to_use=lambda: tuple(self.song.visible_tracks) + tuple(
                self.song.return_tracks) + (self.song.master_track, ),
            name=u'Session_Ring')
        self._session = SessionComponent(
            is_enabled=False,
            session_ring=self._session_ring,
            name=u'Session',
            layer=Layer(
                clip_launch_buttons=u'pads',
                scene_launch_buttons=u'scene_launch_buttons',
                stop_track_clip_buttons=u'shifted_pad_row_1',
                stop_all_clips_button=u'shifted_scene_launch_button_1'))
        self._session.set_rgb_mode(CLIP_COLOR_TABLE, RGB_COLOR_TABLE)
        self._session.set_enabled(True)
        self._session_navigation = SessionNavigationComponent(
            session_ring=self._session_ring, name=u'Session_Navigation')

    def _create_mixer(self):
        self._mixer = MixerComponent(
            name=u'Mixer',
            is_enabled=False,
            auto_name=True,
            tracks_provider=self._session_ring,
            track_assigner=RightAlignTracksTrackAssigner(
                song=self.song, include_master_track=True),
            invert_mute_feedback=True,
            layer=Layer(volume_controls=u'sliders',
                        volume_leds=u'slider_leds'))
        self._mixer.set_enabled(True)
        self._mixer_button_modes = DisplayingNavigatableModesComponent(
            name=u'Mixer_Button_Modes')
        self._mixer_button_modes.add_mode(
            u'mute_solo',
            AddLayerMode(
                self._mixer,
                Layer(mute_buttons=u'mixer_soft_button_row_0',
                      solo_buttons=u'mixer_soft_button_row_1')))
        self._mixer_button_modes.add_mode(
            u'monitor_arm',
            AddLayerMode(
                self._mixer,
                Layer(monitoring_state_buttons=u'mixer_soft_button_row_0',
                      arm_buttons=u'mixer_soft_button_row_1')))
        self._mixer_button_modes.layer = Layer(
            prev_mode_button=u'mixer_up_button',
            next_mode_button=u'mixer_down_button',
            display_1=u'mixer_display_1',
            display_2=u'mixer_display_2',
            color_field_1=u'mixer_color_field_1',
            color_field_2=u'mixer_color_field_2')
        self._mixer_button_modes.selected_mode = u'mute_solo'

    def _create_transport(self):
        self._transport = TransportComponent(
            name=u'Transport',
            is_enabled=False,
            layer=Layer(play_button=u'play_button',
                        stop_button=u'stop_button',
                        seek_backward_button=u'rw_button',
                        seek_forward_button=u'ff_button',
                        loop_button=u'loop_button',
                        record_button=u'record_button',
                        continue_playing_button=u'play_button_with_shift'))
        self._transport.set_enabled(True)

    def _create_session_recording(self):
        self._session_recording = SessionRecordingComponent(
            name=u'Session_Recording',
            is_enabled=False,
            layer=Layer(record_button=u'record_button_with_shift'))
        self._session_recording.set_enabled(True)

    def _create_auto_arm(self):
        self._auto_arm = AutoArmComponent(is_enabled=False, name=u'Auto_Arm')

    def _create_track_navigation(self):
        self._view_control = NotifyingViewControlComponent(
            name=u'view_control',
            is_enabled=False,
            track_provider=self._session_ring,
            layer=Layer(
                prev_track_button=u'track_left_button',
                next_track_button=u'track_right_button',
                prev_track_page_button=u'track_left_button_with_shift',
                next_track_page_button=u'track_right_button_with_shift',
                prev_scene_button=u'up_button_with_shift',
                next_scene_button=u'down_button_with_shift'))
        self._view_control.set_enabled(True)
        self._session_ring_selection_linking = self.register_disconnectable(
            SessionRingSelectionLinking(
                session_ring=self._session_ring,
                selection_changed_notifier=self._view_control))

    def _create_drums(self):
        self._drum_group = DrumGroupComponent(
            name=u'Drum_Group', translation_channel=DRUM_FEEDBACK_CHANNEL)

    def _create_device(self):
        self._banking_info = BankingInfo(BANK_DEFINITIONS)
        self._device = DeviceComponent(
            device_decorator_factory=DeviceDecoratorFactory(),
            device_bank_registry=self._device_bank_registry,
            banking_info=self._banking_info,
            name=u'Device')
        self._device_parameters = DeviceParameterComponent(
            parameter_provider=self._device, name=u'Device_Parameters')

    def _create_device_navigation(self):
        self._device_navigation = DisplayingDeviceNavigationComponent(
            banking_info=self._banking_info,
            device_bank_registry=self._device_bank_registry,
            device_component=self._device,
            num_visible_items=NUM_VISIBLE_ITEMS,
            name=u'Device_Navigation')

    def _create_actions(self):
        self._actions = ActionsComponent(
            name=u'Actions',
            is_enabled=False,
            layer=Layer(
                actions_color_fields=u'color_field_line_2_with_shift',
                undo_button=u'select_buttons_with_shift_raw[0]',
                redo_button=u'select_buttons_with_shift_raw[1]',
                metronome_button=u'select_buttons_with_shift_raw[2]',
                capture_midi_button=u'select_buttons_with_shift_raw[7]'))
        self._actions.set_enabled(True)

    def _create_clip_actions(self):
        self._clip_actions = ClipActionsComponent(
            name=u'Clip_Actions',
            is_enabled=False,
            layer=Layer(delete_button=u'clear_button',
                        duplicate_button=u'duplicate_button',
                        double_loop_button=u'duplicate_button_with_shift'))
        self._clip_actions.set_enabled(True)

    def _create_background(self):
        self._background = BackgroundComponent(
            name=u'Background',
            is_enabled=False,
            add_nop_listeners=True,
            layer=Layer(
                select_button_7_with_shift=u'select_buttons_with_shift_raw[3]',
                select_button_4_with_shift=u'select_buttons_with_shift_raw[4]',
                select_button_5_with_shift=u'select_buttons_with_shift_raw[5]',
                select_button_6_with_shift=u'select_buttons_with_shift_raw[6]')
        )
        self._background.set_enabled(True)

    def _create_modes(self):
        self._encoder_modes = DisplayingSkinableModesComponent(
            name=u'Encoder_Modes')
        self._encoder_modes.add_mode(u'devices', [
            partial(self._switch_display_layout, sysex.BOX_SCREEN_LAYOUT_BYTE),
            AddLayerMode(
                self._encoder_modes,
                Layer(mode_display=self._elements.text_display_line_5,
                      mode_color_fields=self._elements.color_field_line_2,
                      mode_selection_fields=self._elements.
                      selection_field_line_2)),
            LayerMode(
                self._device_navigation,
                Layer(select_buttons=u'pads_flattened',
                      device_color_fields=u'color_field_lines_0_1_flattened',
                      device_name_display_1=u'text_display_line_0',
                      device_name_display_2=u'text_display_line_2',
                      device_bank_name_display_1=u'text_display_line_1',
                      device_bank_name_display_2=u'text_display_line_3',
                      device_selection_fields=
                      u'selection_field_lines_0_1_flattened',
                      selected_device_name_display=u'center_display_1')),
            SetAttributeMode(self._device_navigation, u'scroll_left_layer',
                             Layer(button=u'up_button')),
            SetAttributeMode(self._device_navigation, u'scroll_right_layer',
                             Layer(button=u'down_button')),
            LayerMode(
                self._device,
                Layer(prev_bank_button=u'display_up_button',
                      next_bank_button=u'display_down_button')),
            AddLayerMode(
                self._mixer,
                Layer(selected_track_color_field=u'center_color_field',
                      selected_track_name_display=u'center_display_1')),
            AddLayerMode(
                self._background,
                Layer(center_display_2=u'center_display_2',
                      scene_launch_buttons=u'scene_launch_buttons',
                      encoders=u'encoders')),
            AddLayerMode(
                self._actions,
                Layer(actions_display=u'text_display_line_5_with_shift',
                      actions_selection_fields=
                      u'selection_field_line_2_with_shift'))
        ])
        self._encoder_modes.add_mode(u'pan', [
            partial(self._switch_display_layout,
                    sysex.KNOB_SCREEN_LAYOUT_BYTE),
            AddLayerMode(
                self._encoder_modes,
                Layer(mode_display=self._elements.text_display_line_3,
                      mode_color_fields=self._elements.color_field_line_2,
                      mode_selection_fields=self._elements.
                      selection_field_line_1,
                      selected_mode_color_field=u'center_color_field')),
            AddLayerMode(
                self._mixer,
                Layer(pan_controls=u'encoders',
                      track_names_display=u'text_display_line_0',
                      pan_value_display=u'text_display_line_1',
                      pan_encoder_color_fields=u'encoder_color_fields',
                      track_color_fields=u'color_field_line_0',
                      mixer_display=u'center_display_1',
                      pan_display=u'center_display_2')),
            AddLayerMode(
                self._background,
                Layer(display_up_button=u'display_up_button',
                      display_down_button=u'display_down_button')),
            AddLayerMode(
                self._actions,
                Layer(actions_display=u'text_display_line_3_with_shift',
                      actions_selection_fields=
                      u'selection_field_line_1_with_shift'))
        ])
        self._encoder_modes.add_mode(u'sends', [
            partial(self._switch_display_layout,
                    sysex.KNOB_SCREEN_LAYOUT_BYTE),
            AddLayerMode(
                self._encoder_modes,
                Layer(mode_display=self._elements.text_display_line_3,
                      mode_color_fields=self._elements.color_field_line_2,
                      mode_selection_fields=self._elements.
                      selection_field_line_1,
                      selected_mode_color_field=u'center_color_field')),
            AddLayerMode(
                self._mixer,
                Layer(send_controls=u'encoders',
                      send_up_button=u'display_up_button',
                      send_down_button=u'display_down_button',
                      track_names_display=u'text_display_line_0',
                      track_color_fields=u'color_field_line_0',
                      mixer_display=u'center_display_1',
                      send_index_display=u'center_display_2',
                      send_value_display=u'text_display_line_1',
                      send_encoder_color_fields=u'encoder_color_fields')),
            AddLayerMode(
                self._actions,
                Layer(actions_display=u'text_display_line_3_with_shift',
                      actions_selection_fields=
                      u'selection_field_line_1_with_shift'))
        ])
        self._pad_modes = ModesComponent(name=u'Pad_Modes')
        self._pad_modes.add_mode(
            u'drum',
            LayerMode(
                self._drum_group,
                Layer(matrix=u'pads_quadratic',
                      scroll_up_button=u'up_button',
                      scroll_down_button=u'down_button')))
        self._pad_modes.add_mode(
            u'disabled',
            AddLayerMode(
                self._background,
                Layer(matrix=u'pads_quadratic',
                      scroll_up_button=u'up_button',
                      scroll_down_button=u'down_button')))
        self._main_modes = ModesComponent(name=u'Encoder_Modes')
        set_main_mode = partial(setattr, self._main_modes, u'selected_mode')
        self._main_modes.add_mode(u'device_control', [
            partial(self._switch_display_layout,
                    sysex.KNOB_SCREEN_LAYOUT_BYTE),
            AddLayerMode(
                self._mixer,
                Layer(track_select_buttons=u'select_buttons',
                      track_names_display=u'text_display_line_3',
                      track_color_fields=u'color_field_line_2',
                      track_selection_fields=u'selection_field_line_1',
                      selected_track_color_field=u'center_color_field')),
            LayerMode(
                self._device_parameters,
                Layer(parameter_controls=u'encoders',
                      name_display_line=u'text_display_line_0',
                      value_display_line=u'text_display_line_1',
                      parameter_color_fields=u'color_field_line_0',
                      encoder_color_fields=u'encoder_color_fields')),
            LayerMode(
                self._session_navigation,
                Layer(up_button=u'up_button', down_button=u'down_button')),
            LayerMode(
                self._device,
                Layer(prev_bank_button=u'display_up_button',
                      next_bank_button=u'display_down_button')),
            LayerMode(
                self._device_navigation,
                Layer(selected_device_name_display=u'center_display_1',
                      selected_device_bank_name_display=u'center_display_2')),
            AddLayerMode(
                self._actions,
                Layer(actions_display=u'text_display_line_3_with_shift',
                      actions_selection_fields=
                      u'selection_field_line_1_with_shift'))
        ])
        self._main_modes.add_mode(
            u'options', [
                self._encoder_modes,
                LayerMode(
                    self._encoder_modes,
                    Layer(devices_button=u'select_buttons_raw[0]',
                          pan_button=u'select_buttons_raw[1]',
                          sends_button=u'select_buttons_raw[2]')),
                SetAttributeMode(self._encoder_modes, u'selected_mode',
                                 u'devices'),
                AddLayerMode(
                    self._background,
                    Layer(select_button_3=u'select_buttons_raw[3]',
                          select_button_4=u'select_buttons_raw[4]',
                          select_button_5=u'select_buttons_raw[5]',
                          select_button_6=u'select_buttons_raw[6]',
                          select_button_7=u'select_buttons_raw[7]'))
            ],
            behaviour=ReenterBehaviour(
                on_reenter=partial(set_main_mode, u'device_control')))
        self._main_modes.add_mode(u'grid', [
            partial(self._switch_display_layout,
                    sysex.KNOB_SCREEN_LAYOUT_BYTE), self._pad_modes,
            AddLayerMode(
                self._mixer,
                Layer(track_select_buttons=u'select_buttons',
                      track_names_display=u'text_display_line_3',
                      track_color_fields=u'color_field_line_2',
                      track_selection_fields=u'selection_field_line_1',
                      selected_track_color_field=u'center_color_field')),
            self._select_grid_mode,
            LayerMode(
                self._device_parameters,
                Layer(parameter_controls=u'encoders',
                      name_display_line=u'text_display_line_0',
                      value_display_line=u'text_display_line_1',
                      parameter_color_fields=u'color_field_line_0',
                      encoder_color_fields=u'encoder_color_fields')),
            LayerMode(
                self._device,
                Layer(prev_bank_button=u'display_up_button',
                      next_bank_button=u'display_down_button')),
            LayerMode(
                self._device_navigation,
                Layer(selected_device_name_display=u'center_display_1',
                      selected_device_bank_name_display=u'center_display_2')),
            AddLayerMode(self._background,
                         Layer(scene_launch_buttons=u'scene_launch_buttons')),
            AddLayerMode(
                self._actions,
                Layer(actions_display=u'text_display_line_3_with_shift',
                      actions_selection_fields=
                      u'selection_field_line_1_with_shift'))
        ],
                                  behaviour=ReenterBehaviour(
                                      on_reenter=partial(
                                          set_main_mode, u'device_control')))
        self._main_modes.layer = Layer(options_button=u'options_button',
                                       grid_button=u'grid_button')
        self._main_modes.selected_mode = u'device_control'

    @listens(u'instrument')
    def __on_drum_group_found(self):
        self._drum_group.set_drum_group_device(
            self._drum_group_finder.drum_group)
        self._select_grid_mode()

    @listens(u'selected_track')
    def __on_selected_track_changed(self):
        track = self.song.view.selected_track
        self.__on_selected_track_implicit_arm_changed.subject = track
        self._drum_group_finder.device_parent = track
        self._select_grid_mode()

    @listens(u'session_record')
    def __on_session_record_changed(self):
        self._set_feedback_velocity()

    @listens(u'implicit_arm')
    def __on_selected_track_implicit_arm_changed(self):
        self._set_feedback_velocity()

    @listens(u'record_mode')
    def __on_record_mode_changed(self):
        self._set_feedback_velocity()

    def _select_grid_mode(self):
        if self._main_modes.selected_mode == u'grid':
            drum_device = self._drum_group_finder.drum_group
            self._pad_modes.selected_mode = u'drum' if drum_device else u'disabled'
            if drum_device:
                self.set_controlled_track(self.song.view.selected_track)
                self._set_feedback_velocity()
            else:
                self.release_controlled_track()

    def _set_feedback_velocity(self):
        if is_song_recording(
                self.song) and self.song.view.selected_track.implicit_arm:
            feedback_velocity = Rgb.RED.midi_value
        else:
            feedback_velocity = Rgb.GREEN.midi_value
        self._c_instance.set_feedback_velocity(int(feedback_velocity))
Example #5
0
class ATOMSQ(ControlSurface):
    def __init__(self, *a, **k):
        super(ATOMSQ, self).__init__(*a, **k)
        with self.component_guard():
            self._elements = Elements()
            with inject(element_container=const(self._elements)).everywhere():
                self._create_background()
                self._create_transport()
                self._create_undo()
                self._create_view_toggle()
                self._create_device_parameters()
                self._create_translating_background()
                self._create_device_navigation()
                self._create_launch_and_stop()
                self._create_session()
                self._create_mixer()
                self._create_view_control()
                self._create_button_labels()
                self._create_record_modes()
                self._create_lower_pad_modes()
                self._create_main_modes()
        self.__on_main_view_changed.subject = self.application.view

    def disconnect(self):
        super(ATOMSQ, self).disconnect()
        self._send_midi(midi.NATIVE_MODE_OFF_MESSAGE)

    def port_settings_changed(self):
        self._send_midi(midi.NATIVE_MODE_ON_MESSAGE)
        if self._main_modes.selected_mode == b'instrument':
            self.schedule_message(
                1, self._elements.upper_firmware_toggle_switch.send_value, 1)
        if self._main_modes.selected_mode != b'song':
            self.schedule_message(
                1, self._elements.lower_firmware_toggle_switch.send_value, 1)
        super(ATOMSQ, self).port_settings_changed()

    def _create_background(self):
        self._background = BackgroundComponent(
            name=b'Background',
            is_enabled=False,
            add_nop_listeners=True,
            layer=Layer(**{name: name
                           for name in BANK_BUTTON_NAMES}))
        self._background.set_enabled(True)

    def _create_transport(self):
        self._transport = TransportComponent(
            name=b'Transport',
            is_enabled=False,
            layer=Layer(scroll_encoder=b'display_encoder',
                        play_button=b'play_button',
                        loop_button=b'play_button_with_shift',
                        stop_button=b'stop_button',
                        metronome_button=b'click_button',
                        capture_midi_button=b'record_button_with_shift',
                        prev_cue_button=b'display_left_button',
                        next_cue_button=b'display_right_button',
                        shift_button=b'shift_button'))
        self._transport.set_enabled(True)

    def _create_undo(self):
        self._undo = UndoRedoComponent(
            name=b'Undo',
            is_enabled=False,
            layer=Layer(undo_button=b'stop_button_with_shift'))
        self._undo.set_enabled(True)

    def _create_view_toggle(self):
        self._view_toggle = ViewToggleComponent(
            name=b'View_Toggle',
            is_enabled=False,
            layer=Layer(main_view_toggle_button=b'bank_a_button',
                        browser_view_toggle_button=b'bank_b_button',
                        detail_view_toggle_button=b'bank_d_button',
                        clip_view_toggle_button=b'bank_h_button'))

    def _create_device_parameters(self):
        self._device_parameters = SimpleDeviceParameterComponent(
            name=b'Device_Parameters',
            device_bank_registry=self._device_bank_registry,
            toggle_lock=self.toggle_lock,
            layer=Layer(device_name_display=b'device_name_display'),
            is_enabled=False)
        self._device_parameters.set_enabled(True)

    def _create_translating_background(self):
        self._translating_background = TranslatingBackgroundComponent(
            name=b'Translating_Background',
            is_enabled=False,
            add_nop_listeners=True,
            layer=Layer(encoders=b'encoders',
                        channel_selection_buttons=b'display_buttons'))

    def _create_device_navigation(self):
        self._device_navigation = SimpleDeviceNavigationComponent(
            name=b'Device_Navigation',
            is_enabled=False,
            layer=Layer(prev_button=b'display_buttons_raw[1]',
                        next_button=b'display_buttons_raw[2]'))

    def _create_launch_and_stop(self):
        self._launch_and_stop = LaunchAndStopComponent(
            name=b'Launch_And_Stop',
            is_enabled=False,
            layer=Layer(clip_launch_button=b'display_buttons_raw[3]',
                        scene_launch_button=b'display_buttons_raw[4]',
                        track_stop_button=b'display_buttons_raw[5]'))

    def _create_session(self):
        self._session_ring = SessionRingComponent(name=b'Session_Ring',
                                                  num_tracks=SESSION_WIDTH,
                                                  num_scenes=SESSION_HEIGHT)
        self._session = SessionComponent(
            name=b'Session',
            is_enabled=False,
            session_ring=self._session_ring,
            layer=Layer(clip_launch_buttons=b'upper_pads'))
        self._session_navigation = SessionNavigationComponent(
            name=b'Session_Navigation',
            is_enabled=False,
            session_ring=self._session_ring,
            layer=Layer(up_button=b'up_button_with_shift',
                        down_button=b'down_button_with_shift'))
        self._session_navigation.set_enabled(True)

    def _create_mixer(self):
        self._mixer = MixerComponent(
            name=b'Mixer',
            auto_name=True,
            tracks_provider=self._session_ring,
            track_assigner=SimpleTrackAssigner(),
            channel_strip_component_type=ChannelStripComponent)
        self._mixer.selected_strip().layer = Layer(
            track_name_display=b'track_name_display')
        self._mixer.set_enabled(True)

    def _create_view_control(self):
        self._view_control = NotifyingViewControlComponent(
            name=b'View_Control',
            is_enabled=False,
            track_provider=self._session_ring,
            enable_skinning=False,
            layer=Layer(next_track_button=b'right_button',
                        prev_track_button=b'left_button',
                        next_scene_button=b'down_button',
                        prev_scene_button=b'up_button'))
        self._view_control.set_enabled(True)
        self._session_ring_selection_linking = self.register_disconnectable(
            SessionRingSelectionLinking(
                session_ring=self._session_ring,
                selection_changed_notifier=self._view_control))

    def _create_button_labels(self):
        self._button_labels = ButtonLabelsComponent(
            is_enabled=False,
            layer=Layer(display_lines=b'button_label_display_matrix'))
        self._button_labels.set_enabled(True)

    def _create_record_modes(self):
        self._session_record = SessionRecordingComponent(
            name=b'Session_Record',
            is_enabled=False,
            layer=Layer(record_button=b'record_button'))
        self._record_modes = ModesComponent(name=b'Record_Modes')
        self._record_modes.add_mode(b'session',
                                    EnablingMode(self._session_record))
        self._record_modes.add_mode(
            b'arrange',
            AddLayerMode(self._transport,
                         layer=Layer(record_button=b'record_button')))
        self.__on_main_view_changed()

    def _create_lower_pad_modes(self):
        self._lower_pad_modes = ModesComponent(
            name=b'Lower_Pad_Modes',
            is_enabled=False,
            layer=Layer(cycle_mode_button=b'minus_button'))
        self._lower_pad_modes.add_mode(
            b'select',
            AddLayerMode(self._mixer,
                         Layer(track_select_buttons=b'lower_pads')),
            cycle_mode_button_color=b'Session.StopClipDisabled')
        self._lower_pad_modes.add_mode(
            b'stop',
            AddLayerMode(self._session,
                         Layer(stop_track_clip_buttons=b'lower_pads')),
            cycle_mode_button_color=b'Session.StopClip')
        self._lower_pad_modes.selected_mode = b'select'

    def _create_main_modes(self):
        self._main_modes = ModesComponent(
            name=b'Main_Modes',
            is_enabled=False,
            layer=Layer(song_button=b'song_mode_button',
                        instrument_button=b'instrument_mode_button',
                        editor_button=b'editor_mode_button',
                        user_button=b'user_mode_button'))
        device_params_mode = AddLayerMode(
            self._device_parameters, Layer(parameter_controls=b'encoders'))
        enable_lower_fw_functions = partial(
            self._elements.lower_firmware_toggle_switch.send_value, 1)
        disable_upper_fw_functions = partial(
            self._elements.upper_firmware_toggle_switch.send_value, 0)
        self._main_modes.add_mode(
            b'song',
            (partial(self._elements.lower_firmware_toggle_switch.send_value,
                     0), disable_upper_fw_functions,
             self._elements.display_buttons.reset, self._view_toggle,
             self._launch_and_stop, self._session, self._lower_pad_modes,
             AddLayerMode(self._session.scene(0),
                          Layer(launch_button=b'plus_button')),
             AddLayerMode(
                 self._mixer.selected_strip(),
                 Layer(volume_control=b'encoders_raw[0]',
                       pan_control=b'encoders_raw[1]',
                       send_controls=self._elements.encoders.submatrix[2:, :],
                       solo_button=b'display_buttons_raw[0]',
                       mute_button=b'display_buttons_raw[1]',
                       arm_button=b'display_buttons_raw[2]')),
             AddLayerMode(self._mixer,
                          Layer(crossfader_control=b'touch_strip'))))
        self._main_modes.add_mode(
            b'instrument',
            (enable_lower_fw_functions,
             partial(self._elements.upper_firmware_toggle_switch.send_value,
                     1), device_params_mode))
        self._main_modes.add_mode(
            b'editor',
            (enable_lower_fw_functions, disable_upper_fw_functions,
             device_params_mode, self._device_navigation,
             AddLayerMode(
                 self._device_parameters,
                 Layer(device_lock_button=b'display_buttons_raw[0]',
                       device_on_off_button=b'display_buttons_raw[3]',
                       prev_bank_button=b'display_buttons_raw[4]',
                       next_bank_button=b'display_buttons_raw[5]'))))
        self._main_modes.add_mode(
            b'user', (enable_lower_fw_functions, disable_upper_fw_functions,
                      self._translating_background))
        self._main_modes.selected_mode = b'instrument'
        self._main_modes.set_enabled(True)
        self.__on_main_modes_changed.subject = self._main_modes

    @listens(b'selected_mode')
    def __on_main_modes_changed(self, mode):
        self._button_labels.show_button_labels_for_mode(mode)
        self._elements.track_name_display.clear_send_cache()
        self._elements.device_name_display.clear_send_cache()

    @listens(b'is_view_visible', b'Session')
    def __on_main_view_changed(self):
        if self.application.view.is_view_visible(b'Session'):
            self._record_modes.selected_mode = b'session'
        else:
            self._record_modes.selected_mode = b'arrange'
class Launchkey_Mini_MK3(InstrumentControlMixin, NovationBase):
    model_family_code = ids.LK_MINI_MK3_FAMILY_CODE
    element_class = Elements
    session_height = SESSION_HEIGHT
    skin = skin
    suppress_layout_switch = False

    def __init__(self, *a, **k):
        self._last_pad_layout_byte = midi.PAD_SESSION_LAYOUT
        self._last_pot_layout_byte = midi.POT_VOLUME_LAYOUT
        (super(Launchkey_Mini_MK3, self).__init__)(*a, **k)

    def disconnect(self):
        self._elements.pad_layout_switch.send_value(midi.PAD_DRUM_LAYOUT)
        self._auto_arm.set_enabled(False)
        super(Launchkey_Mini_MK3, self).disconnect()

    def on_identified(self, midi_bytes):
        self._elements.incontrol_mode_switch.send_value(
            midi.INCONTROL_ONLINE_VALUE)
        self._elements.pad_layout_switch.send_value(self._last_pad_layout_byte)
        self._elements.pot_layout_switch.send_value(self._last_pot_layout_byte)
        self._target_track_changed()
        self._drum_group_changed()
        self._auto_arm.set_enabled(True)
        self.set_feedback_channels([DRUM_FEEDBACK_CHANNEL])
        super(Launchkey_Mini_MK3, self).on_identified(midi_bytes)

    def port_settings_changed(self):
        self._auto_arm.set_enabled(False)
        super(Launchkey_Mini_MK3, self).port_settings_changed()

    def _create_components(self):
        super(Launchkey_Mini_MK3, self)._create_components()
        self.register_slot(self._elements.incontrol_mode_switch, nop, 'value')
        self._background = BackgroundComponent(name='Background',
                                               add_nop_listeners=True)
        self._create_auto_arm()
        self._create_view_control()
        self._create_transport()
        self._create_device()
        self._create_drum_group()
        self._create_pot_modes()
        self._create_stop_solo_mute_modes()
        self._create_pad_modes()
        self._create_recording_modes()

    def _create_session_layer(self):
        return super(Launchkey_Mini_MK3, self)._create_session_layer() + Layer(
            scene_launch_buttons='scene_launch_buttons')

    def _create_session_navigation_layer(self):
        return Layer(up_button='scene_launch_button_with_shift',
                     down_button='stop_solo_mute_button_with_shift')

    def _create_auto_arm(self):
        self._auto_arm = AutoArmComponent(name='Auto_Arm', is_enabled=False)

    def _create_view_control(self):
        self._view_control = NotifyingViewControlComponent(
            name='Track_Scroller',
            is_enabled=False,
            track_provider=(self._session_ring),
            layer=Layer(prev_track_button='left_button',
                        next_track_button='right_button'))
        self._view_control.set_enabled(True)
        self._session_ring_selection_linking = self.register_disconnectable(
            SessionRingSelectionLinking(
                session_ring=(self._session_ring),
                selection_changed_notifier=(self._view_control)))

    def _create_transport(self):
        self._transport = TransportComponent(
            name='Transport',
            is_enabled=False,
            layer=Layer(play_button='play_button',
                        continue_playing_button='play_button_with_shift',
                        capture_midi_button='record_button_with_shift'))
        self._transport.set_enabled(True)

    def _create_recording_modes(self):
        super(Launchkey_Mini_MK3, self)._create_recording_modes()
        self._recording_modes.add_mode(
            'arrange',
            AddLayerMode(self._transport,
                         Layer(record_button='record_button')))
        self._Launchkey_Mini_MK3__on_main_view_changed.subject = self.application.view
        self._select_recording_mode()

    def _create_device(self):
        self._device = SimpleDeviceParameterComponent(
            name='Device',
            is_enabled=False,
            layer=Layer(parameter_controls='pots'))

    def _create_drum_group(self):
        self._drum_group = DrumGroupComponent(
            name='Drum_Group',
            is_enabled=False,
            translation_channel=DRUM_FEEDBACK_CHANNEL,
            layer=Layer(
                matrix='drum_pads',
                scroll_page_up_button='scene_launch_button_with_shift',
                scroll_page_down_button='stop_solo_mute_button_with_shift'))

    def _create_pot_modes(self):
        self._pot_modes = ModesComponent(
            name='Pot_Modes',
            is_enabled=False,
            layer=Layer(mode_selection_control='pot_layout_switch'))
        self._pot_modes.add_mode('custom', None)
        self._pot_modes.add_mode(
            'volume', AddLayerMode(self._mixer, Layer(volume_controls='pots')))
        self._pot_modes.add_mode('device', self._device)
        self._pot_modes.add_mode(
            'pan', AddLayerMode(self._mixer, Layer(pan_controls='pots')))
        self._pot_modes.add_mode(
            'send_a', AddLayerMode(self._mixer, Layer(send_a_controls='pots')))
        self._pot_modes.add_mode(
            'send_b', AddLayerMode(self._mixer, Layer(send_b_controls='pots')))
        self._pot_modes.selected_mode = 'volume'
        self._pot_modes.set_enabled(True)
        self._Launchkey_Mini_MK3__on_pot_mode_byte_changed.subject = self._pot_modes

    def _create_stop_solo_mute_modes(self):
        self._stop_solo_mute_modes = ModesComponent(
            name='Stop_Solo_Mute_Modes',
            is_enabled=False,
            support_momentary_mode_cycling=False)
        bottom_row = self._elements.clip_launch_matrix.submatrix[:, 1:]
        self._stop_solo_mute_modes.add_mode(
            'launch', None, cycle_mode_button_color='Mode.Launch.On')
        self._stop_solo_mute_modes.add_mode(
            'stop', (AddLayerMode(self._session,
                                  Layer(stop_track_clip_buttons=bottom_row))),
            cycle_mode_button_color='Session.StopClip')
        self._stop_solo_mute_modes.add_mode(
            'solo',
            (AddLayerMode(self._mixer, Layer(solo_buttons=bottom_row))),
            cycle_mode_button_color='Mixer.SoloOn')
        self._stop_solo_mute_modes.add_mode(
            'mute',
            (AddLayerMode(self._mixer, Layer(mute_buttons=bottom_row))),
            cycle_mode_button_color='Mixer.MuteOff')
        self._stop_solo_mute_modes.selected_mode = 'launch'
        self._stop_solo_mute_modes.set_enabled(True)

    def _create_pad_modes(self):
        self._pad_modes = ModesComponent(
            name='Pad_Modes',
            is_enabled=False,
            layer=Layer(mode_selection_control='pad_layout_switch'))
        bg_mode = AddLayerMode(
            (self._background),
            layer=Layer(scene_launch_buttons='scene_launch_buttons'))
        self._pad_modes.add_mode('custom', bg_mode)
        self._pad_modes.add_mode('drum', (bg_mode, self._drum_group))
        self._pad_modes.add_mode(
            'session',
            LayerMode((self._stop_solo_mute_modes),
                      layer=Layer(cycle_mode_button=(
                          self._elements.scene_launch_buttons_raw[1]))))
        self._pad_modes.selected_mode = 'session'
        self._pad_modes.set_enabled(True)
        self._Launchkey_Mini_MK3__on_pad_mode_changed.subject = self._pad_modes
        self._Launchkey_Mini_MK3__on_pad_mode_byte_changed.subject = self._pad_modes

    def _select_recording_mode(self):
        if self.application.view.is_view_visible('Session'):
            self._recording_modes.selected_mode = 'track' if self._pad_modes.selected_mode == 'drum' else 'session'
        else:
            self._recording_modes.selected_mode = 'arrange'

    @listens('is_view_visible', 'Session')
    def __on_main_view_changed(self):
        self._select_recording_mode()

    @listens('selected_mode')
    def __on_pad_mode_changed(self, mode):
        self._select_recording_mode()
        self._update_controlled_track()

    @listens('mode_byte')
    def __on_pad_mode_byte_changed(self, mode_byte):
        self._last_pad_layout_byte = mode_byte

    @listens('mode_byte')
    def __on_pot_mode_byte_changed(self, mode_byte):
        self._last_pot_layout_byte = mode_byte

    def _drum_group_changed(self):
        self._drum_group.set_drum_group_device(
            self._drum_group_finder.drum_group)

    def _is_instrument_mode(self):
        return self._pad_modes.selected_mode == 'drum'
Example #7
0
class Launchkey_MK3(InstrumentControlMixin, NovationBase):
    element_class = Elements
    session_height = SESSION_HEIGHT
    mixer_class = MixerComponent
    channel_strip_class = ChannelStripComponent
    skin = skin
    suppress_layout_switch = False

    def __init__(self, *a, **k):
        self._is_small_model = False
        self._last_pad_layout_byte = midi.PAD_SESSION_LAYOUT
        self._last_pot_layout_byte = None
        self._last_fader_layout_byte = midi.VOLUME_LAYOUT
        super(Launchkey_MK3, self).__init__(*a, **k)

    def disconnect(self):
        self._elements.pad_layout_switch.send_value(midi.PAD_DRUM_LAYOUT)
        self._auto_arm.set_enabled(False)
        super(Launchkey_MK3, self).disconnect()

    def on_identified(self, midi_bytes):
        if self._last_pot_layout_byte is None:
            self._last_pot_layout_byte = midi.VOLUME_LAYOUT if self._is_small_model else midi.PAN_LAYOUT
            self._pot_modes.selected_mode = u'volume' if self._is_small_model else u'pan'
        self._elements.incontrol_mode_switch.send_value(midi.INCONTROL_ONLINE_VALUE)
        self._elements.pad_layout_switch.send_value(self._last_pad_layout_byte)
        self._elements.pot_layout_switch.send_value(self._last_pot_layout_byte)
        if not self._is_small_model:
            self._elements.fader_layout_switch.send_value(self._last_fader_layout_byte)
        self._target_track_changed()
        self._drum_group_changed()
        self._auto_arm.set_enabled(True)
        self.set_feedback_channels([DRUM_FEEDBACK_CHANNEL])
        super(Launchkey_MK3, self).on_identified(midi_bytes)

    def port_settings_changed(self):
        self._auto_arm.set_enabled(False)
        super(Launchkey_MK3, self).port_settings_changed()

    def _create_components(self):
        super(Launchkey_MK3, self)._create_components()
        self.register_slot(self._elements.incontrol_mode_switch, nop, u'value')
        self._create_auto_arm()
        self._create_background()
        self._create_notification()
        self._create_view_control()
        self._create_transport()
        self._create_recording_modes()
        self._create_undo()
        self._create_quantization()
        self._create_device()
        self._create_drum_group()
        self._pot_modes = self._create_pot_or_fader_modes(u'pot')
        self._create_stop_solo_mute_modes()
        self._create_pad_modes()
        if not self._is_small_model:
            self._fader_modes = self._create_pot_or_fader_modes(u'fader')
            self._setup_master_fader()
            self._create_fader_button_modes()

    def _setup_master_fader(self):
        strip = self._mixer.master_strip()
        strip.set_volume_control(self._elements.master_fader)
        strip.volume_display.set_control_element(self._elements.master_fader_parameter_value_display)
        self._elements.master_fader_parameter_name_display.display_message(u'Master Volume')

    def _create_session_navigation_layer(self):
        return Layer(up_button=u'up_button', down_button=u'down_button')

    def _create_session_layer(self):
        return super(Launchkey_MK3, self)._create_session_layer() + Layer(scene_launch_buttons=u'scene_launch_buttons')

    def _create_auto_arm(self):
        self._auto_arm = AutoArmComponent(name=u'Auto_Arm', is_enabled=False)

    def _create_background(self):
        self._background = BackgroundComponent(name=u'Background', is_enabled=False, add_nop_listeners=True, layer=Layer(secondary_up_button=u'secondary_up_button', secondary_down_button=u'secondary_down_button', device_select_button=u'device_select_button', unused_matrix=self._elements.device_select_matrix.submatrix[:, 1:], pot_parameter_name_displays=u'pot_parameter_name_displays', pot_parameter_value_displays=u'pot_parameter_value_displays', fader_parameter_name_displays=u'fader_parameter_name_displays', fader_parameter_value_displays=u'fader_parameter_value_displays'))
        self._background.set_enabled(True)

    def _create_notification(self):
        self._notification_component = NotificationComponent(name=u'Notifications', is_enabled=False, layer=Layer(display_lines=u'notification_display'))
        self._notification_component.set_enabled(True)

    def _create_view_control(self):
        self._view_control = NotifyingViewControlComponent(name=u'Track_Scroller', is_enabled=False, track_provider=self._session_ring, layer=Layer(prev_track_button=u'left_button', next_track_button=u'right_button'))
        self._view_control.set_enabled(True)
        self._session_ring_selection_linking = self.register_disconnectable(SessionRingSelectionLinking(session_ring=self._session_ring, selection_changed_notifier=self._view_control))

    def _create_transport(self):
        self._transport = TransportComponent(name=u'Transport', is_enabled=False, layer=Layer(play_button=u'play_button', alt_stop_button=u'stop_button', loop_button=u'loop_button', metronome_button=u'click_button', capture_midi_button=u'capture_midi_button'))
        self._transport.set_enabled(True)

    def _create_undo(self):
        self._undo = UndoRedoComponent(name=u'Undo', is_enabled=False, layer=Layer(undo_button=u'undo_button'))
        self._undo.set_enabled(True)

    def _create_quantization(self):
        self._quantization = QuantizationComponent(name=u'Quantization')
        self._clip_actions = ClipActionsComponent(name=u'Clip_Actions', is_enabled=False, layer=Layer(quantize_button=u'quantize_button'))
        self._clip_actions.set_enabled(True)
        ClipActionsComponent.quantization_component = self._quantization

    def _create_device(self):
        self._device = DeviceComponent(name=u'Device', is_enabled=False, show_notification=self._notification_component.show_notification, device_bank_registry=self._device_bank_registry, toggle_lock=self.toggle_lock, use_parameter_banks=True, layer=Layer(device_lock_button=u'device_lock_button'))
        self._device.set_enabled(True)

    def _create_drum_group(self):
        self._drum_group = DrumGroupComponent(name=u'Drum_Group', is_enabled=False, translation_channel=DRUM_FEEDBACK_CHANNEL, layer=Layer(matrix=u'drum_pads', scroll_page_up_button=u'up_button', scroll_page_down_button=u'down_button'))

    def _create_pot_or_fader_modes(self, modes_type_name):
        modes = ModesComponent(name=u'{}_Modes'.format(modes_type_name.title()), is_enabled=False, layer=Layer(mode_selection_control=u'{}_layout_switch'.format(modes_type_name)))
        elements_name = u'{}s'.format(modes_type_name)
        name_displays_element_name = u'{}_parameter_name_displays'.format(modes_type_name)
        value_displays_element_name = u'{}_parameter_value_displays'.format(modes_type_name)

        def add_pot_or_fader_mixer_mode(parameter_name):
            modes.add_mode(parameter_name, (partial(getattr(self._mixer, u'set_{}_parameter_name'.format(modes_type_name)), parameter_name.replace(u'_', u' ').title()), AddLayerMode(self._mixer, Layer(**{u'{}_controls'.format(parameter_name): elements_name,
              u'{}_parameter_name_displays'.format(modes_type_name): name_displays_element_name,
              u'{}_displays'.format(parameter_name): value_displays_element_name}))))

        modes.add_mode(u'dummy', None)
        add_pot_or_fader_mixer_mode(u'volume')
        modes.add_mode(u'device', AddLayerMode(self._device, Layer(parameter_controls=elements_name, parameter_name_displays=name_displays_element_name, parameter_value_displays=value_displays_element_name)))
        if modes_type_name == u'pot':
            add_pot_or_fader_mixer_mode(u'pan')
        else:
            modes.add_mode(u'pan', None)
        add_pot_or_fader_mixer_mode(u'send_a')
        add_pot_or_fader_mixer_mode(u'send_b')
        for i in range(4):
            modes.add_mode(u'custom{}'.format(i), None)

        modes.selected_mode = u'pan' if modes_type_name == u'pot' else u'volume'
        modes.set_enabled(True)
        self.register_slot(modes, getattr(self, u'_on_{}_mode_byte_changed'.format(modes_type_name)), u'mode_byte')
        return modes

    def _create_fader_button_modes(self):
        self._fader_button_modes = ModesComponent(name=u'Fader_Modes', is_enabled=False, support_momentary_mode_cycling=False, layer=Layer(cycle_mode_button=u'fader_button_modes_button'))
        self._fader_button_modes.add_mode(u'arm', AddLayerMode(self._mixer, Layer(arm_buttons=u'fader_buttons')), cycle_mode_button_color=u'DefaultButton.Off')
        self._fader_button_modes.add_mode(u'track_select', AddLayerMode(self._mixer, Layer(track_select_buttons=u'fader_buttons')), cycle_mode_button_color=u'DefaultButton.On')
        self._fader_button_modes.selected_mode = u'arm'
        self._fader_button_modes.set_enabled(True)

    def _create_stop_solo_mute_modes(self):
        self._stop_solo_mute_modes = ModesComponent(name=u'Stop_Solo_Mute_Modes', is_enabled=False, support_momentary_mode_cycling=False)
        lower_matrix_row = self._elements.clip_launch_matrix.submatrix[:, 1:]
        self._stop_solo_mute_modes.add_mode(u'launch', None, cycle_mode_button_color=u'Mode.Launch.On')
        self._stop_solo_mute_modes.add_mode(u'stop', AddLayerMode(self._session, Layer(stop_track_clip_buttons=lower_matrix_row)), cycle_mode_button_color=u'Session.StopClip')
        self._stop_solo_mute_modes.add_mode(u'solo', AddLayerMode(self._mixer, Layer(solo_buttons=lower_matrix_row)), cycle_mode_button_color=u'Mixer.SoloOn')
        self._stop_solo_mute_modes.add_mode(u'mute', AddLayerMode(self._mixer, Layer(mute_buttons=lower_matrix_row)), cycle_mode_button_color=u'Mixer.MuteOff')
        self._stop_solo_mute_modes.selected_mode = u'launch'
        self._stop_solo_mute_modes.set_enabled(True)
        self.__on_stop_solo_mute_mode_changed.subject = self._stop_solo_mute_modes

    def _create_pad_modes(self):
        self._pad_modes = ModesComponent(name=u'Pad_Modes', is_enabled=False, layer=Layer(mode_selection_control=u'pad_layout_switch'))
        suppress_scene_launch_buttons = AddLayerMode(self._background, layer=Layer(scene_launch_buttons=u'scene_launch_buttons'))
        suppress_all_buttons_around_pads = AddLayerMode(self._background, layer=Layer(scene_launch_buttons=u'scene_launch_buttons', up_button=u'up_button', down_button=u'down_button'))
        self._pad_modes.add_mode(u'dummy', suppress_all_buttons_around_pads)
        self._pad_modes.add_mode(u'drum', (suppress_scene_launch_buttons, self._drum_group))
        self._pad_modes.add_mode(u'session', LayerMode(self._stop_solo_mute_modes, layer=Layer(cycle_mode_button=self._elements.scene_launch_buttons_raw[1])))
        for i in range(6):
            self._pad_modes.add_mode(u'custom{}'.format(i), suppress_all_buttons_around_pads)

        upper_matrix_row = self._elements.device_select_matrix.submatrix[:, :1]
        self._pad_modes.add_mode(u'device_select', (suppress_scene_launch_buttons, self._device.show_device_name_and_bank, AddLayerMode(self._device, layer=Layer(bank_select_buttons=upper_matrix_row, prev_button=u'up_button', next_button=u'down_button'))))
        self._pad_modes.selected_mode = u'session'
        self._pad_modes.set_enabled(True)
        self.__on_pad_mode_changed.subject = self._pad_modes
        self.__on_pad_mode_byte_changed.subject = self._pad_modes

    @listens(u'selected_mode')
    def __on_pad_mode_changed(self, mode):
        self._recording_modes.selected_mode = u'track' if mode == u'drum' else u'session'
        self._update_controlled_track()

    @listens(u'selected_mode')
    def __on_stop_solo_mute_mode_changed(self, mode):
        if mode:
            self._notification_component.show_notification(u'Lower Pad Mode', mode.title())

    @listens(u'mode_byte')
    def __on_pad_mode_byte_changed(self, mode_byte):
        self._last_pad_layout_byte = mode_byte

    def _on_pot_mode_byte_changed(self, mode_byte):
        self._last_pot_layout_byte = mode_byte

    def _on_fader_mode_byte_changed(self, mode_byte):
        self._last_fader_layout_byte = mode_byte

    def _drum_group_changed(self):
        self._drum_group.set_drum_group_device(self._drum_group_finder.drum_group)

    def _target_track_changed(self):
        super(Launchkey_MK3, self)._target_track_changed()
        self._notification_component.show_notification(u'Track', self._target_track.target_track.name)

    def _is_instrument_mode(self):
        return self._pad_modes.selected_mode == u'drum'

    def _extract_product_id_bytes(self, midi_bytes):
        u""" Extends standard to deal with each model having a different ID byte, determine
        whether the model is one of the small models and compose the target product ID
        bytes based on the bytes that were received. """
        id_bytes = super(Launchkey_MK3, self)._extract_product_id_bytes(midi_bytes)
        model_id_byte = id_bytes[3]
        if id_bytes[:3] == sysex.NOVATION_MANUFACTURER_ID and model_id_byte in midi.MODEL_ID_BYTES and id_bytes[4:] == midi.MODEL_ID_BYTE_SUFFIX:
            self._is_small_model = model_id_byte in midi.SMALL_MODEL_ID_BYTES
            self._product_id_bytes = sysex.NOVATION_MANUFACTURER_ID + id_bytes[3:]
        return id_bytes