Esempio n. 1
0
 def _create_pad_sensitivity_update(self):
     all_pad_sysex_control = create_sysex_element(
         sysex.ALL_PADS_SENSITIVITY_PREFIX)
     pad_sysex_control = create_sysex_element(sysex.PAD_SENSITIVITY_PREFIX)
     sensitivity_sender = pad_parameter_sender(all_pad_sysex_control,
                                               pad_sysex_control)
     self._pad_sensitivity_update = PadUpdateComponent(
         all_pads=list(range(64)),
         parameter_sender=sensitivity_sender,
         default_profile=action_pad_sensitivity,
         update_delay=TIMER_DELAY)
Esempio n. 2
0
 def _create_pad_sensitivity_update(self):
     all_pad_sysex_control = SysexElement(sysex.make_pad_setting_message)
     pad_sysex_control = SysexElement(sysex.make_pad_setting_message)
     sensitivity_sender = pad_parameter_sender(all_pad_sysex_control, pad_sysex_control)
     self._pad_sensitivity_update = PadUpdateComponent(all_pads=range(64), parameter_sender=sensitivity_sender, default_profile=default_profile, update_delay=TIMER_DELAY, is_root=True)
     self._pad_sensitivity_update.set_profile('drums', playing_profile)
     self._pad_sensitivity_update.set_profile('instrument', playing_profile)
     self._pad_sensitivity_update.set_profile('loop', loop_selector_profile)
Esempio n. 3
0
class Push(PushBase):
    """
    Push controller script.
    
    Disclaimer: Any use of the Push control surface code (the "Code")
    or parts thereof for commercial purposes or in a commercial context
    is not allowed. Though we do not grant a license for non-commercial
    use of the Code, you may use it in this regard but should be aware that
    (1) we reserve the right to deny the future use any time and
    (2) you need to check whether the use is allowed under the national law
    applicable to your use.
    """
    input_target_name_for_auto_arm = 'Push Input'
    device_component_class = DeviceComponent
    selected_track_parameter_provider_class = SelectedTrackParameterProvider
    bank_definitions = BANK_DEFINITIONS
    note_editor_class = NoteEditorComponent
    sliced_simpler_class = SlicedSimplerComponent

    def __init__(self, *a, **k):
        super(Push, self).__init__(*a, **k)
        with self.component_guard():
            self.initialize()
        logger.info('Push script loaded')
        self._send_midi(sysex.WELCOME_MESSAGE)

    def disconnect(self):
        super(Push, self).disconnect()
        logger.info('Push script unloaded')
        self._send_midi(sysex.GOOD_BYE_MESSAGE)

    def port_settings_changed(self):
        super(Push, self).port_settings_changed()
        self._start_handshake_task.restart()

    def on_select_clip_slot(self, clip_slot):
        self._selector.on_select_clip(clip_slot)

    def on_select_scene(self, scene):
        self._selector.on_select_scene(scene)

    def on_select_track(self, track):
        self._selector.on_select_track(track)

    def _create_components(self):
        self._init_handshake()
        self._init_selector()
        super(Push, self)._create_components()
        self._init_browser()
        self._init_track_modes()

    def _create_settings(self):
        settings = create_settings(preferences=self.preferences)
        self.__on_pad_curve.subject = settings['curve']
        self.__on_pad_threshold.subject = settings['threshold']
        self.__on_aftertouch_threshold.subject = settings[
            'aftertouch_threshold']
        return settings

    def _create_device_decorator_factory(self):
        return SimplerDecoratorFactory()

    def _init_settings(self):
        super(Push, self)._init_settings()
        self._init_global_pad_parameters()
        self._update_pad_params()

    def _init_selector(self):
        self._selector = SelectComponent(name='Selector', is_root=True)
        self._selector.layer = Layer(select_button='select_button')
        self._selector.selection_display_layer = Layer(
            display_line='display_line3', priority=consts.DIALOG_PRIORITY)

    def _init_handshake(self):
        dongle_message, dongle = make_dongle_message(
            sysex.DONGLE_ENQUIRY_PREFIX)
        identity_control = create_sysex_element(sysex.IDENTITY_PREFIX,
                                                sysex.IDENTITY_ENQUIRY)
        dongle_control = create_sysex_element(sysex.DONGLE_PREFIX,
                                              dongle_message)
        presentation_control = create_sysex_element(
            sysex.DONGLE_PREFIX,
            sysex.make_presentation_message(self.application))
        self._handshake = HandshakeComponent(
            identity_control=identity_control,
            dongle_control=dongle_control,
            presentation_control=presentation_control,
            dongle=dongle,
            is_root=True)
        self._on_handshake_success.subject = self._handshake
        self._on_handshake_failure.subject = self._handshake
        self._start_handshake_task = self._tasks.add(
            task.sequence(task.wait(HANDSHAKE_DELAY),
                          task.run(self._start_handshake)))
        self._start_handshake_task.kill()

    def _init_user(self):
        super(Push, self)._init_user()
        self._on_push_hardware_mode_changed.subject = self._user
        self._update_pad_params()

    def _with_firmware_version(self, major_version, minor_version,
                               control_element):
        return MinimumFirmwareVersionElement(major_version, minor_version,
                                             control_element, self._handshake)

    def _start_handshake(self):
        self._start_handshake_task.kill()
        self.elements.playhead_element.proxied_object = self._c_instance.playhead
        self.elements.velocity_levels_element.proxied_object = self._c_instance.velocity_levels
        self._note_repeat.set_note_repeat(self._c_instance.note_repeat)
        self._accent_component.set_full_velocity(
            self._c_instance.full_velocity)
        for control in self.controls:
            receive_value_backup = getattr(control, '_receive_value_backup',
                                           nop)
            if receive_value_backup != nop:
                control.receive_value = receive_value_backup
            send_midi_backup = getattr(control, '_send_midi_backup', nop)
            if send_midi_backup != nop:
                control.send_midi = send_midi_backup

        self._handshake._start_handshake()
        self.update()

    @listens('success')
    def _on_handshake_success(self):
        logger.info('Handshake succeded with firmware version %.2f!' %
                    self._handshake.firmware_version)
        self.update()
        self._c_instance.set_firmware_version(self._handshake.firmware_version)
        if self._handshake.has_version_requirements(1, 16):
            self._user.settings = self._settings
        else:
            settings = copy(self._settings)
            del settings['aftertouch_threshold']
            self._user.settings = settings

    @listens('failure')
    def _on_handshake_failure(self, bootloader_mode):
        logger.error('Handshake failed, performing harakiri!')
        if bootloader_mode:
            self._c_instance.set_firmware_version(0.0)
        self._c_instance.playhead.enabled = False
        self.elements.playhead_element.proxied_object = NullPlayhead()
        self._c_instance.velocity_levels.enabled = False
        self.elements.velocity_levels_element.proxied_object = NullVelocityLevels(
        )
        self._note_repeat.set_note_repeat(None)
        self._accent_component.set_full_velocity(None)
        for control in self.controls:
            receive_value_backup = getattr(control, 'receive_value', nop)
            send_midi_backup = getattr(control, 'send_midi', nop)
            try:
                control.receive_value = nop
                if receive_value_backup != nop:
                    control._receive_value_backup = receive_value_backup
                control.send_midi = nop
                if send_midi_backup != nop:
                    control._send_midi_backup = send_midi_backup
            except AttributeError:
                pass

        return

    @listens('mode')
    def _on_push_hardware_mode_changed(self, mode):
        if mode == LIVE_MODE:
            if self._start_handshake_task.is_running:
                self._start_handshake()
            elif self._handshake.handshake_succeeded:
                self.update()

    def _create_background_layer(self):
        return super(Push, self)._create_background_layer() + Layer(
            display_line1='display_line1',
            display_line2='display_line2',
            display_line3='display_line3',
            display_line4='display_line4',
            in_button='in_button',
            out_button='out_button',
            pad_parameters=self._pad_parameter_control)

    def _create_message_box_background_layer(self):
        return super(Push, self)._create_message_box_background_layer(
        ) + BackgroundLayer(
            'in_button', 'out_button', priority=consts.MESSAGE_BOX_PRIORITY)

    def _create_track_frozen_layer(self):
        return Layer(display='display_line2',
                     _notification=self._notification.use_full_display(1))

    def _create_notification_component(self):
        return NotificationComponent(display_lines='display_lines',
                                     is_root=True,
                                     is_enabled=True)

    def _create_message_box_layer(self):
        return Layer(display_line1='display_line1',
                     display_line2='display_line2',
                     display_line3='display_line3',
                     display_line4='display_line4',
                     cancel_button='select_buttons_raw[-1]',
                     priority=consts.MESSAGE_BOX_PRIORITY)

    def _create_clip_mode(self):
        return [self._track_modes, (self._mixer, self._mixer_layer)] + super(
            Push, self)._create_clip_mode()

    def _create_clip_loop_layer(self):
        return super(Push, self)._create_clip_loop_layer() + Layer(
            name_display=self.elements.display_line1.subdisplay[:36],
            value_display=self.elements.display_line2.subdisplay[:36])

    def _create_clip_audio_layer(self):
        return super(Push, self)._create_clip_audio_layer() + Layer(
            name_display=self.elements.display_line1.subdisplay[36:],
            value_display=self.elements.display_line2.subdisplay[36:])

    def _create_clip_name_layer(self):
        return super(Push, self)._create_clip_name_layer() + Layer(
            display='display_line3')

    def _create_session_recording(self):
        return FixedLengthSessionRecordingComponent(
            fixed_length_setting=self._fixed_length_setting,
            clip_creator=self._clip_creator,
            view_controller=self._view_control,
            name='Session_Recording',
            is_root=True)

    def _init_track_modes(self):
        self._track_modes = ModesComponent(name='Track_Modes')
        self._track_modes.set_enabled(False)
        self._track_modes.add_mode(
            'stop', AddLayerMode(self._stop_clips,
                                 self._stop_track_clips_layer))
        self._track_modes.add_mode(
            'solo', AddLayerMode(self._mixer, self._mixer_solo_layer))
        self._track_modes.add_mode(
            'mute', AddLayerMode(self._mixer, self._mixer_mute_layer))
        self._track_modes.layer = self._create_track_modes_layer()
        self._track_modes.selected_mode = 'mute'

    def _browser_back_to_top(self):
        self._browser_mode.component.back_to_top()

    def _browser_reset_load_memory(self):
        self._browser_mode.component.reset_load_memory()

    def _init_browser(self):
        class BrowserMode(MultiEntryMode):
            def __init__(self, create_browser=nop, *a, **k):
                super(BrowserMode,
                      self).__init__(LazyComponentMode(create_browser), *a,
                                     **k)

            def enter_mode(browser_mode_self):
                super(BrowserMode, browser_mode_self).enter_mode()
                self._scales_enabler.selected_mode = 'disabled'

            @property
            def component(self):
                return self._mode.component

        self._browser_mode = BrowserMode(self._create_browser)
        self._browser_hotswap_mode = MultiEntryMode(
            BrowserHotswapMode(application=self.application))
        self._on_browse_mode_changed.subject = self.application.view

    def _init_browse_mode(self):
        self._main_modes.add_mode('browse', [
            self._when_track_is_not_frozen(
                self._enable_stop_mute_solo_as_modifiers,
                partial(self._view_control.show_view, 'Browser'),
                self._browser_back_to_top, self._browser_hotswap_mode,
                self._browser_mode, self._browser_reset_load_memory)
        ],
                                  groups=['add_effect', 'add_track', 'browse'],
                                  behaviour=mixin(DynamicBehaviourMixin,
                                                  CancellableBehaviour)
                                  (lambda: not self._browser_hotswap_mode._mode
                                   .can_hotswap() and 'add_effect_left'))
        self._main_modes.add_mode(
            'add_effect_right', [
                self._when_track_is_not_frozen(
                    self._enable_stop_mute_solo_as_modifiers,
                    self._browser_back_to_top,
                    LazyComponentMode(self._create_create_device_right))
            ],
            behaviour=mixin(ExcludingBehaviourMixin,
                            CancellableBehaviour)(['add_track', 'browse']),
            groups=['add_effect'])
        self._main_modes.add_mode(
            'add_effect_left', [
                self._when_track_is_not_frozen(
                    self._enable_stop_mute_solo_as_modifiers,
                    self._browser_back_to_top,
                    LazyComponentMode(self._create_create_device_left))
            ],
            behaviour=mixin(ExcludingBehaviourMixin,
                            CancellableBehaviour)(['add_track', 'browse']),
            groups=['add_effect'])
        self._main_modes.add_mode(
            'add_instrument_track', [
                self._enable_stop_mute_solo_as_modifiers,
                self._browser_back_to_top,
                LazyComponentMode(self._create_create_instrument_track)
            ],
            behaviour=mixin(ExcludingBehaviourMixin, AlternativeBehaviour)(
                excluded_groups=['browse', 'add_effect'],
                alternative_mode='add_default_track'),
            groups=['add_track'])
        self._main_modes.add_mode('add_default_track', [
            self._enable_stop_mute_solo_as_modifiers,
            self._browser_back_to_top,
            LazyComponentMode(self._create_create_default_track)
        ],
                                  groups=['add_track'])
        self._main_modes.add_effect_right_button.mode_unselected_color = self._main_modes.add_effect_left_button.mode_unselected_color = self._main_modes.add_instrument_track_button.mode_unselected_color = 'DefaultButton.On'

    @listens('browse_mode')
    def _on_browse_mode_changed(self):
        if not self.application.browser.hotswap_target:
            if self._main_modes.selected_mode == 'browse' or self._browser_hotswap_mode.is_entered:
                self._main_modes.selected_mode = 'device'

    def _create_browser(self):
        state_buttons = self.elements.track_state_buttons.submatrix[:7, :]
        browser = BrowserComponent(
            name='Browser',
            is_enabled=False,
            layer=Layer(encoder_controls='global_param_controls',
                        display_line1='display_line1',
                        display_line2='display_line2',
                        display_line3='display_line3',
                        display_line4='display_line4',
                        enter_button='in_button',
                        exit_button='out_button',
                        select_buttons='select_buttons',
                        state_buttons=state_buttons,
                        shift_button=WithPriority(consts.SHARED_PRIORITY,
                                                  'shift_button'),
                        prehear_button='track_state_buttons_raw[7]',
                        _notification=self._notification.use_full_display(2)),
            make_browser_model=make_browser_model,
            preferences=self.preferences)
        return browser

    def _create_create_device_right(self):
        return CreateDeviceComponent(
            name='Create_Device_Right',
            browser_component=self._browser_mode.component,
            browser_mode=self._browser_mode,
            browser_hotswap_mode=self._browser_hotswap_mode,
            insert_left=False,
            is_enabled=False)

    def _create_create_device_left(self):
        return CreateDeviceComponent(
            name='Create_Device_Right',
            browser_component=self._browser_mode.component,
            browser_mode=self._browser_mode,
            browser_hotswap_mode=self._browser_hotswap_mode,
            insert_left=True,
            is_enabled=False)

    def _create_create_default_track(self):
        create_default_track = CreateDefaultTrackComponent(
            name='Create_Default_Track', is_enabled=False)
        create_default_track.options.layer = Layer(
            display_line='display_line4',
            label_display_line='display_line3',
            blank_display_line2='display_line2',
            blank_display_line1='display_line1',
            select_buttons='select_buttons',
            state_buttons='track_state_buttons',
            priority=consts.DIALOG_PRIORITY)
        return create_default_track

    def _create_create_instrument_track(self):
        return CreateInstrumentTrackComponent(
            name='Create_Instrument_Track',
            browser_component=self._browser_mode.component,
            browser_mode=self._browser_mode,
            browser_hotswap_mode=self._browser_hotswap_mode,
            is_enabled=False)

    def _create_device_mode(self):
        return [
            self._when_track_is_not_frozen(
                self._enable_stop_mute_solo_as_modifiers,
                partial(self._view_control.show_view, 'Detail/DeviceChain'),
                self._device_parameter_component, self._device_navigation,
                self._device_note_editor_mode)
        ]

    def _create_scales(self):
        scales = InstrumentScalesComponent(
            note_layout=self._note_layout,
            is_enabled=False,
            name='Scales',
            layer=(BackgroundLayer('display_line1',
                                   'display_line2',
                                   priority=consts.DIALOG_PRIORITY),
                   Layer(
                       scale_line1=self.elements.display_line1.subdisplay[:18],
                       scale_line2=self.elements.display_line2.subdisplay[:18],
                       scale_line3=self.elements.display_line3.subdisplay[:9],
                       scale_line4=self.elements.display_line4.subdisplay[:9],
                       top_display_line=self.elements.display_line3.
                       subdisplay[9:],
                       bottom_display_line=self.elements.display_line4.
                       subdisplay[9:],
                       top_buttons='select_buttons',
                       bottom_buttons='track_state_buttons',
                       encoder_controls='global_param_controls',
                       _notification=self._notification.use_single_line(
                           0, get_slice[18:], align_right),
                       priority=consts.DIALOG_PRIORITY),
                   Layer(presets_toggle_button='shift_button')))
        scales.presets_layer = (
            BackgroundLayer('track_state_buttons',
                            'global_param_controls',
                            'display_line1',
                            'display_line2',
                            priority=consts.DIALOG_PRIORITY),
            Layer(top_display_line='display_line3',
                  bottom_display_line='display_line4',
                  top_buttons='select_buttons',
                  _notification=self._notification.use_single_line(0),
                  priority=consts.DIALOG_PRIORITY))
        return scales

    def _create_scales_enabler(self):
        return EnablingModesComponent(
            component=self._create_scales(),
            enabled_color='DefaultButton.On',
            is_enabled=False,
            layer=Layer(cycle_mode_button='scale_presets_button'))

    def _create_drum_component(self):
        return DrumGroupComponent(name='Drum_Group',
                                  is_enabled=False,
                                  quantizer=self._quantize,
                                  selector=self._selector)

    def _init_note_settings_component(self):
        self._note_settings_component = NoteSettingsComponent(
            grid_resolution=self._grid_resolution,
            is_enabled=False,
            layer=Layer(top_display_line='display_line1',
                        bottom_display_line='display_line2',
                        info_display_line='display_line3',
                        clear_display_line='display_line4',
                        full_velocity_button='accent_button',
                        priority=consts.MOMENTARY_DIALOG_PRIORITY))

    def _create_note_editor_mode_selector_layer(self):
        return super(Push,
                     self)._create_note_editor_mode_selector_layer() + Layer(
                         display_line='display_line4',
                         priority=consts.MOMENTARY_DIALOG_PRIORITY)

    def _create_note_editor_track_automation_layer(self):
        return super(
            Push, self)._create_note_editor_track_automation_layer() + Layer(
                name_display_line='display_line1',
                graphic_display_line='display_line2',
                value_display_line='display_line3',
                priority=consts.MOMENTARY_DIALOG_PRIORITY)

    def _create_note_editor_device_automation_layer(self):
        return super(
            Push, self)._create_note_editor_device_automation_layer() + Layer(
                name_display_line='display_line1',
                value_display_line='display_line2',
                graphic_display_line='display_line3',
                priority=consts.MOMENTARY_DIALOG_PRIORITY)

    def _init_stop_clips_action(self):
        self._stop_clips = StopClipComponent(session_ring=self._session_ring,
                                             name='Stop_Clip',
                                             is_root=True)
        self._stop_clips.layer = Layer(
            stop_all_clips_button=self._with_shift('global_track_stop_button'))
        self._stop_track_clips_layer = Layer(
            stop_track_clips_buttons='track_state_buttons')

    def _init_quantize_actions(self):
        self._quantize_settings = QuantizationSettingsComponent(
            is_enabled=False,
            layer=(BackgroundLayer('global_param_controls',
                                   'select_buttons',
                                   'track_state_buttons',
                                   priority=consts.MOMENTARY_DIALOG_PRIORITY),
                   Layer(
                       swing_amount_encoder='parameter_controls_raw[0]',
                       quantize_to_encoder='parameter_controls_raw[1]',
                       quantize_amount_encoder='parameter_controls_raw[2]',
                       record_quantization_encoder='parameter_controls_raw[7]',
                       record_quantization_toggle_button=
                       'track_state_buttons_raw[7]',
                       display_line1='display_line1',
                       display_line2='display_line2',
                       display_line3='display_line3',
                       display_line4='display_line4',
                       priority=consts.MOMENTARY_DIALOG_PRIORITY)))
        self._quantize = self._for_non_frozen_tracks(QuantizationComponent(
            name='Selected_Clip_Quantize',
            settings=self._quantize_settings,
            is_enabled=False,
            layer=Layer(action_button='quantize_button')),
                                                     is_root=True)

    def _init_fixed_length(self):
        super(Push, self)._init_fixed_length()
        self._fixed_length_settings_component.layer = (
            BackgroundLayer(self.elements.track_state_buttons.submatrix[:7, :],
                            'display_line1',
                            'display_line2',
                            priority=consts.MOMENTARY_DIALOG_PRIORITY),
            Layer(length_option_buttons='select_buttons',
                  label_display_line='display_line3',
                  option_display_line='display_line4',
                  legato_launch_toggle_button='track_state_buttons_raw[7]',
                  _notification=self._notification.use_single_line(1),
                  priority=consts.MOMENTARY_DIALOG_PRIORITY))

    def _create_note_repeat_layer(self):
        return super(Push, self)._create_note_repeat_layer() + Layer(
            pad_parameters=self._pad_parameter_control,
            priority=consts.DIALOG_PRIORITY)

    def _create_user_component(self):
        sysex_control = create_sysex_element(sysex.MODE_CHANGE)
        user = UserComponent(value_control=sysex_control, is_root=True)
        user.layer = Layer(action_button='user_button')
        user.settings_layer = Layer(display_line1='display_line1',
                                    display_line2='display_line2',
                                    display_line3='display_line3',
                                    display_line4='display_line4',
                                    encoders='global_param_controls')
        user.settings_layer.priority = consts.DIALOG_PRIORITY
        return user

    def _init_value_components(self):
        super(Push, self)._init_value_components()
        self._swing_amount.display.layer = (
            BackgroundLayer('display_line4', priority=consts.DIALOG_PRIORITY),
            Layer(label_display='display_line1',
                  value_display='display_line3',
                  graphic_display='display_line2',
                  priority=consts.DIALOG_PRIORITY))
        self._tempo.display.layer = (BackgroundLayer(
            'display_line3', 'display_line4', priority=consts.DIALOG_PRIORITY),
                                     Layer(label_display='display_line1',
                                           value_display='display_line2',
                                           priority=consts.DIALOG_PRIORITY))
        self._master_vol.display.layer = (BackgroundLayer(
            'display_line4', priority=consts.DIALOG_PRIORITY),
                                          Layer(
                                              label_display='display_line1',
                                              value_display='display_line3',
                                              graphic_display='display_line2',
                                              priority=consts.DIALOG_PRIORITY))
        self._master_cue_vol.display.layer = (
            BackgroundLayer('display_line4', priority=consts.DIALOG_PRIORITY),
            Layer(label_display='display_line1',
                  value_display='display_line3',
                  graphic_display='display_line2',
                  priority=consts.DIALOG_PRIORITY))

    def _create_note_mode(self):
        return super(Push,
                     self)._create_note_mode() + [self._global_pad_parameters]

    def _create_note_mode_behaviour(self):
        return self._auto_arm.auto_arm_restore_behaviour(
            ReenterBehaviour, on_reenter=self._switch_note_mode_layout)

    def _instantiate_session(self):
        return SpecialSessionComponent(
            session_ring=self._session_ring,
            is_enabled=False,
            auto_name=True,
            fixed_length_recording=self._create_fixed_length_recording(),
            layer=self._create_session_layer())

    def _create_session_mode(self):
        return [
            self._session_overview_mode, self._session_mode,
            self._session_navigation
        ]

    def _create_session_overview_layer(self):
        return Layer(button_matrix='shifted_matrix')

    def _set_session_skin(self, session):
        session.set_rgb_mode(CLIP_COLOR_TABLE,
                             RGB_COLOR_TABLE,
                             clip_slots_only=True)

    def _on_selected_track_changed(self):
        super(Push, self)._on_selected_track_changed()
        self._main_modes.pop_groups(['add_effect'])

    def _init_main_modes(self):
        super(Push, self)._init_main_modes()
        self.__on_main_mode_button_value.replace_subjects([
            self.elements.vol_mix_mode_button,
            self.elements.pan_send_mix_mode_button,
            self.elements.single_track_mix_mode_button,
            self.elements.clip_mode_button, self.elements.device_mode_button,
            self.elements.browse_mode_button
        ])

    def _create_mixer_layer(self):
        return Layer(track_select_buttons='select_buttons',
                     track_names_display='display_line4')

    def _create_mixer_solo_layer(self):
        return Layer(solo_buttons='track_state_buttons')

    def _create_mixer_mute_layer(self):
        return Layer(mute_buttons='track_state_buttons')

    def _create_mixer_pan_send_layer(self):
        return Layer(track_select_buttons='select_buttons',
                     pan_send_toggle='pan_send_mix_mode_button',
                     pan_send_controls='fine_grain_param_controls',
                     track_names_display='display_line4',
                     pan_send_names_display='display_line1',
                     pan_send_graphics_display='display_line2',
                     selected_track_name_display='display_line3',
                     pan_send_values_display=ComboElement(
                         'display_line3', 'any_touch_button'))

    def _create_mixer_volume_layer(self):
        return Layer(track_select_buttons='select_buttons',
                     volume_controls='fine_grain_param_controls',
                     track_names_display='display_line4',
                     volume_names_display='display_line1',
                     volume_graphics_display='display_line2',
                     selected_track_name_display='display_line3',
                     volume_values_display=ComboElement(
                         'display_line3', 'any_touch_button'))

    def _create_mixer_track_layer(self):
        return Layer(track_select_buttons='select_buttons',
                     selected_track_name_display='display_line3',
                     track_names_display='display_line4')

    def _init_mixer(self):
        self._mixer = SpecialMixerComponent(tracks_provider=self._session_ring,
                                            is_root=True)
        self._mixer.set_enabled(False)
        self._mixer.name = 'Mixer'
        self._mixer_layer = self._create_mixer_layer()
        self._mixer_pan_send_layer = self._create_mixer_pan_send_layer()
        self._mixer_volume_layer = self._create_mixer_volume_layer()
        self._mixer_track_layer = self._create_mixer_track_layer()
        self._mixer_solo_layer = self._create_mixer_solo_layer()
        self._mixer_mute_layer = self._create_mixer_mute_layer()
        for track in xrange(self.elements.matrix.width()):
            strip = self._mixer.channel_strip(track)
            strip.name = 'Channel_Strip_' + str(track)
            strip.set_invert_mute_feedback(True)
            strip.set_delete_handler(self._delete_component)
            strip._do_select_track = self.on_select_track
            strip.layer = Layer(shift_button='shift_button',
                                duplicate_button='duplicate_button',
                                selector_button='select_button')

        self._mixer.selected_strip().name = 'Selected_Channel_strip'
        self._mixer.master_strip().name = 'Master_Channel_strip'
        self._mixer.master_strip()._do_select_track = self.on_select_track
        self._mixer.master_strip().layer = Layer(
            select_button='master_select_button',
            selector_button='select_button')
        self._mixer.set_enabled(True)

    def _create_track_mixer_layer(self):
        return super(Push, self)._create_track_mixer_layer() + Layer(
            name_display_line='display_line1',
            graphic_display_line='display_line2',
            value_display_line=ComboElement('display_line3',
                                            'any_touch_button'))

    def _create_device_parameter_layer(self):
        return super(Push, self)._create_device_parameter_layer() + Layer(
            name_display_line='display_line1',
            value_display_line='display_line2',
            graphic_display_line=ComboElement('display_line3',
                                              'any_touch_button'))

    def _create_device_navigation(self):
        return DeviceNavigationComponent(
            device_bank_registry=self._device_bank_registry,
            banking_info=self._banking_info,
            is_enabled=False,
            session_ring=self._session_ring,
            layer=Layer(enter_button='in_button',
                        exit_button='out_button',
                        select_buttons='select_buttons',
                        state_buttons='track_state_buttons',
                        display_line='display_line4',
                        _notification=self._notification.use_single_line(2)),
            info_layer=Layer(
                display_line1='display_line1',
                display_line2='display_line2',
                display_line3='display_line3',
                display_line4='display_line4',
                _notification=self._notification.use_full_display(2)),
            delete_handler=self._delete_component)

    @listens_group('value')
    def __on_main_mode_button_value(self, value, sender):
        if not value:
            self._scales_enabler.selected_mode = 'disabled'

    def _create_controls(self):
        self._create_pad_sensitivity_update()

        class Deleter(object):
            @property
            def is_deleting(_):
                return self._delete_default_component.is_deleting

            def delete_clip_envelope(_, param):
                return self._delete_default_component.delete_clip_envelope(
                    param)

        self.elements = Elements(
            deleter=Deleter(),
            undo_handler=self.song,
            pad_sensitivity_update=self._pad_sensitivity_update,
            playhead=self._c_instance.playhead,
            velocity_levels=self._c_instance.velocity_levels)

    def _create_pad_sensitivity_update(self):
        all_pad_sysex_control = create_sysex_element(
            sysex.ALL_PADS_SENSITIVITY_PREFIX)
        pad_sysex_control = create_sysex_element(sysex.PAD_SENSITIVITY_PREFIX)
        sensitivity_sender = pad_parameter_sender(all_pad_sysex_control,
                                                  pad_sysex_control)
        self._pad_sensitivity_update = PadUpdateComponent(
            all_pads=range(64),
            parameter_sender=sensitivity_sender,
            default_profile=action_pad_sensitivity,
            update_delay=TIMER_DELAY,
            is_root=True)

    def _init_global_pad_parameters(self):
        self._pad_parameter_control = self._with_firmware_version(
            1, 16,
            create_sysex_element(
                sysex.PAD_PARAMETER_PREFIX,
                default_value=sysex.make_pad_parameter_message()))
        aftertouch_threshold = self._settings['aftertouch_threshold'].value
        self._global_pad_parameters = GlobalPadParameters(
            aftertouch_threshold=aftertouch_threshold,
            is_enabled=False,
            layer=Layer(pad_parameter=self._pad_parameter_control))

    @listens('value')
    def __on_pad_curve(self, _value):
        self._update_pad_params()

    @listens('value')
    def __on_pad_threshold(self, value):
        self._user.set_settings_info_text(
            '' if value >= CRITICAL_THRESHOLD_LIMIT else consts.MessageBoxText.
            STUCK_PAD_WARNING)
        self._update_pad_params()

    @listens('value')
    def __on_aftertouch_threshold(self, value):
        self._global_pad_parameters.aftertouch_threshold = value

    def _update_pad_params(self):
        new_pad_parameters = make_pad_parameters(
            self._settings['curve'].value, self._settings['threshold'].value)
        self._pad_sensitivity_update.set_profile('instrument',
                                                 new_pad_parameters)
        self._pad_sensitivity_update.set_profile('drums', new_pad_parameters)
        self._pad_sensitivity_update.set_profile('loop',
                                                 action_pad_sensitivity)

    def _update_calibration(self):
        self._send_midi(sysex.CALIBRATION_SET)

    def update(self):
        self._update_calibration()
        super(Push, self).update()
Esempio n. 4
0
class Push(PushBase):
    """
    Push controller script.
    
    Disclaimer: Any use of the Push control surface code (the "Code")
    or parts thereof for commercial purposes or in a commercial context
    is not allowed. Though we do not grant a license for non-commercial
    use of the Code, you may use it in this regard but should be aware that
    (1) we reserve the right to deny the future use any time and
    (2) you need to check whether the use is allowed under the national law
    applicable to your use.
    """
    input_target_name_for_auto_arm = 'Push Input'
    device_component_class = DeviceComponent
    selected_track_parameter_provider_class = SelectedTrackParameterProvider
    bank_definitions = BANK_DEFINITIONS
    note_editor_class = NoteEditorComponent
    sliced_simpler_class = SlicedSimplerComponent

    def __init__(self, *a, **k):
        super(Push, self).__init__(*a, **k)
        with self.component_guard():
            self.initialize()
        logger.info('Push script loaded')
        self._send_midi(sysex.WELCOME_MESSAGE)

    def disconnect(self):
        super(Push, self).disconnect()
        logger.info('Push script unloaded')
        self._send_midi(sysex.GOOD_BYE_MESSAGE)

    def port_settings_changed(self):
        super(Push, self).port_settings_changed()
        self._start_handshake_task.restart()

    def on_select_clip_slot(self, clip_slot):
        self._selector.on_select_clip(clip_slot)

    def on_select_scene(self, scene):
        self._selector.on_select_scene(scene)

    def on_select_track(self, track):
        self._selector.on_select_track(track)

    def _create_components(self):
        self._init_handshake()
        self._init_selector()
        super(Push, self)._create_components()
        self._init_browser()
        self._init_track_modes()

    def _create_settings(self):
        settings = create_settings(preferences=self.preferences)
        self.__on_pad_curve.subject = settings['curve']
        self.__on_pad_threshold.subject = settings['threshold']
        self.__on_aftertouch_threshold.subject = settings['aftertouch_threshold']
        return settings

    def _create_device_decorator_factory(self):
        return SimplerDecoratorFactory()

    def _init_settings(self):
        super(Push, self)._init_settings()
        self._init_global_pad_parameters()
        self._update_pad_params()

    def _init_selector(self):
        self._selector = SelectComponent(name='Selector', is_root=True)
        self._selector.layer = Layer(select_button='select_button')
        self._selector.selection_display_layer = Layer(display_line='display_line3', priority=consts.DIALOG_PRIORITY)

    def _init_handshake(self):
        dongle_message, dongle = make_dongle_message(sysex.DONGLE_ENQUIRY_PREFIX)
        identity_control = create_sysex_element(sysex.IDENTITY_PREFIX, sysex.IDENTITY_ENQUIRY)
        dongle_control = create_sysex_element(sysex.DONGLE_PREFIX, dongle_message)
        presentation_control = create_sysex_element(sysex.DONGLE_PREFIX, sysex.make_presentation_message(self.application))
        self._handshake = HandshakeComponent(identity_control=identity_control, dongle_control=dongle_control, presentation_control=presentation_control, dongle=dongle, is_root=True)
        self._on_handshake_success.subject = self._handshake
        self._on_handshake_failure.subject = self._handshake
        self._start_handshake_task = self._tasks.add(task.sequence(task.wait(HANDSHAKE_DELAY), task.run(self._start_handshake)))
        self._start_handshake_task.kill()

    def _init_user(self):
        super(Push, self)._init_user()
        self._on_push_hardware_mode_changed.subject = self._user
        self._update_pad_params()

    def _with_firmware_version(self, major_version, minor_version, control_element):
        return MinimumFirmwareVersionElement(major_version, minor_version, control_element, self._handshake)

    def _start_handshake(self):
        self._start_handshake_task.kill()
        self.elements.playhead_element.proxied_object = self._c_instance.playhead
        self.elements.velocity_levels_element.proxied_object = self._c_instance.velocity_levels
        self._note_repeat.set_note_repeat(self._c_instance.note_repeat)
        self._accent_component.set_full_velocity(self._c_instance.full_velocity)
        for control in self.controls:
            receive_value_backup = getattr(control, '_receive_value_backup', nop)
            if receive_value_backup != nop:
                control.receive_value = receive_value_backup
            send_midi_backup = getattr(control, '_send_midi_backup', nop)
            if send_midi_backup != nop:
                control.send_midi = send_midi_backup

        self._handshake._start_handshake()
        self.update()

    @listens('success')
    def _on_handshake_success(self):
        logger.info('Handshake succeded with firmware version %.2f!' % self._handshake.firmware_version)
        self.update()
        self._c_instance.set_firmware_version(self._handshake.firmware_version)
        if self._handshake.has_version_requirements(1, 16):
            self._user.settings = self._settings
        else:
            settings = copy(self._settings)
            del settings['aftertouch_threshold']
            self._user.settings = settings

    @listens('failure')
    def _on_handshake_failure(self, bootloader_mode):
        logger.error('Handshake failed, performing harakiri!')
        if bootloader_mode:
            self._c_instance.set_firmware_version(0.0)
        self._c_instance.playhead.enabled = False
        self.elements.playhead_element.proxied_object = NullPlayhead()
        self._c_instance.velocity_levels.enabled = False
        self.elements.velocity_levels_element.proxied_object = NullVelocityLevels()
        self._note_repeat.set_note_repeat(None)
        self._accent_component.set_full_velocity(None)
        for control in self.controls:
            receive_value_backup = getattr(control, 'receive_value', nop)
            send_midi_backup = getattr(control, 'send_midi', nop)
            try:
                control.receive_value = nop
                if receive_value_backup != nop:
                    control._receive_value_backup = receive_value_backup
                control.send_midi = nop
                if send_midi_backup != nop:
                    control._send_midi_backup = send_midi_backup
            except AttributeError:
                pass

    @listens('mode')
    def _on_push_hardware_mode_changed(self, mode):
        if mode == LIVE_MODE:
            if self._start_handshake_task.is_running:
                self._start_handshake()
            elif self._handshake.handshake_succeeded:
                self.update()

    def _create_background_layer(self):
        return super(Push, self)._create_background_layer() + Layer(display_line1='display_line1', display_line2='display_line2', display_line3='display_line3', display_line4='display_line4', in_button='in_button', out_button='out_button', pad_parameters=self._pad_parameter_control)

    def _create_message_box_background_layer(self):
        return super(Push, self)._create_message_box_background_layer() + BackgroundLayer('in_button', 'out_button', priority=consts.MESSAGE_BOX_PRIORITY)

    def _create_track_frozen_layer(self):
        return Layer(display='display_line2', _notification=self._notification.use_full_display(1))

    def _create_notification_component(self):
        return NotificationComponent(display_lines='display_lines', is_root=True, is_enabled=True)

    def _create_message_box_layer(self):
        return Layer(display_line1='display_line1', display_line2='display_line2', display_line3='display_line3', display_line4='display_line4', cancel_button='select_buttons_raw[-1]', priority=consts.MESSAGE_BOX_PRIORITY)

    def _create_clip_mode(self):
        return [self._track_modes, (self._mixer, self._mixer_layer)] + super(Push, self)._create_clip_mode()

    def _create_clip_loop_layer(self):
        return super(Push, self)._create_clip_loop_layer() + Layer(name_display=self.elements.display_line1.subdisplay[:36], value_display=self.elements.display_line2.subdisplay[:36])

    def _create_clip_audio_layer(self):
        return super(Push, self)._create_clip_audio_layer() + Layer(name_display=self.elements.display_line1.subdisplay[36:], value_display=self.elements.display_line2.subdisplay[36:])

    def _create_clip_name_layer(self):
        return super(Push, self)._create_clip_name_layer() + Layer(display='display_line3')

    def _create_session_recording(self):
        return FixedLengthSessionRecordingComponent(fixed_length_setting=self._fixed_length_setting, clip_creator=self._clip_creator, view_controller=self._view_control, name='Session_Recording', is_root=True)

    def _init_track_modes(self):
        self._track_modes = ModesComponent(name='Track_Modes')
        self._track_modes.set_enabled(False)
        self._track_modes.add_mode('stop', AddLayerMode(self._stop_clips, self._stop_track_clips_layer))
        self._track_modes.add_mode('solo', AddLayerMode(self._mixer, self._mixer_solo_layer))
        self._track_modes.add_mode('mute', AddLayerMode(self._mixer, self._mixer_mute_layer))
        self._track_modes.layer = self._create_track_modes_layer()
        self._track_modes.selected_mode = 'mute'

    def _browser_back_to_top(self):
        self._browser_mode.component.back_to_top()

    def _browser_reset_load_memory(self):
        self._browser_mode.component.reset_load_memory()

    def _init_browser(self):

        class BrowserMode(MultiEntryMode):

            def __init__(self, create_browser = nop, *a, **k):
                super(BrowserMode, self).__init__(LazyComponentMode(create_browser), *a, **k)

            def enter_mode(browser_mode_self):
                super(BrowserMode, browser_mode_self).enter_mode()
                self._scales_enabler.selected_mode = 'disabled'

            @property
            def component(self):
                return self._mode.component

        self._browser_mode = BrowserMode(self._create_browser)
        self._browser_hotswap_mode = MultiEntryMode(BrowserHotswapMode(application=self.application))
        self._on_browse_mode_changed.subject = self.application.view

    def _init_browse_mode(self):
        self._main_modes.add_mode('browse', [self._when_track_is_not_frozen(self._enable_stop_mute_solo_as_modifiers, partial(self._view_control.show_view, 'Browser'), self._browser_back_to_top, self._browser_hotswap_mode, self._browser_mode, self._browser_reset_load_memory)], groups=['add_effect', 'add_track', 'browse'], behaviour=mixin(DynamicBehaviourMixin, CancellableBehaviour)(lambda : not self._browser_hotswap_mode._mode.can_hotswap() and 'add_effect_left'))
        self._main_modes.add_mode('add_effect_right', [self._when_track_is_not_frozen(self._enable_stop_mute_solo_as_modifiers, self._browser_back_to_top, LazyComponentMode(self._create_create_device_right))], behaviour=mixin(ExcludingBehaviourMixin, CancellableBehaviour)(['add_track', 'browse']), groups=['add_effect'])
        self._main_modes.add_mode('add_effect_left', [self._when_track_is_not_frozen(self._enable_stop_mute_solo_as_modifiers, self._browser_back_to_top, LazyComponentMode(self._create_create_device_left))], behaviour=mixin(ExcludingBehaviourMixin, CancellableBehaviour)(['add_track', 'browse']), groups=['add_effect'])
        self._main_modes.add_mode('add_instrument_track', [self._enable_stop_mute_solo_as_modifiers, self._browser_back_to_top, LazyComponentMode(self._create_create_instrument_track)], behaviour=mixin(ExcludingBehaviourMixin, AlternativeBehaviour)(excluded_groups=['browse', 'add_effect'], alternative_mode='add_default_track'), groups=['add_track'])
        self._main_modes.add_mode('add_default_track', [self._enable_stop_mute_solo_as_modifiers, self._browser_back_to_top, LazyComponentMode(self._create_create_default_track)], groups=['add_track'])
        self._main_modes.add_effect_right_button.mode_unselected_color = self._main_modes.add_effect_left_button.mode_unselected_color = self._main_modes.add_instrument_track_button.mode_unselected_color = 'DefaultButton.On'

    @listens('browse_mode')
    def _on_browse_mode_changed(self):
        if not self.application.browser.hotswap_target:
            if self._main_modes.selected_mode == 'browse' or self._browser_hotswap_mode.is_entered:
                self._main_modes.selected_mode = 'device'

    def _create_browser(self):
        state_buttons = self.elements.track_state_buttons.submatrix[:7, :]
        browser = BrowserComponent(name='Browser', is_enabled=False, layer=Layer(encoder_controls='global_param_controls', display_line1='display_line1', display_line2='display_line2', display_line3='display_line3', display_line4='display_line4', enter_button='in_button', exit_button='out_button', select_buttons='select_buttons', state_buttons=state_buttons, shift_button=WithPriority(consts.SHARED_PRIORITY, 'shift_button'), prehear_button='track_state_buttons_raw[7]', _notification=self._notification.use_full_display(2)), make_browser_model=make_browser_model, preferences=self.preferences)
        return browser

    def _create_create_device_right(self):
        return CreateDeviceComponent(name='Create_Device_Right', browser_component=self._browser_mode.component, browser_mode=self._browser_mode, browser_hotswap_mode=self._browser_hotswap_mode, insert_left=False, is_enabled=False)

    def _create_create_device_left(self):
        return CreateDeviceComponent(name='Create_Device_Right', browser_component=self._browser_mode.component, browser_mode=self._browser_mode, browser_hotswap_mode=self._browser_hotswap_mode, insert_left=True, is_enabled=False)

    def _create_create_default_track(self):
        create_default_track = CreateDefaultTrackComponent(name='Create_Default_Track', is_enabled=False)
        create_default_track.options.layer = Layer(display_line='display_line4', label_display_line='display_line3', blank_display_line2='display_line2', blank_display_line1='display_line1', select_buttons='select_buttons', state_buttons='track_state_buttons', priority=consts.DIALOG_PRIORITY)
        return create_default_track

    def _create_create_instrument_track(self):
        return CreateInstrumentTrackComponent(name='Create_Instrument_Track', browser_component=self._browser_mode.component, browser_mode=self._browser_mode, browser_hotswap_mode=self._browser_hotswap_mode, is_enabled=False)

    def _create_device_mode(self):
        return [self._when_track_is_not_frozen(self._enable_stop_mute_solo_as_modifiers, partial(self._view_control.show_view, 'Detail/DeviceChain'), self._device_parameter_component, self._device_navigation, self._device_note_editor_mode)]

    def _create_scales(self):
        scales = InstrumentScalesComponent(note_layout=self._note_layout, is_enabled=False, name='Scales', layer=(BackgroundLayer('display_line1', 'display_line2', priority=consts.DIALOG_PRIORITY), Layer(scale_line1=self.elements.display_line1.subdisplay[:18], scale_line2=self.elements.display_line2.subdisplay[:18], scale_line3=self.elements.display_line3.subdisplay[:9], scale_line4=self.elements.display_line4.subdisplay[:9], top_display_line=self.elements.display_line3.subdisplay[9:], bottom_display_line=self.elements.display_line4.subdisplay[9:], top_buttons='select_buttons', bottom_buttons='track_state_buttons', encoder_controls='global_param_controls', _notification=self._notification.use_single_line(0, get_slice[18:], align_right), priority=consts.DIALOG_PRIORITY), Layer(presets_toggle_button='shift_button')))
        scales.presets_layer = (BackgroundLayer('track_state_buttons', 'global_param_controls', 'display_line1', 'display_line2', priority=consts.DIALOG_PRIORITY), Layer(top_display_line='display_line3', bottom_display_line='display_line4', top_buttons='select_buttons', _notification=self._notification.use_single_line(0), priority=consts.DIALOG_PRIORITY))
        return scales

    def _create_scales_enabler(self):
        return EnablingModesComponent(component=self._create_scales(), enabled_color='DefaultButton.On', is_enabled=False, layer=Layer(cycle_mode_button='scale_presets_button'))

    def _create_drum_component(self):
        return DrumGroupComponent(name='Drum_Group', is_enabled=False, quantizer=self._quantize, selector=self._selector)

    def _init_note_settings_component(self):
        self._note_settings_component = NoteSettingsComponent(grid_resolution=self._grid_resolution, is_enabled=False, layer=Layer(top_display_line='display_line1', bottom_display_line='display_line2', info_display_line='display_line3', clear_display_line='display_line4', full_velocity_button='accent_button', priority=consts.MOMENTARY_DIALOG_PRIORITY))

    def _create_note_editor_mode_selector_layer(self):
        return super(Push, self)._create_note_editor_mode_selector_layer() + Layer(display_line='display_line4', priority=consts.MOMENTARY_DIALOG_PRIORITY)

    def _create_note_editor_track_automation_layer(self):
        return super(Push, self)._create_note_editor_track_automation_layer() + Layer(name_display_line='display_line1', graphic_display_line='display_line2', value_display_line='display_line3', priority=consts.MOMENTARY_DIALOG_PRIORITY)

    def _create_note_editor_device_automation_layer(self):
        return super(Push, self)._create_note_editor_device_automation_layer() + Layer(name_display_line='display_line1', value_display_line='display_line2', graphic_display_line='display_line3', priority=consts.MOMENTARY_DIALOG_PRIORITY)

    def _init_stop_clips_action(self):
        self._stop_clips = StopClipComponent(session_ring=self._session_ring, name='Stop_Clip', is_root=True)
        self._stop_clips.layer = Layer(stop_all_clips_button=self._with_shift('global_track_stop_button'))
        self._stop_track_clips_layer = Layer(stop_track_clips_buttons='track_state_buttons')

    def _init_quantize_actions(self):
        self._quantize_settings = QuantizationSettingsComponent(is_enabled=False, layer=(BackgroundLayer('global_param_controls', 'select_buttons', 'track_state_buttons', priority=consts.MOMENTARY_DIALOG_PRIORITY), Layer(swing_amount_encoder='parameter_controls_raw[0]', quantize_to_encoder='parameter_controls_raw[1]', quantize_amount_encoder='parameter_controls_raw[2]', record_quantization_encoder='parameter_controls_raw[7]', record_quantization_toggle_button='track_state_buttons_raw[7]', display_line1='display_line1', display_line2='display_line2', display_line3='display_line3', display_line4='display_line4', priority=consts.MOMENTARY_DIALOG_PRIORITY)))
        self._quantize = self._for_non_frozen_tracks(QuantizationComponent(name='Selected_Clip_Quantize', settings=self._quantize_settings, is_enabled=False, layer=Layer(action_button='quantize_button')), is_root=True)

    def _init_fixed_length(self):
        super(Push, self)._init_fixed_length()
        self._fixed_length_settings_component.layer = (BackgroundLayer(self.elements.track_state_buttons.submatrix[:7, :], 'display_line1', 'display_line2', priority=consts.MOMENTARY_DIALOG_PRIORITY), Layer(length_option_buttons='select_buttons', label_display_line='display_line3', option_display_line='display_line4', legato_launch_toggle_button='track_state_buttons_raw[7]', _notification=self._notification.use_single_line(1), priority=consts.MOMENTARY_DIALOG_PRIORITY))

    def _create_note_repeat_layer(self):
        return super(Push, self)._create_note_repeat_layer() + Layer(pad_parameters=self._pad_parameter_control, priority=consts.DIALOG_PRIORITY)

    def _create_user_component(self):
        sysex_control = create_sysex_element(sysex.MODE_CHANGE)
        user = UserComponent(value_control=sysex_control, is_root=True)
        user.layer = Layer(action_button='user_button')
        user.settings_layer = Layer(display_line1='display_line1', display_line2='display_line2', display_line3='display_line3', display_line4='display_line4', encoders='global_param_controls')
        user.settings_layer.priority = consts.DIALOG_PRIORITY
        return user

    def _init_value_components(self):
        super(Push, self)._init_value_components()
        self._swing_amount.display.layer = (BackgroundLayer('display_line4', priority=consts.DIALOG_PRIORITY), Layer(label_display='display_line1', value_display='display_line3', graphic_display='display_line2', priority=consts.DIALOG_PRIORITY))
        self._tempo.display.layer = (BackgroundLayer('display_line3', 'display_line4', priority=consts.DIALOG_PRIORITY), Layer(label_display='display_line1', value_display='display_line2', priority=consts.DIALOG_PRIORITY))
        self._master_vol.display.layer = (BackgroundLayer('display_line4', priority=consts.DIALOG_PRIORITY), Layer(label_display='display_line1', value_display='display_line3', graphic_display='display_line2', priority=consts.DIALOG_PRIORITY))
        self._master_cue_vol.display.layer = (BackgroundLayer('display_line4', priority=consts.DIALOG_PRIORITY), Layer(label_display='display_line1', value_display='display_line3', graphic_display='display_line2', priority=consts.DIALOG_PRIORITY))

    def _create_note_mode(self):
        return super(Push, self)._create_note_mode() + [self._global_pad_parameters]

    def _create_note_mode_behaviour(self):
        return self._auto_arm.auto_arm_restore_behaviour(ReenterBehaviour, on_reenter=self._switch_note_mode_layout)

    def _instantiate_session(self):
        return SpecialSessionComponent(session_ring=self._session_ring, is_enabled=False, auto_name=True, fixed_length_recording=self._create_fixed_length_recording(), layer=self._create_session_layer())

    def _create_session_mode(self):
        return [self._session_overview_mode, self._session_mode, self._session_navigation]

    def _create_session_overview_layer(self):
        return Layer(button_matrix='shifted_matrix')

    def _set_session_skin(self, session):
        session.set_rgb_mode(CLIP_COLOR_TABLE, RGB_COLOR_TABLE, clip_slots_only=True)

    def _on_selected_track_changed(self):
        super(Push, self)._on_selected_track_changed()
        self._main_modes.pop_groups(['add_effect'])

    def _init_main_modes(self):
        super(Push, self)._init_main_modes()
        self.__on_main_mode_button_value.replace_subjects([self.elements.vol_mix_mode_button,
         self.elements.pan_send_mix_mode_button,
         self.elements.single_track_mix_mode_button,
         self.elements.clip_mode_button,
         self.elements.device_mode_button,
         self.elements.browse_mode_button])

    def _create_mixer_layer(self):
        return Layer(track_select_buttons='select_buttons', track_names_display='display_line4')

    def _create_mixer_solo_layer(self):
        return Layer(solo_buttons='track_state_buttons')

    def _create_mixer_mute_layer(self):
        return Layer(mute_buttons='track_state_buttons')

    def _create_mixer_pan_send_layer(self):
        return Layer(track_select_buttons='select_buttons', pan_send_toggle='pan_send_mix_mode_button', pan_send_controls='fine_grain_param_controls', track_names_display='display_line4', pan_send_names_display='display_line1', pan_send_graphics_display='display_line2', selected_track_name_display='display_line3', pan_send_values_display=ComboElement('display_line3', 'any_touch_button'))

    def _create_mixer_volume_layer(self):
        return Layer(track_select_buttons='select_buttons', volume_controls='fine_grain_param_controls', track_names_display='display_line4', volume_names_display='display_line1', volume_graphics_display='display_line2', selected_track_name_display='display_line3', volume_values_display=ComboElement('display_line3', 'any_touch_button'))

    def _create_mixer_track_layer(self):
        return Layer(track_select_buttons='select_buttons', selected_track_name_display='display_line3', track_names_display='display_line4')

    def _init_mixer(self):
        self._mixer = SpecialMixerComponent(tracks_provider=self._session_ring, is_root=True)
        self._mixer.set_enabled(False)
        self._mixer.name = 'Mixer'
        self._mixer_layer = self._create_mixer_layer()
        self._mixer_pan_send_layer = self._create_mixer_pan_send_layer()
        self._mixer_volume_layer = self._create_mixer_volume_layer()
        self._mixer_track_layer = self._create_mixer_track_layer()
        self._mixer_solo_layer = self._create_mixer_solo_layer()
        self._mixer_mute_layer = self._create_mixer_mute_layer()
        for track in xrange(self.elements.matrix.width()):
            strip = self._mixer.channel_strip(track)
            strip.name = 'Channel_Strip_' + str(track)
            strip.set_invert_mute_feedback(True)
            strip.set_delete_handler(self._delete_component)
            strip._do_select_track = self.on_select_track
            strip.layer = Layer(shift_button='shift_button', duplicate_button='duplicate_button', selector_button='select_button')

        self._mixer.selected_strip().name = 'Selected_Channel_strip'
        self._mixer.master_strip().name = 'Master_Channel_strip'
        self._mixer.master_strip()._do_select_track = self.on_select_track
        self._mixer.master_strip().layer = Layer(select_button='master_select_button', selector_button='select_button')
        self._mixer.set_enabled(True)

    def _create_track_mixer_layer(self):
        return super(Push, self)._create_track_mixer_layer() + Layer(name_display_line='display_line1', graphic_display_line='display_line2', value_display_line=ComboElement('display_line3', 'any_touch_button'))

    def _create_device_parameter_layer(self):
        return super(Push, self)._create_device_parameter_layer() + Layer(name_display_line='display_line1', value_display_line='display_line2', graphic_display_line=ComboElement('display_line3', 'any_touch_button'))

    def _create_device_navigation(self):
        return DeviceNavigationComponent(device_bank_registry=self._device_bank_registry, banking_info=self._banking_info, is_enabled=False, session_ring=self._session_ring, layer=Layer(enter_button='in_button', exit_button='out_button', select_buttons='select_buttons', state_buttons='track_state_buttons', display_line='display_line4', _notification=self._notification.use_single_line(2)), info_layer=Layer(display_line1='display_line1', display_line2='display_line2', display_line3='display_line3', display_line4='display_line4', _notification=self._notification.use_full_display(2)), delete_handler=self._delete_component)

    @listens_group('value')
    def __on_main_mode_button_value(self, value, sender):
        if not value:
            self._scales_enabler.selected_mode = 'disabled'

    def _create_controls(self):
        self._create_pad_sensitivity_update()

        class Deleter(object):

            @property
            def is_deleting(_):
                return self._delete_default_component.is_deleting

            def delete_clip_envelope(_, param):
                return self._delete_default_component.delete_clip_envelope(param)

        self.elements = Elements(deleter=Deleter(), undo_handler=self.song, pad_sensitivity_update=self._pad_sensitivity_update, playhead=self._c_instance.playhead, velocity_levels=self._c_instance.velocity_levels)

    def _create_pad_sensitivity_update(self):
        all_pad_sysex_control = create_sysex_element(sysex.ALL_PADS_SENSITIVITY_PREFIX)
        pad_sysex_control = create_sysex_element(sysex.PAD_SENSITIVITY_PREFIX)
        sensitivity_sender = pad_parameter_sender(all_pad_sysex_control, pad_sysex_control)
        self._pad_sensitivity_update = PadUpdateComponent(all_pads=range(64), parameter_sender=sensitivity_sender, default_profile=action_pad_sensitivity, update_delay=TIMER_DELAY, is_root=True)

    def _init_global_pad_parameters(self):
        self._pad_parameter_control = self._with_firmware_version(1, 16, create_sysex_element(sysex.PAD_PARAMETER_PREFIX, default_value=sysex.make_pad_parameter_message()))
        aftertouch_threshold = self._settings['aftertouch_threshold'].value
        self._global_pad_parameters = GlobalPadParameters(aftertouch_threshold=aftertouch_threshold, is_enabled=False, layer=Layer(pad_parameter=self._pad_parameter_control))

    @listens('value')
    def __on_pad_curve(self, _value):
        self._update_pad_params()

    @listens('value')
    def __on_pad_threshold(self, value):
        self._user.set_settings_info_text('' if value >= CRITICAL_THRESHOLD_LIMIT else consts.MessageBoxText.STUCK_PAD_WARNING)
        self._update_pad_params()

    @listens('value')
    def __on_aftertouch_threshold(self, value):
        self._global_pad_parameters.aftertouch_threshold = value

    def _update_pad_params(self):
        new_pad_parameters = make_pad_parameters(self._settings['curve'].value, self._settings['threshold'].value)
        self._pad_sensitivity_update.set_profile('instrument', new_pad_parameters)
        self._pad_sensitivity_update.set_profile('drums', new_pad_parameters)
        self._pad_sensitivity_update.set_profile('loop', action_pad_sensitivity)

    def _update_calibration(self):
        self._send_midi(sysex.CALIBRATION_SET)

    def update(self):
        self._update_calibration()
        super(Push, self).update()
Esempio n. 5
0
 def _create_pad_sensitivity_update(self):
     all_pad_sysex_control = create_sysex_element(sysex.ALL_PADS_SENSITIVITY_PREFIX)
     pad_sysex_control = create_sysex_element(sysex.PAD_SENSITIVITY_PREFIX)
     sensitivity_sender = pad_parameter_sender(all_pad_sysex_control, pad_sysex_control)
     self._pad_sensitivity_update = PadUpdateComponent(all_pads=range(64), parameter_sender=sensitivity_sender, default_profile=action_pad_sensitivity, update_delay=TIMER_DELAY, is_root=True)
Esempio n. 6
0
class Push2(IdentifiableControlSurface, PushBase):
    drum_group_note_editor_skin = 'DrumGroupNoteEditor'
    slicing_note_editor_skin = 'SlicingNoteEditor'
    drum_group_velocity_levels_skin = 'DrumGroupVelocityLevels'
    slicing_velocity_levels_skin = 'VelocityLevels'
    input_target_name_for_auto_arm = 'Push2 Input'
    note_editor_velocity_range_thresholds = VELOCITY_RANGE_THRESHOLDS
    device_component_class = DeviceComponent
    device_provider_class = Push2DeviceProvider
    selected_track_parameter_provider_class = SelectedTrackParameterProvider
    bank_definitions = BANK_DEFINITIONS
    note_editor_class = Push2NoteEditorComponent
    sliced_simpler_class = Push2SlicedSimplerComponent
    RESEND_MODEL_DATA_TIMEOUT = 5.0
    DEFUNCT_EXTERNAL_PROCESS_RELAUNCH_TIMEOUT = 2.0

    def __init__(self, c_instance = None, model = None, bank_definitions = None, *a, **k):
        if not model is not None:
            raise AssertionError
            self._model = model
            self._real_time_mapper = c_instance.real_time_mapper
            self._clip_decorator_factory = ClipDecoratorFactory()
            self._real_time_data_list = []
            self.bank_definitions = bank_definitions is not None and bank_definitions
        super(Push2, self).__init__(c_instance=c_instance, product_id_bytes=sysex.IDENTITY_RESPONSE_PRODUCT_ID_BYTES, *a, **k)
        self._board_revision = 0
        self._firmware_collector = FirmwareCollector()
        self._firmware_version = FirmwareVersion(0, 0, 0)
        self._real_time_client = RealTimeClientModel()
        self._connected = False
        self._identified = False
        self._initialized = False
        self.register_disconnectable(model)
        self.register_disconnectable(self._clip_decorator_factory)
        with self.component_guard():
            self._model.realTimeClient = self._real_time_client
            self._real_time_client.clientId = self._real_time_mapper.client_id
        logger.info('Push 2 script loaded')

    def initialize(self):
        if not self._initialized:
            self._initialized = True
            self._init_hardware_settings()
            self._init_pad_curve()
            self._hardware_settings.hardware_initialized()
            self._pad_curve_sender.send()
            self._send_color_palette()
            super(Push2, self).initialize()
            self._init_transport_state()
            self.__on_selected_track_frozen_changed.subject = self.song.view
            self.__on_selected_track_frozen_changed()
            self._switch_to_live_mode()
            self.update()
        latest_stable_firmware = self._firmware_collector.latest_stable_firmware
        if latest_stable_firmware is not None and latest_stable_firmware.version > self._firmware_version and self._board_revision > 0 and self._identified:
            self._firmware_update.start(latest_stable_firmware)

    def _try_initialize(self):
        if self._connected and self._identified:
            self.initialize()

    def on_process_state_changed(self, state):
        logger.debug('Process state changed %r' % state)
        StateEnum = MidiRemoteScript.Push2ProcessState
        self._connected = state == StateEnum.connected
        if state == StateEnum.died:
            if self._initialized:
                self._setup_component.make_it_go_boom = False
            self._c_instance.launch_external_process()
        elif state == StateEnum.connected:
            with self.component_guard():
                self._try_initialize()
            self._model.commit_changes(send_all=True)
        elif state in (StateEnum.defunct_process_terminated, StateEnum.defunct_process_killed):
            self._tasks.add(task.sequence(task.wait(self.DEFUNCT_EXTERNAL_PROCESS_RELAUNCH_TIMEOUT), task.run(self._c_instance.launch_external_process)))

    def on_user_data_arrived(self, message):
        if self._initialized:
            logger.debug('User data arrived %r' % message)
            data = json.loads(message)
            self._process_qml_errors(data)
            self._firmware_update.process_firmware_response(data)

    def _process_qml_errors(self, data):
        qmlerrors = [ entry for entry in data if entry['type'] == 'qmlerror' ]
        if qmlerrors:
            first_error = qmlerrors[0]
            line = first_error['line']
            url = first_error['url']
            description = first_error['description'].replace('"', '\\"')
            code = '\n' * (line - 1) + 'raise QmlError("%s")' % description
            exec compile(code, url, 'exec')

    def disconnect(self):
        super(Push2, self).disconnect()
        self.__dict__.clear()
        logger.info('Push 2 script unloaded')

    def register_real_time_data(self, real_time_data):
        self._real_time_data_list.append(real_time_data)

    def _commit_real_time_data_changes(self):
        update_real_time_attachments(self._real_time_data_list)

    def _create_device_decorator_factory(self):
        return DeviceDecoratorFactory()

    def _create_skin(self):
        return self.register_disconnectable(make_default_skin())

    def _create_injector(self):
        return inject(double_press_context=const(self._double_press_context), expect_dialog=const(self.expect_dialog), show_notification=const(self.show_notification), commit_model_changes=const(self._model.commit_changes), register_real_time_data=const(self.register_real_time_data), percussion_instrument_finder=const(self._percussion_instrument_finder), selection=lambda : PushSelection(application=self.application, device_component=self._device_component, navigation_component=self._device_navigation))

    def _create_components(self):
        self._init_dialog_modes()
        super(Push2, self)._create_components()
        self._init_browser()
        self._init_session_ring_selection_linking()
        self._init_firmware_update()
        self._init_setup_component()
        self._init_convert_enabler()
        self._init_mute_solo_stop()

    @contextmanager
    def _component_guard(self):
        with super(Push2, self)._component_guard():
            with inject(real_time_mapper=const(self._c_instance.real_time_mapper)).everywhere():
                yield
                self._commit_real_time_data_changes()
                self._model.commit_changes()

    def _create_notification_component(self):
        notification = NotificationComponent(is_root=True)
        self._model.notificationView = notification
        return notification

    def _create_background_layer(self):
        return super(Push2, self)._create_background_layer() + Layer(mix_button='mix_button', page_left_button='page_left_button', page_right_button='page_right_button', mute_button='global_mute_button', solo_button='global_solo_button', track_stop_button='global_track_stop_button', convert_button='convert_button', layout_button='layout_button', setup_button='setup_button')

    def _create_message_box_background_layer(self):
        return super(Push2, self)._create_message_box_background_layer() + BackgroundLayer('mix_button', 'page_left_button', 'page_right_button', 'convert_button', 'layout_button', 'setup_button')

    def _create_message_box_layer(self):
        return Layer(cancel_button='track_state_buttons_raw[0]', priority=consts.MESSAGE_BOX_PRIORITY)

    def _init_message_box(self):
        super(Push2, self)._init_message_box()
        self._model.liveDialogView = self._dialog._message_box

    def _create_convert(self):
        convert = ConvertComponent(decorator_factory=self._device_decorator_factory, name='Convert', tracks_provider=self._session_ring, is_enabled=False, layer=make_dialog_layer(action_buttons='select_buttons', cancel_button='track_state_buttons_raw[0]'))
        self.__on_convert_closed.subject = convert
        self.__on_convert_suceeded.subject = convert
        self._model.convertView = convert
        return convert

    def _init_note_settings_component(self):
        self._note_settings_component = NoteSettingsComponent(grid_resolution=self._grid_resolution, is_enabled=False, layer=Layer(full_velocity_button='accent_button', priority=consts.MOMENTARY_DIALOG_PRIORITY))
        self._model.noteSettingsView = self._note_settings_component

    def _select_note_mode(self):
        super(Push2, self)._select_note_mode()
        self._note_settings_component.set_color_mode('drum_pad' if self._note_modes.selected_mode == 'drums' else 'clip')

    def _init_note_editor_settings_component(self):
        super(Push2, self)._init_note_editor_settings_component()
        self._model.stepSettingsView = self._note_editor_settings_component.step_settings

    def _init_automation_component(self):
        self._automation_component = AutomationComponent()
        self._model.stepAutomationSettingsView = self._automation_component

    def _init_convert_enabler(self):
        self._convert_enabler = ConvertEnabler(is_root=True, is_enabled=True, enter_dialog_mode=self._enter_dialog_mode, exit_dialog_mode=self._exit_dialog_mode)
        self._convert_enabler.layer = Layer(convert_toggle_button='convert_button')

    @listens('cancel')
    def __on_convert_closed(self):
        self._dialog_modes.selected_mode = None

    @listens('success')
    def __on_convert_suceeded(self, action_name):
        if action_name == 'audio_clip_to_simpler':
            self._main_modes.selected_mode = 'device'

    def _init_main_modes(self):
        super(Push2, self)._init_main_modes()
        self._main_modes.add_mode('user', [self._user_mode_ui_blocker, SetAttributeMode(obj=self._user, attribute='mode', value=sysex.USER_MODE)], behaviour=UserButtonBehavior(user_component=self._user))
        self._model.modeState = self.register_disconnectable(ModeCollector(main_modes=self._main_modes, mix_modes=self._mix_modes, global_mix_modes=self._mixer_control, device_modes=self._device_navigation.modes))
        self.__on_main_mode_button_value.replace_subjects([self.elements.vol_mix_mode_button,
         self.elements.pan_send_mix_mode_button,
         self.elements.single_track_mix_mode_button,
         self.elements.clip_mode_button,
         self.elements.device_mode_button,
         self.elements.browse_mode_button,
         self.elements.create_device_button,
         self.elements.create_track_button])

    @listens_group('value')
    def __on_main_mode_button_value(self, value, sender):
        if not value:
            self._exit_modal_modes()

    def _exit_modal_modes(self):
        self._dialog_modes.selected_mode = None
        self._setup_enabler.selected_mode = 'disabled'

    def _create_capture_and_insert_scene_component(self):
        return CaptureAndInsertSceneComponent(name='Capture_And_Insert_Scene', decorator_factory=self._clip_decorator_factory, is_root=True)

    def _init_mute_solo_stop(self):
        self._mute_solo_stop = MuteSoloStopClipComponent(is_root=True, item_provider=self._session_ring, track_list_component=self._track_list_component, cancellation_action_performers=[self._device_navigation, self._drum_component] + self._note_editor_settings_component.editors, solo_track_button='global_solo_button', mute_track_button='global_mute_button', stop_clips_button='global_track_stop_button')
        self._mute_solo_stop.layer = Layer(stop_all_clips_button=self._with_shift('global_track_stop_button'))
        self._master_selector = MasterTrackComponent(tracks_provider=self._session_ring, is_enabled=False, layer=Layer(toggle_button='master_select_button'))
        self._master_selector.set_enabled(True)

    def _create_instrument_layer(self):
        return super(Push2, self)._create_instrument_layer() + Layer(prev_loop_page_button='page_left_button', next_loop_page_button='page_right_button')

    def _create_drum_step_sequencer_layer(self):
        return super(Push2, self)._create_drum_step_sequencer_layer() + Layer(prev_loop_page_button='page_left_button', next_loop_page_button='page_right_button')

    def _create_slice_step_sequencer_layer(self):
        return super(Push2, self)._create_slice_step_sequencer_layer() + Layer(prev_loop_page_button='page_left_button', next_loop_page_button='page_right_button')

    def _create_color_chooser(self):
        color_chooser = ColorChooserComponent()
        color_chooser.layer = Layer(matrix='matrix', priority=consts.MOMENTARY_DIALOG_PRIORITY)
        return color_chooser

    def _create_session(self):
        session = super(Push2, self)._create_session()
        for scene_ix in xrange(8):
            scene = session.scene(scene_ix)
            for track_ix in xrange(8):
                clip_slot = scene.clip_slot(track_ix)
                clip_slot.layer += Layer(select_color_button='shift_button')
                clip_slot.set_decorator_factory(self._clip_decorator_factory)

        return session

    def _create_session_navigation_layer(self):
        return Layer(left_button='nav_left_button', right_button='nav_right_button', up_button='nav_up_button', down_button='nav_down_button', page_left_button='page_left_button', page_right_button='page_right_button', page_up_button='octave_up_button', page_down_button='octave_down_button')

    def on_select_clip_slot(self, clip_slot):
        self.show_notification('Clip Selected: ' + select_clip_and_get_name_from_slot(clip_slot, self.song))

    def on_select_scene(self, scene):
        self.show_notification('Scene Selected: ' + select_scene_and_get_name(scene, self.song))

    def _create_session_mode(self):
        session_modes = MessengerModesComponent(is_enabled=False)
        session_modes.add_mode('session', [self._session_mode], message=consts.MessageBoxText.LAYOUT_SESSION_VIEW)
        session_modes.add_mode('overview', [self._session_overview_mode], message=consts.MessageBoxText.LAYOUT_SESSION_OVERVIEW)
        session_modes.layer = Layer(cycle_mode_button='layout_button')
        session_modes.selected_mode = 'session'
        return [session_modes, self._session_navigation]

    def _create_session_overview_layer(self):
        return Layer(button_matrix='matrix')

    def _instantiate_session(self):
        return SessionComponent(session_ring=self._session_ring, is_enabled=False, auto_name=True, clip_slot_copy_handler=DecoratingCopyHandler(decorator_factory=self._clip_decorator_factory), fixed_length_recording=self._create_fixed_length_recording(), color_chooser=self._create_color_chooser(), layer=self._create_session_layer())

    def _create_drum_component(self):
        return DrumGroupComponent(name='Drum_Group', is_enabled=False, tracks_provider=self._session_ring, device_decorator_factory=self._device_decorator_factory, quantizer=self._quantize, color_chooser=self._create_color_chooser())

    def _init_drum_component(self):
        super(Push2, self)._init_drum_component()
        self._drum_component.layer += Layer(select_color_button='shift_button')

    def _create_device_mode(self):
        self._drum_pad_parameter_component = DrumPadParameterComponent(device_component=self._device_component, view_model=self._model, is_enabled=False, layer=Layer(choke_encoder='parameter_controls_raw[0]', transpose_encoder='parameter_controls_raw[1]'))
        self._device_or_pad_parameter_chooser = ModesComponent()
        self._device_or_pad_parameter_chooser.add_mode('device', [make_freeze_aware(self._device_parameter_component, self._device_parameter_component.layer), self._device_view])
        self._device_or_pad_parameter_chooser.add_mode('drum_pad', [make_freeze_aware(self._drum_pad_parameter_component, self._drum_pad_parameter_component.layer)])
        self._device_or_pad_parameter_chooser.selected_mode = 'device'
        return [partial(self._view_control.show_view, 'Detail/DeviceChain'),
         self._device_or_pad_parameter_chooser,
         self._setup_freeze_aware_device_navigation(),
         self._device_note_editor_mode,
         SetAttributeMode(obj=self._note_editor_settings_component, attribute='parameter_provider', value=self._device_component)]

    def _setup_freeze_aware_device_navigation(self):

        def create_layer_setter(layer_name, layer):
            return SetAttributeMode(obj=self._device_navigation, attribute=layer_name, value=layer)

        return make_freeze_aware(self._device_navigation, self._device_navigation.layer, default_mode_extras=[create_layer_setter('scroll_right_layer', Layer(button=self.elements.track_state_buttons_raw[-1])), create_layer_setter('scroll_left_layer', Layer(button=self.elements.track_state_buttons_raw[0]))], frozen_mode_extras=[lambda : setattr(self._device_navigation.modes, 'selected_mode', 'default'), create_layer_setter('scroll_right_layer', Layer()), create_layer_setter('scroll_left_layer', Layer())])

    @listens('drum_pad_selection')
    def __on_drum_pad_selection_changed(self):
        show_pad_parameters = self._device_navigation.is_drum_pad_selected and self._device_navigation.is_drum_pad_unfolded
        new_mode = 'drum_pad' if show_pad_parameters else 'device'
        if show_pad_parameters:
            selected_pad = self._device_navigation.item_provider.selected_item
            self._drum_pad_parameter_component.drum_pad = selected_pad
        self._device_or_pad_parameter_chooser.selected_mode = new_mode
        self._automation_component.set_drum_pad_selected(self._device_navigation.is_drum_pad_selected)

    def _init_browser(self):
        self._browser_component_mode = BrowserComponentMode(weakref.ref(self._model), self._create_browser)
        self._new_track_browser_component_mode = BrowserComponentMode(weakref.ref(self._model), self._create_new_track_browser)

    def _init_browse_mode(self):
        application = Live.Application.get_application()
        browser = application.browser
        self._main_modes.add_mode('browse', [BrowseMode(application=application, song=self.song, browser=browser, drum_group_component=self._drum_component, component_mode=self._browser_component_mode)], behaviour=BrowserModeBehaviour())
        self._main_modes.add_mode('add_device', [AddDeviceMode(application=application, song=self.song, browser=browser, drum_group_component=self._drum_component, component_mode=self._browser_component_mode)], behaviour=BrowserModeBehaviour())
        self._main_modes.add_mode('add_track', [AddTrackMode(browser=browser, component_mode=self._new_track_browser_component_mode)], behaviour=BrowserModeBehaviour())

    def _create_browser_layer(self):
        return (BackgroundLayer('select_buttons', 'track_state_buttons', priority=consts.DIALOG_PRIORITY), Layer(up_button='nav_up_button', down_button='nav_down_button', right_button='nav_right_button', left_button='nav_left_button', back_button='track_state_buttons_raw[-2]', open_button='track_state_buttons_raw[-1]', load_button='select_buttons_raw[-1]', scroll_encoders=self.elements.global_param_controls.submatrix[:-1, :], scroll_focused_encoder='parameter_controls_raw[-1]', close_button='track_state_buttons_raw[0]', prehear_button='track_state_buttons_raw[1]', priority=consts.DIALOG_PRIORITY))

    def _create_browser(self):
        browser = BrowserComponent(name='Browser', is_enabled=False, preferences=self.preferences, main_modes_ref=weakref.ref(self._main_modes), layer=self._create_browser_layer())
        self._on_browser_loaded.add_subject(browser)
        self._on_browser_closed.add_subject(browser)
        browser.load_neighbour_overlay.layer = Layer(load_previous_button='track_state_buttons_raw[7]', load_next_button='select_buttons_raw[7]', priority=consts.DIALOG_PRIORITY)
        return browser

    def _create_new_track_browser(self):
        browser = NewTrackBrowserComponent(name='NewTrackBrowser', is_enabled=False, preferences=self.preferences, layer=self._create_browser_layer())
        self._on_browser_loaded.add_subject(browser)
        self._on_browser_closed.add_subject(browser)
        return browser

    @listens_group('loaded')
    def _on_browser_loaded(self, sender):
        if sender.browse_for_audio_clip:
            self._main_modes.selected_mode = 'clip'
        else:
            browser = Live.Application.get_application().browser
            if browser.hotswap_target is None:
                self._main_modes.selected_mode = 'device'
            drum_rack = find_drum_group_device(self.song.view.selected_track)
            if drum_rack and is_empty_rack(drum_rack):
                self._device_navigation.request_drum_pad_selection()
            if drum_rack and self._device_navigation.is_drum_pad_selected:
                if not self._device_navigation.is_drum_pad_unfolded:
                    self._device_navigation.unfold_current_drum_pad()
                self._device_navigation.sync_selection_to_selected_device()

    @listens_group('close')
    def _on_browser_closed(self, sender):
        if sender.browse_for_audio_clip:
            self._main_modes.selected_mode = 'clip'
        elif self._main_modes.selected_mode == 'add_track':
            self._main_modes.selected_mode = self._main_modes.active_modes[0]
        else:
            self._main_modes.selected_mode = 'device'

    def _is_on_master(self):
        return self.song.view.selected_track == self.song.master_track

    def _determine_mix_mode(self):
        selected_mode = self._main_modes.selected_mode
        mix_mode = self._mix_modes.selected_mode
        if selected_mode == 'mix':
            if self._is_on_master():
                if mix_mode == 'global':
                    self._mix_modes.push_mode('track')
            elif mix_mode == 'track' and 'global' in self._mix_modes.active_modes:
                self._mix_modes.pop_mode('track')

    def _on_selected_track_changed(self):
        if self._initialized:
            super(Push2, self)._on_selected_track_changed()
            self._close_browse_mode()
            self._determine_mix_mode()

    def _close_browse_mode(self):
        selected_mode = self._main_modes.selected_mode
        if selected_mode in ('browse', 'add_device', 'add_track'):
            self._main_modes.pop_mode(selected_mode)

    @listens('selected_track.is_frozen')
    def __on_selected_track_frozen_changed(self):
        frozen = self.song.view.selected_track.is_frozen
        self._main_modes.browse_button.enabled = self._main_modes.add_track_button.enabled = self._main_modes.add_device_button.enabled = not frozen
        self._close_browse_mode()

    def _create_device_parameter_component(self):
        return DeviceParameterComponent(parameter_provider=self._device_component, is_enabled=False, layer=Layer(parameter_controls='fine_grain_param_controls'))

    def _create_device_navigation(self):
        self._chain_selection = ChainSelectionComponent(is_enabled=False, layer=Layer(select_buttons='select_buttons', priority=consts.DIALOG_PRIORITY))
        self._chain_selection.scroll_left_layer = Layer(button='select_buttons_raw[0]', priority=consts.DIALOG_PRIORITY)
        self._chain_selection.scroll_right_layer = Layer(button='select_buttons_raw[-1]', priority=consts.DIALOG_PRIORITY)
        self._bank_selection = BankSelectionComponent(bank_registry=self._device_bank_registry, banking_info=self._banking_info, device_options_provider=self._device_component, is_enabled=False, layer=Layer(option_buttons='track_state_buttons', select_buttons='select_buttons', priority=consts.DIALOG_PRIORITY))
        self._bank_selection.scroll_left_layer = Layer(button='select_buttons_raw[0]', priority=consts.DIALOG_PRIORITY)
        self._bank_selection.scroll_right_layer = Layer(button='select_buttons_raw[-1]', priority=consts.DIALOG_PRIORITY)
        move_device = MoveDeviceComponent(is_enabled=False, layer=Layer(move_encoders='global_param_controls'))
        device_navigation = DeviceNavigationComponent(name='DeviceNavigation', device_bank_registry=self._device_bank_registry, banking_info=self._banking_info, device_component=self._device_component, delete_handler=self._delete_component, chain_selection=self._chain_selection, bank_selection=self._bank_selection, move_device=move_device, track_list_component=self._track_list_component, is_enabled=False, layer=Layer(select_buttons='track_state_buttons'))
        device_navigation.scroll_left_layer = Layer(button='track_state_buttons_raw[0]')
        device_navigation.scroll_right_layer = Layer(button='track_state_buttons_raw[-1]')
        self.__on_drum_pad_selection_changed.subject = device_navigation
        self.device_provider.allow_update_callback = lambda : device_navigation.device_selection_update_allowed
        return device_navigation

    def _init_device(self):
        super(Push2, self)._init_device()
        self._device_component.layer = Layer(parameter_touch_buttons=ButtonMatrixElement(rows=[self.elements.global_param_touch_buttons_raw]))
        self._device_view = DeviceViewComponent(name='DeviceView', device_component=self._device_component, view_model=self._model, is_enabled=False)
        self._model.devicelistView = self._device_navigation
        self._model.chainListView = self._chain_selection
        self._model.parameterBankListView = self._bank_selection
        self._model.editModeOptionsView = self._bank_selection.options

    def _create_view_control_component(self):
        return ViewControlComponent(name='View_Control', tracks_provider=self._session_ring)

    def _create_session_recording(self):
        return SessionRecordingComponent(fixed_length_setting=self._fixed_length_setting, clip_creator=self._clip_creator, view_controller=self._view_control, name='Session_Recording', is_root=True)

    def _init_session_ring(self):
        self._session_ring = SessionRingTrackProvider(name='Session_Ring', num_tracks=NUM_TRACKS, num_scenes=NUM_SCENES, is_enabled=True, is_root=True)

    def _init_session_ring_selection_linking(self):
        self._sessionring_link = self.register_disconnectable(SessionRingSelectionLinking(session_ring=self._session_ring, selection_changed_notifier=self._view_control))

    def _init_track_list(self):
        self._track_list_component = TrackListComponent(tracks_provider=self._session_ring, trigger_recording_on_release_callback=self._session_recording.set_trigger_recording_on_release, color_chooser=self._create_color_chooser(), is_enabled=False, is_root=True, layer=Layer(track_action_buttons='select_buttons', lock_override_button='select_button', delete_button='delete_button', duplicate_button='duplicate_button', arm_button='record_button', select_color_button='shift_button'))
        self._track_list_component.set_enabled(True)
        self._model.tracklistView = self._track_list_component

    def _create_main_mixer_modes(self):
        self._mixer_control = MixerControlComponent(name='Global_Mix_Component', view_model=self._model.mixerView, tracks_provider=self._session_ring, is_enabled=False, layer=Layer(controls='fine_grain_param_controls', volume_button='track_state_buttons_raw[0]', panning_button='track_state_buttons_raw[1]', send_slot_one_button='track_state_buttons_raw[2]', send_slot_two_button='track_state_buttons_raw[3]', send_slot_three_button='track_state_buttons_raw[4]', send_slot_four_button='track_state_buttons_raw[5]', send_slot_five_button='track_state_buttons_raw[6]', cycle_sends_button='track_state_buttons_raw[7]'))
        self._model.mixerView.realtimeMeterData = self._mixer_control.real_time_meter_handlers
        track_mixer_control = TrackMixerControlComponent(name='Track_Mix_Component', is_enabled=False, tracks_provider=self._session_ring, layer=Layer(controls='fine_grain_param_controls', scroll_left_button='track_state_buttons_raw[6]', scroll_right_button='track_state_buttons_raw[7]'))
        routing_control = RoutingControlComponent(is_enabled=False, layer=Layer(monitor_state_encoder='parameter_controls_raw[0]', input_output_choice_encoder='parameter_controls_raw[1]', routing_type_encoder='parameter_controls_raw[2]', routing_channel_encoders=self.elements.global_param_controls.submatrix[3:7, :], routing_channel_position_encoder='parameter_controls_raw[7]'))
        track_mix_or_routing_chooser = TrackOrRoutingControlChooserComponent(tracks_provider=self._session_ring, track_mixer_component=track_mixer_control, routing_control_component=routing_control, is_enabled=False, layer=Layer(mix_button='track_state_buttons_raw[0]', routing_button='track_state_buttons_raw[1]'))
        self._model.mixerView.trackControlView = track_mix_or_routing_chooser
        self._mix_modes = ModesComponent(is_enabled=False)
        self._mix_modes.add_mode('global', self._mixer_control)
        self._mix_modes.add_mode('track', track_mix_or_routing_chooser)
        self._mix_modes.selected_mode = 'global'
        self._model.mixerSelectView = self._mixer_control
        self._model.trackMixerSelectView = track_mixer_control

        class MixModeBehaviour(ReenterBehaviour):

            def press_immediate(behaviour_self, component, mode):
                if self._is_on_master() and self._mix_modes.selected_mode != 'track':
                    self._mix_modes.selected_mode = 'track'
                super(MixModeBehaviour, behaviour_self).press_immediate(component, mode)

            def on_reenter(behaviour_self):
                if not self._is_on_master():
                    self._mix_modes.cycle_mode()

        self._main_modes.add_mode('mix', [self._mix_modes, SetAttributeMode(obj=self._note_editor_settings_component, attribute='parameter_provider', value=self._track_parameter_provider)], behaviour=MixModeBehaviour())

    def _init_dialog_modes(self):
        self._dialog_modes = ModesComponent(is_root=True)
        self._dialog_modes.add_mode('convert', LazyComponentMode(self._create_convert))
        self.__dialog_mode_button_value.replace_subjects([self.elements.scale_presets_button, self.elements.convert_button])

    @listens_group('value')
    def __dialog_mode_button_value(self, value, sender):
        if not value:
            self._setup_enabler.selected_mode = 'disabled'

    def _enter_dialog_mode(self, mode_name):
        self._dialog_modes.selected_mode = None if self._dialog_modes.selected_mode == mode_name else mode_name

    def _exit_dialog_mode(self, mode_name):
        if self._dialog_modes.selected_mode == mode_name:
            self._dialog_modes.selected_mode = None

    def _create_scales(self):
        root_note_buttons = ButtonMatrixElement(rows=[self.elements.track_state_buttons_raw[1:-1], self.elements.select_buttons_raw[1:-1]])
        scales = ScalesComponent(note_layout=self._note_layout, is_enabled=False, layer=make_dialog_layer(root_note_buttons=root_note_buttons, in_key_toggle_button='select_buttons_raw[0]', fixed_toggle_button='select_buttons_raw[-1]', scale_encoders=self.elements.global_param_controls.submatrix[1:-1, :], layout_encoder='parameter_controls_raw[0]', direction_encoder='parameter_controls_raw[-1]', up_button='nav_up_button', down_button='nav_down_button', right_button='nav_right_button', left_button='nav_left_button'))
        self._model.scalesView = scales
        return scales

    def _init_scales(self):
        self._dialog_modes.add_mode('scales', self._create_scales())
        super(Push2, self)._init_scales()

    def _create_scales_enabler(self):
        return ScalesEnabler(enter_dialog_mode=self._enter_dialog_mode, exit_dialog_mode=self._exit_dialog_mode, is_enabled=False, is_root=True, layer=Layer(toggle_button='scale_presets_button'))

    def _create_clip_mode(self):
        base_loop_layer = Layer(shift_button='shift_button', loop_button='track_state_buttons_raw[1]')
        self._loop_controller = LoopSettingsControllerComponent(is_enabled=False)
        self._model.loopSettingsView = self._loop_controller
        audio_clip_layer = Layer(warp_mode_encoder='parameter_controls_raw[5]', transpose_encoder='parameter_controls_raw[6]', detune_encoder=self._with_shift('parameter_controls_raw[6]'), gain_encoder='parameter_controls_raw[7]', shift_button='shift_button')
        audio_clip_controller = AudioClipSettingsControllerComponent(is_enabled=False)
        self._model.audioClipSettingsView = audio_clip_controller
        clip_control_mode_selector = ModesComponent(is_enabled=False)
        clip_control_mode_selector.add_mode('midi', [make_freeze_aware(self._loop_controller, base_loop_layer + Layer(encoders=self.elements.global_param_controls.submatrix[:3, :]))])
        clip_control_mode_selector.add_mode('audio', [make_freeze_aware(self._loop_controller, base_loop_layer + Layer(encoders=self.elements.global_param_controls.submatrix[1:4, :], zoom_encoder='fine_grain_param_controls_raw[0]')), make_freeze_aware(audio_clip_controller, audio_clip_layer)])
        clip_control_mode_selector.add_mode('no_clip', [])
        clip_control_mode_selector.selected_mode = 'no_clip'
        clip_control = ClipControlComponent(loop_controller=self._loop_controller, audio_clip_controller=audio_clip_controller, mode_selector=clip_control_mode_selector, decorator_factory=self._clip_decorator_factory, is_enabled=False)
        self._model.clipView = clip_control
        return [partial(self._view_control.show_view, 'Detail/Clip'), clip_control_mode_selector, clip_control]

    def _init_quantize_actions(self):
        self._quantize_settings = QuantizationSettingsComponent(name='Quantization_Settings', quantization_names=QUANTIZATION_NAMES_UNICODE, is_enabled=False, layer=make_dialog_layer(swing_amount_encoder='parameter_controls_raw[0]', quantize_to_encoder='parameter_controls_raw[1]', quantize_amount_encoder='parameter_controls_raw[2]', record_quantization_encoder='parameter_controls_raw[4]', record_quantization_toggle_button='track_state_buttons_raw[4]', priority=consts.MOMENTARY_DIALOG_PRIORITY))
        self._model.quantizeSettingsView = self._quantize_settings
        self._quantize = self._for_non_frozen_tracks(QuantizationComponent(name='Selected_Clip_Quantize', settings=self._quantize_settings, is_enabled=False, layer=Layer(action_button='quantize_button')), is_root=True)

    def _init_fixed_length(self):
        super(Push2, self)._init_fixed_length()
        self._fixed_length_settings_component.layer = make_dialog_layer(length_option_buttons='select_buttons', fixed_length_toggle_button='track_state_buttons_raw[0]', legato_launch_toggle_button='track_state_buttons_raw[7]', priority=consts.MOMENTARY_DIALOG_PRIORITY)
        self._model.fixedLengthSelectorView = self._fixed_length_settings_component
        self._model.fixedLengthSettings = self._fixed_length_setting

    def _init_value_components(self):
        super(Push2, self)._init_value_components()
        self._model.importantGlobals.swing = self._swing_amount.display
        self._model.importantGlobals.tempo = self._tempo.display
        self._model.importantGlobals.masterVolume = self._master_vol.display
        self._model.importantGlobals.cueVolume = self._master_cue_vol.display

    def _create_main_modes_layer(self):
        return Layer(mix_button='mix_button', clip_button='clip_mode_button', device_button='device_mode_button', browse_button='browse_mode_button', add_device_button='create_device_button', add_track_button='create_track_button') + Layer(user_button='user_button', priority=consts.USER_BUTTON_PRIORITY)

    def _should_send_palette(self):
        return self._firmware_version < FirmwareVersion(1, 0, 59)

    def _send_color_palette(self):
        if self._should_send_palette():
            with self.component_guard():
                palette_entry = SysexElement(sysex.make_rgb_palette_entry_message)
                finalize_palette = SysexElement(sysex.make_reapply_palette_message)
                for index, hex_color, white_balance in COLOR_TABLE:
                    palette_entry.send_value(index, hex_color, white_balance)

                finalize_palette.send_value()

    def _init_pad_curve(self):
        self._pad_curve_sender = PadVelocityCurveSender(curve_sysex_element=SysexElement(sysex.make_pad_velocity_curve_message), threshold_sysex_element=SysexElement(sysex.make_pad_threshold_message), settings=self._setup_settings.pad_settings, chunk_size=sysex.PAD_VELOCITY_CURVE_CHUNK_SIZE)

    def _create_user_component(self):
        self._user_mode_ui_blocker = Component(is_enabled=False, layer=self._create_message_box_background_layer())
        sysex_control = SysexElement(send_message_generator=sysex.make_mode_switch_messsage, sysex_identifier=sysex.make_message_identifier(sysex.MODE_SWITCH_MESSAGE_ID))
        user = UserComponent(value_control=sysex_control, is_enabled=True, is_root=True)
        return user

    def _create_settings(self):
        return create_settings(preferences=self.preferences)

    def _init_hardware_settings(self):
        self._setup_settings = self.register_disconnectable(Settings(preferences=self.preferences))
        self._hardware_settings = HardwareSettingsComponent(led_brightness_element=SysexElement(sysex.make_led_brightness_message), display_brightness_element=SysexElement(sysex.make_display_brightness_message), settings=self._setup_settings.hardware)

    def _init_transport_state(self):
        self._model.transportState = TransportState(song=self.song)

    def _init_setup_component(self):
        self._setup_settings.general.workflow = 'scene' if self._settings['workflow'].value else 'clip'
        self.__on_workflow_setting_changed.subject = self._setup_settings.general
        setup = SetupComponent(name='Setup', settings=self._setup_settings, pad_curve_sender=self._pad_curve_sender, firmware_switcher=self._firmware_switcher, is_enabled=False, layer=make_dialog_layer(category_radio_buttons='select_buttons', priority=consts.SETUP_DIALOG_PRIORITY, make_it_go_boom_button='track_state_buttons_raw[7]'))
        setup.general.layer = Layer(workflow_encoder='parameter_controls_raw[0]', display_brightness_encoder='parameter_controls_raw[1]', led_brightness_encoder='parameter_controls_raw[2]', priority=consts.SETUP_DIALOG_PRIORITY)
        setup.info.layer = Layer(install_firmware_button='track_state_buttons_raw[6]', priority=consts.SETUP_DIALOG_PRIORITY)
        setup.pad_settings.layer = Layer(sensitivity_encoder='parameter_controls_raw[4]', gain_encoder='parameter_controls_raw[5]', dynamics_encoder='parameter_controls_raw[6]', priority=consts.SETUP_DIALOG_PRIORITY)
        setup.display_debug.layer = Layer(show_row_spaces_button='track_state_buttons_raw[0]', show_row_margins_button='track_state_buttons_raw[1]', show_row_middle_button='track_state_buttons_raw[2]', show_button_spaces_button='track_state_buttons_raw[3]', show_unlit_button_button='track_state_buttons_raw[4]', show_lit_button_button='track_state_buttons_raw[5]', priority=consts.SETUP_DIALOG_PRIORITY)
        setup.profiling.layer = Layer(show_qml_stats_button='track_state_buttons_raw[0]', show_usb_stats_button='track_state_buttons_raw[1]', show_realtime_ipc_stats_button='track_state_buttons_raw[2]', priority=consts.SETUP_DIALOG_PRIORITY)
        self._model.setupView = setup
        self._setup_enabler = EnablingModesComponent(component=setup, enabled_color='DefaultButton.On', disabled_color='DefaultButton.On')
        self._setup_enabler.layer = Layer(cycle_mode_button='setup_button')
        self._setup_component = setup

    def _init_firmware_update(self):
        self._firmware_update = FirmwareUpdateComponent(layer=self._create_message_box_background_layer())
        self._model.firmwareUpdate = self._firmware_update
        self._firmware_switcher = FirmwareSwitcher(firmware_collector=self._firmware_collector, firmware_update=self._firmware_update, installed_firmware_version=self._firmware_version)
        self._model.firmwareSwitcher = self._firmware_switcher

    @listens('workflow')
    def __on_workflow_setting_changed(self, value):
        self._settings['workflow'].value = value == 'scene'

    def _create_note_mode(self):

        class NoteLayoutSwitcher(Component):
            cycle_button = ButtonControl()

            def __init__(self, switch_note_mode_layout = None, *a, **k):
                raise switch_note_mode_layout is not None or AssertionError
                super(NoteLayoutSwitcher, self).__init__(*a, **k)
                self._switch_note_mode_layout = switch_note_mode_layout

            @cycle_button.pressed
            def cycle_button(self, button):
                self._switch_note_mode_layout()

        note_layout_switcher = NoteLayoutSwitcher(switch_note_mode_layout=self._switch_note_mode_layout, is_enabled=False, layer=Layer(cycle_button='layout_button'))
        return super(Push2, self)._create_note_mode() + [note_layout_switcher]

    def _create_note_mode_behaviour(self):
        return self._auto_arm.auto_arm_restore_behaviour()

    def _create_controls(self):
        self._create_pad_sensitivity_update()

        class Deleter(object):

            @property
            def is_deleting(_):
                return self._delete_default_component.is_deleting

            def delete_clip_envelope(_, param):
                return self._delete_default_component.delete_clip_envelope(param)

        self.elements = Elements(deleter=Deleter(), undo_handler=self.song, pad_sensitivity_update=self._pad_sensitivity_update, playhead=self._c_instance.playhead, velocity_levels=self._c_instance.velocity_levels, model=self._model)
        self.__on_param_encoder_touched.replace_subjects(self.elements.global_param_touch_buttons_raw)
        self._update_encoder_model()

    def _create_pad_sensitivity_update(self):
        all_pad_sysex_control = SysexElement(sysex.make_pad_setting_message)
        pad_sysex_control = SysexElement(sysex.make_pad_setting_message)
        sensitivity_sender = pad_parameter_sender(all_pad_sysex_control, pad_sysex_control)
        self._pad_sensitivity_update = PadUpdateComponent(all_pads=range(64), parameter_sender=sensitivity_sender, default_profile=default_profile, update_delay=TIMER_DELAY, is_root=True)
        self._pad_sensitivity_update.set_profile('drums', playing_profile)
        self._pad_sensitivity_update.set_profile('instrument', playing_profile)
        self._pad_sensitivity_update.set_profile('loop', loop_selector_profile)

    def _update_full_velocity(self, accent_is_active):
        super(Push2, self)._update_full_velocity(accent_is_active)
        self._slice_step_sequencer.full_velocity = accent_is_active

    def _update_playhead_color(self, color):
        super(Push2, self)._update_playhead_color(color)
        self._slice_step_sequencer.playhead_color = color

    @listens_group('value')
    def __on_param_encoder_touched(self, value, encoder):
        self._update_encoder_model()

    def _update_encoder_model(self):
        self._model.controls.encoders = [ NamedTuple(__id__='encoder_%i' % i, touched=e.is_pressed()) for i, e in enumerate(self.elements.global_param_touch_buttons_raw) ]

    def _with_firmware_version(self, major_version, minor_version, control_element):
        """
        We consider all features to be available for Push 2
        """
        return control_element

    def _send_hardware_settings(self):
        self._hardware_settings.send()
        self._pad_curve_sender.send()
        self._send_color_palette()

    def port_settings_changed(self):
        super(Push2, self).port_settings_changed()
        if self._initialized:
            self._send_hardware_settings()
            self.update()

    def on_identified(self, response_bytes):
        try:
            major, minor, build, sn, board_revision = sysex.extract_identity_response_info(response_bytes)
            self._firmware_version = FirmwareVersion(major, minor, build)
            self._firmware_version.release_type = self._firmware_collector.get_release_type(self._firmware_version)
            self._model.hardwareInfo.firmwareVersion = self._firmware_version
            self._model.hardwareInfo.serialNumber = sn
            logger.info('Push 2 identified')
            logger.info('Firmware %i.%i Build %i' % (major, minor, build))
            logger.info('Serial number %i' % sn)
            logger.info('Board Revision %i' % board_revision)
            self._board_revision = board_revision
            self._identified = True
            self._try_initialize()
        except IndexError:
            logger.warning("Couldn't identify Push 2 unit")

    def update(self):
        if self._initialized:
            super(Push2, self).update()