def _set_global_buttons(self):
     is_momentary = True
     self._undo_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 85)
     self._do_undo.subject = self._undo_button
     self._redo_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 87)
     self._do_redo.subject = self._redo_button
     self._stop_all_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 111)
     self._do_stop_all.subject = self._stop_all_button
     self._toggle_detail_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1, 121)
     self._action_toogle_detail_view.subject = self._toggle_detail_button
     self._fire_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 9)
     self._do_fire_button.subject = self._fire_button
     self._g_clear_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1, 106)
     self._hold_clear_action.subject = self._g_clear_button
     self._g_duplicate_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1, 107)
     self._hold_duplicate_action.subject = self._g_duplicate_button
     self.track_left_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 120)
     self.track_right_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 121)
     self.set_sel_arm_button = StateButton(is_momentary, MIDI_CC_TYPE, 2, 56)
     self._reenable_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1, 120)
     self._do_auto_reenable.subject = self._reenable_button
     self._on_change_reenabled.subject = self.song()
     self._on_change_reenabled()
     self._a_trk_left.subject = self.track_left_button
     self._a_trk_right.subject = self.track_right_button
     self._a_sel_arm.subject = self.set_sel_arm_button
Example #2
0
    def _set_up_device_control(self):
        is_momentary = True
        device = MaschineDeviceComponent()
        param_controls = []
        for index in range(8):
            param_controls.append(
                SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL,
                              DEVICE_CC_OFF + index))

        device.set_parameter_controls(tuple(param_controls))
        self.device_control = param_controls
        device.set_on_off_button(
            StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL,
                        DEVICE_BUTTON_CC_OFF))
        device.set_bank_nav_buttons(
            StateButton(is_momentary, MIDI_CC_TYPE, 3, 104),
            ButtonElement(is_momentary, MIDI_CC_TYPE, 3, 105))
        self._device_nav_button_left = StateButton(is_momentary, MIDI_CC_TYPE,
                                                   3, 106)
        self._device_nav_button_right = StateButton(is_momentary, MIDI_CC_TYPE,
                                                    3, 107)
        self._navigate_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 127)
        self._nav_value_left.subject = self._device_nav_button_left
        self._nav_value_right.subject = self._device_nav_button_right
        self._do_focus_navigate.subject = self._navigate_button
        self.set_device_component(device)
        return device
Example #3
0
    def _set_up_mixer(self):
        is_momentary = True
        self._mixer = MaschineMixerComponent(8)
        self.send_sliders = []
        for track in range(8):
            self.send_sliders.append(
                SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL,
                              SEND_CC_OFF + track))

        for track in range(8):
            strip = self._mixer.channel_strip(track)
            strip.set_arm_button(
                StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL,
                            ARM_CC_OFF + track))
            strip.set_solo_button(
                StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL,
                            SOLO_CC_OFF + track))
            strip.set_mute_button(
                StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL,
                            MUTE_CC_OFF + track))
            strip.set_volume_control(
                SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL,
                              LEVEL_CC_OFF + track))
            strip.set_pan_control(
                SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, PAN_CC_OFF + track))
            strip.set_select_button(
                StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL,
                            SELECT_CC_OFF + track))
            st = tuple([self.send_sliders[track]])
            strip.set_send_controls(st)

        self.send_slider_toggle_button = StateButton(False, MIDI_CC_TYPE, 0,
                                                     90)
        self._do_toggle_send.subject = self.send_slider_toggle_button
        self._session.set_mixer(self._mixer)
Example #4
0
 def _set_global_buttons(self):
     is_momentary = True
     self._undo_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 85)
     self._do_undo.subject = self._undo_button
     self._redo_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 87)
     self._do_redo.subject = self._redo_button
     self._stop_all_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 111)
     self._do_stop_all.subject = self._stop_all_button
     self._toggle_detail_button = ButtonElement(is_momentary, MIDI_CC_TYPE,
                                                1, 121)
     self._action_toogle_detail_view.subject = self._toggle_detail_button
     self._fire_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 9)
     self._do_fire_button.subject = self._fire_button
     self._g_clear_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1,
                                          106)
     self._hold_clear_action.subject = self._g_clear_button
     self._g_duplicate_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1,
                                              107)
     self._hold_duplicate_action.subject = self._g_duplicate_button
     self.track_left_button = StateButton(is_momentary, MIDI_CC_TYPE, 0,
                                          120)
     self.track_right_button = StateButton(is_momentary, MIDI_CC_TYPE, 0,
                                           121)
     self.set_sel_arm_button = StateButton(is_momentary, MIDI_CC_TYPE, 2,
                                           56)
     self._reenable_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1,
                                           120)
     self._do_auto_reenable.subject = self._reenable_button
     self._on_change_reenabled.subject = self.song()
     self._on_change_reenabled()
     self._a_trk_left.subject = self.track_left_button
     self._a_trk_right.subject = self.track_right_button
     self._a_sel_arm.subject = self.set_sel_arm_button
 def create_button(ccval, channel=0, cname=None):
     button = StateButton(True,
                          MIDI_CC_TYPE,
                          channel,
                          ccval,
                          name=cname)
     button.last_value = 0
     return button
Example #6
0
 def create_button(self, index):
     button = StateButton(True,
                          MIDI_CC_TYPE,
                          BASE_CONTROL_CHANNEL,
                          22 + index,
                          name='Page_Button_' + str(index + 1) + '_Control')
     button.cindex = index
     return ButtonHandler(index, button, self)
Example #7
0
 def _init_maschine(self):
     self.__jog_wheel_section = JogWheelSection()
     self.__metro_tap_button = StateButton(True,
                                           MIDI_CC_TYPE,
                                           BASE_CONTROL_CHANNEL,
                                           CC_TAP_METRO_BUTTON,
                                           name='Tap_metro_button')
     self._do_tap_metro.subject = self.__metro_tap_button
     self.update_arrange_button()
 def _set_modecontrol(self):
     is_momentary = True
     self.scene_mode_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 112)
     self.clip_mode_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 113)
     self.pad_mode_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 114)
     self.control_mode_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 115)
     self.xfade_assign_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 116)
     self.scene_mode_button.add_value_listener(self._a_mode_scene)
     self.clip_mode_button.add_value_listener(self._a_mode_clip)
     self.pad_mode_button.add_value_listener(self._a_mode_pad)
     self.control_mode_button.add_value_listener(self._a_mode_control)
 def _set_mode_buttons(self):
     self.xfade_assign_button = StateButton(True, MIDI_CC_TYPE, 0, 116)
     self._pad_select_button = StateButton(False, MIDI_CC_TYPE, 0, 117)
     self._pad_solo_button = StateButton(False, MIDI_CC_TYPE, 0, 118)
     self._mute_button = StateButton(False, MIDI_CC_TYPE, 0, 119)
     self._pad_scale_up = GatedColorButton(True, MIDI_CC_TYPE, 83, 0)
     self._pad_scale_down = GatedColorButton(True, MIDI_CC_TYPE, 94, 16)
     self._pad_select_button.add_value_listener(self._do_pad_select_multi)
     self._mute_button.add_value_listener(self._do_mute_button)
     self._pad_solo_button.add_value_listener(self._do_pad_solo_multi)
     self.xfade_assign_button.add_value_listener(self._do_xfade_assign)
     self._pad_scale_up.add_value_listener(self._do_pad_note_up)
     self._pad_scale_down.add_value_listener(self._do_pad_note_down)
 def _setup_transport(self):
     is_momentary = True
     transport = TransportComponent()
     studiotransport = MaschineTransport()
     playButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 108)
     stopButton = StateButton(not is_momentary, MIDI_CC_TYPE, 0, 110)
     recordButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 109)
     overdubButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 107)
     metrononmeButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 104)
     eventRecButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 98)
     playButton.name = 'Play'
     stopButton.name = 'Stop'
     recordButton.name = 'Record'
     overdubButton.name = 'Overdub'
     metrononmeButton.name = 'Metronome'
     transport.set_play_button(playButton)
     transport.set_stop_button(stopButton)
     transport.set_record_button(recordButton)
     transport.set_overdub_button(overdubButton)
     transport.set_metronome_button(metrononmeButton)
     studiotransport.set_session_auto_button(eventRecButton)
     studiotransport.set_arrangement_overdub_button(StateButton(is_momentary, MIDI_CC_TYPE, 0, 106))
     studiotransport.set_back_arrange_button(StateButton(is_momentary, MIDI_CC_TYPE, 0, 105))
     transport.set_nudge_buttons(StateButton(is_momentary, MIDI_CC_TYPE, 1, 51), StateButton(is_momentary, MIDI_CC_TYPE, 1, 50))
     punchinbutton = ToggleButton(MIDI_CC_TYPE, 1, 52)
     punchoutbutton = ToggleButton(MIDI_CC_TYPE, 1, 53)
     punchinbutton.name = 'Punch In'
     punchoutbutton.name = 'Punch Out'
     transport.set_punch_buttons(punchinbutton, punchoutbutton)
     transport.set_loop_button(StateButton(is_momentary, MIDI_CC_TYPE, 1, 54))
     self.song_follow_button = ButtonElement(True, MIDI_CC_TYPE, 2, 98)
     self._do_song_follow.subject = self.song_follow_button
     self._song_follow_changed.subject = self.song().view
     self._song_follow_changed()
     self.prehear_knob = SliderElement(MIDI_CC_TYPE, 0, 41)
     self.prehear_knob.connect_to(self.song().master_track.mixer_device.cue_volume)
     self.transp_ff_button = ButtonElement(True, MIDI_CC_TYPE, 1, 59)
     self.transp_rw_button = ButtonElement(True, MIDI_CC_TYPE, 1, 58)
     transport.set_seek_buttons(self.transp_ff_button, self.transp_rw_button)
     self.xfadeKnob = SliderElement(MIDI_CC_TYPE, 1, 105)
     self.xfadeKnob.connect_to(self.song().master_track.mixer_device.crossfader)
     self.master_knob = SliderElement(MIDI_CC_TYPE, 0, 99)
     self.master_knob.connect_to(self.song().master_track.mixer_device.volume)
     self.tap_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 88)
     self._do_tap_tempo.subject = self.tap_button
     self.cue_add_delete_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 55)
     self.cue_prev_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 56)
     self.cue_next_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 57)
     self._do_toggle_cue.subject = self.cue_add_delete_button
     self._do_toggle_prev_cue.subject = self.cue_prev_button
     self._do_toggle_next_cue.subject = self.cue_next_button
Example #11
0
 def set_up_function_buttons(self):
     is_momentary = True
     self.keycolor_mod_button = StateButton(is_momentary, MIDI_CC_TYPE, 1,
                                            73)
     self._do_key_color.subject = self.keycolor_mod_button
     self._update_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 86)
     self._do_update_display.subject = self._update_button
    def _set_up_session(self, mode_selector):
        is_momentary = True
        self._session = MaschineSessionComponent()
        self._session.set_color_manager(mode_selector.get_color_manager())
        self.nav_buttons = (
         self.create_gated_button(92, COLOR_HUE_NAV), self.create_gated_button(81, COLOR_HUE_NAV),
         self.create_gated_button(93, COLOR_HUE_NAV), self.create_gated_button(91, COLOR_HUE_NAV))
        self._session.set_scene_bank_buttons(self.nav_buttons[0], self.nav_buttons[1])
        self._session.set_track_bank_buttons(self.nav_buttons[2], self.nav_buttons[3])
        track_stop_buttons = [ StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, index + STOP_CC_OFF) for index in range(4) ]
        self._session.set_stop_track_clip_buttons(tuple(track_stop_buttons))
        self._matrix = []
        self._bmatrix = ButtonMatrixElement()
        for scene_index in range(4):
            button_row = []
            for track_index in range(4):
                button = self.create_pad_button(scene_index, track_index, mode_selector)
                button_row.append(button)

            self._matrix.append(tuple(button_row))
            self._bmatrix.add_row(tuple(button_row))

        self._session.set_matrix(self._matrix)
        for button, (track_index, scene_index) in self._bmatrix.iterbuttons():
            if button:
                scene = self._session.scene(scene_index)
                clip_slot = scene.clip_slot(track_index)
                clip_slot.set_launch_button(button)
                clip_slot.set_triggered_to_play_value(1)
                clip_slot.set_triggered_to_record_value(1)
                clip_slot.set_started_value(1)
                clip_slot.set_recording_value(1)
                clip_slot.set_stopped_value(1)

        self._session._link()
    def _set_up_device_control(self):
        is_momentary = True
        device = MaschineDeviceComponent(self)
        device.set_device_changed_listener(self._handle_device_changed)
        device.set_device_parm_listener(self._hande_device_parm_changed)
        param_controls = []
        for index in range(8):
            param_controls.append(SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_CC_OFF + index))

        device.set_parameter_controls(tuple(param_controls))
        device.set_on_off_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_BUTTON_CC_OFF))
        device.set_bank_nav_buttons(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_BUTTON_CC_OFF + 4), ButtonElement(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_BUTTON_CC_OFF + 5))
        self._device_nav_button_left = StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_BUTTON_CC_OFF + 6)
        self._device_nav_button_right = StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_BUTTON_CC_OFF + 7)
        self._device_nav_button_left.add_value_listener(self._nav_value_left)
        self._device_nav_button_right.add_value_listener(self._nav_value_right)
        device.name = 'Device_Component'
        self.set_device_component(device)
        return device
Example #14
0
    def __init__(self, monochrome=False, *a, **k):
        super(ModeSelector, self).__init__(*a, **k)
        is_momentary = True
        self._monochrome = monochrome
        self._scene_mode = SceneMode(0)
        self._clip_mode = StudioClipMode(1)
        self._pad_mode = PadMode(2, monochrome)
        self._drum_mode = DrumMode(2, monochrome)
        self._control_mode = ControlMode(2, monochrome)
        self._pad_mode.set_alternate_mode(self._drum_mode)
        self._drum_mode.set_alternate_mode(self._pad_mode)
        self._tracks_assign = TrackAssign()
        self._arm_mode = TrackArmMode(self._tracks_assign)
        self._solo_mode = TrackSoloMode(self._tracks_assign)
        self._stop_mode = TrackStopMode(self._tracks_assign)
        self._mute_mode = TrackMuteMode(self._tracks_assign)
        self._select_mode = TrackSelectMode(self._tracks_assign)
        self._xfade_mode = TrackXFadeMode(self._tracks_assign)
        self._color_select_mode = MaschineColorSelectMode(5)
        self._mode = self._clip_mode
        self._return_mode = None

        def create_button(ccval, channel=0):
            return StateButton(is_momentary, MIDI_CC_TYPE, channel, ccval)

        self._scene_mode_button = create_button(112)
        self._clip_mode_button = create_button(113)
        self._pad_mode_button = create_button(114)
        self._select_button = create_button(117)
        self._solo_button = create_button(118)
        self._mute_button = create_button(119)
        self._xfade_button = self.canonical_parent.create_gated_button(84, 5)
        self._stop_button = self.canonical_parent.create_gated_button(94, 16)
        self._arm_button = self.canonical_parent.create_gated_button(83, 0)
        self._solo_button = StateButton(False, MIDI_CC_TYPE, 0, 118)
        self._clip_mode_button.send_value(127, True)
        self._select_clip_mode.subject = self._clip_mode_button
        self._select_scene_mode.subject = self._scene_mode_button
        self._select_pad_mode.subject = self._pad_mode_button
        self._select_arm.subject = self._arm_button
        self._select_solo.subject = self._solo_button
        self._select_stop.subject = self._stop_button
        self._select_xfade.subject = self._xfade_button
        self._select_mute.subject = self._mute_button
        self._select_select.subject = self._select_button
        self._arm_exclusive_button = create_button(51, 2)
        self._action_arm_exclusive.subject = self._arm_exclusive_button
        self._solo_exclusive_button = create_button(52, 2)
        self._action_solo_exclusive.subject = self._solo_exclusive_button
        self._scene_mode_button.send_value(0, True)
        self._clip_mode_button.send_value(1, True)
        self._pad_mode_button.send_value(0, True)
        self._mode._active = True
        return
 def __init__(self, c_instance):
     ControlSurface.__init__(self, c_instance, False)
     with self.component_guard():
         self._suppress_send_midi = True
         self.togglecolor = (10, 30, 50, 70, 90)
         self.toggleindex = 0
         self._c_ref = c_instance
         self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143
         self._c_inst = c_instance
         is_momentary = True
         self._active = True
         self._modifier_down = False
         self._return_mode = 0
         self._returntopad = False
         self._mode = CLIP_MODE
         self.init_slot = 0
         self.init_done = False
         self._midi_pause_count = 0
         self.nav_index = 0
         self._base_note = 0
         self._octave = 0.55
         self._scale_select_mode = MODE_PRESS_NONE
         self.send_slider_index = 0
         self._pad_mode = PM_OFF
         self._note_display_mode = ND_KEYBOARD1
         self._set_suppress_rebuild_requests(True)
         self._scenematrix = SceneMatrix(self)
         self._master_knob = Mk2KnobControl(self)
         self._device = self._set_up_device_control()
         self.show_message(str(''))
         self.request_rebuild_midi_map()
         self._set_global_buttons()
         self._set_mode_buttons()
         self._setup_transport()
         self._set_modecontrol()
         self._set_up_session()
         self._set_up_mixer()
         self._set_up_timer()
         self._set_up_machine_knobs()
         self.current_scale_index = 0
         self.assign_transpose(SCALES[self.current_scale_index])
         self.set_highlighting_session_component(self._session)
         self._navigate_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 127)
         self._navigate_button.add_value_listener(self._do_focus_navigate)
         self.display_update_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 86)
         self.display_update_button.add_value_listener(self._a_display_update)
         self._set_suppress_rebuild_requests(False)
         self.song().view.add_detail_clip_listener(self.clip_handle)
         self.song().add_visible_tracks_listener(self.clip_handle)
         self.song().add_scenes_listener(self.clip_handle)
         self.application().view.add_view_focus_changed_listener(self.focus_changed)
         self.log_message('########## LIVE 9 MASCHINE MK2 V 1.02 #############')
         self._suppress_send_midi = False
 def _set_global_buttons(self):
     is_momentary = True
     self._undo_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 85)
     self._undo_button.add_value_listener(self._do_undo)
     self._redo_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 87)
     self._redo_button.add_value_listener(self._do_redo)
     self._armsolomode_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 89)
     self._armsolomode_button.add_value_listener(self._do_armsolo_mode)
     self._fire_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 9)
     self._fire_button.add_value_listener(self._do_fire_button)
     self.track_left_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 120)
     self.track_right_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 121)
     self.track_left_button.add_value_listener(self._a_trk_left)
     self.track_right_button.add_value_listener(self._a_trk_right)
     self.test_button = StateButton(True, MIDI_CC_TYPE, 5, 60)
     self.note_repeat_button = StateButton(True, MIDI_CC_TYPE, 5, 61)
     self.test_button.add_value_listener(self.do_test)
     self.note_repeat_button.add_value_listener(self.do_note_repeat)
     self.reset_test_button = StateButton(True, MIDI_CC_TYPE, 5, 62)
     self.reset_test_button.add_value_listener(self.do_reset)
Example #17
0
 def set_up_function_buttons(self):
     is_momentary = True
     self._do_octave_button.subject = StateButton(is_momentary,
                                                  MIDI_CC_TYPE, 1, 70)
     self._do_scale_button.subject = StateButton(is_momentary, MIDI_CC_TYPE,
                                                 1, 71)
     self._do_note_button.subject = StateButton(is_momentary, MIDI_CC_TYPE,
                                                1, 72)
     self._do_loop_mod.subject = StateButton(is_momentary, MIDI_CC_TYPE, 1,
                                             69)
     self._color_edit_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 3,
                                             114)
     self._do_color_button.subject = self._color_edit_button
     self.scrub_mode_button = StateButton(is_momentary, MIDI_CC_TYPE, 2, 53)
     self._action_scrub_mode.subject = self.scrub_mode_button
     self._action_loop_button.subject = StateButton(is_momentary,
                                                    MIDI_CC_TYPE, 2, 54)
     self._action_quant_button.subject = StateButton(
         is_momentary, MIDI_CC_TYPE, 2, 55)
 def _setup_transport(self):
     is_momentary = True
     self.shiftButton = SysExButton(120, name='Shift_Button')
     self.__play_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 108, name='Play_Button')
     self._hand_play_pressed.subject = self.__play_button
     self._listen_playing.subject = self.song()
     self._channel_led_left = SliderElement(MIDI_CC_TYPE, 0, 38)
     self._channel_led_right = SliderElement(MIDI_CC_TYPE, 0, 39)
     self._channel_led_left.last_raw = 0.0
     self._channel_led_left.last_send = 0
     self._channel_led_right.last_raw = 0.0
     self._channel_led_right.last_send = 0
     self._listen_master_left.subject = self.song().master_track
     self._listen_master_right.subject = self.song().master_track
     self._auto_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 98, name='Auto_Button')
     self._listen_automation_record.subject = self.song()
     self._handle_automation_record.subject = self._auto_button
     self._do_direction_up.subject = StateButton(is_momentary, MIDI_CC_TYPE, 0, 40, name='Up_Arrow')
     self._do_direction_down.subject = StateButton(is_momentary, MIDI_CC_TYPE, 0, 41, name='Down_Arrow')
     self._do_direction_left.subject = StateButton(is_momentary, MIDI_CC_TYPE, 0, 42, name='Left_Arrow')
     self._do_direction_right.subject = StateButton(is_momentary, MIDI_CC_TYPE, 0, 43, name='Right_Arrow')
    def _set_up_mixer(self):
        is_momentary = True
        self._mixer = MixerComponent(8)
        self.send_sliders = []
        for track in range(8):
            self.send_sliders.append(SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, SEND_CC_OFF + track))

        for track in range(8):
            strip = self._mixer.channel_strip(track)
            strip.set_arm_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, ARM_CC_OFF + track))
            strip.set_solo_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, SOLO_CC_OFF + track))
            strip.set_mute_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, MUTE_CC_OFF + track))
            strip.set_volume_control(SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, LEVEL_CC_OFF + track))
            strip.set_pan_control(SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, PAN_CC_OFF + track))
            strip.set_select_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, SELECT_CC_OFF + track))
            st = tuple([self.send_sliders[track]])
            strip.set_send_controls(st)

        self.send_slider_toggle_button = StateButton(False, MIDI_CC_TYPE, 0, 90)
        self.send_slider_toggle_button.add_value_listener(self._do_toggle_send)
        self._session.set_mixer(self._mixer)
Example #20
0
 def __init__(self, *a, **k):
     super(EditSection, self).__init__(*a, **k)
     is_momentary = True
     self.mikro_shift_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 2,
                                             80)
     self._do_shift_mikro.subject = self.mikro_shift_button
     self.shift_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1, 80)
     self._do_shift.subject = self.shift_button
     self.alt_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1, 82)
     self._do_alt.subject = self.alt_button
     self.mikro_shift = False
     self.shiftdown = False
     self.altdown = False
     self.edit_state = ES_NONE
     self._down_button = None
     self._action_set_quant.subject = SliderElement(MIDI_CC_TYPE, 2, 110)
     self._action_init_loop.subject = SliderElement(MIDI_CC_TYPE, 2, 111)
     self._nav_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 115)
     self._action_navigate.subject = self._nav_button
     self._copy_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 95)
     self._action_duplicate.subject = self._copy_button
     self._quantize_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 97)
     self._action_quantize.subject = self._quantize_button
     self._paste_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 96)
     self._action_new.subject = self._paste_button
     self._note_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 101)
     self._action_note.subject = self._note_button
     self._clear_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 103)
     self._action_clear.subject = self._clear_button
     self._nudge_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 100)
     self._action_nudge_button.subject = self._nudge_button
     self.action_time = False
     self.pad_action = False
     self.pad_wheel_action = False
     self.quantize = 5
     self.quantize_amount = 1.0
     self.initial_clip_len = 4.0
     self._focused_clip = None
     self._focused_c_index = None
     self._color_edit = False
     self.nav_index = 0
     return
Example #21
0
 def __init__(self, *a, **k):
     super(EncoderView, self).__init__(*a, **k)
     self.__session = self.canonical_parent.get_session()
     self.__encoders = [self.create_encoders(index) for index in range(8)]
     self.__buttons = [self.create_button(index) for index in range(8)]
     self._return_tracks_change.subject = self.song()
     self._tracks_change.subject = self.song()
     self._handle_track_changed.subject = self.song().view
     self._handle_visble_tracks_changed.subject = self.song()
     self._left_button = StateButton(True, MIDI_CC_TYPE,
                                     BASE_CONTROL_CHANNEL,
                                     CC_LEFT_PAGE_BUTTON)
     self._right_button = StateButton(True, MIDI_CC_TYPE,
                                      BASE_CONTROL_CHANNEL,
                                      CC_RIGHT_PAGE_BUTTON)
     self._mixer_button = StateButton(True, MIDI_CC_TYPE,
                                      BASE_CONTROL_CHANNEL, CC_MIXER_BUTTON)
     self._plugin_button = StateButton(True, MIDI_CC_TYPE,
                                       BASE_CONTROL_CHANNEL,
                                       CC_PLUGIN_BUTTON)
     self._handle_left.subject = self._left_button
     self._handle_right.subject = self._right_button
     self._handle_mixer_button.subject = self._mixer_button
     self._handle_plugin_button.subject = self._plugin_button
Example #22
0
 def _setup_transport(self):
     is_momentary = True
     transport = TransportComponent()
     self.__play_button = StateButton(is_momentary,
                                      MIDI_CC_TYPE,
                                      BASE_CONTROL_CHANNEL,
                                      CC_PLAY_BUTTON,
                                      name='Play_Button')
     self.__restart_button = StateButton(True, MIDI_CC_TYPE,
                                         BASE_CONTROL_CHANNEL,
                                         CC_RESTART_BUTTON)
     stop_button = StateButton(not is_momentary,
                               MIDI_CC_TYPE,
                               BASE_CONTROL_CHANNEL,
                               CC_STOP_BUTTON,
                               name='Stop_Button')
     self._rec_button = StateButton(is_momentary,
                                    MIDI_CC_TYPE,
                                    BASE_CONTROL_CHANNEL,
                                    CC_RECORD_BUTTON,
                                    name='Record_Button')
     metrononme_button = StateButton(is_momentary,
                                     MIDI_CC_TYPE,
                                     BASE_CONTROL_CHANNEL,
                                     CC_METRONOME_BUTTON,
                                     name='Metronome_Button')
     self._song_follow_button = StateButton(is_momentary,
                                            MIDI_CC_TYPE,
                                            BASE_CONTROL_CHANNEL,
                                            CC_FOLLOW_BUTTON,
                                            name='Follow_Button')
     self._do_rec_button.subject = self._rec_button
     if self._has_stop_button:
         transport.set_play_button(self.__play_button)
         transport.set_stop_button(stop_button)
     else:
         self._hand_play_pressed.subject = self.__play_button
         self._listen_playing.subject = self.song()
     self._stopall_button = StateButton(True, MIDI_CC_TYPE, 0,
                                        CC_ALL_BUTTON)
     self._do_stop_all.subject = self._stopall_button
     self._auto_button = StateButton(True,
                                     MIDI_CC_TYPE,
                                     0,
                                     CC_AUTO_BUTTON,
                                     name='Auto_Button')
     self._handle_automation_record.subject = self._auto_button
     self._listen_automation_record.subject = self.song()
     self._handle_restart_button.subject = self.__restart_button
     self._handle_follows_button.subject = self._song_follow_button
     self._follow_song_changed.subject = self.song().view
     transport.set_metronome_button(metrononme_button)
     self._listen_overdub.subject = self.song()
 def __init__(self, session, *a, **k):
     super(ModifierComponent, self).__init__(*a, **k)
     self.__session = session
     self.__delete_button = StateButton(True,
                                        MIDI_CC_TYPE,
                                        0,
                                        95,
                                        name='Clear_Button')
     self.__do_delete.subject = self.__delete_button
     self.__duplicate_button = StateButton(True,
                                           MIDI_CC_TYPE,
                                           0,
                                           96,
                                           name='Duplicate_Button')
     self.__do_duplicate.subject = self.__duplicate_button
     self._select_button = StateButton(True,
                                       MIDI_CC_TYPE,
                                       0,
                                       80,
                                       name='Select_Button')
     self.__do_select_button.subject = self._select_button
     self.__lock_button = StateButton(True,
                                      MIDI_CC_TYPE,
                                      0,
                                      47,
                                      name='Lock_Button')
     self.__do_lock.subject = self.__lock_button
     self.__macro_button = StateButton(True,
                                       MIDI_CC_TYPE,
                                       0,
                                       90,
                                       name='Macro_Button')
     self.__do_macro.subject = self.__macro_button
     self._left_button = StateButton(True,
                                     MIDI_CC_TYPE,
                                     0,
                                     107,
                                     name='Metro_Button')
     self._right_button = StateButton(True,
                                      MIDI_CC_TYPE,
                                      0,
                                      104,
                                      name='Loop_Button')
     self._rec_button = StateButton(True,
                                    MIDI_CC_TYPE,
                                    0,
                                    109,
                                    name='Record_Button')
     self._do_left_button.subject = self._left_button
     self._do_right_button.subject = self._right_button
     self._do_rec_button.subject = self._rec_button
     self._listen_overdub.subject = self.song()
     self._listen_loop.subject = self.song()
     self._listen_metronome.subject = self.song()
     self.__action_listener = None
     self.__shift_listener = None
     return
Example #24
0
 def __init__(self, parent):
     self._parent = parent
     self.master_track = parent.song().master_track
     self.volume_button = None
     self._set_volume_button(StateButton(True, MIDI_CC_TYPE, 3, 110))
     self.the_slider = SliderElement(MIDI_CC_TYPE, 1, 86)
     self.the_slider.add_value_listener(self._do_main_slider, True)
     self.xfade_button = None
     self._set_xfade_button(StateButton(True, MIDI_CC_TYPE, 3, 116))
     self.swing_button = None
     self._set_swing_button(StateButton(True, MIDI_CC_TYPE, 3, 111))
     self.mode = KN2_MODE_VOLUME
     self.previous_mode = -1
     self.tempo_button = None
     self._set_tempo_button(StateButton(True, MIDI_CC_TYPE, 3, 112))
     self.push_button = None
     self._set_push_button(StateButton(True, MIDI_CC_TYPE, 1, 87))
     self.clipn_v_button = None
     self.clipn_h_button = None
     self._set_clipn_h_button(StateButton(True, MIDI_CC_TYPE, 3, 114))
     self._set_clipn_v_button(StateButton(True, MIDI_CC_TYPE, 3, 115))
     self.toggle_buttons = [
         self.volume_button, self.xfade_button, self.swing_button,
         self.tempo_button, self.clipn_h_button, self.clipn_v_button
     ]
     self.shift_button = None
     self._set_shift_button(StateButton(True, MIDI_CC_TYPE, 3, 113))
     self.shift_on = False
     self.scroll_mod_left_button = None
     self.scroll_mod_right_button = None
     self._set_scroll_mod_left_button(
         StateButton(True, MIDI_CC_TYPE, 0, 105))
     self._set_scroll_mod_right_button(
         StateButton(True, MIDI_CC_TYPE, 0, 106))
     self._prev_mode = KN2_MODE_VOLUME
     self.lrmode = LR_CONTROL_CLIP
     self._challenge = Live.Application.get_random_int(
         0, 400000000) & 2139062143
     self.loop_div_index = 0
     self.loop_incdex = 4.0
     self.arrow_mode_button = ColorButton(True, MIDI_CC_TYPE, 30)
     self.arrow_mode_button.add_value_listener(self.toggle_arrow_mode)
     self.arrow_mode_button.send_color(LR_MODE_HUES[self.lrmode])
     self.arrow_mode_button.send_value(127, True)
     self.navflags = 0
     self.octave_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 70)
     self.octave_mod_button.add_value_listener(self._action_octave)
     self.scale_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 71)
     self.scale_mod_button.add_value_listener(self._action_scale)
     self.basenote_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 72)
     self.basenote_mod_button.add_value_listener(self._action_base_note)
     self.keycolor_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 73)
     self.keycolor_mod_button.add_value_listener(self._action_key_color)
     self.pad_to_mainknob_mode = 0
     self._measure_left_click = 0
     self._measure_right_click = 0
     self.mode_assign_map = {
         KN2_MODE_VOLUME:
         (self.chg_volume, 0, 'Master Knob controls MASTER Volume',
          KN2_MODE_CUE),
         KN2_MODE_CUE: (self.chg_cue, 0, 'Master Knob controls Cue Level',
                        KN2_MODE_VOLUME),
         KN2_MODE_TEMPO_COARSE:
         (self.chg_tempo, 3, 'Master Knob controls TEMPO Coarse',
          KN2_MODE_TEMPO_FINE),
         KN2_MODE_TEMPO_FINE:
         (self.chg_tempo_fine, 3, 'Master Knob controls TEMPO Fine',
          KN2_MODE_TEMPO_COARSE),
         KN2_MODE_XFADE: (self.chg_xfade, 1,
                          'Master Knob controls Crossfader', -1),
         KN2_MODE_QUANT:
         (self.chg_quant, 2, 'Master Knob controls Recording Quantize',
          KN2_MODE_CLIP_QUANT),
         KN2_MODE_CLIP_QUANT:
         (self.chg_clip_q, 2, 'Master Knob controls Clip Start Quantize',
          KN2_MODE_QUANT),
         KN2_MODE_CLIPN_HOR: (self.nav_c_hor, 4,
                              'Master Knob Clip View horizontally', -1),
         KN2_MODE_CLIPN_VER: (self.nav_c_ver, 5,
                              'Master Knob Clip View vertically', -1),
         KN2_MODE_GENERAL: (self.chg_general, -1, None, -1),
         KN2_P_SCALES: (self.modify_pad_scaling, -1, None, -1)
     }
class MaschineMk2(ControlSurface):
    __module__ = __name__
    __doc__ = 'Control Script for Maschine Mk2 and Maschine Mikro Mk2'

    def __init__(self, c_instance):
        ControlSurface.__init__(self, c_instance, False)
        with self.component_guard():
            self._suppress_send_midi = True
            self.togglecolor = (10, 30, 50, 70, 90)
            self.toggleindex = 0
            self._c_ref = c_instance
            self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143
            self._c_inst = c_instance
            is_momentary = True
            self._active = True
            self._modifier_down = False
            self._return_mode = 0
            self._returntopad = False
            self._mode = CLIP_MODE
            self.init_slot = 0
            self.init_done = False
            self._midi_pause_count = 0
            self.nav_index = 0
            self._base_note = 0
            self._octave = 0.55
            self._scale_select_mode = MODE_PRESS_NONE
            self.send_slider_index = 0
            self._pad_mode = PM_OFF
            self._note_display_mode = ND_KEYBOARD1
            self._set_suppress_rebuild_requests(True)
            self._scenematrix = SceneMatrix(self)
            self._master_knob = Mk2KnobControl(self)
            self._device = self._set_up_device_control()
            self.show_message(str(''))
            self.request_rebuild_midi_map()
            self._set_global_buttons()
            self._set_mode_buttons()
            self._setup_transport()
            self._set_modecontrol()
            self._set_up_session()
            self._set_up_mixer()
            self._set_up_timer()
            self._set_up_machine_knobs()
            self.current_scale_index = 0
            self.assign_transpose(SCALES[self.current_scale_index])
            self.set_highlighting_session_component(self._session)
            self._navigate_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 127)
            self._navigate_button.add_value_listener(self._do_focus_navigate)
            self.display_update_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 86)
            self.display_update_button.add_value_listener(self._a_display_update)
            self._set_suppress_rebuild_requests(False)
            self.song().view.add_detail_clip_listener(self.clip_handle)
            self.song().add_visible_tracks_listener(self.clip_handle)
            self.song().add_scenes_listener(self.clip_handle)
            self.application().view.add_view_focus_changed_listener(self.focus_changed)
            self.log_message('########## LIVE 9 MASCHINE MK2 V 1.02 #############')
            self._suppress_send_midi = False

    def _set_up_mixer(self):
        is_momentary = True
        self._mixer = MixerComponent(8)
        self.send_sliders = []
        for track in range(8):
            self.send_sliders.append(SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, SEND_CC_OFF + track))

        for track in range(8):
            strip = self._mixer.channel_strip(track)
            strip.set_arm_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, ARM_CC_OFF + track))
            strip.set_solo_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, SOLO_CC_OFF + track))
            strip.set_mute_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, MUTE_CC_OFF + track))
            strip.set_volume_control(SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, LEVEL_CC_OFF + track))
            strip.set_pan_control(SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, PAN_CC_OFF + track))
            strip.set_select_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, SELECT_CC_OFF + track))
            st = tuple([self.send_sliders[track]])
            strip.set_send_controls(st)

        self.send_slider_toggle_button = StateButton(False, MIDI_CC_TYPE, 0, 90)
        self.send_slider_toggle_button.add_value_listener(self._do_toggle_send)
        self._session.set_mixer(self._mixer)

    def _set_global_buttons(self):
        is_momentary = True
        self._undo_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 85)
        self._undo_button.add_value_listener(self._do_undo)
        self._redo_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 87)
        self._redo_button.add_value_listener(self._do_redo)
        self._armsolomode_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 89)
        self._armsolomode_button.add_value_listener(self._do_armsolo_mode)
        self._fire_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 9)
        self._fire_button.add_value_listener(self._do_fire_button)
        self.track_left_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 120)
        self.track_right_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 121)
        self.track_left_button.add_value_listener(self._a_trk_left)
        self.track_right_button.add_value_listener(self._a_trk_right)
        self.test_button = StateButton(True, MIDI_CC_TYPE, 5, 60)
        self.note_repeat_button = StateButton(True, MIDI_CC_TYPE, 5, 61)
        self.test_button.add_value_listener(self.do_test)
        self.note_repeat_button.add_value_listener(self.do_note_repeat)
        self.reset_test_button = StateButton(True, MIDI_CC_TYPE, 5, 62)
        self.reset_test_button.add_value_listener(self.do_reset)

    def _set_mode_buttons(self):
        self.xfade_assign_button = StateButton(True, MIDI_CC_TYPE, 0, 116)
        self._pad_select_button = StateButton(False, MIDI_CC_TYPE, 0, 117)
        self._pad_solo_button = StateButton(False, MIDI_CC_TYPE, 0, 118)
        self._mute_button = StateButton(False, MIDI_CC_TYPE, 0, 119)
        self._pad_scale_up = GatedColorButton(True, MIDI_CC_TYPE, 83, 0)
        self._pad_scale_down = GatedColorButton(True, MIDI_CC_TYPE, 94, 16)
        self._pad_select_button.add_value_listener(self._do_pad_select_multi)
        self._mute_button.add_value_listener(self._do_mute_button)
        self._pad_solo_button.add_value_listener(self._do_pad_solo_multi)
        self.xfade_assign_button.add_value_listener(self._do_xfade_assign)
        self._pad_scale_up.add_value_listener(self._do_pad_note_up)
        self._pad_scale_down.add_value_listener(self._do_pad_note_down)

    def set_appointed_device(self, device):
        with self.component_guard():
            self._device_component.set_device(device)

    def _set_up_device_control(self):
        is_momentary = True
        device = MaschineDeviceComponent(self)
        device.set_device_changed_listener(self._handle_device_changed)
        device.set_device_parm_listener(self._hande_device_parm_changed)
        param_controls = []
        for index in range(8):
            param_controls.append(SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_CC_OFF + index))

        device.set_parameter_controls(tuple(param_controls))
        device.set_on_off_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_BUTTON_CC_OFF))
        device.set_bank_nav_buttons(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_BUTTON_CC_OFF + 4), ButtonElement(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_BUTTON_CC_OFF + 5))
        self._device_nav_button_left = StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_BUTTON_CC_OFF + 6)
        self._device_nav_button_right = StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_BUTTON_CC_OFF + 7)
        self._device_nav_button_left.add_value_listener(self._nav_value_left)
        self._device_nav_button_right.add_value_listener(self._nav_value_right)
        device.name = 'Device_Component'
        self.set_device_component(device)
        return device

    def _setup_transport(self):
        is_momentary = True
        transport = TransportComponent()
        playButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 108)
        stopButton = StateButton(not is_momentary, MIDI_CC_TYPE, 0, 110)
        recordButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 109)
        overdubButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 107)
        metrononmeButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 104)
        playButton.name = 'Play'
        stopButton.name = 'Stop'
        recordButton.name = 'Record'
        overdubButton.name = 'Overdub'
        metrononmeButton.name = 'Metronome'
        transport.set_play_button(playButton)
        transport.set_stop_button(stopButton)
        transport.set_record_button(recordButton)
        transport.set_overdub_button(overdubButton)
        transport.set_metronome_button(metrononmeButton)
        transport.set_nudge_buttons(StateButton(is_momentary, MIDI_CC_TYPE, 1, 51), StateButton(is_momentary, MIDI_CC_TYPE, 1, 50))
        punchinbutton = ToggleButton(MIDI_CC_TYPE, 1, 52)
        punchoutbutton = ToggleButton(MIDI_CC_TYPE, 1, 53)
        punchinbutton.name = 'Punch In'
        punchoutbutton.name = 'Punch Out'
        transport.set_punch_buttons(punchinbutton, punchoutbutton)
        transport.set_loop_button(StateButton(is_momentary, MIDI_CC_TYPE, 1, 54))
        self.transp_ff_button = ButtonElement(True, MIDI_CC_TYPE, 1, 59)
        self.transp_rw_button = ButtonElement(True, MIDI_CC_TYPE, 1, 58)
        transport.set_seek_buttons(self.transp_ff_button, self.transp_rw_button)
        self.xfadeKnob = SliderElement(MIDI_CC_TYPE, 1, 100)
        self.xfadeKnob.connect_to(self.song().master_track.mixer_device.crossfader)
        self.tap_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 88)
        self.tap_button.add_value_listener(self._do_tap_tempo)
        self.cue_add_delete_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 55)
        self.cue_prev_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 56)
        self.cue_next_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 57)
        self.cue_add_delete_button.add_value_listener(self._do_toggle_cue)
        self.cue_prev_button.add_value_listener(self._do_toggle_prev_cue)
        self.cue_next_button.add_value_listener(self._do_toggle_next_cue)

    def _set_up_machine_knobs(self):
        master_track = self.song().master_track
        self.master_volume = SliderElement(MIDI_CC_TYPE, 0, 40)
        self.prehear = SliderElement(MIDI_CC_TYPE, 0, 41)
        self.master_volume.connect_to(master_track.mixer_device.volume)
        self.prehear.connect_to(master_track.mixer_device.cue_volume)

    def _set_up_session(self):
        is_momentary = True
        self._session = MaschineSessionComponent()
        self._session.add_offset_listener(self.notify_track_scroll)
        nhue = COLOR_HUE_NAV
        self.nav_buttons = (GatedColorButton(True, MIDI_CC_TYPE, 92, nhue),
         GatedColorButton(True, MIDI_CC_TYPE, 81, nhue),
         GatedColorButton(True, MIDI_CC_TYPE, 93, nhue),
         GatedColorButton(True, MIDI_CC_TYPE, 91, nhue))
        self._session.set_scene_bank_buttons(self.nav_buttons[0], self.nav_buttons[1])
        self._session.set_track_bank_buttons(self.nav_buttons[2], self.nav_buttons[3])
        self._session.set_stop_all_clips_button(StateButton(is_momentary, MIDI_CC_TYPE, 0, 111))
        track_stop_buttons = [ StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, index + STOP_CC_OFF) for index in range(4) ]
        self._session.set_stop_track_clip_buttons(tuple(track_stop_buttons))
        self._init_matrix()
        self._set_up_buttons()
        self._session._link()
        self._session.set_advance(STEP4)

    def _set_up_buttons(self):
        self._bmatrix = ButtonMatrixElement()
        for scene_index in range(4):
            button_row = []
            scene = self._session.scene(scene_index)
            for track_index in range(4):
                button = self._matrix[scene_index][track_index]
                clip_slot = scene.clip_slot(track_index)
                clip_slot.set_launch_button(button)
                clip_slot.set_triggered_to_play_value(1)
                clip_slot.set_triggered_to_record_value(1)
                clip_slot.set_started_value(1)
                clip_slot.set_recording_value(1)
                clip_slot.set_stopped_value(1)

            self._bmatrix.add_row(tuple(button_row))

    def _init_matrix(self):
        is_momentary = True
        self._button_sequence = []
        self._matrix = []
        for scene_index in range(4):
            button_row = []
            for track_index in range(4):
                button = VarButtonElement(is_momentary, 0, scene_index, track_index, self)
                partner = TwinButton(is_momentary, 1, button)
                partner.add_value_listener(self.ox, True)
                button_row.append(button)

            self._matrix.append(tuple(button_row))

        for scene_index in [3,
         2,
         1,
         0]:
            for track_index in range(4):
                self._button_sequence.append(self._matrix[scene_index][track_index])

        self._session.set_matrix(self._matrix)

    def set_pad_translations(self, pad_translations):
        ControlSurface.set_pad_translations(pad_translations)

    def refresh_state(self):
        ControlSurface.refresh_state(self)
        self._update_hardware()

    def ox(self, value, button):
        if not isinstance(button, TwinButton):
            raise AssertionError
            self._mode == PAD_MODE and button.fire(value)

    def _update_hardware(self):
        self._session.update()
        self._set_suppress_rebuild_requests(True)
        self._set_mode()
        self._master_knob.update()
        if self._scenematrix.soloexclusive:
            self._armsolomode_button.send_value(1, True)
        else:
            self._armsolomode_button.send_value(0, True)
        self._master_knob.start_up()
        self._pad_scale_up.activate()
        self._pad_scale_down.activate()
        self.current_scale_to_display()
        self.send_to_display(KEY_COLOR_MODES_STRINGS[self._note_display_mode], 1)
        for scene_index in range(4):
            scene = self._session.scene(scene_index)
            for track_index in range(4):
                button = self._matrix[scene_index][track_index]
                button.refresh()

        self._set_suppress_rebuild_requests(False)

    def get_color(self, value, track_index, scene_index):
        if not self._active:
            return
        if self._mode == SCENE_MODE or self._mode == CONTROL_MODE or self._pad_mode == PM_ON:
            element = self._scenematrix.get_element(scene_index, track_index)
            return element.get_color(value)
        elif self._mode == CLIP_MODE:
            scene = self._session.scene(scene_index)
            clip_slot = scene.clip_slot(track_index)._clip_slot
            cindex = 0
            if value == 0:
                cindex = 1
            if clip_slot != None:
                if clip_slot.has_clip:
                    if clip_slot.clip.is_recording or clip_slot.clip.will_record_on_start:
                        return PColor.CLIP_RECORD[cindex]
                    if clip_slot.clip.is_playing:
                        return PColor.CLIP_PLAY[cindex]
                    elif clip_slot.clip.is_triggered:
                        return PColor.CLIP_PLAY[cindex]
                    else:
                        return PColor.CLIP_STOPPED[cindex]
                elif clip_slot.will_record_on_start:
                    return PColor.CLIP_RECORD[cindex]
                elif clip_slot.is_playing:
                    return PColor.CLIP_GROUP_PLAY[cindex]
                elif clip_slot.controls_other_clips:
                    return PColor.CLIP_GROUP_CONTROL[cindex]
                elif clip_slot.is_triggered:
                    return PColor.CLIP_GROUP_TRIGGER[cindex]
        elif self._mode == PAD_MODE:
            button = self._matrix[scene_index][track_index]
            return self.get_color_by_note_mode(button.get_identifier(), value > 0)

    def step_key_color_mode(self):
        self._note_display_mode = (self._note_display_mode + 1) % len(KEY_COLOR_MODES_STRINGS)
        self.show_message('Pad Mode Key Color = ' + KEY_COLOR_MODES_STRINGS[self._note_display_mode])
        self.send_to_display('Colors: ' + KEY_COLOR_MODES_STRINGS[self._note_display_mode], 1)
        if self._mode == PAD_MODE:
            for note_index in range(16):
                button = self._button_sequence[note_index]
                button.send_color_direct(self.get_color_by_note_mode(button.get_identifier(), False))

    def get_color_by_note_mode(self, midi_note, on):
        if self._note_display_mode == ND_BASE_OTHER:
            interval = (midi_note + 12 - self._base_note) % 12
            if on:
                return INTERVAL_COLOR_MAP[interval][0]
            else:
                return INTERVAL_COLOR_MAP[interval][1]
        elif on:
            return KEY_COLOR_MAP[midi_note % 12][0]
        else:
            return KEY_COLOR_MAP[midi_note % 12][1]

    def _send_midi(self, midi_bytes, **keys):
        self._c_ref.send_midi(midi_bytes)
        if self._midi_pause_count == 2:
            time.sleep(0.002)
            self._midi_pause_count = 0
        else:
            self._midi_pause_count = self._midi_pause_count + 1
        return True

    def clip_handle(self):
        if self._mode == SCENE_MODE or self._mode == CONTROL_MODE or self._modifier_down:
            self._scenematrix.update()

    def _a_display_update(self, value):
        if not self.display_update_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            (value != 0 or not self.display_update_button.is_momentary()) and self._update_hardware()
            self.show_message('Maschine Display Updated')

    def _set_up_timer(self):
        self.blink_state = 1

    def update_display(self):
        with self.component_guard():
            with self._is_sending_scheduled_messages():
                self._task_group.update(0.1)
            if self._mode == CLIP_MODE and not self._modifier_down:
                if self.blink_state == 0:
                    self._session.notify(1, 0)
                elif self.blink_state == 1:
                    self._session.notify(1, 1)
                elif self.blink_state == 3:
                    self._session.notify(2, 0)
                elif self.blink_state == 4:
                    self._session.notify(2, 1)
            elif self._mode == PAD_MODE:
                pass
            elif self.blink_state == 0:
                self._scenematrix.notify_scene_mode(1)
            elif self.blink_state == 2:
                self._scenematrix.notify_scene_mode(0)
            self.blink_state = (self.blink_state + 1) % 4
            self.init_slot += 1

    def _invoke_track_edit(self, mode):
        self._deassign_matrix()
        self._scenematrix.assign()
        self._scenematrix.set_mode(mode)
        self._pad_mode = PM_ON
        self.request_rebuild_midi_map()
        self._scenematrix.update()

    def _set_modecontrol(self):
        is_momentary = True
        self.scene_mode_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 112)
        self.clip_mode_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 113)
        self.pad_mode_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 114)
        self.control_mode_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 115)
        self.xfade_assign_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 116)
        self.scene_mode_button.add_value_listener(self._a_mode_scene)
        self.clip_mode_button.add_value_listener(self._a_mode_clip)
        self.pad_mode_button.add_value_listener(self._a_mode_pad)
        self.control_mode_button.add_value_listener(self._a_mode_control)

    def _set_mode(self, mode = None):
        if mode == None:
            mode = self._mode
        if mode == SCENE_MODE:
            self.clip_mode_button.send_value(OFF_VALUE, True)
            self.pad_mode_button.send_value(OFF_VALUE, True)
            self.control_mode_button.send_value(OFF_VALUE, True)
            self.scene_mode_button.send_value(ON_VALUE, True)
        elif mode == CLIP_MODE:
            self.scene_mode_button.send_value(OFF_VALUE, True)
            self.pad_mode_button.send_value(OFF_VALUE, True)
            self.control_mode_button.send_value(OFF_VALUE, True)
            self.clip_mode_button.send_value(ON_VALUE, True)
        elif mode == PAD_MODE:
            self.scene_mode_button.send_value(OFF_VALUE, True)
            self.clip_mode_button.send_value(OFF_VALUE, True)
            self.control_mode_button.send_value(OFF_VALUE, True)
            self.pad_mode_button.send_value(ON_VALUE, True)
        elif mode == CONTROL_MODE:
            self.scene_mode_button.send_value(OFF_VALUE, True)
            self.clip_mode_button.send_value(OFF_VALUE, True)
            self.pad_mode_button.send_value(OFF_VALUE, True)
            self.control_mode_button.send_value(ON_VALUE, True)

    def _reset_matrix(self):
        for scene_index in range(4):
            scene = self._session.scene(scene_index)
            for track_index in range(4):
                button = self._matrix[scene_index][track_index]
                clip_slot = scene.clip_slot(track_index)
                clip_slot.set_launch_button(button)

    def update_button_matrix(self):
        self._session.update()
        for scene_index in range(4):
            scene = self._session.scene(scene_index)
            for track_index in range(4):
                button = self._matrix[scene_index][track_index]
                clip_slot = scene.clip_slot(track_index)
                if clip_slot._clip_slot != None and clip_slot._clip_slot.clip != None:
                    button.send_value(1, True)
                else:
                    button.send_value(0, True)

    def _deassign_matrix(self):
        for scene_index in range(4):
            scene = self._session.scene(scene_index)
            for track_index in range(4):
                clip_slot = scene.clip_slot(track_index)
                clip_slot.set_launch_button(None)

    def _from_pad_mode(self, matrix_mode):
        self._mode = SCENE_MODE
        self._register_buttons()
        self._scenematrix.assign()
        self._scenematrix.set_mode(matrix_mode)
        self._set_suppress_rebuild_requests(True)
        self.request_rebuild_midi_map()
        self._scenematrix.update()
        self._set_suppress_rebuild_requests(False)

    def _enter_pad_mode(self):
        self._set_mode(PAD_MODE)
        if self._mode == CLIP_MODE:
            self._deassign_matrix()
        elif self._mode == SCENE_MODE:
            self._scenematrix.deassign()
        elif self._mode == CONTROL_MODE:
            self._scenematrix.deassign()
            self._master_knob.exit_matrix_mode()
        self._mode = PAD_MODE
        self._set_suppress_rebuild_requests(True)
        for row in range(4):
            for column in range(4):
                button = self._matrix[row][column]
                button.send_value(0, True)
                button.set_to_notemode(True)
                self._forwarding_registry[MIDI_NOTE_ON_STATUS, button.get_identifier()] = button
                self._forwarding_registry[MIDI_NOTE_OFF_STATUS, button.get_identifier()] = button

        self._set_suppress_rebuild_requests(False)

    def _register_buttons(self, update = False):
        self._set_suppress_rebuild_requests(True)
        for row in range(4):
            for column in range(4):
                button = self._matrix[row][column]
                button.set_to_notemode(False)
                if update:
                    button.send_value(127, True)
                fwkey = [MIDI_NOTE_ON_STATUS]
                fwkey.append(button.get_identifier())
                self._forwarding_registry[tuple(fwkey)] = button
                self._forwarding_registry[MIDI_NOTE_OFF_STATUS, button.get_identifier()] = button

        self._set_suppress_rebuild_requests(False)

    def _back_to_clip_mode(self):
        self._pad_mode = PM_OFF
        self._scenematrix.set_mode(SCENE_MODE_NORMAL)
        self._scenematrix.deassign()
        self._set_up_clip_matrix()

    def _set_up_clip_matrix(self):
        for row in range(4):
            for column in range(4):
                button = self._matrix[row][column]
                button.set_to_notemode(False)

        self._set_suppress_rebuild_requests(True)
        self.request_rebuild_midi_map()
        self._reset_matrix()
        self.update_button_matrix()
        self._set_suppress_rebuild_requests(False)

    def _enter_scene_mode(self):
        self._set_mode(SCENE_MODE)
        if self._mode == CLIP_MODE:
            self._deassign_matrix()
        elif self._mode == CONTROL_MODE:
            self._master_knob.exit_matrix_mode()
        elif self._mode == PAD_MODE:
            self._register_buttons()
        self._mode = SCENE_MODE
        self._scenematrix.assign()
        self._scenematrix.set_mode(SCENE_MODE_NORMAL)
        self._return_mode = SCENE_MODE_NORMAL
        self.request_rebuild_midi_map()

    def _enter_clip_mode(self):
        self._set_suppress_rebuild_requests(True)
        self._set_mode(CLIP_MODE)
        if self._mode == SCENE_MODE:
            self._scenematrix.deassign()
        elif self._mode == CONTROL_MODE:
            self._master_knob.exit_matrix_mode()
        self._mode = CLIP_MODE
        self._set_up_clip_matrix()
        self.request_rebuild_midi_map()
        self._set_suppress_rebuild_requests(False)

    def _enter_control_mode(self):
        self._set_mode(CONTROL_MODE)
        if self._mode == CLIP_MODE:
            self._deassign_matrix()
        elif self._mode == PAD_MODE:
            self._mode = CONTROL_MODE
            self._register_buttons()
        self._mode = CONTROL_MODE
        self._set_suppress_rebuild_requests(True)
        self._scenematrix.set_mode(SCENE_MODE_CONTROL)
        self._return_mode = SCENE_MODE_CONTROL
        self._scenematrix.assign()
        self._master_knob.switch_to_matrix_mode()
        self._set_suppress_rebuild_requests(False)
        self.request_rebuild_midi_map()
        self._scenematrix.update()

    def _a_mode_scene(self, value):
        if not self.scene_mode_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.show_message('SCENE MODE')
            self._enter_scene_mode()

    def _a_mode_clip(self, value):
        if not self.clip_mode_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.show_message('CLIP MODE')
            self._enter_clip_mode()

    def _a_mode_pad(self, value):
        if not self.pad_mode_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.show_message('PAD MODE')
            self._enter_pad_mode()

    def _a_mode_control(self, value):
        if not self.control_mode_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.show_message('CONTROL MODE')
            self._enter_control_mode()

    def _do_pad_select_multi(self, value):
        if not value in range(128):
            raise AssertionError
            self._modifier_down = value != 0
            if self._mode == PAD_MODE or self._returntopad:
                value != 0 and self._from_pad_mode(SCENE_MODE_SELECT)
                self._returntopad = True
            else:
                self._returntopad = False
                self._enter_pad_mode()
        elif self._mode == CLIP_MODE:
            if value != 0:
                self._invoke_track_edit(SCENE_MODE_SELECT)
            else:
                self._back_to_clip_mode()
        elif self._mode != PAD_MODE:
            if value == 0:
                self._scenematrix.set_mode(self._return_mode)
            else:
                if self._scenematrix.in_main_mode():
                    self._return_mode = self._scenematrix.mode
                self._scenematrix.set_mode(SCENE_MODE_SELECT)

    def _do_mute_button(self, value):
        if not self._mute_button != None:
            raise AssertionError
            if not value in range(128):
                raise AssertionError
                self._modifier_down = value != 0
                (self._mode == PAD_MODE or self._returntopad) and value != 0 and self._from_pad_mode(SCENE_MODE_MUTE)
                self._returntopad = True
            else:
                self._returntopad = False
                self._enter_pad_mode()
        elif self._mode == SCENE_MODE or self._mode == CONTROL_MODE:
            if value == 0:
                self._scenematrix.set_mode(self._return_mode)
                self._pad_mode = PM_OFF
            else:
                if self._scenematrix.in_main_mode():
                    self._return_mode = self._scenematrix.mode
                self._scenematrix.set_mode(SCENE_MODE_MUTE)
                self._pad_mode = PM_ON
        elif self._mode == CLIP_MODE:
            if value > 0:
                self._invoke_track_edit(SCENE_MODE_MUTE)
            else:
                self._back_to_clip_mode()
                self._pad_mode = PM_OFF

    def _do_pad_solo_multi(self, value):
        if not value in range(128):
            raise AssertionError
            self._modifier_down = value != 0
            if self._mode == PAD_MODE or self._returntopad:
                value != 0 and self._from_pad_mode(SCENE_MODE_SOLO)
                self._returntopad = True
            else:
                self._returntopad = False
                self._enter_pad_mode()
        elif self._mode == CLIP_MODE:
            if value != 0:
                self._invoke_track_edit(SCENE_MODE_SOLO)
            else:
                self._back_to_clip_mode()
        elif self._mode != PAD_MODE:
            if value == 0:
                self._scenematrix.set_mode(self._return_mode)
            else:
                if self._scenematrix.in_main_mode():
                    self._return_mode = self._scenematrix.mode
                self._scenematrix.set_mode(SCENE_MODE_SOLO)

    def _do_xfade_assign(self, value):
        if not self.xfade_assign_button != None:
            raise AssertionError
            if not value in range(128):
                raise AssertionError
                (self._mode == PAD_MODE or self._returntopad) and value != 0 and self._from_pad_mode(SCENE_MODE_XFADE)
                self._returntopad = True
            else:
                self._returntopad = False
                self._enter_pad_mode()
        elif self._mode == CLIP_MODE:
            if value != 0:
                self._invoke_track_edit(SCENE_MODE_XFADE)
            else:
                self._back_to_clip_mode()
        elif self._mode == SCENE_MODE or self._mode == CONTROL_MODE:
            if value == 0:
                self._scenematrix.set_mode(self._return_mode)
            else:
                if self._scenematrix.in_main_mode():
                    self._return_mode = self._scenematrix.mode
                self._scenematrix.set_mode(SCENE_MODE_XFADE)

    def _do_pad_note_up(self, value):
        if not self._pad_scale_up != None:
            raise AssertionError
            if not value in range(128):
                raise AssertionError
                self._pad_scale_up.send_value(value, True)
                self._modifier_down = value != 0
                (self._mode == PAD_MODE or self._returntopad) and value != 0 and self._from_pad_mode(SCENE_MODE_ARM)
                self._returntopad = True
            else:
                self._returntopad = False
                self._enter_pad_mode()
        elif self._mode == CLIP_MODE:
            self.show_message('Arm tracks with pads')
            if value != 0:
                self._invoke_track_edit(SCENE_MODE_ARM)
            else:
                self._back_to_clip_mode()
        elif self._mode == SCENE_MODE or self._mode == CONTROL_MODE:
            self.show_message('Arm tracks with pads')
            if value == 0:
                self._scenematrix.set_mode(self._return_mode)
            else:
                if self._scenematrix.in_main_mode():
                    self._return_mode = self._scenematrix.mode
                self._scenematrix.set_mode(SCENE_MODE_ARM)

    def _do_pad_note_down(self, value):
        if not self._pad_scale_down != None:
            raise AssertionError
            if not value in range(128):
                raise AssertionError
                self._pad_scale_down.send_value(value, True)
                self._modifier_down = value != 0
                (self._mode == PAD_MODE or self._returntopad) and value != 0 and self._from_pad_mode(SCENE_MODE_STOP)
                self._returntopad = True
            else:
                self._returntopad = False
                self._enter_pad_mode()
        elif self._mode == CLIP_MODE:
            self.show_message('Stop tracks with pads')
            if value != 0:
                self._invoke_track_edit(SCENE_MODE_STOP)
            else:
                self._back_to_clip_mode()
        elif self._mode == SCENE_MODE or self._mode == CONTROL_MODE:
            self.show_message('Stop tracks with pads')
            if value == 0:
                self._scenematrix.set_mode(self._return_mode)
            else:
                if self._scenematrix.in_main_mode():
                    self._return_mode = self._scenematrix.mode
                self._scenematrix.set_mode(SCENE_MODE_STOP)

    def modify_track_offset(self, delta):
        self._scenematrix.mod_track_offset(delta)

    def modify_scene_offset(self, delta):
        self._scenematrix.mod_scene_offset(delta)

    def move_view_horizontal(self, delta):
        if delta == 1:
            self._session.bank_right()
        else:
            self._session.bank_left()
        if self._mode == CONTROL_MODE:
            self._scenematrix.update()

    def inc_octave(self, inc):
        scale = SCALES[self.current_scale_index]
        octave = scale.to_octave(self._octave)
        newoctave = octave + inc
        if newoctave < 0:
            newoctave = 0
        elif newoctave > scale.octave_range:
            newoctave = scale.octave_range
        self._octave = scale.to_relative(newoctave, self._octave)
        scale = SCALES[self.current_scale_index]
        self.show_message(' OCTAVE ' + BASE_NOTE[self._base_note] + str(newoctave - 2) + ' to ' + scale.name)
        self.current_scale_to_display()

    def inc_base_note(self, inc):
        newbase = self._base_note + inc
        if newbase < 0:
            self._base_note = 0
        elif newbase > 11:
            self._base_note = 11
        else:
            self._base_note = newbase
        scale = SCALES[self.current_scale_index]
        self.show_message(' Base Note ' + BASE_NOTE[self._base_note] + ' to ' + scale.name)
        self.current_scale_to_display()

    def current_scale_to_display(self):
        scale = SCALES[self.current_scale_index]
        text = scale.name + ' ' + BASE_NOTE[self._base_note] + str(scale.to_octave(self._octave))
        self.send_to_display(text)

    def inc_scale(self, inc):
        nr_of_scales = len(SCALES)
        newindex = self.current_scale_index + inc
        if newindex < 0:
            newindex = 0
        elif newindex >= nr_of_scales:
            newindex = nr_of_scales - 1
        else:
            self.current_scale_index += inc
        newscale = SCALES[self.current_scale_index]
        self.show_message(' PAD Scale ' + newscale.name + ' ' + BASE_NOTE[self._base_note] + str(newscale.to_octave(self._octave) - 2))
        self.current_scale_to_display()

    def update_transpose(self):
        self.assign_transpose(SCALES[self.current_scale_index])
        self._set_suppress_rebuild_requests(True)
        self.request_rebuild_midi_map()
        self._set_suppress_rebuild_requests(False)

    def set_scale(self, scale):
        raise isinstance(scale, PadScale) or AssertionError
        scale_len = len(scale.notevalues)
        octave = scale.to_octave(self._octave)

    def assign_transpose(self, scale):
        raise isinstance(scale, PadScale) or AssertionError
        scale_len = len(scale.notevalues)
        octave = scale.to_octave(self._octave)
        last_note_val = None
        for note_index in range(16):
            button = self._button_sequence[note_index]
            scale_index = note_index % scale_len
            octave_offset = note_index / scale_len
            note_value = scale.notevalues[scale_index] + self._base_note + octave * 12 + octave_offset * 12
            if note_value < 128:
                last_note_val = note_value
            elif last_note_val != None:
                note_value = last_note_val
            button.set_send_note(note_value)
            if self._mode == PAD_MODE:
                button.send_color_direct(self.get_color_by_note_mode(note_value, False))

    def do_reset(self, value):
        if value == 0:
            return
        for row in range(4):
            for column in range(4):
                button = self._matrix[row][column]
                data_byte1 = button._original_identifier
                button.send_midi((MIDI_CC_STATUS + 2, data_byte1, 0))

    def do_test(self, value):
        color = self.togglecolor[self.toggleindex]
        self.toggleindex = (self.toggleindex + 1) % len(self.togglecolor)
        if value == 0:
            return
        for row in range(4):
            for column in range(4):
                button = self._matrix[row][column]
                self.schedule_message(1, self.dosend, (color,
                 127,
                 127,
                 row,
                 column))

    def dosend(self, parm = None):
        button = self._matrix[parm[3]][parm[4]]
        data_byte1 = button._original_identifier
        button.send_midi((MIDI_CC_STATUS + 0, data_byte1, parm[0]))
        button.send_midi((MIDI_CC_STATUS + 1, data_byte1, parm[1]))
        button.send_midi((MIDI_CC_STATUS + 2, data_byte1, parm[2]))

    def do_note_repeat(self, value):
        nrvalue = 0
        if value != 0:
            nrvalue = 1
        self._c_ref.set_note_repeat_state(nrvalue)

    def _do_toggle_send(self, value):
        nr_of_tracks = len(self.song().return_tracks)
        if value == 0 or nr_of_tracks < 1:
            return
        prev = self.send_slider_index
        self.send_slider_index += 1
        if self.send_slider_index >= nr_of_tracks:
            self.send_slider_index = 0
        self.show_message(' Set Send ' + str(SENDS[self.send_slider_index]))
        if prev != self.send_slider_index:
            for track in range(8):
                strip = self._mixer.channel_strip(track)
                slider_list = []
                for index in range(self.send_slider_index + 1):
                    if index < self.send_slider_index - 1:
                        slider_list.append(None)
                    else:
                        slider_list.append(self.send_sliders[track])
                    strip.set_send_controls(tuple(slider_list))

    def _do_armsolo_mode(self, value):
        if not self._armsolomode_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self._scenematrix.set_armsolo_exclusive(self._armsolomode_button)

    def _do_fire_button(self, value):
        if not self._fire_button != None:
            raise AssertionError
            if not value in range(128):
                raise AssertionError
                clip_slot = (value != 0 or not self._mute_button.is_momentary()) and self.song().view.highlighted_clip_slot
                clip_slot and clip_slot.fire()

    def _do_undo(self, value):
        if not self._undo_button != None:
            raise AssertionError
            if not value in range(128):
                raise AssertionError
                (value != 0 or not self._undo_button.is_momentary()) and self.song().can_undo == 1 and self.song().undo()
                self.show_message(str('UNDO'))

    def _do_redo(self, value):
        if not self._redo_button != None:
            raise AssertionError
            if not value in range(128):
                raise AssertionError
                (value != 0 or not self._redo_button.is_momentary()) and self.song().can_redo == 1 and self.song().redo()
                self.show_message(str('REDO'))

    def _a_trk_left(self, value):
        if not value in range(128):
            raise AssertionError
            if value != 0:
                direction = self.application().view.is_view_visible('Session') and Live.Application.Application.View.NavDirection.left
                self.application().view.scroll_view(direction, 'Session', True)

    def _a_trk_right(self, value):
        if not value in range(128):
            raise AssertionError
            if value != 0:
                direction = self.application().view.is_view_visible('Session') and Live.Application.Application.View.NavDirection.right
                self.application().view.scroll_view(direction, 'Session', True)

    def _nav_value_left(self, value):
        if not self._device_nav_button_left != None:
            raise AssertionError
            if not value in range(128):
                raise AssertionError
                modifier_pressed = True
                value > 0 and (not self.application().view.is_view_visible('Detail') or not self.application().view.is_view_visible('Detail/DeviceChain')) and self.application().view.show_view('Detail')
                self.application().view.show_view('Detail/DeviceChain')
            else:
                direction = Live.Application.Application.View.NavDirection.left
                self.application().view.scroll_view(direction, 'Detail/DeviceChain', not modifier_pressed)

    def _nav_value_right(self, value):
        if not self._device_nav_button_right != None:
            raise AssertionError
            if not value in range(128):
                raise AssertionError
                modifier_pressed = value > 0 and True
                (not self.application().view.is_view_visible('Detail') or not self.application().view.is_view_visible('Detail/DeviceChain')) and self.application().view.show_view('Detail')
                self.application().view.show_view('Detail/DeviceChain')
            else:
                direction = Live.Application.Application.View.NavDirection.right
                self.application().view.scroll_view(direction, 'Detail/DeviceChain', not modifier_pressed)

    def _do_tap_tempo(self, value):
        if not value in range(128):
            raise AssertionError
            value > 0 and self.song().tap_tempo()

    def _do_toggle_cue(self, value):
        if not value in range(128):
            raise AssertionError
            value > 0 and self.song().set_or_delete_cue()

    def _do_toggle_prev_cue(self, value):
        if not value in range(128):
            raise AssertionError
            value > 0 and self.song().jump_to_prev_cue()

    def _do_toggle_next_cue(self, value):
        if not value in range(128):
            raise AssertionError
            value > 0 and self.song().jump_to_next_cue()

    def focus_changed(self):
        pass

    def _handle_device_changed(self, device):
        pass

    def _hande_device_parm_changed(self):
        pass

    def _do_focus_navigate(self, value):
        if not self._navigate_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            self.nav_index = value != 0 and (self.nav_index + 1) % len(VIEWS_ALL)
            self.application().view.focus_view(VIEWS_ALL[self.nav_index])
            self.show_message('Focus on : ' + str(VIEWS_ALL[self.nav_index]))

    def scroll_focus(self, delta):
        if delta == 1:
            self.nav_index = (self.nav_index + 1) % len(VIEWS_ALL)
        elif self.nav_index == 0:
            self.nav_index = len(VIEWS_ALL) - 1
        else:
            self.nav_index -= 1
        self.show_message('Focus on : ' + str(VIEWS_ALL[self.nav_index]))
        self.application().view.focus_view(VIEWS_ALL[self.nav_index])

    def scroll_device(self, delta):
        if not (delta == 1 or delta == -1):
            raise AssertionError
            direction = delta == 1 and Live.Application.Application.View.NavDirection.right
        else:
            direction = Live.Application.Application.View.NavDirection.left
        self.application().view.scroll_view(direction, 'Detail/DeviceChain', True)

    def scroll_scene(self, delta):
        if not self.track_left_button != None:
            raise AssertionError
            raise delta == 1 or delta == -1 or AssertionError
            direction = delta == 1 and Live.Application.Application.View.NavDirection.down
        else:
            direction = Live.Application.Application.View.NavDirection.up
        self.application().view.scroll_view(direction, 'Session', True)

    def index_in_strip(self, track):
        for ind in range(len(self._mixer._channel_strips)):
            strack = self._mixer._channel_strips[ind]._track
            if strack == track:
                return ind

        return -1

    def notify_track_scroll(self):
        self._scenematrix.update_control_selection()
        if self._mode == CONTROL_MODE:
            self._scenematrix.eval_matrix()
            self._scenematrix.fire_values()

    def send_to_display(self, text, grid = 0):
        if USE_DISPLAY == False:
            return
        if len(text) > 28:
            text = text[:27]
        msgsysex = [240,
         0,
         0,
         102,
         23,
         18,
         min(grid, 3) * 28]
        filled = text.ljust(28)
        for c in filled:
            msgsysex.append(ord(c))

        msgsysex.append(247)
        self._send_midi(tuple(msgsysex))

    def send_color(self, button, hue, sat, bright):
        raise isinstance(button, ButtonElement) or AssertionError
        raise hue in range(128) or AssertionError
        raise sat in range(128) or AssertionError
        raise bright in range(128) or AssertionError
        data_byte1 = button._original_identifier
        button.send_midi((MIDI_CC_STATUS + 2, data_byte1, bright))
        button.send_midi((MIDI_CC_STATUS + 1, data_byte1, sat))
        button.send_midi((MIDI_CC_STATUS + 0, data_byte1, hue))

    def turn_off_matrix(self):
        for row in range(4):
            for column in range(4):
                button = self._matrix[row][column]
                self.send_color(button, 2, 0, 0)
                button.set_to_notemode(False)

    def remove_listener(self, control, callback):
        if control != None and control.value_has_listener(callback):
            control.remove_value_listener(callback)
        control.disconnect()

    def disconnect(self):
        self.turn_off_matrix()
        self.scene_mode_button.send_value(0, True)
        self.clip_mode_button.send_value(0, True)
        self.pad_mode_button.send_value(0, True)
        self.control_mode_button.send_value(0, True)
        time.sleep(0.2)
        self._active = False
        self._suppress_send_midi = True
        self.remove_listener(self.scene_mode_button, self._a_mode_scene)
        self.remove_listener(self.clip_mode_button, self._a_mode_clip)
        self.remove_listener(self.pad_mode_button, self._a_mode_pad)
        self.remove_listener(self.control_mode_button, self._a_mode_control)
        self.remove_listener(self._undo_button, self._do_undo)
        self.remove_listener(self._redo_button, self._do_redo)
        self.remove_listener(self._armsolomode_button, self._do_armsolo_mode)
        self.remove_listener(self.xfade_assign_button, self._do_xfade_assign)
        self.remove_listener(self._fire_button, self._do_fire_button)
        self._session.remove_offset_listener(self.notify_track_scroll)
        self._mixer.disconnect()
        ControlSurface.disconnect(self)
 def _setup_transport(self):
     is_momentary = True
     transport = TransportComponent()
     playButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 108)
     stopButton = StateButton(not is_momentary, MIDI_CC_TYPE, 0, 110)
     recordButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 109)
     overdubButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 107)
     metrononmeButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 104)
     playButton.name = 'Play'
     stopButton.name = 'Stop'
     recordButton.name = 'Record'
     overdubButton.name = 'Overdub'
     metrononmeButton.name = 'Metronome'
     transport.set_play_button(playButton)
     transport.set_stop_button(stopButton)
     transport.set_record_button(recordButton)
     transport.set_overdub_button(overdubButton)
     transport.set_metronome_button(metrononmeButton)
     transport.set_nudge_buttons(StateButton(is_momentary, MIDI_CC_TYPE, 1, 51), StateButton(is_momentary, MIDI_CC_TYPE, 1, 50))
     punchinbutton = ToggleButton(MIDI_CC_TYPE, 1, 52)
     punchoutbutton = ToggleButton(MIDI_CC_TYPE, 1, 53)
     punchinbutton.name = 'Punch In'
     punchoutbutton.name = 'Punch Out'
     transport.set_punch_buttons(punchinbutton, punchoutbutton)
     transport.set_loop_button(StateButton(is_momentary, MIDI_CC_TYPE, 1, 54))
     self.transp_ff_button = ButtonElement(True, MIDI_CC_TYPE, 1, 59)
     self.transp_rw_button = ButtonElement(True, MIDI_CC_TYPE, 1, 58)
     transport.set_seek_buttons(self.transp_ff_button, self.transp_rw_button)
     self.xfadeKnob = SliderElement(MIDI_CC_TYPE, 1, 100)
     self.xfadeKnob.connect_to(self.song().master_track.mixer_device.crossfader)
     self.tap_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 88)
     self.tap_button.add_value_listener(self._do_tap_tempo)
     self.cue_add_delete_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 55)
     self.cue_prev_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 56)
     self.cue_next_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 57)
     self.cue_add_delete_button.add_value_listener(self._do_toggle_cue)
     self.cue_prev_button.add_value_listener(self._do_toggle_prev_cue)
     self.cue_next_button.add_value_listener(self._do_toggle_next_cue)
Example #27
0
class EncoderView(CompoundComponent):
    __module__ = __name__
    __mode = ENC_MODE_VOL
    __prev_mode = None
    __session = None
    __encoders = None
    __buttons = None
    __send_offset = 0
    __last_non_step_mode = ENC_MODE_VOL
    __track = None
    __device = None
    __selection_map = {}
    __nr_of_banks = 0
    __bank_index = 0
    __param_list = None
    __plugin_down = False

    def __init__(self, *a, **k):
        super(EncoderView, self).__init__(*a, **k)
        self.__session = self.canonical_parent.get_session()
        self.__encoders = [self.create_encoders(index) for index in range(8)]
        self.__buttons = [self.create_button(index) for index in range(8)]
        self._return_tracks_change.subject = self.song()
        self._tracks_change.subject = self.song()
        self._handle_track_changed.subject = self.song().view
        self._handle_visble_tracks_changed.subject = self.song()
        self._left_button = StateButton(True, MIDI_CC_TYPE,
                                        BASE_CONTROL_CHANNEL,
                                        CC_LEFT_PAGE_BUTTON)
        self._right_button = StateButton(True, MIDI_CC_TYPE,
                                         BASE_CONTROL_CHANNEL,
                                         CC_RIGHT_PAGE_BUTTON)
        self._mixer_button = StateButton(True, MIDI_CC_TYPE,
                                         BASE_CONTROL_CHANNEL, CC_MIXER_BUTTON)
        self._plugin_button = StateButton(True, MIDI_CC_TYPE,
                                          BASE_CONTROL_CHANNEL,
                                          CC_PLUGIN_BUTTON)
        self._handle_left.subject = self._left_button
        self._handle_right.subject = self._right_button
        self._handle_mixer_button.subject = self._mixer_button
        self._handle_plugin_button.subject = self._plugin_button

    def connect(self):
        self.__assign_encoders()
        self.setup_select_track()
        self.light_mode()

    def light_mode(self):
        self._mixer_button.send_value(
            (self.__mode == ENC_MODE_VOL or self.__mode == ENC_MODE_SENDS)
            and 127 or 0)
        self._plugin_button.send_value(self.__mode == ENC_MODE_DEVICE and 127
                                       or 0)

    def create_encoders(self, index):
        touch = TouchButton(MIDI_CC_TYPE,
                            0,
                            10 + index,
                            name='Touch_Tap' + str(index + 1) + '_Control')
        touch.cindex = index
        slider = Encoder(MIDI_CC_TYPE,
                         0,
                         70 + index,
                         index,
                         self,
                         name='Encoder' + str(index + 1) + '_Control')
        slider.cindex = index
        return EncoderHandler(index, slider, touch, self)

    def create_button(self, index):
        button = StateButton(True,
                             MIDI_CC_TYPE,
                             BASE_CONTROL_CHANNEL,
                             22 + index,
                             name='Page_Button_' + str(index + 1) + '_Control')
        button.cindex = index
        return ButtonHandler(index, button, self)

    def gettrack(self, index, off):
        tracks = self.song().visible_tracks
        if index + off < len(tracks):
            return tracks[index + off]
        return

    def notify_shift(self, value):
        pass

    def notify_touch(self, parameter):
        pass

    def is_shift_down(self):
        return self.canonical_parent.is_shift_down()

    def is_modifier_down(self):
        return self.canonical_parent.is_shift_down()

    def set_led_value(self, index, value):
        pass

    def reset_led(self):
        pass

    def navigate(self,
                 nav_dir,
                 modifier,
                 alt_modifier=False,
                 nav_src=NAV_SRC_ENCODER):
        pass

    def handle_offset_changed(self):
        if self.__mode == ENC_MODE_VOL or self.__mode == ENC_MODE_SENDS:
            self.__assign_encoders(False)

    def setup_select_track(self):
        if self.__track:
            self._handle_device_changed.subject = None
            self._handle_devices_changed.subject = None
        self.__track = self.song().view.selected_track
        if self.__track:
            self.__device = self.__track.view.selected_device
            self._handle_device_changed.subject = self.__track.view
            self._handle_devices_changed.subject = self.__track
            self._handle_device_changed(True)
            if self.__device:
                self._handle_parameters_changed.subject = self.__device
            else:
                self._handle_parameters_changed.subject = None
        return

    def refresh_state(self):
        for encoder in self.__encoders:
            encoder.refresh()

        for button in self.__buttons:
            button.refresh()

        self.update_display(None, True)
        return

    @subject_slot('value')
    def _handle_mixer_button(self, value):
        if value == 0:
            return
        if self.__mode == ENC_MODE_VOL:
            self.__mode = ENC_MODE_SENDS
            self.__assign_encoders()
            self.light_mode()
        else:
            if self.__mode == ENC_MODE_SENDS or self.__mode == ENC_MODE_DEVICE:
                self.__mode = ENC_MODE_VOL
                self.__assign_encoders()
                self.light_mode()

    @subject_slot('value')
    def _handle_plugin_button(self, value):
        self.__plugin_down = value > 0
        if value == 0:
            return
        if self.__mode == ENC_MODE_VOL or self.__mode == ENC_MODE_SENDS:
            self.__mode = ENC_MODE_DEVICE
            self.__assign_encoders()
            self.light_mode()
            self.application().view.show_view('Detail/DeviceChain')

    @subject_slot('value')
    def _handle_left(self, value):
        self._left_button.send_value(value)
        if value:
            if self.canonical_parent.is_shift_down():
                if self.__mode == ENC_MODE_SENDS:
                    new_val = self.__send_offset - 1
                    if new_val >= 0:
                        self.__send_offset = new_val
                        self.__assign_encoders()
                elif self.__mode == ENC_MODE_DEVICE:
                    self.navigate_device(-1)
            elif self.__plugin_down and self.__mode == ENC_MODE_DEVICE:
                self.nav_device_param_banks(-1)
            else:
                self.canonical_parent.get_session().bank_left(1)
                self.__assign_encoders()

    @subject_slot('value')
    def _handle_right(self, value):
        self._left_button.send_value(value)
        if value:
            if self.canonical_parent.is_shift_down():
                if self.__mode == ENC_MODE_SENDS:
                    nr_of_ret_tracks = len(self.song().return_tracks)
                    new_val = self.__send_offset + 1
                    if new_val < nr_of_ret_tracks:
                        self.__send_offset = new_val
                        self.__assign_encoders()
                elif self.__mode == ENC_MODE_DEVICE:
                    self.navigate_device(1)
            elif self.__plugin_down and self.__mode == ENC_MODE_DEVICE:
                self.nav_device_param_banks(1)
            else:
                self.canonical_parent.get_session().bank_right(1)
                self.__assign_encoders()

    @subject_slot('return_tracks')
    def _return_tracks_change(self):
        if self.__send_offset >= len(self.song().return_tracks):
            if self.__mode == ENC_MODE_SENDS:
                self.__assign_encoders()

    @subject_slot('tracks')
    def _tracks_change(self):
        self.__assign_encoders(False)
        self._cleanup_mapping()

    @subject_slot('selected_track')
    def _handle_track_changed(self):
        self.setup_select_track()

    @subject_slot('devices')
    def _handle_devices_changed(self):
        if self.__device != self.__track.view.selected_device:
            self._handle_device_changed()

    @subject_slot('selected_device')
    def _handle_device_changed(self, force_change=False):
        self.__device = self.__track.view.selected_device
        if self.__device:
            self._handle_parameters_changed.subject = self.__device
            self._hande_device_name_changed.subject = self.__device
        else:
            self._handle_parameters_changed.subject = None
            self._hande_device_name_changed.subject = None
        if self.__mode == ENC_MODE_DEVICE:
            if not self.__device:
                self.__device = self._choose_device()
                if self.__device:
                    self.song().view.select_device(self.__device)
            self.__bank_index = self._get_stored_bank_index()
            self.__assign_encoders()
            self.update_display()
        return

    @subject_slot('name')
    def _hande_device_name_changed(self):
        if self.__mode == ENC_MODE_DEVICE:
            self.update_display()

    def _choose_device(self):
        device_list = self.__track.devices
        if not device_list or len(device_list) == 0:
            return
        if len(device_list) > 0:
            return device_list[0]
        return

    @subject_slot('parameters')
    def _handle_parameters_changed(self):
        if self.__mode == ENC_MODE_DEVICE:
            self.__assign_encoders(False)

    @subject_slot('visible_tracks')
    def _handle_visble_tracks_changed(self):
        self.__assign_encoders(False)
        self._cleanup_mapping()
        if self.__mode == ENC_MODE_VOL:
            self.refresh_state()

    def _cleanup_mapping(self):
        tracks = self.song().visible_tracks
        cmaps = {}
        keys = self.__selection_map.keys()
        for track in tracks:
            cmaps[track] = True

        for track in keys:
            if track not in cmaps and track in self.__selection_map:
                del self.__selection_map[track]

    def get_param_list_left(self):
        result = ''
        if self.__param_list:
            for i in xrange(0, 4):
                namestr = i < len(self.__param_list) and self.__param_list[
                    i] != EMPTY_PARAM and self.__param_list[i][0].name or ''
                if i < len(self.__param_list) - 1 and i < 3:
                    result += to_display_name(namestr) + '|'
                else:
                    result += to_display_name(namestr)

        return result

    def get_param_list_right(self):
        result = ''
        if self.__param_list:
            for i in xrange(4, 8):
                namestr = i < len(self.__param_list) and self.__param_list[
                    i] != EMPTY_PARAM and self.__param_list[i][0].name or ''
                if i < len(self.__param_list) - 1 and i < 7:
                    result += to_display_name(namestr) + '|'
                else:
                    result += to_display_name(namestr)

        return result

    def get_track_names(self):
        trackoff = self.__session.track_offset()
        track_names = ''
        for i in range(4):
            track = self.gettrack(i, trackoff)
            if self.__mode in (ENC_MODE_VOL, ENC_MODE_PAN, ENC_MODE_SENDS):
                if track is None:
                    track_names += '  --  '
                else:
                    track_names += to_display_name(track.name)
            if i < 3:
                track_names += '|'

        return track_names

    def update_track_names(self):
        if self.__mode in (ENC_MODE_VOL, ENC_MODE_SENDS, ENC_MODE_PAN):
            track_names = self.get_track_names()
            self.canonical_parent.send_to_display(track_names, 2)
            self.canonical_parent.send_to_display(track_names, 3)

    def update_display(self, track_names=None, force=False):
        if self.__mode == ENC_MODE_VOL:
            self.canonical_parent.send_to_display('Level & Mute ', 0, force)
            self.canonical_parent.send_to_display('Pan', 1, force)
            names = track_names is not None and track_names or self.get_track_names(
            )
            self.canonical_parent.send_to_display(names, 2, force)
            self.canonical_parent.send_to_display(names, 3, force)
        else:
            if self.__mode == ENC_MODE_SENDS:
                nr_of_ret_tracks = len(self.song().return_tracks)
                if self.__send_offset < nr_of_ret_tracks:
                    self.canonical_parent.send_to_display(
                        'Sends ' + SENDS[self.__send_offset] + ' & Mute ', 0,
                        force)
                else:
                    self.canonical_parent.send_to_display(
                        'Sends - & Mute ', 0, force)
                if self.__send_offset + 1 < nr_of_ret_tracks:
                    self.canonical_parent.send_to_display(
                        'Sends ' + SENDS[self.__send_offset + 1] + '', 1,
                        force)
                else:
                    self.canonical_parent.send_to_display('', 1, force)
                names = track_names is not None and track_names or self.get_track_names(
                )
                self.canonical_parent.send_to_display(names, 2, force)
                self.canonical_parent.send_to_display(names, 3, force)
            else:
                if self.__mode == ENC_MODE_DEVICE:
                    if self.__device:
                        self.canonical_parent.send_to_display(
                            self.__device.class_name + ':' +
                            filter_umls(self.__device.name), 0, force)
                        self.canonical_parent.send_to_display('', 1, force)
                        self.canonical_parent.send_to_display(
                            self.get_param_list_left(), 2, force)
                        self.canonical_parent.send_to_display(
                            self.get_param_list_right(), 3, force)
                    else:
                        self.canonical_parent.send_to_display(
                            '<No Device>', 0, force)
                        self.canonical_parent.send_to_display('', 1, force)
                        self.canonical_parent.send_to_display('', 2, force)
                        self.canonical_parent.send_to_display('', 3, force)
        return

    def __assign_encoders(self, show_message=True):
        trackoff = self.__session.track_offset()
        if self.__mode in (ENC_MODE_VOL, ENC_MODE_PAN, ENC_MODE_SENDS):
            for i in range(4):
                left_enc = self.__encoders[i]
                right_enc = self.__encoders[i + 4]
                left_button = self.__buttons[i]
                track = self.gettrack(i, trackoff)
                if track is None:
                    left_enc.assign_parameter(None)
                    right_enc.assign_parameter(None)
                    left_button.deassign_track()
                else:
                    left_button.assign_mute_track(track)
                    if self.__mode == ENC_MODE_VOL:
                        left_enc.assign_parameter(track.mixer_device.volume,
                                                  track, True)
                        right_enc.assign_parameter(track.mixer_device.panning,
                                                   track, True)
                    if self.__mode == ENC_MODE_SENDS:
                        nr_of_ret_tracks = len(self.song().return_tracks)
                        if self.__send_offset < nr_of_ret_tracks:
                            left_enc.assign_parameter(
                                track.mixer_device.sends[self.__send_offset],
                                track, True)
                        else:
                            left_enc.assign_parameter(None)
                        if self.__send_offset + 1 < nr_of_ret_tracks:
                            right_enc.assign_parameter(
                                track.mixer_device.sends[self.__send_offset +
                                                         1], track, True)
                        else:
                            right_enc.assign_parameter(None)

        else:
            if self.__mode == ENC_MODE_DEVICE:
                paramlist = self.get_device_parameter()
                self.__param_list = paramlist
                if paramlist:
                    for i in range(8):
                        enc = self.__encoders[i]
                        parm = i < len(paramlist) and paramlist[i] or None
                        if not parm or parm == EMPTY_PARAM:
                            enc.assign_parameter(None)
                        else:
                            enc.assign_parameter(parm[0], False)

                else:
                    for i in range(8):
                        enc = self.__encoders[i]
                        enc.assign_parameter(None)

        self.update_display()
        return

    def get_device_parameter(self):
        mapping = None
        if self.__device:
            parmlist = []
            params = self.__device.parameters
            if self.__device.class_name in DEVICE_MAP:
                mappingObj = DEVICE_MAP[self.__device.class_name]
                if isinstance(mappingObj, tuple):
                    mapping = mappingObj
                elif 'params' in mappingObj:
                    mapping = mappingObj['params']
            if mapping != None:
                self.__nr_of_banks = len(mapping)
                bank_mapping = mapping[self.__bank_index]
                for idx in xrange(0, 8):
                    if bank_mapping != None and idx < len(
                            bank_mapping) and bank_mapping[idx] != None:
                        mp = bank_mapping[idx]
                        if isinstance(mp, tuple):
                            mp_len = len(mp)
                            if mp_len == 4:
                                parmlist.append(
                                    (self.__device.parameters[mp[0]], mp[1],
                                     mp[2], mp[3]))
                            elif mp_len == 3:
                                parmlist.append(
                                    (self.__device.parameters[mp[0]], mp[1],
                                     mp[2]))
                            elif mp_len == 2:
                                parmlist.append(
                                    (self.__device.parameters[mp[0]], mp[1],
                                     None, None))
                        else:
                            parmlist.append(EMPTY_PARAM)

                return parmlist
            self.__nr_of_banks = max(0, len(params) - 2) / 8 + 1
            for i in xrange(0, 8):
                idx = self.__bank_index * 8 + i + 1
                if idx < len(self.__device.parameters):
                    parmlist.append(
                        (self.__device.parameters[idx], DEF_NAME, None))

            return parmlist
        else:
            return
        return

    def _get_selmap(self):
        if self.__track not in self.__selection_map:
            self.__selection_map[self.__track] = TrackDeviceSelection(
                self.__track)
        return self.__selection_map[self.__track]

    def _get_stored_bank_index(self):
        if not self.__device:
            return 0
        selmap = self._get_selmap()
        if selmap:
            return selmap.get_bank_index(self.__device)
        return 0

    def nav_device_param_banks(self, nav_dir):
        prev = self.__bank_index
        newpos = min(max(0, prev + nav_dir), self.__nr_of_banks - 1)
        if newpos != prev:
            self.__bank_index = newpos
            selmap = self._get_selmap()
            if selmap:
                selmap.register_selected_bank(self.__device, self.__bank_index)
            self.__assign_encoders(True)

    def navigate_device(self, nav_dir):
        device_list = self.__track.devices
        if self.__device and isinstance(self.__device.canonical_parent,
                                        Live.Chain.Chain):
            device_list = self.__device.canonical_parent.devices
        self.__do_device_nav(vindexof(device_list, self.__device), device_list,
                             nav_dir)

    def __do_device_nav(self, index, device_list, nav_dir):
        if index != None and len(device_list) > 1:
            newvalue = min(max(0, index + nav_dir), len(device_list) - 1)
            if newvalue != index:
                self.song().view.select_device(device_list[newvalue])
        return
class Mk2KnobControl:
    __module__ = __name__
    __doc__ = "Mk2 Module for Controlling Parameters with Master Knob"

    def __init__(self, parent):
        self._parent = parent
        self.master_track = parent.song().master_track
        self.volume_button = None
        self._set_volume_button(StateButton(True, MIDI_CC_TYPE, 3, 110))
        self.the_slider = SliderElement(MIDI_CC_TYPE, 1, 86)
        self.the_slider.add_value_listener(self._do_main_slider, True)
        self.xfade_button = None
        self._set_xfade_button(StateButton(True, MIDI_CC_TYPE, 3, 116))
        self.swing_button = None
        self._set_swing_button(StateButton(True, MIDI_CC_TYPE, 3, 111))
        self.mode = KN2_MODE_VOLUME
        self.previous_mode = -1
        self.tempo_button = None
        self._set_tempo_button(StateButton(True, MIDI_CC_TYPE, 3, 112))
        self.push_button = None
        self._set_push_button(StateButton(True, MIDI_CC_TYPE, 1, 87))
        self.clipn_v_button = None
        self.clipn_h_button = None
        self._set_clipn_h_button(StateButton(True, MIDI_CC_TYPE, 3, 114))
        self._set_clipn_v_button(StateButton(True, MIDI_CC_TYPE, 3, 115))
        self.toggle_buttons = [
            self.volume_button,
            self.xfade_button,
            self.swing_button,
            self.tempo_button,
            self.clipn_h_button,
            self.clipn_v_button,
        ]
        self.shift_button = None
        self._set_shift_button(StateButton(True, MIDI_CC_TYPE, 3, 113))
        self.shift_on = False
        self.scroll_mod_left_button = None
        self.scroll_mod_right_button = None
        self._set_scroll_mod_left_button(StateButton(True, MIDI_CC_TYPE, 0, 105))
        self._set_scroll_mod_right_button(StateButton(True, MIDI_CC_TYPE, 0, 106))
        self._prev_mode = KN2_MODE_VOLUME
        self.lrmode = LR_CONTROL_CLIP
        self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143
        self.loop_div_index = 0
        self.loop_incdex = 4.0
        self.arrow_mode_button = ColorButton(True, MIDI_CC_TYPE, 30)
        self.arrow_mode_button.add_value_listener(self.toggle_arrow_mode)
        self.arrow_mode_button.send_color(LR_MODE_HUES[self.lrmode])
        self.arrow_mode_button.send_value(127, True)
        self.navflags = 0
        self.octave_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 70)
        self.octave_mod_button.add_value_listener(self._action_octave)
        self.scale_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 71)
        self.scale_mod_button.add_value_listener(self._action_scale)
        self.basenote_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 72)
        self.basenote_mod_button.add_value_listener(self._action_base_note)
        self.keycolor_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 73)
        self.keycolor_mod_button.add_value_listener(self._action_key_color)
        self.pad_to_mainknob_mode = 0
        self._measure_left_click = 0
        self._measure_right_click = 0
        self.mode_assign_map = {
            KN2_MODE_VOLUME: (self.chg_volume, 0, "Master Knob controls MASTER Volume", KN2_MODE_CUE),
            KN2_MODE_CUE: (self.chg_cue, 0, "Master Knob controls Cue Level", KN2_MODE_VOLUME),
            KN2_MODE_TEMPO_COARSE: (self.chg_tempo, 3, "Master Knob controls TEMPO Coarse", KN2_MODE_TEMPO_FINE),
            KN2_MODE_TEMPO_FINE: (self.chg_tempo_fine, 3, "Master Knob controls TEMPO Fine", KN2_MODE_TEMPO_COARSE),
            KN2_MODE_XFADE: (self.chg_xfade, 1, "Master Knob controls Crossfader", -1),
            KN2_MODE_QUANT: (self.chg_quant, 2, "Master Knob controls Recording Quantize", KN2_MODE_CLIP_QUANT),
            KN2_MODE_CLIP_QUANT: (self.chg_clip_q, 2, "Master Knob controls Clip Start Quantize", KN2_MODE_QUANT),
            KN2_MODE_CLIPN_HOR: (self.nav_c_hor, 4, "Master Knob Clip View horizontally", -1),
            KN2_MODE_CLIPN_VER: (self.nav_c_ver, 5, "Master Knob Clip View vertically", -1),
            KN2_MODE_GENERAL: (self.chg_general, -1, None, -1),
            KN2_P_SCALES: (self.modify_pad_scaling, -1, None, -1),
        }

    def start_up(self):
        self._set_mode(KN2_MODE_VOLUME)
        self.arrow_mode_button.send_value(127, True)

    def toggle_arrow_mode(self, value):
        if value > 0:
            self.lrmode = (self.lrmode + 1) % 4
            self.arrow_mode_button.send_hue(LR_MODE_HUES[self.lrmode])
            self._parent.show_message(
                "Left/Right Buttons Control:    " + L_MODE_FUNCTION[self.lrmode] + " / " + R_MODE_FUNCTION[self.lrmode]
            )

    def switch_to_matrix_mode(self):
        if self.mode != KN2_MODE_GENERAL:
            self.previous_mode = self.mode
            self._set_mode(KN2_MODE_GENERAL)

    def exit_matrix_mode(self):
        if self.mode == KN2_MODE_GENERAL:
            self._set_mode(self.previous_mode)
            self.previous_mode = -1

    def update_shift(self):
        if self.shift_on:
            self.shift_button.send_value(127, True)
        else:
            self.shift_button.send_value(0, True)

    def _set_mode(self, mode):
        if not mode in range(11):
            raise AssertionError
            self.update_shift()
            if mode == self.mode:
                return
            self._prev_mode = mode
            self.mode = mode
            self.switch_radio_buttons(self.mode_assign_map[self.mode][1])
            message = self.mode_assign_map[self.mode][2]
            message != None and self._parent.show_message(message)

    def switch_radio_buttons(self, which):
        for index in range(len(self.toggle_buttons)):
            if index == which:
                self.toggle_buttons[index].send_value(127, True)
            else:
                self.toggle_buttons[index].send_value(0, True)

    def update(self):
        self.switch_radio_buttons(self.mode_assign_map[self.mode][1])
        self.arrow_mode_button.send_color(LR_MODE_HUES[self.lrmode])
        self.arrow_mode_button.send_value(127, True)

    def _do_main_slider(self, value, encoder):
        if not value in range(128):
            raise AssertionError
            if not isinstance(encoder, EncoderElement):
                raise AssertionError
                if value == 1:
                    delta = 1
                else:
                    delta = -1
                if self.pad_to_mainknob_mode != 0:
                    self.mode_assign_map[KN2_P_SCALES][0](delta)
                elif self.navflags == 0:
                    self.mode_assign_map[self.mode][0](delta)
                if self.lrmode == LR_CONTROL_CLIP:
                    self.navflags & LEFT_DOWN != 0 and self.nav_c_hor(delta)
                self.navflags & RIGHT_DOWN != 0 and self.nav_c_ver(delta)
        elif self.lrmode == LR_CONTROL_SEL:
            if self.navflags & LEFT_DOWN != 0:
                self.nav_track(delta)
            if self.navflags & RIGHT_DOWN != 0:
                self._parent.scroll_scene(delta)
        elif self.lrmode == LR_CONTROL_DEV:
            if self.navflags & LEFT_DOWN != 0:
                self.nav_track(delta)
            if self.navflags & RIGHT_DOWN != 0:
                self._parent.scroll_device(delta)
        elif self.lrmode == LR_CONTROL_LOOP:
            if self.navflags & LEFT_DOWN != 0:
                self.adjust_loop_start(delta)
            if self.navflags & RIGHT_DOWN != 0:
                self.adjust_loop_length(delta)

    def modify_pad_scaling(self, delta):
        if self.pad_to_mainknob_mode & PAD_KNOB_OCTAVE != 0:
            self._parent.inc_octave(delta)
        if self.pad_to_mainknob_mode & PAD_KNOB_SCALE != 0:
            self._parent.inc_scale(delta)
        if self.pad_to_mainknob_mode & PAD_KNOB_BASEN != 0:
            self._parent.inc_base_note(delta)
        self._parent.update_transpose()

    def adjust_loop_start(self, delta):
        loopval = self._parent.song().loop_start
        loopval += self.loop_incdex * delta
        if loopval < 0:
            loopval = 0
        elif loopval > 999:
            loopval = 999
        self._parent.song().loop_start = loopval

    def adjust_loop_length(self, delta):
        loopval = self._parent.song().loop_length
        loopval += self.loop_incdex * delta
        if loopval < self.loop_incdex:
            loopval = self.loop_incdex
        elif loopval > 999:
            loopval = 999
        self._parent.song().loop_length = loopval

    def chg_general(self, delta):
        self._parent._scenematrix.control_handler.mod_value(delta, self.shift_on)

    def nav_track(self, direction):
        if direction == 1:
            self._parent._a_trk_right(1)
        else:
            self._parent._a_trk_left(1)

    def nav_c_hor(self, direction):
        self._parent.move_view_horizontal(direction)

    def nav_c_ver(self, direction):
        if direction == 1:
            self._parent._session.bank_up()
        else:
            self._parent._session.bank_down()

    def chg_volume(self, diff):
        if self.shift_on:
            self.repeat(self.master_track.mixer_device.volume, diff)
        else:
            self.master_track.mixer_device.volume.value = self.calc_new_parm(
                self.master_track.mixer_device.volume, diff
            )

    def chg_xfade(self, diff):
        if self.shift_on:
            self.repeat(self.master_track.mixer_device.crossfader, diff)
        else:
            self.master_track.mixer_device.crossfader.value = self.calc_new_parm(
                self.master_track.mixer_device.crossfader, diff
            )

    def chg_cue(self, diff):
        if self.shift_on:
            self.repeat(self.master_track.mixer_device.cue_volume, diff)
        else:
            self.master_track.mixer_device.cue_volume.value = self.calc_new_parm(
                self.master_track.mixer_device.cue_volume, diff
            )

    def repeat(self, parm, delta):
        count = 0
        while count < SHIFT_INC:
            parm.value = self.calc_new_parm(parm, delta)
            count += 1

    def calc_new_parm(self, parm, delta):
        parm_range = parm.max - parm.min
        int_val = int((parm.value - parm.min) / parm_range * PARM_RANGE + 0.1)
        inc_val = min(PARM_RANGE, max(0, int_val + delta))
        return float(inc_val) / float(PARM_RANGE) * parm_range + parm.min

    def chg_quant(self, diff):
        rec_quant = self._parent.song().midi_recording_quantization
        index = self.get_quant_index(rec_quant)
        new_index = index + diff
        if new_index >= 0 and new_index < len(QUANT_CONST):
            self._parent.song().midi_recording_quantization = QUANT_CONST[new_index]
            self._parent.show_message(QUANT_DESCR[new_index])

    def chg_clip_q(self, diff):
        quant = self._parent.song().clip_trigger_quantization
        self._parent.song().clip_trigger_quantization = max(0, min(13, quant + diff))
        self._parent.show_message("Clip Quantize " + CLIQ_DESCR[self._parent.song().clip_trigger_quantization])

    def chg_tempo_fine(self, diff):
        if diff < 0:
            amount = -0.01
        else:
            amount = 0.01
        self.chg_tempo(amount)

    def chg_tempo(self, diff):
        self._parent.song().tempo = max(20, min(999, self._parent.song().tempo + diff))

    def get_quant_index(self, const):
        for index in range(len(QUANT_CONST)):
            if const == QUANT_CONST[index]:
                return index

        return -1

    def _action_octave(self, value):
        if value != 0:
            self.pad_to_mainknob_mode |= PAD_KNOB_OCTAVE
        else:
            self.pad_to_mainknob_mode &= ~PAD_KNOB_OCTAVE

    def _action_scale(self, value):
        if value != 0:
            self.pad_to_mainknob_mode |= PAD_KNOB_SCALE
        else:
            self.pad_to_mainknob_mode &= ~PAD_KNOB_SCALE

    def _action_base_note(self, value):
        if value != 0:
            self.pad_to_mainknob_mode |= PAD_KNOB_BASEN
        else:
            self.pad_to_mainknob_mode &= ~PAD_KNOB_BASEN

    def _action_key_color(self, value):
        if value != 0:
            self._parent.step_key_color_mode()

    def _set_volume_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.volume_button != None:
                self.volume_button.remove_value_listener(self._action_volume)
            self.volume_button = button
            self.volume_button != None and self.volume_button.add_value_listener(self._action_volume)

    def _action_volume(self, value):
        if not self.volume_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_VOLUME and self._set_mode(KN2_MODE_VOLUME)

    def _set_xfade_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.xfade_button != None:
                self.xfade_button.remove_value_listener(self._action_xfade)
            self.xfade_button = button
            self.xfade_button != None and self.xfade_button.add_value_listener(self._action_xfade)

    def _action_xfade(self, value):
        if not self.xfade_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_XFADE and self._set_mode(KN2_MODE_XFADE)

    def _set_swing_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.swing_button != None:
                self.swing_button.remove_value_listener(self._action_swing)
            self.swing_button = button
            self.swing_button != None and self.swing_button.add_value_listener(self._action_swing)

    def _action_swing(self, value):
        if not self.swing_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_QUANT and self._set_mode(KN2_MODE_QUANT)

    def _set_tempo_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.tempo_button != None:
                self.tempo_button.remove_value_listener(self._action_tempo)
            self.tempo_button = button
            self.tempo_button != None and self.tempo_button.add_value_listener(self._action_tempo)

    def _action_tempo(self, value):
        if not self.tempo_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_TEMPO_COARSE and self._set_mode(KN2_MODE_TEMPO_COARSE)

    def _set_clipn_h_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.clipn_h_button != None:
                self.clipn_h_button.remove_value_listener(self._action_clipnh)
            self.clipn_h_button = button
            self.clipn_h_button != None and self.clipn_h_button.add_value_listener(self._action_clipnh)

    def _action_clipnh(self, value):
        if not self.clipn_h_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_CLIPN_HOR and self._set_mode(KN2_MODE_CLIPN_HOR)

    def _set_clipn_v_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.clipn_v_button != None:
                self.clipn_v_button.remove_value_listener(self._action_clipnv)
            self.clipn_v_button = button
            self.clipn_v_button != None and self.clipn_v_button.add_value_listener(self._action_clipnv)

    def _action_clipnv(self, value):
        if not self.clipn_v_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_CLIPN_VER and self._set_mode(KN2_MODE_CLIPN_VER)

    def _set_shift_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.shift_button != None:
                self.shift_button.remove_value_listener(self._action_shift)
            self.shift_button = button
            self.shift_button != None and self.shift_button.add_value_listener(self._action_shift)

    def _action_shift(self, value):
        if not self.shift_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            self.shift_on = value != 0 and not self.shift_on
            self.update_shift()

    def _set_scroll_mod_left_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.scroll_mod_left_button != None:
                self.scroll_mod_left_button.remove_value_listener(self._action_scroll_left)
            self.scroll_mod_left_button = button
            self.scroll_mod_left_button != None and self.scroll_mod_left_button.add_value_listener(
                self._action_scroll_left
            )

    def _set_scroll_mod_right_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.scroll_mod_right_button != None:
                self.scroll_mod_right_button.remove_value_listener(self._action_scroll_right)
            self.scroll_mod_right_button = button
            self.scroll_mod_right_button != None and self.scroll_mod_right_button.add_value_listener(
                self._action_scroll_right
            )

    def _action_scroll_left(self, value):
        if not self.scroll_mod_left_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.scroll_mod_left_button.send_value(127, True)
            self.navflags |= LEFT_DOWN
            self._measure_left_click = int(round(time.time() * 1000))
        else:
            self.scroll_mod_left_button.send_value(0, True)
            self.navflags &= ~LEFT_DOWN
            clicktime = int(round(time.time() * 1000)) - self._measure_left_click
            if clicktime < CLICK_TIME:
                if self._parent._modifier_down:
                    self._parent.modify_track_offset(-1)
                elif self._parent._mode == PAD_MODE:
                    self._do_lr_as_scale_mode(-1)
                elif self._parent._mode == SCENE_MODE:
                    self._parent.modify_scene_offset(-1)
                elif self._parent._mode == CLIP_MODE:
                    self._parent.move_view_horizontal(-1)
                elif self._parent._mode == CONTROL_MODE:
                    self._parent.move_view_horizontal(-1)

    def _action_scroll_right(self, value):
        if not self.scroll_mod_right_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.scroll_mod_right_button.send_value(127, True)
            self.navflags |= RIGHT_DOWN
            self._measure_right_click = int(round(time.time() * 1000))
        else:
            self.scroll_mod_right_button.send_value(0, True)
            self.navflags &= ~RIGHT_DOWN
            clicktime = int(round(time.time() * 1000)) - self._measure_right_click
            if clicktime < CLICK_TIME:
                if self._parent._modifier_down:
                    self._parent.modify_track_offset(1)
                elif self._parent._mode == PAD_MODE:
                    self._do_lr_as_scale_mode(1)
                elif self._parent._mode == SCENE_MODE:
                    self._parent.modify_scene_offset(1)
                elif self._parent._mode == CLIP_MODE:
                    self._parent.move_view_horizontal(1)
                elif self._parent._mode == CONTROL_MODE:
                    self._parent.move_view_horizontal(1)

    def _do_lr_as_scale_mode(self, delta):
        if self.pad_to_mainknob_mode == PAD_KNOB_SCALE:
            self._parent.inc_scale(delta)
        elif self.pad_to_mainknob_mode == PAD_KNOB_BASEN:
            self._parent.inc_base_note(delta)
        else:
            self._parent.inc_octave(delta)
        self._parent.update_transpose()

    def _set_push_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.push_button != None:
                self.push_button.remove_value_listener(self._action_push)
            self.push_button = button
            self.push_button != None and self.push_button.add_value_listener(self._action_push)

    def _action_push(self, value):
        if not self.push_button != None:
            raise AssertionError
            if not value in range(128):
                raise AssertionError
                next_mode = self.mode_assign_map[self.mode][3]
                next_mode != -1 and self._set_mode(next_mode)
            self.loop_div_index = (
                self.lrmode == LR_CONTROL_LOOP
                and self.navflags != 0
                and (self.loop_div_index + 1) % len(LOOP_KNOB_DIVISION)
            )
            self._parent.show_message(
                "Loop Selection Granularity : " + str(LOOP_KNOB_DIVISION[self.loop_div_index]) + " beats "
            )
            self.loop_incdex = LOOP_KNOB_DIVISION[self.loop_div_index]

    def remove_listener(self, control, callback):
        if control != None and control.value_has_listener(callback):
            control.remove_value_listener(callback)
        control.disconnect()

    def disconnect(self):
        self.remove_listener(self.the_slider, self._do_main_slider)
        self.remove_listener(self.arrow_mode_button, self.toggle_arrow_mode)
        self.remove_listener(self.volume_button, self._action_volume)
        self.remove_listener(self.xfade_button, self._action_xfade)
        self.remove_listener(self.swing_button, self._action_swing)
        self.remove_listener(self.clipn_h_button, self._action_clipnh)
        self.remove_listener(self.clipn_v_button, self._action_clipnv)
        self.remove_listener(self.shift_button, self._action_shift)
        self.remove_listener(self.scroll_mod_left_button, self._action_scroll_left)
        self.remove_listener(self.scroll_mod_right_button, self._action_scroll_right)
        self.remove_listener(self.push_button, self._action_push)
        self.remove_listener(self.octave_mod_button, self._action_octave)
        self.remove_listener(self.scale_mod_button, self._action_scale)
        self.remove_listener(self.basenote_mod_button, self._action_base_note)
        self.remove_listener(self.keycolor_mod_button, self._action_key_color)
        self._parent = None
        self.master_track = None
        self.the_slider = None
        self.mode_assign_map = None
class Maschine(ControlSurface):
    __module__ = __name__
    __doc__ = 'Basic Control Script for All Maschine Modell Mikro, Mikro Mk2, Mk1, Mk2, Studio'

    def __init__(self, c_instance):
        super(Maschine, self).__init__(c_instance)
        with self.component_guard():
            register_sender(self)
            self._diplay_cache = ['',
             '',
             '',
             '']
            self._suppress_send_midi = True
            is_momentary = True
            self._c_ref = c_instance
            self.display_task = DisplayTask()
            self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143
            self._active = False
            self._midi_pause_count = 0
            self.blink_state = 0
            self.send_slider_index = 0
            self.nav_index = 0
            self.arm_selected_track = False
            self.undo_state = 0
            self.redo_state = 0
            self._set_suppress_rebuild_requests(True)
            self._modeselect = ModeSelector(self.is_monochrome())
            self._device = self._set_up_device_control()
            self._set_up_session(self._modeselect)
            self._set_up_mixer()
            self._setup_transport()
            self._set_global_buttons()
            self._editsection = EditSection()
            self._editsection.connect_session(self._session)
            self._editsection.set_mode_selector(self._modeselect)
            self._session.set_mode(self._modeselect._clip_mode)
            self._audio_clip_editor = AudioClipEditComponent()
            self._note_repeater = NoteRepeatComponent(c_instance.note_repeat)
            self._midi_edit = MidiEditSection()
            self._init_settings()
            self._init_maschine()
            self.set_highlighting_session_component(self._session)
            self.set_pad_translations(PAD_TRANSLATIONS)
            self._on_selected_track_changed()
            self.set_up_function_buttons()
            self.show_message(str(''))
            self.request_rebuild_midi_map()
            self._set_suppress_rebuild_requests(False)
            self._active = True
            self._display_device_param = False
            self.set_feedback_channels(FEEDBACK_CHANNELS)
            self._final_init()
            self._suppress_send_midi = False
            self.apply_preferences()
            self.init_text_display()
            self._on_appointed_device_changed.subject = self.song()

    def _init_maschine(self):
        pass

    def _final_init(self):
        pass

    def create_pad_button(self, scene_index, track_index, color_source):
        pass

    def create_gated_button(self, identifier, hue):
        pass

    def apply_preferences(self):
        pref_dict = self._pref_dict
        if 'step_advance' in pref_dict:
            self._session.set_step_advance(pref_dict['step_advance'])
        if 'solo_exclusive' in pref_dict:
            self._modeselect.set_solo_exclusive(pref_dict['solo_exclusive'])
        else:
            self._modeselect.set_solo_exclusive(True)
        if 'arm_exclusive' in pref_dict:
            self._modeselect.set_arm_exclusive(pref_dict['arm_exclusive'])
        else:
            self._modeselect.set_arm_exclusive(True)
        if 'quantize_val' in pref_dict:
            self._editsection.quantize = pref_dict['quantize_val']
        else:
            self._editsection.quantize = 5
        if 'initial_cliplen' in pref_dict:
            self._editsection.initial_clip_len = pref_dict['initial_cliplen']
        else:
            self._editsection.initial_clip_len = 4.0
        if 'auto_arm_sel_track' in pref_dict:
            self.arm_selected_track = pref_dict['auto_arm_sel_track']
        else:
            self.arm_selected_track = False
        if 'note_color_mode' in pref_dict:
            self._modeselect._pad_mode._note_display_mode = pref_dict['note_color_mode']
        else:
            self._modeselect._pad_mode._note_display_mode = ND_KEYBOARD1
        self._pref_dict['note_color_mode'] = self._modeselect._pad_mode._note_display_mode
        self.set_sel_arm_button.send_value(self.arm_selected_track and 127 or 0, True)
        self._note_repeater.recall_values(self._pref_dict)

    def store_preferences(self):
        self._pref_dict['step_advance'] = self._session.get_step_advance()
        self._pref_dict['solo_exclusive'] = self._modeselect.is_solo_exclusive()
        self._pref_dict['arm_exclusive'] = self._modeselect.is_arm_exclusive()
        self._pref_dict['quantize_val'] = self._editsection.quantize
        self._pref_dict['initial_cliplen'] = self._editsection.initial_clip_len
        self._pref_dict['auto_arm_sel_track'] = self.arm_selected_track
        self._pref_dict['note_color_mode'] = self._modeselect._pad_mode._note_display_mode
        self._note_repeater.store_values(self._pref_dict)

    def _init_settings(self):
        from pickle import loads, dumps
        from encodings import ascii
        nop(ascii)
        preferences = self._c_instance.preferences(self.preferences_name())
        self._pref_dict = {}
        try:
            self._pref_dict = loads(str(preferences))
        except Exception:
            pass

        pref_dict = self._pref_dict
        preferences.set_serializer(lambda : dumps(pref_dict))

    def preferences_name(self):
        return 'Maschine'

    def _pre_serialize(self):
        from pickle import dumps
        from encodings import ascii
        nop(ascii)
        preferences = self._c_instance.preferences('Maschine')
        self.store_preferences()
        dump = dumps(self._pref_dict)
        preferences.set_serializer(lambda : dump)

    def toggle_nav_mode(self):
        self._session.switch_step_advance()
        self.show_message(' View Navigation in steps of ' + str(self._session.get_step_advance()))

    def _set_up_session(self, mode_selector):
        is_momentary = True
        self._session = MaschineSessionComponent()
        self._session.set_color_manager(mode_selector.get_color_manager())
        self.nav_buttons = (self.create_gated_button(92, COLOR_HUE_NAV),
         self.create_gated_button(81, COLOR_HUE_NAV),
         self.create_gated_button(93, COLOR_HUE_NAV),
         self.create_gated_button(91, COLOR_HUE_NAV))
        self._session.set_scene_bank_buttons(self.nav_buttons[0], self.nav_buttons[1])
        self._session.set_track_bank_buttons(self.nav_buttons[2], self.nav_buttons[3])
        track_stop_buttons = [ StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, index + STOP_CC_OFF) for index in range(4) ]
        self._session.set_stop_track_clip_buttons(tuple(track_stop_buttons))
        self._matrix = []
        self._bmatrix = ButtonMatrixElement()
        for scene_index in range(4):
            button_row = []
            for track_index in range(4):
                button = self.create_pad_button(scene_index, track_index, mode_selector)
                button_row.append(button)

            self._matrix.append(tuple(button_row))
            self._bmatrix.add_row(tuple(button_row))

        self._session.set_matrix(self._matrix)
        for button, (track_index, scene_index) in self._bmatrix.iterbuttons():
            if button:
                scene = self._session.scene(scene_index)
                clip_slot = scene.clip_slot(track_index)
                clip_slot.set_launch_button(button)
                clip_slot.set_triggered_to_play_value(1)
                clip_slot.set_triggered_to_record_value(1)
                clip_slot.set_started_value(1)
                clip_slot.set_recording_value(1)
                clip_slot.set_stopped_value(1)

        self._session._link()

    def _set_up_mixer(self):
        is_momentary = True
        self._mixer = MaschineMixerComponent(8)
        self.send_sliders = []
        for track in range(8):
            self.send_sliders.append(SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, SEND_CC_OFF + track))

        for track in range(8):
            strip = self._mixer.channel_strip(track)
            strip.set_arm_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, ARM_CC_OFF + track))
            strip.set_solo_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, SOLO_CC_OFF + track))
            strip.set_mute_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, MUTE_CC_OFF + track))
            strip.set_volume_control(SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, LEVEL_CC_OFF + track))
            strip.set_pan_control(SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, PAN_CC_OFF + track))
            strip.set_select_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, SELECT_CC_OFF + track))
            st = tuple([self.send_sliders[track]])
            strip.set_send_controls(st)

        self.send_slider_toggle_button = StateButton(False, MIDI_CC_TYPE, 0, 90)
        self._do_toggle_send.subject = self.send_slider_toggle_button
        self._session.set_mixer(self._mixer)

    def _set_global_buttons(self):
        is_momentary = True
        self._undo_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 85)
        self._do_undo.subject = self._undo_button
        self._redo_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 87)
        self._do_redo.subject = self._redo_button
        self._stop_all_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 111)
        self._do_stop_all.subject = self._stop_all_button
        self._toggle_detail_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1, 121)
        self._action_toogle_detail_view.subject = self._toggle_detail_button
        self._fire_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 9)
        self._do_fire_button.subject = self._fire_button
        self._g_clear_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1, 106)
        self._hold_clear_action.subject = self._g_clear_button
        self._g_duplicate_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1, 107)
        self._hold_duplicate_action.subject = self._g_duplicate_button
        self.track_left_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 120)
        self.track_right_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 121)
        self.set_sel_arm_button = StateButton(is_momentary, MIDI_CC_TYPE, 2, 56)
        self._reenable_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1, 120)
        self._do_auto_reenable.subject = self._reenable_button
        self._on_change_reenabled.subject = self.song()
        self._on_change_reenabled()
        self._a_trk_left.subject = self.track_left_button
        self._a_trk_right.subject = self.track_right_button
        self._a_sel_arm.subject = self.set_sel_arm_button

    def _set_up_device_control(self):
        is_momentary = True
        device = MaschineDeviceComponent()
        param_controls = []
        for index in range(8):
            param_controls.append(SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_CC_OFF + index))

        device.set_parameter_controls(tuple(param_controls))
        self.device_control = param_controls
        device.set_on_off_button(StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL, DEVICE_BUTTON_CC_OFF))
        device.set_bank_nav_buttons(StateButton(is_momentary, MIDI_CC_TYPE, 3, 104), ButtonElement(is_momentary, MIDI_CC_TYPE, 3, 105))
        self._device_nav_button_left = StateButton(is_momentary, MIDI_CC_TYPE, 3, 106)
        self._device_nav_button_right = StateButton(is_momentary, MIDI_CC_TYPE, 3, 107)
        self._navigate_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 127)
        self._nav_value_left.subject = self._device_nav_button_left
        self._nav_value_right.subject = self._device_nav_button_right
        self._do_focus_navigate.subject = self._navigate_button
        self.set_device_component(device)
        return device

    def _setup_transport(self):
        is_momentary = True
        transport = TransportComponent()
        studiotransport = MaschineTransport()
        playButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 108)
        stopButton = StateButton(not is_momentary, MIDI_CC_TYPE, 0, 110)
        recordButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 109)
        overdubButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 107)
        metrononmeButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 104)
        eventRecButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 98)
        playButton.name = 'Play'
        stopButton.name = 'Stop'
        recordButton.name = 'Record'
        overdubButton.name = 'Overdub'
        metrononmeButton.name = 'Metronome'
        transport.set_play_button(playButton)
        transport.set_stop_button(stopButton)
        transport.set_record_button(recordButton)
        transport.set_overdub_button(overdubButton)
        transport.set_metronome_button(metrononmeButton)
        studiotransport.set_session_auto_button(eventRecButton)
        studiotransport.set_arrangement_overdub_button(StateButton(is_momentary, MIDI_CC_TYPE, 0, 106))
        studiotransport.set_back_arrange_button(StateButton(is_momentary, MIDI_CC_TYPE, 0, 105))
        transport.set_nudge_buttons(StateButton(is_momentary, MIDI_CC_TYPE, 1, 51), StateButton(is_momentary, MIDI_CC_TYPE, 1, 50))
        punchinbutton = ToggleButton(MIDI_CC_TYPE, 1, 52)
        punchoutbutton = ToggleButton(MIDI_CC_TYPE, 1, 53)
        punchinbutton.name = 'Punch In'
        punchoutbutton.name = 'Punch Out'
        transport.set_punch_buttons(punchinbutton, punchoutbutton)
        transport.set_loop_button(StateButton(is_momentary, MIDI_CC_TYPE, 1, 54))
        self.song_follow_button = ButtonElement(True, MIDI_CC_TYPE, 2, 98)
        self._do_song_follow.subject = self.song_follow_button
        self._song_follow_changed.subject = self.song().view
        self._song_follow_changed()
        self.prehear_knob = SliderElement(MIDI_CC_TYPE, 0, 41)
        self.prehear_knob.connect_to(self.song().master_track.mixer_device.cue_volume)
        self.transp_ff_button = ButtonElement(True, MIDI_CC_TYPE, 1, 59)
        self.transp_rw_button = ButtonElement(True, MIDI_CC_TYPE, 1, 58)
        transport.set_seek_buttons(self.transp_ff_button, self.transp_rw_button)
        self.xfadeKnob = SliderElement(MIDI_CC_TYPE, 1, 105)
        self.xfadeKnob.connect_to(self.song().master_track.mixer_device.crossfader)
        self.master_knob = SliderElement(MIDI_CC_TYPE, 0, 99)
        self.master_knob.connect_to(self.song().master_track.mixer_device.volume)
        self.tap_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 88)
        self._do_tap_tempo.subject = self.tap_button
        self.cue_add_delete_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 55)
        self.cue_prev_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 56)
        self.cue_next_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 57)
        self._do_toggle_cue.subject = self.cue_add_delete_button
        self._do_toggle_prev_cue.subject = self.cue_prev_button
        self._do_toggle_next_cue.subject = self.cue_next_button

    def set_up_function_buttons(self):
        is_momentary = True
        self.keycolor_mod_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 73)
        self._do_key_color.subject = self.keycolor_mod_button
        self._update_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 86)
        self._do_update_display.subject = self._update_button

    @subject_slot('appointed_device')
    def _on_appointed_device_changed(self):
        self._modeselect._device_changed()

    def _update_hardware(self):
        self._session.update()
        self._modeselect.refresh()
        self.update_undo_redo(True)

    def refresh_state(self):
        ControlSurface.refresh_state(self)
        self._update_hardware()

    def _send_midi(self, midi_bytes, **keys):
        self._c_ref.send_midi(midi_bytes)
        return True

    def init_text_display(self):
        if USE_DISPLAY:
            self._modeselect._pad_mode.update_text_display()

    def _on_selected_track_changed(self):
        super(Maschine, self)._on_selected_track_changed()
        self.set_controlled_track(self.song().view.selected_track)
        self._on_devices_changed.subject = self.song().view.selected_track

    @subject_slot('devices')
    def _on_devices_changed(self):
        pass

    def update(self):
        self.set_feedback_channels(FEEDBACK_CHANNELS)
        super(Maschine, self).update()

    def is_monochrome(self):
        return False

    def _deassign_matrix(self):
        for scene_index in range(4):
            scene = self._session.scene(scene_index)
            for track_index in range(4):
                clip_slot = scene.clip_slot(track_index)
                clip_slot.set_launch_button(None)

    def update_display(self):
        with self.component_guard():
            with self._is_sending_scheduled_messages():
                self._task_group.update(0.1)
            self._modeselect.notify(self.blink_state)
            self.blink_state = (self.blink_state + 1) % 4
            self.display_task.tick()
            self.update_undo_redo(False)

    def update_undo_redo(self, force = False):
        if force:
            self.undo_state = self.song().can_undo
            self.redo_state = self.song().can_redo
        if self.song().can_undo != self.undo_state:
            self.undo_state = self.song().can_undo
            self._undo_button.send_value(self.undo_state == 1 and 127 or 0)
        if self.song().can_redo != self.redo_state:
            self.redo_state = self.song().can_redo
            self._redo_button.send_value(self.redo_state == 1 and 127 or 0)

    def adjust_loop_start(self, delta):
        loopval = self.song().loop_start
        self.song().loop_start = min(self.song().song_length, max(0, loopval + delta))

    def adjust_loop_length(self, delta):
        loopval = self.song().loop_length
        self.song().loop_length = min(self.song().song_length, max(abs(delta), loopval + delta))

    def _do_armsolo_mode(self, value):
        pass

    @subject_slot('value')
    def _do_fire_button(self, value):
        raise self._fire_button != None or AssertionError
        raise value in range(128) or AssertionError
        if value != 0:
            if self.isShiftDown():
                self.song().tap_tempo()
            else:
                clip_slot = self.song().view.highlighted_clip_slot
                if clip_slot:
                    clip_slot.fire()

    @subject_slot('value')
    def _do_undo(self, value):
        if value != 0:
            if self.use_layered_buttons() and self.isShiftDown():
                if self.song().can_redo == 1:
                    self.song().redo()
                    self.show_message(str('REDO'))
            elif self.song().can_undo == 1:
                self.song().undo()
                self.show_message(str('UNDO'))

    @subject_slot('value')
    def _do_redo(self, value):
        if value != 0:
            if self.song().can_redo == 1:
                self.song().redo()
                self.show_message(str('REDO'))

    @subject_slot('value')
    def _do_stop_all(self, value):
        if value != 0:
            if self.use_layered_buttons() and self.isShiftDown():
                self.song().stop_all_clips(0)
            else:
                self.song().stop_all_clips(1)

    def isShiftDown(self):
        return self._editsection.isShiftdown()

    def modifiers(self):
        return self._editsection.modifiers()

    def use_layered_buttons(self):
        return False

    def _handle_base_note(self, diff):
        self._modeselect._pad_mode.inc_base_note(diff)

    def _handle_octave(self, diff):
        self._modeselect._pad_mode.inc_octave(diff)
        octave_val = self._modeselect._pad_mode

    def _handle_scale(self, diff):
        self._modeselect._pad_mode.inc_scale(diff)

    @subject_slot('value')
    def _do_update_display(self, value):
        if value != 0:
            self.refresh_state()

    @subject_slot('value')
    def _do_key_color(self, value):
        if not value in range(128):
            raise AssertionError
            value != 0 and self._modeselect._pad_mode.step_key_color_mode()

    @subject_slot('value')
    def _do_tap_tempo(self, value):
        if not value in range(128):
            raise AssertionError
            value != 0 and self.song().tap_tempo()

    @subject_slot('value')
    def _do_toggle_cue(self, value):
        if not value in range(128):
            raise AssertionError
            value != 0 and self.song().set_or_delete_cue()

    @subject_slot('value')
    def _do_toggle_prev_cue(self, value):
        if not value in range(128):
            raise AssertionError
            value != 0 and self.song().jump_to_prev_cue()

    @subject_slot('value')
    def _do_toggle_next_cue(self, value):
        if not value in range(128):
            raise AssertionError
            value != 0 and self.song().jump_to_next_cue()

    @subject_slot('value')
    def _do_toggle_send(self, value):
        if not value in range(128):
            raise AssertionError
            if self.isShiftDown():
                value != 0 and self.refresh_state()
                self.show_message('Refresh Display')
        else:
            nr_of_tracks = len(self.song().return_tracks)
            if value == 0 or nr_of_tracks < 1:
                return
            prev = self.send_slider_index
            self.send_slider_index += 1
            if self.send_slider_index >= nr_of_tracks:
                self.send_slider_index = 0
            self.show_message(' Set Send ' + str(SENDS[self.send_slider_index]))
            self.timed_message(2, ' Set Send ' + str(SENDS[self.send_slider_index]))
            if prev != self.send_slider_index:
                for track in range(8):
                    strip = self._mixer.channel_strip(track)
                    slider_list = []
                    for index in range(self.send_slider_index + 1):
                        if index < self.send_slider_index - 1:
                            slider_list.append(None)
                        else:
                            slider_list.append(self.send_sliders[track])
                        strip.set_send_controls(tuple(slider_list))

    @subject_slot('value')
    def _a_trk_left(self, value):
        if not value in range(128):
            raise AssertionError
            if value != 0:
                if self.application().view.is_view_visible('Session'):
                    direction = Live.Application.Application.View.NavDirection.left
                    self.application().view.scroll_view(direction, 'Session', True)
                    track = self.song().view.selected_track
                    self.timed_message(2, 'T:' + track.name, False)
                    self.arm_selected_track and track.can_be_armed and arm_exclusive(self.song(), track)

    @subject_slot('value')
    def _a_trk_right(self, value):
        if not value in range(128):
            raise AssertionError
            if value != 0:
                if self.application().view.is_view_visible('Session'):
                    direction = Live.Application.Application.View.NavDirection.right
                    self.application().view.scroll_view(direction, 'Session', True)
                    track = self.song().view.selected_track
                    self.timed_message(2, 'T:' + track.name, False)
                    self.arm_selected_track and track.can_be_armed and arm_exclusive(self.song(), track)

    @subject_slot('value')
    def _a_sel_arm(self, value):
        if value != 0:
            if self.arm_selected_track:
                self.arm_selected_track = False
                self.set_sel_arm_button.send_value(0, True)
            else:
                self.arm_selected_track = True
                self.set_sel_arm_button.send_value(127, True)

    @subject_slot('value')
    def _nav_value_left(self, value):
        if not self._device_nav_button_left != None:
            raise AssertionError
            if not value in range(128):
                raise AssertionError
                modifier_pressed = True
                value != 0 and (not self.application().view.is_view_visible('Detail') or not self.application().view.is_view_visible('Detail/DeviceChain')) and self.application().view.show_view('Detail')
                self.application().view.show_view('Detail/DeviceChain')
            else:
                direction = Live.Application.Application.View.NavDirection.left
                self.application().view.scroll_view(direction, 'Detail/DeviceChain', not modifier_pressed)

    @subject_slot('value')
    def _nav_value_right(self, value):
        if not self._device_nav_button_right != None:
            raise AssertionError
            if not value in range(128):
                raise AssertionError
                modifier_pressed = value != 0 and True
                (not self.application().view.is_view_visible('Detail') or not self.application().view.is_view_visible('Detail/DeviceChain')) and self.application().view.show_view('Detail')
                self.application().view.show_view('Detail/DeviceChain')
            else:
                direction = Live.Application.Application.View.NavDirection.right
                self.application().view.scroll_view(direction, 'Detail/DeviceChain', not modifier_pressed)

    @subject_slot('value')
    def _do_focus_navigate(self, value):
        if not self._navigate_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            self.nav_index = value != 0 and (self.nav_index + 1) % len(VIEWS_ALL)
            self.application().view.focus_view(VIEWS_ALL[self.nav_index])
            self.show_message('Focus on : ' + str(VIEWS_ALL[self.nav_index]))

    def focus_clip_detail(self):
        self.application().view.focus_view('Detail/Clip')

    @subject_slot('follow_song')
    def _song_follow_changed(self):
        view = self.song().view
        if view.follow_song:
            self.song_follow_button.send_value(1, True)
        else:
            self.song_follow_button.send_value(0, True)

    @subject_slot('value')
    def _do_song_follow(self, value):
        if value != 0:
            view = self.song().view
            if view.follow_song:
                view.follow_song = False
                self.song_follow_button.send_value(0, True)
            else:
                view.follow_song = True
                self.song_follow_button.send_value(1, True)

    @subject_slot('value')
    def _hold_duplicate_action(self, value):
        if value != 0:
            pass

    @subject_slot('value')
    def _hold_clear_action(self, value):
        if value != 0:
            self._mixer.enter_clear_mode()
            self._device_component.enter_clear_mode()
        else:
            self._mixer.exit_clear_mode()
            self._device_component.exit_clear_mode()

    @subject_slot('value')
    def _action_toogle_main_view(self, value):
        if value != 0:
            appv = self.application().view
            if appv.is_view_visible('Arranger'):
                appv.show_view('Session')
            else:
                appv.show_view('Arranger')

    @subject_slot('value')
    def _action_toogle_detail_view(self, value):
        if value != 0:
            appv = self.application().view
            if self.isShiftDown():
                if appv.is_view_visible('Arranger'):
                    appv.show_view('Session')
                else:
                    appv.show_view('Arranger')
            elif appv.is_view_visible('Detail/Clip'):
                appv.show_view('Detail/DeviceChain')
            else:
                appv.show_view('Detail/Clip')

    @subject_slot('re_enable_automation_enabled')
    def _on_change_reenabled(self):
        if self.song().re_enable_automation_enabled:
            self._reenable_button.turn_on()
        else:
            self._reenable_button.turn_off()

    @subject_slot('value')
    def _do_auto_reenable(self, value):
        if value != 0:
            self.song().re_enable_automation()

    def to_color_edit_mode(self, active):
        pass

    def clear_display_all(self):
        self.send_to_display('', 0)
        self.send_to_display('', 1)
        self.send_to_display('', 2)
        self.send_to_display('', 3)

    def clear_display(self, grid):
        self.send_to_display('', grid)

    def timed_message(self, grid, text, hold = False):
        if USE_DISPLAY == False:
            self.show_message(text)
        else:
            self.display_task.set_func(self.clear_display, grid)
            self.send_to_display(text, grid)
            if hold:
                self.display_task.hold()
            self.display_task.start()

    def timed_message_release(self):
        self.display_task.release()

    def update_bank_display(self):
        if USE_DISPLAY:
            name, bank = self._device._current_bank_details()
            if self._display_device_param:
                prms = len(bank)
                d1 = ''
                for i in range(4):
                    parm = bank[i]
                    if parm:
                        name = parm.name
                        d1 += name[:6] + (i < 3 and '|' or '')
                    else:
                        d1 += '      ' + (i < 3 and '|' or '')

                self.send_to_display(d1, 2)
                d1 = ''
                for i in range(4):
                    parm = bank[i + 4]
                    if parm:
                        name = parm.name
                        d1 += name[:6] + (i < 3 and '|' or '')
                    else:
                        d1 += '      ' + (i < 3 and '|' or '')

                self.send_to_display(d1, 4)
            else:
                self.timed_message(2, 'Bank: ' + name)

    def display_parameters(self, paramlist):
        if USE_DISPLAY == False:
            return

    def send_to_display(self, text, grid = 0):
        if USE_DISPLAY == False:
            return
        if self._diplay_cache[grid] == text:
            return
        self._diplay_cache[grid] = text
        if len(text) > 28:
            text = text[:27]
        msgsysex = [240,
         0,
         0,
         102,
         23,
         18,
         min(grid, 3) * 28]
        filled = text.ljust(28)
        for c in filled:
            msgsysex.append(ord(c))

        msgsysex.append(247)
        self._send_midi(tuple(msgsysex))

    def cleanup(self):
        pass

    def disconnect(self):
        self._pre_serialize()
        self.clear_display_all()
        for button, (track_index, scene_index) in self._bmatrix.iterbuttons():
            if button:
                button.send_color_direct(PColor.OFF[0])

        time.sleep(0.2)
        self._active = False
        self._suppress_send_midi = True
        super(Maschine, self).disconnect()
Example #30
0
 def _set_global_buttons(self):
     self._undo_button = StateButton(True, MIDI_CC_TYPE,
                                     BASE_CONTROL_CHANNEL, CC_UNDO_BUTTON)
     self._do_undo.subject = self._undo_button
     self._redo_button = StateButton(True, MIDI_CC_TYPE,
                                     BASE_CONTROL_CHANNEL, CC_REDO_BUTTON)
     self._do_redo.subject = self._redo_button
     self._shift_button = StateButton(True, MIDI_CC_TYPE,
                                      BASE_CONTROL_CHANNEL, CC_SHIFT_BUTTON)
     self._do_shift.subject = self._shift_button
     self._erase_button = StateButton(True, MIDI_CC_TYPE,
                                      BASE_CONTROL_CHANNEL, CC_ERASE_BUTTON)
     self._do_erase.subject = self._erase_button
     self._duplicate_button = StateButton(True, MIDI_CC_TYPE,
                                          BASE_CONTROL_CHANNEL,
                                          CC_DUPLICATE_BUTTON)
     self._do_duplicate.subject = self._duplicate_button
     self._selectback_button = StateButton(True, MIDI_CC_TYPE,
                                           BASE_CONTROL_CHANNEL,
                                           CC_BACKSEL_BUTTON)
     self._do_selectback.subject = self._selectback_button
     self._event_button = StateButton(True, MIDI_CC_TYPE,
                                      BASE_CONTROL_CHANNEL,
                                      CC_EVENTS_BUTTON)
     self._do_event_button.subject = self._event_button
     self.__arrange_button = StateButton(True,
                                         MIDI_CC_TYPE,
                                         BASE_CONTROL_CHANNEL,
                                         CC_ARRANGE_BUTTON,
                                         name='Arrange_button')
     self.__arrange_button.view_mode = VM_SESSION
     self._do_arrange.subject = self.__arrange_button
Example #31
0
 def create_button(ccval, channel=0):
     return StateButton(is_momentary, MIDI_CC_TYPE, channel, ccval)
class ModifierComponent(CompoundComponent):
    __shift_down = False
    __select_down = False
    __delete_down = False
    __duplicate_down = False
    __browse_down = False
    __macro_down = False
    __quantize_setting = 5

    def __init__(self, session, *a, **k):
        super(ModifierComponent, self).__init__(*a, **k)
        self.__session = session
        self.__delete_button = StateButton(True,
                                           MIDI_CC_TYPE,
                                           0,
                                           95,
                                           name='Clear_Button')
        self.__do_delete.subject = self.__delete_button
        self.__duplicate_button = StateButton(True,
                                              MIDI_CC_TYPE,
                                              0,
                                              96,
                                              name='Duplicate_Button')
        self.__do_duplicate.subject = self.__duplicate_button
        self._select_button = StateButton(True,
                                          MIDI_CC_TYPE,
                                          0,
                                          80,
                                          name='Select_Button')
        self.__do_select_button.subject = self._select_button
        self.__lock_button = StateButton(True,
                                         MIDI_CC_TYPE,
                                         0,
                                         47,
                                         name='Lock_Button')
        self.__do_lock.subject = self.__lock_button
        self.__macro_button = StateButton(True,
                                          MIDI_CC_TYPE,
                                          0,
                                          90,
                                          name='Macro_Button')
        self.__do_macro.subject = self.__macro_button
        self._left_button = StateButton(True,
                                        MIDI_CC_TYPE,
                                        0,
                                        107,
                                        name='Metro_Button')
        self._right_button = StateButton(True,
                                         MIDI_CC_TYPE,
                                         0,
                                         104,
                                         name='Loop_Button')
        self._rec_button = StateButton(True,
                                       MIDI_CC_TYPE,
                                       0,
                                       109,
                                       name='Record_Button')
        self._do_left_button.subject = self._left_button
        self._do_right_button.subject = self._right_button
        self._do_rec_button.subject = self._rec_button
        self._listen_overdub.subject = self.song()
        self._listen_loop.subject = self.song()
        self._listen_metronome.subject = self.song()
        self.__action_listener = None
        self.__shift_listener = None
        return

    @subject_slot('overdub')
    def _listen_overdub(self):
        if self.__shift_down:
            self._rec_button.set_display_value(
                self.song().overdub and 127 or 0, True)

    @subject_slot('loop')
    def _listen_loop(self):
        if self.__shift_down:
            self._right_button.set_display_value(self.song().loop and 127 or 0,
                                                 True)

    @subject_slot('metronome')
    def _listen_metronome(self):
        if self.__shift_down:
            self._left_button.set_display_value(
                self.song().metronome and 127 or 0, True)

    def _update_shift_status(self):
        if self.__shift_down:
            self._rec_button.set_display_value(
                self.song().overdub and 127 or 0, True)
            self._right_button.set_display_value(self.song().loop and 127 or 0,
                                                 True)
            self._left_button.set_display_value(
                self.song().metronome and 127 or 0, True)
        else:
            self._rec_button.set_display_value(0, True)
            self._left_button.set_display_value(0, True)
            self._right_button.set_display_value(0, True)

    def set_browse_down(self, value):
        self.__browse_down = value

    @subject_slot('value', identify_sender=True)
    def __do_lock(self, value, sender):
        if sender.grabbed:
            return
        if self.__action_listener and value > 0:
            newstate = self.__action_listener.notify_edit_toggle(
                LOCK_BUTTON, self.__shift_down)
            self.__lock_button.set_value(newstate)

    @subject_slot('value', identify_sender=True)
    def __do_macro(self, value, sender):
        if sender.grabbed:
            return
        self.__macro_down = value > 0
        self.__macro_button.set_display_value(value, True)

    def set_edit_state(self, **args):
        if 'lock' in args:
            self.__lock_button.set_value(args['lock'] and 127 or 0)
        else:
            self.__lock_button.set_value(0)

    def set_shiftstatus(self, value):
        self.__shift_down = value > 0
        self._update_shift_status()
        if self.__shift_listener:
            self.__shift_listener.notify_shift(self.__shift_down)

    @subject_slot('value', identify_sender=True)
    def __do_select_button(self, value, sender):
        if sender.grabbed:
            return
        self._select_button.send_value(value)
        self.__select_down = value > 0

    def register_shift_listener(self, listener):
        self.__shift_listener = listener

    @subject_slot('value', identify_sender=True)
    def __do_delete(self, value, sender):
        if sender.grabbed:
            return
        self.__delete_button.send_value(value)
        if not self.__shift_down:
            self.__delete_down = value > 0
        else:
            if value != 0:
                clip = self.song().view.detail_clip
                if clip != None:
                    clip.clear_all_envelopes()
                    self.canonical_parent.show_message('Clear Envelopes ' +
                                                       clip.name)
        return

    @subject_slot('value', identify_sender=True)
    def __do_duplicate(self, value, sender):
        if sender.grabbed:
            return
        self.__duplicate_button.send_value(value)
        if not self.__shift_down:
            self.__duplicate_down = value > 0
        else:
            if value != 0:
                clip = self.song().view.detail_clip
                if clip != None and clip.is_midi_clip:
                    if clip.length <= 128.0:
                        clip.duplicate_loop()
                        self.canonical_parent.show_message(
                            'Double Loop : ' + str(int(clip.length / 4)) +
                            ' Bars')
                        self.application().view.focus_view('Detail/Clip')
                    else:
                        self.canonical_parent.show_message(
                            'Clip is to long to Duplicate')
        return

    def set_action_listener(self, listener):
        self.__action_listener = listener

    def handle_edit(self, clipslotcomp, value):
        if value == 0:
            return
        if clipslotcomp._clip_slot is not None:
            if self.__delete_down:
                self.__handle_delete(clipslotcomp)
            elif self.__duplicate_down:
                self.__handle_duplicate(clipslotcomp)
            elif self.__browse_down:
                self.__handle_mode_scene_clip(clipslotcomp)
            elif self.__macro_down:
                self.__handle_new_action(clipslotcomp)
            elif self.__select_down:
                self.__handle_select_action(clipslotcomp)
            elif self.__shift_down:
                self.__handle_shift_action(clipslotcomp)
        else:
            if self.__browse_down:
                self.__handle_mode_scene_clip(clipslotcomp)
            else:
                if self.__shift_down:
                    self.__handle_shift_action(clipslotcomp)
        return

    def __handle_shift_action(self, clipslotcomp):
        columm, row = clipslotcomp.get_index()
        if row == 0:
            self.handle_edit_action(columm)

    def __handle_select_action(self, clipslotcomp):
        self.song().view.highlighted_clip_slot = clipslotcomp._clip_slot

    @subject_slot('value', identify_sender=True)
    def _do_left_button(self, value, sender):
        if sender.grabbed:
            return
        if self.__shift_down:
            if value == 0:
                return
            self.song().metronome = not self.song().metronome
        else:
            self._left_button.set_display_value(value > 0 and 127 or 0, True)
            if value == 0:
                return
            self.canonical_parent.invoke_nav_left()

    @subject_slot('value', identify_sender=True)
    def _do_right_button(self, value, sender):
        if sender.grabbed:
            return
        if self.__shift_down:
            if value == 0:
                return
            self.song().loop = not self.song().loop
        else:
            self._right_button.set_display_value(value > 0 and 127 or 0, True)
            if value == 0:
                return
            self.canonical_parent.invoke_nav_right()

    @subject_slot('value', identify_sender=True)
    def _do_rec_button(self, value, sender):
        if sender.grabbed:
            return
        if self.__shift_down:
            if value == 0:
                return
            self.song().overdub = not self.song().overdub
        else:
            self._rec_button.set_display_value(value > 0 and 127 or 0, True)
            if value > 0:
                self.canonical_parent.invoke_rec()

    def handle_edit_action(self, index, scale=None):
        if not self.__shift_down:
            return
        if index == 0:
            if self.song().can_undo == 1:
                self.song().undo()
                self.canonical_parent.show_message(str('UNDO'))
        else:
            if index == 1:
                if self.song().can_redo == 1:
                    self.song().redo()
                    self.canonical_parent.show_message(str('REDO'))
            else:
                if index == 2 or index == 3:
                    clip = self.song().view.detail_clip
                    if clip:
                        clip.quantize(QUANT_CONST[self.__quantize_setting],
                                      index == 2 and 1.0 or 0.5)
                        self.canonical_parent.show_message(
                            'Quantize Clip ' + clip.name + ' by ' +
                            QUANT_STRING[self.__quantize_setting])
                else:
                    clip = self.song().view.detail_clip
                    if clip and clip.is_midi_clip:
                        track = clip.canonical_parent.canonical_parent
                        drum_device = find_drum_device(track)
                        if not drum_device:
                            self.__transpose_clip(clip, TRANSPOSE[index],
                                                  scale)

    def __transpose_clip(self, clip, amount, bn_scale):
        notes = clip.get_selected_notes()
        if len(notes) == 0:
            clip.select_all_notes()
            notes = clip.get_selected_notes()
        update_notes = []
        for note in notes:
            pitch, pos, dur, vel, mute = note
            if bn_scale:
                basenote, scale = bn_scale
                pv = scale.transpose_by_scale(basenote, pitch, amount)
            else:
                pv = pitch + amount
            if pv < 0 or pv > 127:
                pv = pitch
            update_notes.append((pv, pos, dur, vel, mute))

        clip.replace_selected_notes(tuple(update_notes))

    def __handle_mode_scene_clip(self, clipslotcomp):
        if clipslotcomp._clip_slot is None:
            return
        clip_slot = clipslotcomp._clip_slot
        self.song().view.highlighted_clip_slot = clip_slot
        return

    def __handle_delete(self, clipslotcomp):
        if self.__shift_down:
            pass
        else:
            clipslotcomp._do_delete_clip()

    def __handle_duplicate(self, clipslotcomp):
        if self.__shift_down:
            pass
        else:
            self.duplicate_clip_slot(clipslotcomp._clip_slot)

    def __handle_new_action(self, clipslotcomp):
        song = self.song()
        clip_slot = clipslotcomp._clip_slot
        track = clip_slot.canonical_parent
        if clip_slot.clip == None and track.has_midi_input:
            try:
                clip_slot.create_clip(4.0)
                song.view.detail_clip = clip_slot.clip
                select_clip_slot(song, clip_slot)
                self.application().view.focus_view('Detail/Clip')
                self.canonical_parent.show_message(
                    'New Midi Clip ' +
                    song.view.highlighted_clip_slot.clip.name)
            except Live.Base.LimitationError:
                pass
            except RuntimeError:
                pass

        return

    def double_clipslot(self, clip_slot):
        song = self.song()
        track = clip_slot.canonical_parent
        if clip_slot.clip is not None and track.has_midi_input:
            clip = clip_slot.clip
            if clip.length <= 2048.0:
                clip.duplicate_loop()
                self.canonical_parent.show_message('Double Loop : ' +
                                                   str(int(clip.length / 4)) +
                                                   ' Bars')
                song.view.detail_clip = clip
                self.application().view.focus_view('Detail/Clip')
            else:
                self.canonical_parent.show_message(
                    'Clip is to long to Duplicate')
        return

    def duplicate_clip_slot(self, clip_slot):
        if clip_slot.has_clip:
            try:
                track = clip_slot.canonical_parent
                index = list(track.clip_slots).index(clip_slot)
                track.duplicate_clip_slot(index)
                self.canonical_parent.show_message('Duplicate Clip ' +
                                                   clip_slot.clip.name)
                select_clip_slot(self.song(), track.clip_slots[index + 1])
            except Live.Base.LimitationError:
                pass
            except RuntimeError:
                pass

    def in_spec_mode(self):
        return self.__shift_down or self.__delete_down or self.__duplicate_down or self.__browse_down or self.__macro_down or self.__select_down

    def modifier_mask(self):
        return (self.__shift_down and MASK_SHIFT
                or 0) | (self.__browse_down and MASK_BROWSE
                         or 0) | (self.__delete_down and MASK_CLEAR or 0) | (
                             self.__duplicate_down and MASK_DUPLICATE
                             or 0) | (self.__macro_down and MASK_SPEC or 0) | (
                                 self.__select_down and MASK_SELECT or 0)

    def is_select_down(self):
        return self.__select_down

    def is_browse_down(self):
        return self.__browse_down

    def is_shift_down(self):
        return self.__shift_down

    def is_delete_down(self):
        return self.__delete_down

    def is_duplicate_down(self):
        return self.__duplicate_down
Example #33
0
class Mk2KnobControl:
    __module__ = __name__
    __doc__ = 'Mk2 Module for Controlling Parameters with Master Knob'

    def __init__(self, parent):
        self._parent = parent
        self.master_track = parent.song().master_track
        self.volume_button = None
        self._set_volume_button(StateButton(True, MIDI_CC_TYPE, 3, 110))
        self.the_slider = SliderElement(MIDI_CC_TYPE, 1, 86)
        self.the_slider.add_value_listener(self._do_main_slider, True)
        self.xfade_button = None
        self._set_xfade_button(StateButton(True, MIDI_CC_TYPE, 3, 116))
        self.swing_button = None
        self._set_swing_button(StateButton(True, MIDI_CC_TYPE, 3, 111))
        self.mode = KN2_MODE_VOLUME
        self.previous_mode = -1
        self.tempo_button = None
        self._set_tempo_button(StateButton(True, MIDI_CC_TYPE, 3, 112))
        self.push_button = None
        self._set_push_button(StateButton(True, MIDI_CC_TYPE, 1, 87))
        self.clipn_v_button = None
        self.clipn_h_button = None
        self._set_clipn_h_button(StateButton(True, MIDI_CC_TYPE, 3, 114))
        self._set_clipn_v_button(StateButton(True, MIDI_CC_TYPE, 3, 115))
        self.toggle_buttons = [
            self.volume_button, self.xfade_button, self.swing_button,
            self.tempo_button, self.clipn_h_button, self.clipn_v_button
        ]
        self.shift_button = None
        self._set_shift_button(StateButton(True, MIDI_CC_TYPE, 3, 113))
        self.shift_on = False
        self.scroll_mod_left_button = None
        self.scroll_mod_right_button = None
        self._set_scroll_mod_left_button(
            StateButton(True, MIDI_CC_TYPE, 0, 105))
        self._set_scroll_mod_right_button(
            StateButton(True, MIDI_CC_TYPE, 0, 106))
        self._prev_mode = KN2_MODE_VOLUME
        self.lrmode = LR_CONTROL_CLIP
        self._challenge = Live.Application.get_random_int(
            0, 400000000) & 2139062143
        self.loop_div_index = 0
        self.loop_incdex = 4.0
        self.arrow_mode_button = ColorButton(True, MIDI_CC_TYPE, 30)
        self.arrow_mode_button.add_value_listener(self.toggle_arrow_mode)
        self.arrow_mode_button.send_color(LR_MODE_HUES[self.lrmode])
        self.arrow_mode_button.send_value(127, True)
        self.navflags = 0
        self.octave_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 70)
        self.octave_mod_button.add_value_listener(self._action_octave)
        self.scale_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 71)
        self.scale_mod_button.add_value_listener(self._action_scale)
        self.basenote_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 72)
        self.basenote_mod_button.add_value_listener(self._action_base_note)
        self.keycolor_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 73)
        self.keycolor_mod_button.add_value_listener(self._action_key_color)
        self.pad_to_mainknob_mode = 0
        self._measure_left_click = 0
        self._measure_right_click = 0
        self.mode_assign_map = {
            KN2_MODE_VOLUME:
            (self.chg_volume, 0, 'Master Knob controls MASTER Volume',
             KN2_MODE_CUE),
            KN2_MODE_CUE: (self.chg_cue, 0, 'Master Knob controls Cue Level',
                           KN2_MODE_VOLUME),
            KN2_MODE_TEMPO_COARSE:
            (self.chg_tempo, 3, 'Master Knob controls TEMPO Coarse',
             KN2_MODE_TEMPO_FINE),
            KN2_MODE_TEMPO_FINE:
            (self.chg_tempo_fine, 3, 'Master Knob controls TEMPO Fine',
             KN2_MODE_TEMPO_COARSE),
            KN2_MODE_XFADE: (self.chg_xfade, 1,
                             'Master Knob controls Crossfader', -1),
            KN2_MODE_QUANT:
            (self.chg_quant, 2, 'Master Knob controls Recording Quantize',
             KN2_MODE_CLIP_QUANT),
            KN2_MODE_CLIP_QUANT:
            (self.chg_clip_q, 2, 'Master Knob controls Clip Start Quantize',
             KN2_MODE_QUANT),
            KN2_MODE_CLIPN_HOR: (self.nav_c_hor, 4,
                                 'Master Knob Clip View horizontally', -1),
            KN2_MODE_CLIPN_VER: (self.nav_c_ver, 5,
                                 'Master Knob Clip View vertically', -1),
            KN2_MODE_GENERAL: (self.chg_general, -1, None, -1),
            KN2_P_SCALES: (self.modify_pad_scaling, -1, None, -1)
        }

    def start_up(self):
        self._set_mode(KN2_MODE_VOLUME)
        self.arrow_mode_button.send_value(127, True)

    def toggle_arrow_mode(self, value):
        if value > 0:
            self.lrmode = (self.lrmode + 1) % 4
            self.arrow_mode_button.send_hue(LR_MODE_HUES[self.lrmode])
            self._parent.show_message('Left/Right Buttons Control:    ' +
                                      L_MODE_FUNCTION[self.lrmode] + ' / ' +
                                      R_MODE_FUNCTION[self.lrmode])

    def switch_to_matrix_mode(self):
        if self.mode != KN2_MODE_GENERAL:
            self.previous_mode = self.mode
            self._set_mode(KN2_MODE_GENERAL)

    def exit_matrix_mode(self):
        if self.mode == KN2_MODE_GENERAL:
            self._set_mode(self.previous_mode)
            self.previous_mode = -1

    def update_shift(self):
        if self.shift_on:
            self.shift_button.send_value(127, True)
        else:
            self.shift_button.send_value(0, True)

    def _set_mode(self, mode):
        if not mode in range(11):
            raise AssertionError
            self.update_shift()
            if mode == self.mode:
                return
            self._prev_mode = mode
            self.mode = mode
            self.switch_radio_buttons(self.mode_assign_map[self.mode][1])
            message = self.mode_assign_map[self.mode][2]
            message != None and self._parent.show_message(message)

    def switch_radio_buttons(self, which):
        for index in range(len(self.toggle_buttons)):
            if index == which:
                self.toggle_buttons[index].send_value(127, True)
            else:
                self.toggle_buttons[index].send_value(0, True)

    def update(self):
        self.switch_radio_buttons(self.mode_assign_map[self.mode][1])
        self.arrow_mode_button.send_color(LR_MODE_HUES[self.lrmode])
        self.arrow_mode_button.send_value(127, True)

    def _do_main_slider(self, value, encoder):
        if not value in range(128):
            raise AssertionError
            if not isinstance(encoder, EncoderElement):
                raise AssertionError
                if value == 1:
                    delta = 1
                else:
                    delta = -1
                if self.pad_to_mainknob_mode != 0:
                    self.mode_assign_map[KN2_P_SCALES][0](delta)
                elif self.navflags == 0:
                    self.mode_assign_map[self.mode][0](delta)
                if self.lrmode == LR_CONTROL_CLIP:
                    self.navflags & LEFT_DOWN != 0 and self.nav_c_hor(delta)
                self.navflags & RIGHT_DOWN != 0 and self.nav_c_ver(delta)
        elif self.lrmode == LR_CONTROL_SEL:
            if self.navflags & LEFT_DOWN != 0:
                self.nav_track(delta)
            if self.navflags & RIGHT_DOWN != 0:
                self._parent.scroll_scene(delta)
        elif self.lrmode == LR_CONTROL_DEV:
            if self.navflags & LEFT_DOWN != 0:
                self.nav_track(delta)
            if self.navflags & RIGHT_DOWN != 0:
                self._parent.scroll_device(delta)
        elif self.lrmode == LR_CONTROL_LOOP:
            if self.navflags & LEFT_DOWN != 0:
                self.adjust_loop_start(delta)
            if self.navflags & RIGHT_DOWN != 0:
                self.adjust_loop_length(delta)

    def modify_pad_scaling(self, delta):
        if self.pad_to_mainknob_mode & PAD_KNOB_OCTAVE != 0:
            self._parent.inc_octave(delta)
        if self.pad_to_mainknob_mode & PAD_KNOB_SCALE != 0:
            self._parent.inc_scale(delta)
        if self.pad_to_mainknob_mode & PAD_KNOB_BASEN != 0:
            self._parent.inc_base_note(delta)
        self._parent.update_transpose()

    def adjust_loop_start(self, delta):
        loopval = self._parent.song().loop_start
        loopval += self.loop_incdex * delta
        if loopval < 0:
            loopval = 0
        elif loopval > 999:
            loopval = 999
        self._parent.song().loop_start = loopval

    def adjust_loop_length(self, delta):
        loopval = self._parent.song().loop_length
        loopval += self.loop_incdex * delta
        if loopval < self.loop_incdex:
            loopval = self.loop_incdex
        elif loopval > 999:
            loopval = 999
        self._parent.song().loop_length = loopval

    def chg_general(self, delta):
        self._parent._scenematrix.control_handler.mod_value(
            delta, self.shift_on)

    def nav_track(self, direction):
        if direction == 1:
            self._parent._a_trk_right(1)
        else:
            self._parent._a_trk_left(1)

    def nav_c_hor(self, direction):
        self._parent.move_view_horizontal(direction)

    def nav_c_ver(self, direction):
        if direction == 1:
            self._parent._session.bank_up()
        else:
            self._parent._session.bank_down()

    def chg_volume(self, diff):
        if self.shift_on:
            self.repeat(self.master_track.mixer_device.volume, diff)
        else:
            self.master_track.mixer_device.volume.value = self.calc_new_parm(
                self.master_track.mixer_device.volume, diff)

    def chg_xfade(self, diff):
        if self.shift_on:
            self.repeat(self.master_track.mixer_device.crossfader, diff)
        else:
            self.master_track.mixer_device.crossfader.value = self.calc_new_parm(
                self.master_track.mixer_device.crossfader, diff)

    def chg_cue(self, diff):
        if self.shift_on:
            self.repeat(self.master_track.mixer_device.cue_volume, diff)
        else:
            self.master_track.mixer_device.cue_volume.value = self.calc_new_parm(
                self.master_track.mixer_device.cue_volume, diff)

    def repeat(self, parm, delta):
        count = 0
        while count < SHIFT_INC:
            parm.value = self.calc_new_parm(parm, delta)
            count += 1

    def calc_new_parm(self, parm, delta):
        parm_range = parm.max - parm.min
        int_val = int((parm.value - parm.min) / parm_range * PARM_RANGE + 0.1)
        inc_val = min(PARM_RANGE, max(0, int_val + delta))
        return float(inc_val) / float(PARM_RANGE) * parm_range + parm.min

    def chg_quant(self, diff):
        rec_quant = self._parent.song().midi_recording_quantization
        index = self.get_quant_index(rec_quant)
        new_index = index + diff
        if new_index >= 0 and new_index < len(QUANT_CONST):
            self._parent.song(
            ).midi_recording_quantization = QUANT_CONST[new_index]
            self._parent.show_message(QUANT_DESCR[new_index])

    def chg_clip_q(self, diff):
        quant = self._parent.song().clip_trigger_quantization
        self._parent.song().clip_trigger_quantization = max(
            0, min(13, quant + diff))
        self._parent.show_message(
            'Clip Quantize ' +
            CLIQ_DESCR[self._parent.song().clip_trigger_quantization])

    def chg_tempo_fine(self, diff):
        if diff < 0:
            amount = -0.01
        else:
            amount = 0.01
        self.chg_tempo(amount)

    def chg_tempo(self, diff):
        self._parent.song().tempo = max(
            20, min(999,
                    self._parent.song().tempo + diff))

    def get_quant_index(self, const):
        for index in range(len(QUANT_CONST)):
            if const == QUANT_CONST[index]:
                return index

        return -1

    def _action_octave(self, value):
        if value != 0:
            self.pad_to_mainknob_mode |= PAD_KNOB_OCTAVE
        else:
            self.pad_to_mainknob_mode &= ~PAD_KNOB_OCTAVE

    def _action_scale(self, value):
        if value != 0:
            self.pad_to_mainknob_mode |= PAD_KNOB_SCALE
        else:
            self.pad_to_mainknob_mode &= ~PAD_KNOB_SCALE

    def _action_base_note(self, value):
        if value != 0:
            self.pad_to_mainknob_mode |= PAD_KNOB_BASEN
        else:
            self.pad_to_mainknob_mode &= ~PAD_KNOB_BASEN

    def _action_key_color(self, value):
        if value != 0:
            self._parent.step_key_color_mode()

    def _set_volume_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.volume_button != None:
                self.volume_button.remove_value_listener(self._action_volume)
            self.volume_button = button
            self.volume_button != None and self.volume_button.add_value_listener(
                self._action_volume)

    def _action_volume(self, value):
        if not self.volume_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_VOLUME and self._set_mode(
                KN2_MODE_VOLUME)

    def _set_xfade_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.xfade_button != None:
                self.xfade_button.remove_value_listener(self._action_xfade)
            self.xfade_button = button
            self.xfade_button != None and self.xfade_button.add_value_listener(
                self._action_xfade)

    def _action_xfade(self, value):
        if not self.xfade_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_XFADE and self._set_mode(
                KN2_MODE_XFADE)

    def _set_swing_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.swing_button != None:
                self.swing_button.remove_value_listener(self._action_swing)
            self.swing_button = button
            self.swing_button != None and self.swing_button.add_value_listener(
                self._action_swing)

    def _action_swing(self, value):
        if not self.swing_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_QUANT and self._set_mode(
                KN2_MODE_QUANT)

    def _set_tempo_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.tempo_button != None:
                self.tempo_button.remove_value_listener(self._action_tempo)
            self.tempo_button = button
            self.tempo_button != None and self.tempo_button.add_value_listener(
                self._action_tempo)

    def _action_tempo(self, value):
        if not self.tempo_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_TEMPO_COARSE and self._set_mode(
                KN2_MODE_TEMPO_COARSE)

    def _set_clipn_h_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.clipn_h_button != None:
                self.clipn_h_button.remove_value_listener(self._action_clipnh)
            self.clipn_h_button = button
            self.clipn_h_button != None and self.clipn_h_button.add_value_listener(
                self._action_clipnh)

    def _action_clipnh(self, value):
        if not self.clipn_h_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_CLIPN_HOR and self._set_mode(
                KN2_MODE_CLIPN_HOR)

    def _set_clipn_v_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.clipn_v_button != None:
                self.clipn_v_button.remove_value_listener(self._action_clipnv)
            self.clipn_v_button = button
            self.clipn_v_button != None and self.clipn_v_button.add_value_listener(
                self._action_clipnv)

    def _action_clipnv(self, value):
        if not self.clipn_v_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.mode != KN2_MODE_CLIPN_VER and self._set_mode(
                KN2_MODE_CLIPN_VER)

    def _set_shift_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.shift_button != None:
                self.shift_button.remove_value_listener(self._action_shift)
            self.shift_button = button
            self.shift_button != None and self.shift_button.add_value_listener(
                self._action_shift)

    def _action_shift(self, value):
        if not self.shift_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            self.shift_on = value != 0 and not self.shift_on
            self.update_shift()

    def _set_scroll_mod_left_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.scroll_mod_left_button != None:
                self.scroll_mod_left_button.remove_value_listener(
                    self._action_scroll_left)
            self.scroll_mod_left_button = button
            self.scroll_mod_left_button != None and self.scroll_mod_left_button.add_value_listener(
                self._action_scroll_left)

    def _set_scroll_mod_right_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.scroll_mod_right_button != None:
                self.scroll_mod_right_button.remove_value_listener(
                    self._action_scroll_right)
            self.scroll_mod_right_button = button
            self.scroll_mod_right_button != None and self.scroll_mod_right_button.add_value_listener(
                self._action_scroll_right)

    def _action_scroll_left(self, value):
        if not self.scroll_mod_left_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.scroll_mod_left_button.send_value(127, True)
            self.navflags |= LEFT_DOWN
            self._measure_left_click = int(round(time.time() * 1000))
        else:
            self.scroll_mod_left_button.send_value(0, True)
            self.navflags &= ~LEFT_DOWN
            clicktime = int(round(
                time.time() * 1000)) - self._measure_left_click
            if clicktime < CLICK_TIME:
                if self._parent._modifier_down:
                    self._parent.modify_track_offset(-1)
                elif self._parent._mode == PAD_MODE:
                    self._do_lr_as_scale_mode(-1)
                elif self._parent._mode == SCENE_MODE:
                    self._parent.modify_scene_offset(-1)
                elif self._parent._mode == CLIP_MODE:
                    self._parent.move_view_horizontal(-1)
                elif self._parent._mode == CONTROL_MODE:
                    self._parent.move_view_horizontal(-1)

    def _action_scroll_right(self, value):
        if not self.scroll_mod_right_button != None:
            raise AssertionError
            raise value in range(128) or AssertionError
            value != 0 and self.scroll_mod_right_button.send_value(127, True)
            self.navflags |= RIGHT_DOWN
            self._measure_right_click = int(round(time.time() * 1000))
        else:
            self.scroll_mod_right_button.send_value(0, True)
            self.navflags &= ~RIGHT_DOWN
            clicktime = int(round(
                time.time() * 1000)) - self._measure_right_click
            if clicktime < CLICK_TIME:
                if self._parent._modifier_down:
                    self._parent.modify_track_offset(1)
                elif self._parent._mode == PAD_MODE:
                    self._do_lr_as_scale_mode(1)
                elif self._parent._mode == SCENE_MODE:
                    self._parent.modify_scene_offset(1)
                elif self._parent._mode == CLIP_MODE:
                    self._parent.move_view_horizontal(1)
                elif self._parent._mode == CONTROL_MODE:
                    self._parent.move_view_horizontal(1)

    def _do_lr_as_scale_mode(self, delta):
        if self.pad_to_mainknob_mode == PAD_KNOB_SCALE:
            self._parent.inc_scale(delta)
        elif self.pad_to_mainknob_mode == PAD_KNOB_BASEN:
            self._parent.inc_base_note(delta)
        else:
            self._parent.inc_octave(delta)
        self._parent.update_transpose()

    def _set_push_button(self, button):
        if not (button == None or isinstance(button, ButtonElement)):
            raise AssertionError
            if self.push_button != None:
                self.push_button.remove_value_listener(self._action_push)
            self.push_button = button
            self.push_button != None and self.push_button.add_value_listener(
                self._action_push)

    def _action_push(self, value):
        if not self.push_button != None:
            raise AssertionError
            if not value in range(128):
                raise AssertionError
                next_mode = self.mode_assign_map[self.mode][3]
                next_mode != -1 and self._set_mode(next_mode)
            self.loop_div_index = self.lrmode == LR_CONTROL_LOOP and self.navflags != 0 and (
                self.loop_div_index + 1) % len(LOOP_KNOB_DIVISION)
            self._parent.show_message(
                'Loop Selection Granularity : ' +
                str(LOOP_KNOB_DIVISION[self.loop_div_index]) + ' beats ')
            self.loop_incdex = LOOP_KNOB_DIVISION[self.loop_div_index]

    def remove_listener(self, control, callback):
        if control != None and control.value_has_listener(callback):
            control.remove_value_listener(callback)
        control.disconnect()

    def disconnect(self):
        self.remove_listener(self.the_slider, self._do_main_slider)
        self.remove_listener(self.arrow_mode_button, self.toggle_arrow_mode)
        self.remove_listener(self.volume_button, self._action_volume)
        self.remove_listener(self.xfade_button, self._action_xfade)
        self.remove_listener(self.swing_button, self._action_swing)
        self.remove_listener(self.clipn_h_button, self._action_clipnh)
        self.remove_listener(self.clipn_v_button, self._action_clipnv)
        self.remove_listener(self.shift_button, self._action_shift)
        self.remove_listener(self.scroll_mod_left_button,
                             self._action_scroll_left)
        self.remove_listener(self.scroll_mod_right_button,
                             self._action_scroll_right)
        self.remove_listener(self.push_button, self._action_push)
        self.remove_listener(self.octave_mod_button, self._action_octave)
        self.remove_listener(self.scale_mod_button, self._action_scale)
        self.remove_listener(self.basenote_mod_button, self._action_base_note)
        self.remove_listener(self.keycolor_mod_button, self._action_key_color)
        self._parent = None
        self.master_track = None
        self.the_slider = None
        self.mode_assign_map = None
class MaschineJam(ControlSurface):
    """Control Script for Maschine JAM Controller"""
    __module__ = __name__
    _midi_count = 0
    _arm_exclusive = True
    _solo_exclusive = True
    _blink_state = 0
    __midi_count = 0
    __play_button = None
    __block_nav = False
    __matrix_state = None

    def __init__(self, c_instance):
        super(MaschineJam, self).__init__(c_instance)
        with self.component_guard():
            self._suppress_send_midi = True
            register_sender(self)
            self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143
            self._set_suppress_rebuild_requests(True)
            self._c_ref = c_instance
            self.request_rebuild_midi_map()
            self.__matrix_state = MatrixState(self)
            self._main_mode_container = JamModes(c_instance.note_repeat)
            self._set_suppress_rebuild_requests(False)
            self._active = True
            self._display_device_param = False
            self._setup_transport()
            self._setup_session()
            self._encoder_modes = EncoderComponent(self._session)
            self._encoder_modes.connect()
            self._encoder_modes.set_state_listener(self)
            self._modifier = ModifierComponent(self._session)
            self._connect_session()
            self._main_mode_container.bind_session(self.__matrix_state)
            self._main_mode_container.bind_modify_component(self._modifier)
            self._setup_mainjogwheel()
            self._init_m4l()
            self._init_settings()
            self.set_pad_translations(PAD_TRANSLATIONS)
            self.set_feedback_channels(FEEDBACK_CHANNELS)
            self._suppress_send_midi = False
            self._main_mode_container._step_mode.set_mode_elements(self._modifier, self._encoder_modes)
            self._main_mode_container._drum_step_mode.set_mode_elements(self._modifier)
            self._final_init()
            self.apply_preferences()

    def _init_m4l(self):
        self._bmatrix.set_user_unbind_listener(self._main_mode_container)

    def _init_settings(self):
        from pickle import loads, dumps
        from encodings import ascii
        nop(ascii)
        preferences = self._c_instance.preferences(self.preferences_name())
        self._pref_dict = {}
        try:
            self._pref_dict = loads(str(preferences))
        except Exception:
            pass

        pref_dict = self._pref_dict
        preferences.set_serializer(lambda : dumps(pref_dict))

    def store_preferences(self):
        self._pref_dict['matrix_color_mode'] = self._session.get_color_mode()

    def apply_preferences(self):
        pref_dict = self._pref_dict
        if 'matrix_color_mode' in pref_dict:
            self._session.set_color_mode(pref_dict['matrix_color_mode'])

    def preferences_name(self):
        return 'MaschineJam'

    def _final_init(self):
        debug_out('########## LIVE 10 Maschine JAM V 1.3 #############')
        self._auto_button.set_display_value(self.song().session_automation_record and 127 or 0)
        self._main_mode_container.init_elements()

    def _init_map(self):
        msgsysex = [
         240, 0, 33, 9, 21, 0, 77, 80, 0, 1, 2]
        for _ in range(80):
            msgsysex.append(COLOR_BLACK)

        msgsysex.append(247)
        self._send_midi(tuple(msgsysex))

    def _set_touch_strip_led(self):
        msgsysex = [
         240, 0, 33, 9, 21, 0, 77, 80, 0, 1, 4]
        for _ in range(8):
            msgsysex.append(0)

        msgsysex.append(247)
        self._send_midi(tuple(msgsysex))

    def handle_sysex(self, midi_bytes):
        if len(midi_bytes) > 11 and midi_bytes[0:10] == (240, 0, 33, 9, 21, 0, 77,
                                                         80, 0, 1):
            msg, value = midi_bytes[10:12]
            if msg == 70:
                self.refresh_state()
                self._modifier.set_shiftstatus(1)
            elif msg == 77:
                self.shiftButton.notify_value(value == 1 and 127 or 0)
                if not self.shiftButton.is_grabbed:
                    self._modifier.set_shiftstatus(value)

    def _setup_transport(self):
        is_momentary = True
        self.shiftButton = SysExButton(120, name='Shift_Button')
        self.__play_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 108, name='Play_Button')
        self._hand_play_pressed.subject = self.__play_button
        self._listen_playing.subject = self.song()
        self._channel_led_left = SliderElement(MIDI_CC_TYPE, 0, 38)
        self._channel_led_right = SliderElement(MIDI_CC_TYPE, 0, 39)
        self._channel_led_left.last_raw = 0.0
        self._channel_led_left.last_send = 0
        self._channel_led_right.last_raw = 0.0
        self._channel_led_right.last_send = 0
        self._listen_master_left.subject = self.song().master_track
        self._listen_master_right.subject = self.song().master_track
        self._auto_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 98, name='Auto_Button')
        self._listen_automation_record.subject = self.song()
        self._handle_automation_record.subject = self._auto_button
        self._do_direction_up.subject = StateButton(is_momentary, MIDI_CC_TYPE, 0, 40, name='Up_Arrow')
        self._do_direction_down.subject = StateButton(is_momentary, MIDI_CC_TYPE, 0, 41, name='Down_Arrow')
        self._do_direction_left.subject = StateButton(is_momentary, MIDI_CC_TYPE, 0, 42, name='Left_Arrow')
        self._do_direction_right.subject = StateButton(is_momentary, MIDI_CC_TYPE, 0, 43, name='Right_Arrow')

    @subject_slot('output_meter_left')
    def _listen_master_left(self):
        cvl = self.song().master_track.output_meter_left
        if cvl != self._channel_led_left.last_raw:
            val = cvl > 0.92 and 127 or int(127 * (cvl * cvl))
            if val != self._channel_led_left.last_send:
                self._channel_led_left.last_raw = cvl
                self._channel_led_left.last_send = val
                self._channel_led_left.send_value(val, True)

    @subject_slot('output_meter_right')
    def _listen_master_right(self):
        cvl = self.song().master_track.output_meter_right
        if cvl != self._channel_led_right.last_raw:
            val = cvl > 0.92 and 127 or int(127 * (cvl * cvl))
            if val != self._channel_led_right.last_send:
                self._channel_led_right.last_raw = cvl
                self._channel_led_right.last_send = val
                self._channel_led_right.send_value(val, True)

    def is_monochrome(self):
        return False

    def _send_midi(self, midi_bytes, **keys):
        self._c_ref.send_midi(midi_bytes)
        if self._midi_count > 2:
            time.sleep(0.001)
            self._midi_count = 0
        self._midi_count += 1
        return True

    def _setup_mainjogwheel(self):
        self._prev_mode = None
        return

    def is_shift_down(self):
        return self._modifier.is_shift_down()

    def modifier_mask(self):
        return self._modifier.modifier_mask()

    def _setup_session(self):
        self._session = JamSessionComponent()
        self._matrix = []
        self._bmatrix = JamButtonMatrix(8, name='Button_Matrix')
        for sceneIndex in range(8):
            button_row = []
            for trackindex in range(8):
                button = PadColorButton(True, 0, sceneIndex, trackindex, self._main_mode_container)
                button_row.append(button)

            self._matrix.append(tuple(button_row))
            self._bmatrix.add_row(tuple(button_row))

        self._session.set_matrix(self._bmatrix, self._matrix)
        self.__matrix_state.register_matrix(self._bmatrix)
        self._bmatrix.prepare_update()
        for button, (trackIndex, sceneIndex) in self._bmatrix.iterbuttons():
            if button:
                scene = self._session.scene(sceneIndex)
                clip_slot = scene.clip_slot(trackIndex)
                clip_slot.set_launch_button(button)
                clip_slot.set_triggered_to_play_value(1)
                clip_slot.set_triggered_to_record_value(1)
                clip_slot.set_started_value(1)
                clip_slot.set_recording_value(1)
                clip_slot.set_stopped_value(1)

        self._session._link()
        self._bmatrix.commit_update()
        self.set_highlighting_session_component(self._session)

    def _connect_session(self):
        for sindex in range(self._session.height()):
            scene = self._session.scene(sindex)
            for cindex in range(self._session.width()):
                clip = scene.clip_slot(cindex)
                clip.set_modifier(self._modifier)
                clip.set_index((cindex, sindex))

    def update_display(self):
        with self.component_guard():
            self._main_mode_container.notify(self._blink_state)
            self._encoder_modes.notify(self._blink_state)
            self._blink_state = (self._blink_state + 1) % 4

    def refresh_state(self):
        self._bmatrix.prepare_update()
        ControlSurface.refresh_state(self)
        self.update_hardware()
        self._bmatrix.commit_update()

    def update_hardware(self):
        self._session.update()
        self.__play_button.set_display_value(self.song().is_playing and 127 or 0, True)
        self._main_mode_container.refresh_state()
        self._encoder_modes.refresh_state()

    def invoke_nav_left(self):
        self._encoder_modes.invoke_nav_left()

    def invoke_nav_right(self):
        self._encoder_modes.invoke_nav_right()

    def invoke_rec(self):
        slot = self.song().view.highlighted_clip_slot
        if slot == None:
            return
        if slot.controls_other_clips:
            slot.fire()
        else:
            if slot.has_clip:
                track = slot.canonical_parent
                if track.can_be_armed:
                    arm_exclusive(self.song(), track)
                self.song().overdub = True
                slot.fire()
            else:
                track = slot.canonical_parent
                if track.can_be_armed:
                    arm_exclusive(self.song(), track)
                    slot.fire()
        return

    @subject_slot('session_automation_record')
    def _listen_automation_record(self):
        self._auto_button.set_display_value(self.song().session_automation_record and 127 or 0, True)

    @subject_slot('value', identify_sender=True)
    def _handle_automation_record(self, value, sender):
        if value == 0 or sender.grabbed:
            return
        self.song().session_automation_record = not self.song().session_automation_record

    @subject_slot('is_playing')
    def _listen_playing(self):
        if self.song().is_playing:
            self.__play_button.set_display_value(127, True)
        else:
            self.__play_button.set_display_value(0, True)

    @subject_slot('value', identify_sender=True)
    def _hand_play_pressed(self, value, sender):
        if value == 0 or sender.grabbed:
            return
        if self.song().is_playing:
            if self._modifier.is_shift_down():
                self.song().start_playing()
            else:
                self.song().stop_playing()
        else:
            self.song().start_playing()

    @subject_slot('value', identify_sender=True)
    def do_undo(self, value, sender):
        if value == 0 or sender.grabbed:
            return
        if self._modifier.is_shift_down():
            if self.song().can_redo == 1:
                self.song().redo()
                self.show_message(str('REDO'))
        else:
            if self.song().can_undo == 1:
                self.song().undo()
                self.show_message(str('UNDO'))

    def notify_state(self, state, value):
        if state == 'controldown':
            self.__block_nav = value
        else:
            if state == 'step':
                self._main_mode_container.notify_state(state, value)

    @subject_slot('value', identify_sender=True)
    def _do_direction_up(self, value, sender):
        if value == 0 or sender.grabbed:
            return
        if not self.__block_nav:
            self._main_mode_container.navigate(-1, 1, self._modifier.is_shift_down(), NAV_SRC_BUTTON)
        else:
            self._encoder_modes.navigate(-1, 1, self._modifier.is_shift_down(), NAV_SRC_BUTTON)

    @subject_slot('value', identify_sender=True)
    def _do_direction_down(self, value, sender):
        if value == 0 or sender.grabbed:
            return
        if not self.__block_nav:
            self._main_mode_container.navigate(1, 1, self._modifier.is_shift_down(), NAV_SRC_BUTTON)
        else:
            self._encoder_modes.navigate(1, -1, self._modifier.is_shift_down(), NAV_SRC_BUTTON)

    @subject_slot('value', identify_sender=True)
    def _do_direction_left(self, value, sender):
        if value == 0 or sender.grabbed:
            return
        if not self.__block_nav:
            encoder_changed = self._main_mode_container.navigate(-1, 0, self._modifier.is_shift_down(), NAV_SRC_BUTTON)
            if encoder_changed:
                self._encoder_modes.navigate(-1, 0, self._modifier.is_shift_down(), NAV_SRC_BUTTON)
        else:
            self._encoder_modes.navigate(-1, 0, self._modifier.is_shift_down(), NAV_SRC_BUTTON)

    @subject_slot('value', identify_sender=True)
    def _do_direction_right(self, value, sender):
        if value == 0 or sender.grabbed:
            return
        if not self.__block_nav:
            encoder_changed = self._main_mode_container.navigate(1, 0, self._modifier.is_shift_down(), NAV_SRC_BUTTON)
            if encoder_changed:
                self._encoder_modes.navigate(1, 0, self._modifier.is_shift_down(), NAV_SRC_BUTTON)
        else:
            self._encoder_modes.navigate(1, 0, self._modifier.is_shift_down(), NAV_SRC_BUTTON)

    @property
    def selected_mode(self):
        return self._main_mode_container.selected_mode

    @selected_mode.setter
    def selected_mode(self, value):
        self._main_mode_container.selected_mode = value

    def get_session(self):
        return self._session

    def toggle_mode(self):
        self._session.set_color_mode()
        self.refresh_state()

    def get_button_matrix(self):
        return self._bmatrix

    def deassign_matrix(self):
        for scene_index in range(8):
            scene = self._session.scene(scene_index)
            for track_index in range(8):
                clip_slot = scene.clip_slot(track_index)
                clip_slot.set_launch_button(None)

        return

    def _pre_serialize(self):
        from pickle import dumps
        from encodings import ascii
        nop(ascii)
        preferences = self._c_instance.preferences('MaschineJam')
        self.store_preferences()
        dump = dumps(self._pref_dict)
        preferences.set_serializer(lambda : dump)

    def disconnect(self):
        self._pre_serialize()
        self._set_touch_strip_led()
        self._encoder_modes.turn_off_bars()
        self._init_map()
        self._channel_led_left.send_value(0, True)
        self._channel_led_right.send_value(0, True)
        self._active = False
        self._suppress_send_midi = True
        super(MaschineJam, self).disconnect()
        return
 def __init__(self, parent):
     self._parent = parent
     self.master_track = parent.song().master_track
     self.volume_button = None
     self._set_volume_button(StateButton(True, MIDI_CC_TYPE, 3, 110))
     self.the_slider = SliderElement(MIDI_CC_TYPE, 1, 86)
     self.the_slider.add_value_listener(self._do_main_slider, True)
     self.xfade_button = None
     self._set_xfade_button(StateButton(True, MIDI_CC_TYPE, 3, 116))
     self.swing_button = None
     self._set_swing_button(StateButton(True, MIDI_CC_TYPE, 3, 111))
     self.mode = KN2_MODE_VOLUME
     self.previous_mode = -1
     self.tempo_button = None
     self._set_tempo_button(StateButton(True, MIDI_CC_TYPE, 3, 112))
     self.push_button = None
     self._set_push_button(StateButton(True, MIDI_CC_TYPE, 1, 87))
     self.clipn_v_button = None
     self.clipn_h_button = None
     self._set_clipn_h_button(StateButton(True, MIDI_CC_TYPE, 3, 114))
     self._set_clipn_v_button(StateButton(True, MIDI_CC_TYPE, 3, 115))
     self.toggle_buttons = [
         self.volume_button,
         self.xfade_button,
         self.swing_button,
         self.tempo_button,
         self.clipn_h_button,
         self.clipn_v_button,
     ]
     self.shift_button = None
     self._set_shift_button(StateButton(True, MIDI_CC_TYPE, 3, 113))
     self.shift_on = False
     self.scroll_mod_left_button = None
     self.scroll_mod_right_button = None
     self._set_scroll_mod_left_button(StateButton(True, MIDI_CC_TYPE, 0, 105))
     self._set_scroll_mod_right_button(StateButton(True, MIDI_CC_TYPE, 0, 106))
     self._prev_mode = KN2_MODE_VOLUME
     self.lrmode = LR_CONTROL_CLIP
     self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143
     self.loop_div_index = 0
     self.loop_incdex = 4.0
     self.arrow_mode_button = ColorButton(True, MIDI_CC_TYPE, 30)
     self.arrow_mode_button.add_value_listener(self.toggle_arrow_mode)
     self.arrow_mode_button.send_color(LR_MODE_HUES[self.lrmode])
     self.arrow_mode_button.send_value(127, True)
     self.navflags = 0
     self.octave_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 70)
     self.octave_mod_button.add_value_listener(self._action_octave)
     self.scale_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 71)
     self.scale_mod_button.add_value_listener(self._action_scale)
     self.basenote_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 72)
     self.basenote_mod_button.add_value_listener(self._action_base_note)
     self.keycolor_mod_button = StateButton(True, MIDI_CC_TYPE, 1, 73)
     self.keycolor_mod_button.add_value_listener(self._action_key_color)
     self.pad_to_mainknob_mode = 0
     self._measure_left_click = 0
     self._measure_right_click = 0
     self.mode_assign_map = {
         KN2_MODE_VOLUME: (self.chg_volume, 0, "Master Knob controls MASTER Volume", KN2_MODE_CUE),
         KN2_MODE_CUE: (self.chg_cue, 0, "Master Knob controls Cue Level", KN2_MODE_VOLUME),
         KN2_MODE_TEMPO_COARSE: (self.chg_tempo, 3, "Master Knob controls TEMPO Coarse", KN2_MODE_TEMPO_FINE),
         KN2_MODE_TEMPO_FINE: (self.chg_tempo_fine, 3, "Master Knob controls TEMPO Fine", KN2_MODE_TEMPO_COARSE),
         KN2_MODE_XFADE: (self.chg_xfade, 1, "Master Knob controls Crossfader", -1),
         KN2_MODE_QUANT: (self.chg_quant, 2, "Master Knob controls Recording Quantize", KN2_MODE_CLIP_QUANT),
         KN2_MODE_CLIP_QUANT: (self.chg_clip_q, 2, "Master Knob controls Clip Start Quantize", KN2_MODE_QUANT),
         KN2_MODE_CLIPN_HOR: (self.nav_c_hor, 4, "Master Knob Clip View horizontally", -1),
         KN2_MODE_CLIPN_VER: (self.nav_c_ver, 5, "Master Knob Clip View vertically", -1),
         KN2_MODE_GENERAL: (self.chg_general, -1, None, -1),
         KN2_P_SCALES: (self.modify_pad_scaling, -1, None, -1),
     }
Example #36
0
class Maschine(ControlSurface):
    """Basic Control Script for All Maschine Modell Mikro, Mikro Mk2, Mk1, Mk2, Studio"""
    __module__ = __name__
    _has_stop_button = False
    use_shift_pads = False
    __shift_down = False
    __delete_down = False
    __duplicate_down = False
    __select_down = False
    __undo_state = 0
    __redo_state = 0
    __play_button = None
    __nudge_value = 0.25 / 2
    __quantize_setting = 5

    def __init__(self, c_instance):
        super(Maschine, self).__init__(c_instance)
        with self.component_guard():
            register_sender(self)
            self.init_type()
            self._diplay_cache = ['', '', '', '']
            self._timed_text = None
            self._suppress_send_midi = True
            self._modifier = None
            self._c_ref = c_instance
            self.display_task = DisplayTask()
            self._challenge = Live.Application.get_random_int(
                0, 400000000) & 2139062143
            self._active = False
            self._midi_pause_count = 0
            self.blink_state = 0
            self.send_slider_index = 0
            self.nav_index = 0
            self.arm_selected_track = False
            self._set_suppress_rebuild_requests(True)
            self._main_mode_container = ModeHandler(c_instance.note_repeat)
            self._setup_session()
            self.__track_buttons = TrackControlComponent(
                self._session, self, name='Track_Select_Button_Matrix')
            self.__track_buttons.assign_buttons()
            self._setup_transport()
            self._set_global_buttons()
            self._encoder_section = EncoderView()
            self._init_maschine()
            self._init_settings()
            self.set_pad_translations(PAD_TRANSLATIONS)
            self._on_selected_track_changed()
            self.show_message(str(''))
            self.request_rebuild_midi_map()
            self._set_suppress_rebuild_requests(False)
            self._active = True
            self._display_device_param = False
            self._session.set_track_offset_listener(self.__update_tracks)
            self.set_feedback_channels(FEEDBACK_CHANNELS)
            self._final_init()
            self._main_mode_container.init_elements()
            self._suppress_send_midi = False
            self.apply_preferences()
            self.init_text_display()
        return

    def init_type(self):
        """
        Needs to be overridden by specific version to define certain
        specialized behavior
        """
        pass

    def _init_maschine(self):
        pass

    def _final_init(self):
        self._encoder_section.connect()

    def create_pad_button(self, scene_index, track_index, color_source):
        pass

    def create_gated_button(self, identifier, hue):
        pass

    def apply_preferences(self):
        pref_dict = self._pref_dict

    def store_preferences(self):
        pass

    def _init_settings(self):
        from pickle import loads, dumps
        from encodings import ascii
        nop(ascii)
        preferences = self._c_instance.preferences(self.preferences_name())
        self._pref_dict = {}
        try:
            self._pref_dict = loads(str(preferences))
        except Exception:
            pass

        pref_dict = self._pref_dict
        preferences.set_serializer(lambda: dumps(pref_dict))

    def preferences_name(self):
        return 'Maschine'

    def _pre_serialize(self):
        from pickle import dumps
        from encodings import ascii
        nop(ascii)
        preferences = self._c_instance.preferences('Maschine')
        self.store_preferences()
        dump = dumps(self._pref_dict)
        preferences.set_serializer(lambda: dump)

    def _setup_session(self):
        self._session = MaschineSessionComponent()
        self._matrix = []
        self._bmatrix = MaschineButtonMatrix(4, name='Button_Matrix')
        for sceneIndex in range(4):
            button_row = []
            for trackindex in range(4):
                button = PadColorButton(True, 0, sceneIndex, trackindex,
                                        self._main_mode_container)
                button_row.append(button)

            self._matrix.append(tuple(button_row))
            self._bmatrix.add_row(tuple(button_row))

        self._session.set_matrix(self._bmatrix, self._matrix)
        for button, (trackIndex, sceneIndex) in self._bmatrix.iterbuttons():
            if button:
                scene = self._session.scene(sceneIndex)
                clip_slot = scene.clip_slot(trackIndex)
                clip_slot.set_launch_button(button)
                clip_slot.set_triggered_to_play_value(1)
                clip_slot.set_triggered_to_record_value(1)
                clip_slot.set_started_value(1)
                clip_slot.set_recording_value(1)
                clip_slot.set_stopped_value(1)

        for sindex in range(self._session.height()):
            scene = self._session.scene(sindex)
            for cindex in range(self._session.width()):
                clip = scene.clip_slot(cindex)
                clip.set_modifier(self)
                clip.set_index((cindex, sindex))

        self.set_highlighting_session_component(self._session)

    def _set_global_buttons(self):
        self._undo_button = StateButton(True, MIDI_CC_TYPE,
                                        BASE_CONTROL_CHANNEL, CC_UNDO_BUTTON)
        self._do_undo.subject = self._undo_button
        self._redo_button = StateButton(True, MIDI_CC_TYPE,
                                        BASE_CONTROL_CHANNEL, CC_REDO_BUTTON)
        self._do_redo.subject = self._redo_button
        self._shift_button = StateButton(True, MIDI_CC_TYPE,
                                         BASE_CONTROL_CHANNEL, CC_SHIFT_BUTTON)
        self._do_shift.subject = self._shift_button
        self._erase_button = StateButton(True, MIDI_CC_TYPE,
                                         BASE_CONTROL_CHANNEL, CC_ERASE_BUTTON)
        self._do_erase.subject = self._erase_button
        self._duplicate_button = StateButton(True, MIDI_CC_TYPE,
                                             BASE_CONTROL_CHANNEL,
                                             CC_DUPLICATE_BUTTON)
        self._do_duplicate.subject = self._duplicate_button
        self._selectback_button = StateButton(True, MIDI_CC_TYPE,
                                              BASE_CONTROL_CHANNEL,
                                              CC_BACKSEL_BUTTON)
        self._do_selectback.subject = self._selectback_button
        self._event_button = StateButton(True, MIDI_CC_TYPE,
                                         BASE_CONTROL_CHANNEL,
                                         CC_EVENTS_BUTTON)
        self._do_event_button.subject = self._event_button
        self.__arrange_button = StateButton(True,
                                            MIDI_CC_TYPE,
                                            BASE_CONTROL_CHANNEL,
                                            CC_ARRANGE_BUTTON,
                                            name='Arrange_button')
        self.__arrange_button.view_mode = VM_SESSION
        self._do_arrange.subject = self.__arrange_button

    def _setup_transport(self):
        is_momentary = True
        transport = TransportComponent()
        self.__play_button = StateButton(is_momentary,
                                         MIDI_CC_TYPE,
                                         BASE_CONTROL_CHANNEL,
                                         CC_PLAY_BUTTON,
                                         name='Play_Button')
        self.__restart_button = StateButton(True, MIDI_CC_TYPE,
                                            BASE_CONTROL_CHANNEL,
                                            CC_RESTART_BUTTON)
        stop_button = StateButton(not is_momentary,
                                  MIDI_CC_TYPE,
                                  BASE_CONTROL_CHANNEL,
                                  CC_STOP_BUTTON,
                                  name='Stop_Button')
        self._rec_button = StateButton(is_momentary,
                                       MIDI_CC_TYPE,
                                       BASE_CONTROL_CHANNEL,
                                       CC_RECORD_BUTTON,
                                       name='Record_Button')
        metrononme_button = StateButton(is_momentary,
                                        MIDI_CC_TYPE,
                                        BASE_CONTROL_CHANNEL,
                                        CC_METRONOME_BUTTON,
                                        name='Metronome_Button')
        self._song_follow_button = StateButton(is_momentary,
                                               MIDI_CC_TYPE,
                                               BASE_CONTROL_CHANNEL,
                                               CC_FOLLOW_BUTTON,
                                               name='Follow_Button')
        self._do_rec_button.subject = self._rec_button
        if self._has_stop_button:
            transport.set_play_button(self.__play_button)
            transport.set_stop_button(stop_button)
        else:
            self._hand_play_pressed.subject = self.__play_button
            self._listen_playing.subject = self.song()
        self._stopall_button = StateButton(True, MIDI_CC_TYPE, 0,
                                           CC_ALL_BUTTON)
        self._do_stop_all.subject = self._stopall_button
        self._auto_button = StateButton(True,
                                        MIDI_CC_TYPE,
                                        0,
                                        CC_AUTO_BUTTON,
                                        name='Auto_Button')
        self._handle_automation_record.subject = self._auto_button
        self._listen_automation_record.subject = self.song()
        self._handle_restart_button.subject = self.__restart_button
        self._handle_follows_button.subject = self._song_follow_button
        self._follow_song_changed.subject = self.song().view
        transport.set_metronome_button(metrononme_button)
        self._listen_overdub.subject = self.song()

    def toggle_nav_mode(self):
        self._session.switch_step_advance()
        self.show_message(' View Navigation in steps of ' +
                          str(self._session.get_step_advance()))

    def _update_hardware(self):
        self._session.update()
        self.update_undo_redo(True)
        self.__track_buttons.refresh_state()
        self._encoder_section.refresh_state()

    def refresh_state(self):
        ControlSurface.refresh_state(self)
        self._update_hardware()

    def _send_midi(self, midi_bytes, **keys):
        self._c_ref.send_midi(midi_bytes)
        return True

    def timed_display(self, text, grid):
        self.timed_message(grid, text, False)

    def init_text_display(self):
        pass

    def _on_selected_track_changed(self):
        super(Maschine, self)._on_selected_track_changed()
        self.set_controlled_track(self.song().view.selected_track)
        self._on_devices_changed.subject = self.song().view.selected_track

    @subject_slot('devices')
    def _on_devices_changed(self):
        pass

    def update(self):
        self.set_feedback_channels(FEEDBACK_CHANNELS)
        super(Maschine, self).update()

    def is_monochrome(self):
        return False

    def has_separate_pad_mode_buttons(self):
        return False

    def get_session(self):
        return self._session

    def handle_track_select(self, track):
        if not track:
            return
        if self.is_delete_down():
            index = vindexof(self.song().tracks, track)
            self.song().delete_track(index)
        else:
            if self.is_duplicate_down():
                index = vindexof(self.song().tracks, track)
                self.song().duplicate_track(index)
            else:
                if self.is_select_down():
                    if track.is_foldable:
                        track.fold_state = track.fold_state == 0 and 1 or 0
                else:
                    if self.is_shift_down():
                        self.song().view.selected_track = track
                    else:
                        self.song().view.selected_track = track
                        arm_exclusive(self.song(), track)

    def get_button_matrix(self):
        return self._bmatrix

    def deassign_matrix(self):
        for scene_index in range(4):
            scene = self._session.scene(scene_index)
            for track_index in range(4):
                clip_slot = scene.clip_slot(track_index)
                clip_slot.set_launch_button(None)

        return

    def update_display(self):
        with self.component_guard():
            with self._is_sending_scheduled_messages():
                self._task_group.update(0.1)
            self._main_mode_container.notify(self.blink_state)
            self.__track_buttons.notify(self.blink_state)
            self.blink_state = (self.blink_state + 1) % 4
            self.display_task.tick()
            self.update_undo_redo(False)
            if self.blink_state == 0:
                self.update_arrange_button()

    def handle_notify(self, blink_state):
        pass

    def __update_tracks(self, track_offset):
        self.__track_buttons.assign_buttons()
        self._encoder_section.handle_offset_changed()

    def update_undo_redo(self, force=False):
        if force:
            self.__undo_state = self.song().can_undo
            self.__redo_state = self.song().can_redo
        if self.song().can_undo != self.__undo_state:
            self.__undo_state = self.song().can_undo
            self._undo_button.send_value(self.__undo_state == 1 and 127 or 0)
        if self.song().can_redo != self.__redo_state:
            self.__redo_state = self.song().can_redo
            self._redo_button.send_value(self.__redo_state == 1 and 127 or 0)

    def navigate(self, nav_dir, modifier, alt_modifier=False):
        self._main_mode_container.navigate(nav_dir, modifier, alt_modifier)
        self._encoder_section.navigate(nav_dir, modifier, alt_modifier)

    def handle_edit_action(self, grid, scale=None):
        if not self.is_shift_down():
            return
        if grid == (0, 3):
            if self.song().can_undo == 1:
                self.song().undo()
                self.show_message(str('UNDO'))
        else:
            if grid == (1, 3):
                if self.song().can_redo == 1:
                    self.song().redo()
                    self.show_message(str('REDO'))
            else:
                if grid == (2, 3):
                    clip = self.song().view.detail_clip
                    if clip and clip.is_midi_clip:
                        clip.select_all_notes()
                else:
                    if grid == (3, 3):
                        clip = self.song().view.detail_clip
                        if clip and clip.is_midi_clip:
                            clip.deselect_all_notes()
                    else:
                        if grid == (0, 2) or grid == (1, 2):
                            clip = self.song().view.detail_clip
                            if clip:
                                clip.quantize(
                                    QUANT_CONST[self.__quantize_setting],
                                    grid[0] == 0 and 1.0 or 0.5)
                                self.show_message(
                                    'Quantize Clip ' + clip.name + ' by ' +
                                    QUANT_STRING[self.__quantize_setting])
                        else:
                            if grid == (2, 2):
                                clip = self.song().view.detail_clip
                                if clip and clip.is_midi_clip:
                                    self.application().view.show_view(
                                        'Detail/Clip')
                                    nudge_notes_in_clip(
                                        clip, clip.get_selected_notes(),
                                        self.__nudge_value * -1)
                            else:
                                if grid == (3, 2):
                                    clip = self.song().view.detail_clip
                                    if clip and clip.is_midi_clip:
                                        self.application().view.show_view(
                                            'Detail/Clip')
                                        nudge_notes_in_clip(
                                            clip, clip.get_selected_notes(),
                                            self.__nudge_value)
                                else:
                                    if grid == (0, 1):
                                        clip = self.song().view.detail_clip
                                        if clip and clip.is_midi_clip:
                                            clip.replace_selected_notes(
                                                tuple([]))
                                    else:
                                        if grid == (1, 1):
                                            clip = self.song().view.detail_clip
                                            if clip:
                                                clip.has_envelopes and clip.clear_all_envelopes(
                                                )
                                        else:
                                            if grid[1] == 0:
                                                clip = self.song(
                                                ).view.detail_clip
                                                if clip and clip.is_midi_clip:
                                                    track = clip.canonical_parent.canonical_parent
                                                    drum_device = find_drum_device(
                                                        track)
                                                    if not drum_device:
                                                        notes = clip.get_selected_notes(
                                                        )
                                                        transpose_notes_in_clip(
                                                            clip, notes,
                                                            TRANSPOSE[grid[0]],
                                                            None)
        return

    @subject_slot('is_playing')
    def _listen_playing(self):
        if self.song().is_playing:
            self.__play_button.set_display_value(127, True)
        else:
            self.__play_button.set_display_value(0, True)

    @subject_slot('value')
    def _hand_play_pressed(self, value):
        if value != 0:
            if self.song().is_playing:
                if self.is_shift_down():
                    self.song().start_playing()
                else:
                    self.song().stop_playing()
            else:
                self.song().start_playing()

    @subject_slot('value')
    def _do_undo(self, value):
        if value != 0:
            if self.use_layered_buttons() and self.is_shift_down():
                if self.song().can_redo == 1:
                    self.song().redo()
                    self.show_message(str('REDO'))
            elif self.song().can_undo == 1:
                self.song().undo()
                self.show_message(str('UNDO'))

    @subject_slot('value')
    def _do_redo(self, value):
        if value != 0:
            if self.song().can_redo == 1:
                self.song().redo()
                self.show_message(str('REDO'))

    def handle_modifier(self, value):
        self._main_mode_container.handle_modifier(value)
        self._encoder_section.notify_shift(value)

    def notify_shift(self, value):
        self.__shift_down = value
        self.handle_modifier(self.get_modifier_state())

    @subject_slot('value')
    def _do_shift(self, value):
        self._shift_button.send_value(value)
        self.__shift_down = value > 0
        self.handle_modifier(self.get_modifier_state())

    @subject_slot('value')
    def _do_erase(self, value):
        self._erase_button.send_value(value)
        self.__delete_down = value > 0

    @subject_slot('value')
    def _do_duplicate(self, value):
        self._duplicate_button.send_value(value)
        self.__duplicate_down = value > 0

    @subject_slot('value')
    def _do_selectback(self, value):
        self._selectback_button.send_value(value)
        self.__select_down = value > 0

    @subject_slot('overdub')
    def _listen_overdub(self):
        self._rec_button.set_display_value(self.song().overdub and 127 or 0,
                                           True)

    @subject_slot('value')
    def _do_rec_button(self, value):
        if self.is_shift_down():
            if value != 0:
                self.song().overdub = not self.song().overdub
        else:
            self.invoke_rec()

    def invoke_rec(self):
        slot = self.song().view.highlighted_clip_slot
        if slot == None:
            return
        if slot.controls_other_clips:
            slot.fire()
        else:
            if slot.has_clip:
                track = slot.canonical_parent
                if track.can_be_armed:
                    arm_exclusive(self.song(), track)
                self.song().overdub = True
                slot.fire()
            else:
                track = slot.canonical_parent
                if track.can_be_armed:
                    arm_exclusive(self.song(), track)
                    slot.fire()
        return

    @subject_slot('value')
    def _do_fire_button(self, value):
        assert self._fire_button != None
        assert value in range(128)
        if value != 0:
            if self.is_shift_down():
                self.song().tap_tempo()
            else:
                clip_slot = self.song().view.highlighted_clip_slot
                if clip_slot:
                    clip_slot.fire()
        return

    @subject_slot('value')
    def _do_stop_all(self, value):
        self._do_stop_all.send_value(value)
        if value != 0:
            if self.is_shift_down():
                self.song().stop_all_clips(0)
            else:
                self.song().stop_all_clips(1)

    @subject_slot('value')
    def _do_test(self, value):
        pass

    @subject_slot('value')
    def _do_arrange(self, value):
        if value != 0:
            appv = self.application().view
            if appv.is_view_visible('Arranger'):
                self.application().view.show_view('Session')
            else:
                self.application().view.show_view('Arranger')
            self.update_arrange_button()

    def update_arrange_button(self):
        appv = self.application().view
        if appv.is_view_visible('Arranger'):
            if self.__arrange_button.view_mode == VM_SESSION:
                self.__arrange_button.set_display_value(127, True)
                self.__arrange_button.view_mode = VM_ARRANGE
        else:
            if self.__arrange_button.view_mode == VM_ARRANGE:
                self.__arrange_button.set_display_value(0, True)
                self.__arrange_button.view_mode = VM_SESSION

    @subject_slot('session_automation_record')
    def _listen_automation_record(self):
        self._auto_button.set_display_value(
            self.song().session_automation_record and 127 or 0, True)

    @subject_slot('value')
    def _do_event_button(self, value):
        self._event_button.send_value(value)
        if value != 0:
            if self.is_shift_down():
                clip = self.song().view.detail_clip
                if clip and clip.is_midi_clip:
                    clip.select_all_notes()

    @subject_slot('value')
    def _handle_follows_button(self, value):
        if value != 0:
            self.song().view.follow_song = not self.song().view.follow_song

    @subject_slot('follow_song')
    def _follow_song_changed(self):
        self._song_follow_button.send_value(
            self.song().view.follow_song and 127 or 0)

    @subject_slot('value')
    def _handle_restart_button(self, value):
        self.__restart_button.send_value(value)
        if value != 0:
            self.song().stop_playing()
            self.song().stop_playing()

    @subject_slot('value')
    def _handle_automation_record(self, value):
        if value > 0:
            self.song().session_automation_record = not self.song(
            ).session_automation_record

    def handle_edit(self, clipslotcomp, value):
        self._main_mode_container.handle_edit(clipslotcomp, value)

    def do_midi_launch(self, clipslotcomp, value):
        if self.is_shift_down():
            clipslotcomp._do_launch_clip(value)
        else:
            if clipslotcomp._clip_slot.has_clip:
                clipslotcomp._do_launch_clip(value)
            else:
                clipslotcomp._clip_slot.create_clip(DEFAULT_CLIP_INIT_SIZE)
                clipslotcomp._do_launch_clip(value)

    def is_select_down(self):
        return self.__select_down

    def is_shift_down(self):
        return self.__shift_down

    def is_delete_down(self):
        return self.__delete_down

    def is_duplicate_down(self):
        return self.__duplicate_down

    def get_modifier_state(self):
        return (self.__shift_down and MODIFIER_SHIFT) | (
            self.__select_down
            and MODIFIER_SELECT) | (self.__delete_down and MODIFIER_DELETE) | (
                self.__duplicate_down and MODIFIER_DUPLICATE)

    def get_track_buttons(self):
        return self.__track_buttons

    def in_spec_mode(self):
        """
        If Some Modifier is being held down. This concerns only the grid matrix.
        In Cases where Shift + Pad Button do not call a function (Maschine Studio only)
        Nothing should happen just like with the Maschine Software
        """
        return self.__shift_down or self.__delete_down or self.__duplicate_down

    def use_shift_matrix(self):
        return False

    def use_layered_buttons(self):
        return False

    def to_color_edit_mode(self, active):
        pass

    def clear_display_all(self):
        self.send_to_display('', 0)
        self.send_to_display('', 1)
        self.send_to_display('', 2)
        self.send_to_display('', 3)

    def clear_display(self, grid):
        if self._timed_text:
            self.send_to_display(self._timed_text, grid)
        else:
            self.send_to_display('', grid)

    def timed_message(self, grid, text, hold=False):
        if USE_DISPLAY == False:
            self.show_message(text)
        else:
            if not self.display_task.active(grid):
                self._timed_text = self._diplay_cache[grid]
            self.display_task.set_func(self.clear_display, grid)
            self.send_to_display(text, grid)
            if hold:
                self.display_task.hold()
            self.display_task.start()

    def timed_message_release(self):
        self.display_task.release()

    def update_bank_display(self):
        if USE_DISPLAY:
            name, bank = self._device._current_bank_details()
            if self._display_device_param:
                prms = len(bank)
                d1 = ''
                for i in range(4):
                    parm = bank[i]
                    if parm:
                        name = parm.name
                        d1 += name[:6] + (i < 3 and '|' or '')
                    else:
                        d1 += '      ' + (i < 3 and '|' or '')

                self.send_to_display(d1, 2)
                d1 = ''
                for i in range(4):
                    parm = bank[i + 4]
                    if parm:
                        name = parm.name
                        d1 += name[:6] + (i < 3 and '|' or '')
                    else:
                        d1 += '      ' + (i < 3 and '|' or '')

                self.send_to_display(d1, 4)
            else:
                self.timed_message(2, 'Bank: ' + name)

    def display_parameters(self, paramlist):
        if USE_DISPLAY == False:
            return

    def send_to_display(self, text, grid=0, force=False):
        if USE_DISPLAY == False:
            return
        if not force and self._diplay_cache[grid] == text:
            return
        self._diplay_cache[grid] = text
        if len(text) > 28:
            text = text[:27]
        msgsysex = [240, 0, 0, 102, 23, 18, min(grid, 3) * 28]
        filled = text.ljust(28)
        for c in filled:
            msgsysex.append(ord(c))

        msgsysex.append(247)
        self._send_midi(tuple(msgsysex))

    def cleanup(self):
        pass

    def disconnect(self):
        self._pre_serialize()
        self.clear_display_all()
        for button, (track_index, _) in self._bmatrix.iterbuttons():
            if button:
                button.turn_off()

        time.sleep(0.2)
        self._active = False
        self._suppress_send_midi = True
        super(Maschine, self).disconnect()
        return
Example #37
0
 def _setup_transport(self):
     is_momentary = True
     transport = TransportComponent()
     studiotransport = MaschineTransport()
     playButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 108)
     stopButton = StateButton(not is_momentary, MIDI_CC_TYPE, 0, 110)
     recordButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 109)
     overdubButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 107)
     metrononmeButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 104)
     eventRecButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 98)
     playButton.name = 'Play'
     stopButton.name = 'Stop'
     recordButton.name = 'Record'
     overdubButton.name = 'Overdub'
     metrononmeButton.name = 'Metronome'
     transport.set_play_button(playButton)
     transport.set_stop_button(stopButton)
     transport.set_record_button(recordButton)
     transport.set_overdub_button(overdubButton)
     transport.set_metronome_button(metrononmeButton)
     studiotransport.set_session_auto_button(eventRecButton)
     studiotransport.set_arrangement_overdub_button(
         StateButton(is_momentary, MIDI_CC_TYPE, 0, 106))
     studiotransport.set_back_arrange_button(
         StateButton(is_momentary, MIDI_CC_TYPE, 0, 105))
     transport.set_nudge_buttons(
         StateButton(is_momentary, MIDI_CC_TYPE, 1, 51),
         StateButton(is_momentary, MIDI_CC_TYPE, 1, 50))
     punchinbutton = ToggleButton(MIDI_CC_TYPE, 1, 52)
     punchoutbutton = ToggleButton(MIDI_CC_TYPE, 1, 53)
     punchinbutton.name = 'Punch In'
     punchoutbutton.name = 'Punch Out'
     transport.set_punch_buttons(punchinbutton, punchoutbutton)
     transport.set_loop_button(
         StateButton(is_momentary, MIDI_CC_TYPE, 1, 54))
     self.song_follow_button = ButtonElement(True, MIDI_CC_TYPE, 2, 98)
     self._do_song_follow.subject = self.song_follow_button
     self._song_follow_changed.subject = self.song().view
     self._song_follow_changed()
     self.transp_ff_button = ButtonElement(True, MIDI_CC_TYPE, 1, 59)
     self.transp_rw_button = ButtonElement(True, MIDI_CC_TYPE, 1, 58)
     transport.set_seek_buttons(self.transp_ff_button,
                                self.transp_rw_button)
     self.xfadeKnob = SliderElement(MIDI_CC_TYPE, 1, 105)
     self.xfadeKnob.connect_to(
         self.song().master_track.mixer_device.crossfader)
     self.master_knob = SliderElement(MIDI_CC_TYPE, 0, 99)
     self.master_knob.connect_to(
         self.song().master_track.mixer_device.volume)
     self.tap_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 88)
     self._do_tap_tempo.subject = self.tap_button
     self.cue_add_delete_button = StateButton(is_momentary, MIDI_CC_TYPE, 1,
                                              55)
     self.cue_prev_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 56)
     self.cue_next_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 57)
     self._do_toggle_cue.subject = self.cue_add_delete_button
     self._do_toggle_prev_cue.subject = self.cue_prev_button
     self._do_toggle_next_cue.subject = self.cue_next_button
Example #38
0
class MaschineMk3(Maschine):
    """Control Script for Maschine Studio"""
    __module__ = __name__
    _gated_buttons = []

    def __init__(self, c_instance):
        super(MaschineMk3, self).__init__(c_instance)

    def init_type(self):
        """
        Needs to be overridden by specific version to define certain
        specialized behavior
        """
        self._has_stop_button = True

    def _init_maschine(self):
        self.__jog_wheel_section = JogWheelSection()
        self.__metro_tap_button = StateButton(True,
                                              MIDI_CC_TYPE,
                                              BASE_CONTROL_CHANNEL,
                                              CC_TAP_METRO_BUTTON,
                                              name='Tap_metro_button')
        self._do_tap_metro.subject = self.__metro_tap_button
        self.update_arrange_button()

    def _final_init(self):
        debug_out('########## LIVE 9 MASCHINE Mk3 V 0.50  ############# ')
        super(MaschineMk3, self)._final_init()

    def use_shift_matrix(self):
        return True

    def has_separate_pad_mode_buttons(self):
        return True

    def handle_sysex(self, midi_bytes):
        if len(midi_bytes) > 11 and midi_bytes[0:10] == (240, 0, 33, 9, 22, 0,
                                                         77, 80, 0, 1):
            msg, value = midi_bytes[10:12]
            if msg == 70:
                self.refresh_state()
                self.notify_shift(True)
            elif msg == 77:
                self.notify_shift(value == 1)

    def _click_measure(self):
        pass

    def preferences_name(self):
        return 'MaschineMk3'

    def apply_preferences(self):
        super(MaschineMk3, self).apply_preferences()

    def store_preferences(self):
        super(MaschineMk3, self).store_preferences()

    def _send_midi(self, midi_bytes, **keys):
        self._c_ref.send_midi(midi_bytes)
        time.sleep(0.001)
        return True

    @subject_slot('value')
    def _do_tap_metro(self, value):
        if self.is_shift_down():
            if value == 0:
                return
            self.song().metronome = not self.song().metronome
        else:
            if value != 0:
                self.song().tap_tempo()
            self.__metro_tap_button.send_value(value)

    @subject_slot('value')
    def _do_stop_all(self, value):
        if value != 0:
            if self.isShiftDown():
                self.song().stop_all_clips(0)
            else:
                self.song().stop_all_clips(1)

    def to_color_edit_mode(self, active):
        if self._editsection.is_color_edit() != active:
            self._editsection.set_color_edit(active)

    def cleanup(self):
        for button in self._gated_buttons:
            button.switch_off()
Example #39
0
class Maschine(ControlSurface):
    """Basic Control Script for All Maschine Modell Mikro, Mikro Mk2, Mk1, Mk2, Studio"""
    __module__ = __name__

    def __init__(self, c_instance):
        super(Maschine, self).__init__(c_instance)
        with self.component_guard():
            register_sender(self)
            self._diplay_cache = ['', '', '', '']
            self._suppress_send_midi = True
            is_momentary = True
            self._c_ref = c_instance
            self.display_task = DisplayTask()
            self._challenge = Live.Application.get_random_int(
                0, 400000000) & 2139062143
            self._active = False
            self._midi_pause_count = 0
            self.blink_state = 0
            self.send_slider_index = 0
            self.nav_index = 0
            self.arm_selected_track = False
            self.undo_state = 0
            self.redo_state = 0
            self._set_suppress_rebuild_requests(True)
            self._modeselect = ModeSelector(self.is_monochrome())
            self._device = self._set_up_device_control()
            self._set_up_session(self._modeselect)
            self._set_up_mixer()
            self._setup_transport()
            self._set_global_buttons()
            self._editsection = EditSection()
            self._editsection.connect_session(self._session)
            self._editsection.set_mode_selector(self._modeselect)
            self._session.set_mode(self._modeselect._clip_mode)
            self._audio_clip_editor = AudioClipEditComponent()
            self._note_repeater = NoteRepeatComponent(c_instance.note_repeat)
            self._midi_edit = MidiEditSection()
            self._init_settings()
            self._init_maschine()
            self.set_highlighting_session_component(self._session)
            self.set_pad_translations(PAD_TRANSLATIONS)
            self._on_selected_track_changed()
            self.set_up_function_buttons()
            self.show_message(str(''))
            self.request_rebuild_midi_map()
            self._set_suppress_rebuild_requests(False)
            self._active = True
            self._display_device_param = False
            self.set_feedback_channels(FEEDBACK_CHANNELS)
            self._final_init()
            self._suppress_send_midi = False
            self.apply_preferences()
            self.init_text_display()
            self._on_appointed_device_changed.subject = self.song()

    def _init_maschine(self):
        pass

    def _final_init(self):
        pass

    def create_pad_button(self, scene_index, track_index, color_source):
        pass

    def create_gated_button(self, identifier, hue):
        pass

    def apply_preferences(self):
        pref_dict = self._pref_dict
        if 'step_advance' in pref_dict:
            self._session.set_step_advance(pref_dict['step_advance'])
        if 'solo_exclusive' in pref_dict:
            self._modeselect.set_solo_exclusive(pref_dict['solo_exclusive'])
        else:
            self._modeselect.set_solo_exclusive(True)
        if 'arm_exclusive' in pref_dict:
            self._modeselect.set_arm_exclusive(pref_dict['arm_exclusive'])
        else:
            self._modeselect.set_arm_exclusive(True)
        if 'quantize_val' in pref_dict:
            self._editsection.quantize = pref_dict['quantize_val']
        else:
            self._editsection.quantize = 5
        if 'initial_cliplen' in pref_dict:
            self._editsection.initial_clip_len = pref_dict['initial_cliplen']
        else:
            self._editsection.initial_clip_len = 4.0
        if 'auto_arm_sel_track' in pref_dict:
            self.arm_selected_track = pref_dict['auto_arm_sel_track']
        else:
            self.arm_selected_track = False
        if 'note_color_mode' in pref_dict:
            self._modeselect._pad_mode._note_display_mode = pref_dict[
                'note_color_mode']
        else:
            self._modeselect._pad_mode._note_display_mode = ND_KEYBOARD1
        self._pref_dict[
            'note_color_mode'] = self._modeselect._pad_mode._note_display_mode
        self.set_sel_arm_button.send_value(
            self.arm_selected_track and 127 or 0, True)
        self._note_repeater.recall_values(self._pref_dict)

    def store_preferences(self):
        self._pref_dict['step_advance'] = self._session.get_step_advance()
        self._pref_dict['solo_exclusive'] = self._modeselect.is_solo_exclusive(
        )
        self._pref_dict['arm_exclusive'] = self._modeselect.is_arm_exclusive()
        self._pref_dict['quantize_val'] = self._editsection.quantize
        self._pref_dict['initial_cliplen'] = self._editsection.initial_clip_len
        self._pref_dict['auto_arm_sel_track'] = self.arm_selected_track
        self._pref_dict[
            'note_color_mode'] = self._modeselect._pad_mode._note_display_mode
        self._note_repeater.store_values(self._pref_dict)

    def _init_settings(self):
        from pickle import loads, dumps
        from encodings import ascii
        nop(ascii)
        preferences = self._c_instance.preferences(self.preferences_name())
        self._pref_dict = {}
        try:
            self._pref_dict = loads(str(preferences))
        except Exception:
            pass

        pref_dict = self._pref_dict
        preferences.set_serializer(lambda: dumps(pref_dict))

    def preferences_name(self):
        return 'Maschine'

    def _pre_serialize(self):
        from pickle import dumps
        from encodings import ascii
        nop(ascii)
        preferences = self._c_instance.preferences('Maschine')
        self.store_preferences()
        dump = dumps(self._pref_dict)
        preferences.set_serializer(lambda: dump)

    def toggle_nav_mode(self):
        self._session.switch_step_advance()
        self.show_message(' View Navigation in steps of ' +
                          str(self._session.get_step_advance()))

    def _set_up_session(self, mode_selector):
        is_momentary = True
        self._session = MaschineSessionComponent()
        self._session.set_color_manager(mode_selector.get_color_manager())
        self.nav_buttons = (self.create_gated_button(92, COLOR_HUE_NAV),
                            self.create_gated_button(81, COLOR_HUE_NAV),
                            self.create_gated_button(93, COLOR_HUE_NAV),
                            self.create_gated_button(91, COLOR_HUE_NAV))
        self._session.set_scene_bank_buttons(self.nav_buttons[0],
                                             self.nav_buttons[1])
        self._session.set_track_bank_buttons(self.nav_buttons[2],
                                             self.nav_buttons[3])
        track_stop_buttons = [
            StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL,
                        index + STOP_CC_OFF) for index in range(4)
        ]
        self._session.set_stop_track_clip_buttons(tuple(track_stop_buttons))
        self._matrix = []
        self._bmatrix = ButtonMatrixElement()
        for scene_index in range(4):
            button_row = []
            for track_index in range(4):
                button = self.create_pad_button(scene_index, track_index,
                                                mode_selector)
                button_row.append(button)

            self._matrix.append(tuple(button_row))
            self._bmatrix.add_row(tuple(button_row))

        self._session.set_matrix(self._matrix)
        for button, (track_index, scene_index) in self._bmatrix.iterbuttons():
            if button:
                scene = self._session.scene(scene_index)
                clip_slot = scene.clip_slot(track_index)
                clip_slot.set_launch_button(button)
                clip_slot.set_triggered_to_play_value(1)
                clip_slot.set_triggered_to_record_value(1)
                clip_slot.set_started_value(1)
                clip_slot.set_recording_value(1)
                clip_slot.set_stopped_value(1)

        self._session._link()

    def _set_up_mixer(self):
        is_momentary = True
        self._mixer = MaschineMixerComponent(8)
        self.send_sliders = []
        for track in range(8):
            self.send_sliders.append(
                SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL,
                              SEND_CC_OFF + track))

        for track in range(8):
            strip = self._mixer.channel_strip(track)
            strip.set_arm_button(
                StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL,
                            ARM_CC_OFF + track))
            strip.set_solo_button(
                StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL,
                            SOLO_CC_OFF + track))
            strip.set_mute_button(
                StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL,
                            MUTE_CC_OFF + track))
            strip.set_volume_control(
                SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL,
                              LEVEL_CC_OFF + track))
            strip.set_pan_control(
                SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL, PAN_CC_OFF + track))
            strip.set_select_button(
                StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL,
                            SELECT_CC_OFF + track))
            st = tuple([self.send_sliders[track]])
            strip.set_send_controls(st)

        self.send_slider_toggle_button = StateButton(False, MIDI_CC_TYPE, 0,
                                                     90)
        self._do_toggle_send.subject = self.send_slider_toggle_button
        self._session.set_mixer(self._mixer)

    def _set_global_buttons(self):
        is_momentary = True
        self._undo_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 85)
        self._do_undo.subject = self._undo_button
        self._redo_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 87)
        self._do_redo.subject = self._redo_button
        self._stop_all_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 111)
        self._do_stop_all.subject = self._stop_all_button
        self._toggle_detail_button = ButtonElement(is_momentary, MIDI_CC_TYPE,
                                                   1, 121)
        self._action_toogle_detail_view.subject = self._toggle_detail_button
        self._fire_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 9)
        self._do_fire_button.subject = self._fire_button
        self._g_clear_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1,
                                             106)
        self._hold_clear_action.subject = self._g_clear_button
        self._g_duplicate_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1,
                                                 107)
        self._hold_duplicate_action.subject = self._g_duplicate_button
        self.track_left_button = StateButton(is_momentary, MIDI_CC_TYPE, 0,
                                             120)
        self.track_right_button = StateButton(is_momentary, MIDI_CC_TYPE, 0,
                                              121)
        self.set_sel_arm_button = StateButton(is_momentary, MIDI_CC_TYPE, 2,
                                              56)
        self._reenable_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1,
                                              120)
        self._do_auto_reenable.subject = self._reenable_button
        self._on_change_reenabled.subject = self.song()
        self._on_change_reenabled()
        self._a_trk_left.subject = self.track_left_button
        self._a_trk_right.subject = self.track_right_button
        self._a_sel_arm.subject = self.set_sel_arm_button

    def _set_up_device_control(self):
        is_momentary = True
        device = MaschineDeviceComponent()
        param_controls = []
        for index in range(8):
            param_controls.append(
                SliderElement(MIDI_CC_TYPE, BASIC_CHANNEL,
                              DEVICE_CC_OFF + index))

        device.set_parameter_controls(tuple(param_controls))
        self.device_control = param_controls
        device.set_on_off_button(
            StateButton(is_momentary, MIDI_CC_TYPE, BASIC_CHANNEL,
                        DEVICE_BUTTON_CC_OFF))
        device.set_bank_nav_buttons(
            StateButton(is_momentary, MIDI_CC_TYPE, 3, 104),
            ButtonElement(is_momentary, MIDI_CC_TYPE, 3, 105))
        self._device_nav_button_left = StateButton(is_momentary, MIDI_CC_TYPE,
                                                   3, 106)
        self._device_nav_button_right = StateButton(is_momentary, MIDI_CC_TYPE,
                                                    3, 107)
        self._navigate_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 127)
        self._nav_value_left.subject = self._device_nav_button_left
        self._nav_value_right.subject = self._device_nav_button_right
        self._do_focus_navigate.subject = self._navigate_button
        self.set_device_component(device)
        return device

    def _setup_transport(self):
        is_momentary = True
        transport = TransportComponent()
        studiotransport = MaschineTransport()
        playButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 108)
        stopButton = StateButton(not is_momentary, MIDI_CC_TYPE, 0, 110)
        recordButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 109)
        overdubButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 107)
        metrononmeButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 104)
        eventRecButton = StateButton(is_momentary, MIDI_CC_TYPE, 0, 98)
        playButton.name = 'Play'
        stopButton.name = 'Stop'
        recordButton.name = 'Record'
        overdubButton.name = 'Overdub'
        metrononmeButton.name = 'Metronome'
        transport.set_play_button(playButton)
        transport.set_stop_button(stopButton)
        transport.set_record_button(recordButton)
        transport.set_overdub_button(overdubButton)
        transport.set_metronome_button(metrononmeButton)
        studiotransport.set_session_auto_button(eventRecButton)
        studiotransport.set_arrangement_overdub_button(
            StateButton(is_momentary, MIDI_CC_TYPE, 0, 106))
        studiotransport.set_back_arrange_button(
            StateButton(is_momentary, MIDI_CC_TYPE, 0, 105))
        transport.set_nudge_buttons(
            StateButton(is_momentary, MIDI_CC_TYPE, 1, 51),
            StateButton(is_momentary, MIDI_CC_TYPE, 1, 50))
        punchinbutton = ToggleButton(MIDI_CC_TYPE, 1, 52)
        punchoutbutton = ToggleButton(MIDI_CC_TYPE, 1, 53)
        punchinbutton.name = 'Punch In'
        punchoutbutton.name = 'Punch Out'
        transport.set_punch_buttons(punchinbutton, punchoutbutton)
        transport.set_loop_button(
            StateButton(is_momentary, MIDI_CC_TYPE, 1, 54))
        self.song_follow_button = ButtonElement(True, MIDI_CC_TYPE, 2, 98)
        self._do_song_follow.subject = self.song_follow_button
        self._song_follow_changed.subject = self.song().view
        self._song_follow_changed()
        self.transp_ff_button = ButtonElement(True, MIDI_CC_TYPE, 1, 59)
        self.transp_rw_button = ButtonElement(True, MIDI_CC_TYPE, 1, 58)
        transport.set_seek_buttons(self.transp_ff_button,
                                   self.transp_rw_button)
        self.xfadeKnob = SliderElement(MIDI_CC_TYPE, 1, 105)
        self.xfadeKnob.connect_to(
            self.song().master_track.mixer_device.crossfader)
        self.master_knob = SliderElement(MIDI_CC_TYPE, 0, 99)
        self.master_knob.connect_to(
            self.song().master_track.mixer_device.volume)
        self.tap_button = StateButton(is_momentary, MIDI_CC_TYPE, 0, 88)
        self._do_tap_tempo.subject = self.tap_button
        self.cue_add_delete_button = StateButton(is_momentary, MIDI_CC_TYPE, 1,
                                                 55)
        self.cue_prev_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 56)
        self.cue_next_button = StateButton(is_momentary, MIDI_CC_TYPE, 1, 57)
        self._do_toggle_cue.subject = self.cue_add_delete_button
        self._do_toggle_prev_cue.subject = self.cue_prev_button
        self._do_toggle_next_cue.subject = self.cue_next_button

    def set_up_function_buttons(self):
        is_momentary = True
        self.keycolor_mod_button = StateButton(is_momentary, MIDI_CC_TYPE, 1,
                                               73)
        self._do_key_color.subject = self.keycolor_mod_button
        self._update_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 86)
        self._do_update_display.subject = self._update_button

    @subject_slot('appointed_device')
    def _on_appointed_device_changed(self):
        self._modeselect._device_changed()

    def _update_hardware(self):
        self._session.update()
        self._modeselect.refresh()
        self.update_undo_redo(True)

    def refresh_state(self):
        ControlSurface.refresh_state(self)
        self._update_hardware()

    def _send_midi(self, midi_bytes, **keys):
        self._c_ref.send_midi(midi_bytes)
        return True

    def init_text_display(self):
        if USE_DISPLAY:
            self._modeselect._pad_mode.update_text_display()

    def _on_selected_track_changed(self):
        super(Maschine, self)._on_selected_track_changed()
        self.set_controlled_track(self.song().view.selected_track)
        self._on_devices_changed.subject = self.song().view.selected_track

    @subject_slot('devices')
    def _on_devices_changed(self):
        pass

    def update(self):
        self.set_feedback_channels(FEEDBACK_CHANNELS)
        super(Maschine, self).update()

    def is_monochrome(self):
        return False

    def _deassign_matrix(self):
        for scene_index in range(4):
            scene = self._session.scene(scene_index)
            for track_index in range(4):
                clip_slot = scene.clip_slot(track_index)
                clip_slot.set_launch_button(None)

        return

    def update_display(self):
        with self.component_guard():
            with self._is_sending_scheduled_messages():
                self._task_group.update(0.1)
            self._modeselect.notify(self.blink_state)
            self.blink_state = (self.blink_state + 1) % 4
            self.display_task.tick()
            self.update_undo_redo(False)

    def update_undo_redo(self, force=False):
        if force:
            self.undo_state = self.song().can_undo
            self.redo_state = self.song().can_redo
        if self.song().can_undo != self.undo_state:
            self.undo_state = self.song().can_undo
            self._undo_button.send_value(self.undo_state == 1 and 127 or 0)
        if self.song().can_redo != self.redo_state:
            self.redo_state = self.song().can_redo
            self._redo_button.send_value(self.redo_state == 1 and 127 or 0)

    def adjust_loop_start(self, delta):
        loopval = self.song().loop_start
        self.song().loop_start = min(self.song().song_length,
                                     max(0, loopval + delta))

    def adjust_loop_length(self, delta):
        loopval = self.song().loop_length
        self.song().loop_length = min(self.song().song_length,
                                      max(abs(delta), loopval + delta))

    def _do_armsolo_mode(self, value):
        pass

    @subject_slot('value')
    def _do_fire_button(self, value):
        assert self._fire_button != None
        assert value in range(128)
        if value != 0:
            if self.isShiftDown():
                self.song().tap_tempo()
            else:
                clip_slot = self.song().view.highlighted_clip_slot
                if clip_slot:
                    clip_slot.fire()
        return

    @subject_slot('value')
    def _do_undo(self, value):
        if value != 0:
            if self.use_layered_buttons() and self.isShiftDown():
                if self.song().can_redo == 1:
                    self.song().redo()
                    self.show_message(str('REDO'))
            elif self.song().can_undo == 1:
                self.song().undo()
                self.show_message(str('UNDO'))

    @subject_slot('value')
    def _do_redo(self, value):
        if value != 0:
            if self.song().can_redo == 1:
                self.song().redo()
                self.show_message(str('REDO'))

    @subject_slot('value')
    def _do_stop_all(self, value):
        if value != 0:
            if self.use_layered_buttons() and self.isShiftDown():
                self.song().stop_all_clips(0)
            else:
                self.song().stop_all_clips(1)

    def isShiftDown(self):
        return self._editsection.isShiftdown()

    def modifiers(self):
        return self._editsection.modifiers()

    def use_layered_buttons(self):
        return False

    def _handle_base_note(self, diff):
        self._modeselect._pad_mode.inc_base_note(diff)

    def _handle_octave(self, diff):
        self._modeselect._pad_mode.inc_octave(diff)
        octave_val = self._modeselect._pad_mode

    def _handle_scale(self, diff):
        self._modeselect._pad_mode.inc_scale(diff)

    @subject_slot('value')
    def _do_update_display(self, value):
        if value != 0:
            self.refresh_state()

    @subject_slot('value')
    def _do_key_color(self, value):
        assert value in range(128)
        if value != 0:
            self._modeselect._pad_mode.step_key_color_mode()

    @subject_slot('value')
    def _do_tap_tempo(self, value):
        assert value in range(128)
        if value != 0:
            self.song().tap_tempo()

    @subject_slot('value')
    def _do_toggle_cue(self, value):
        assert value in range(128)
        if value != 0:
            self.song().set_or_delete_cue()

    @subject_slot('value')
    def _do_toggle_prev_cue(self, value):
        assert value in range(128)
        if value != 0:
            self.song().jump_to_prev_cue()

    @subject_slot('value')
    def _do_toggle_next_cue(self, value):
        assert value in range(128)
        if value != 0:
            self.song().jump_to_next_cue()

    @subject_slot('value')
    def _do_toggle_send(self, value):
        assert value in range(128)
        if self.isShiftDown():
            if value != 0:
                self.refresh_state()
                self.show_message('Refresh Display')
        else:
            nr_of_tracks = len(self.song().return_tracks)
            if value == 0 or nr_of_tracks < 1:
                return
            prev = self.send_slider_index
            self.send_slider_index += 1
            if self.send_slider_index >= nr_of_tracks:
                self.send_slider_index = 0
            self.show_message(' Set Send ' +
                              str(SENDS[self.send_slider_index]))
            self.timed_message(
                2, ' Set Send ' + str(SENDS[self.send_slider_index]))
            if prev != self.send_slider_index:
                for track in range(8):
                    strip = self._mixer.channel_strip(track)
                    slider_list = []
                    for index in range(self.send_slider_index + 1):
                        if index < self.send_slider_index - 1:
                            slider_list.append(None)
                        else:
                            slider_list.append(self.send_sliders[track])
                        strip.set_send_controls(tuple(slider_list))

        return

    @subject_slot('value')
    def _a_trk_left(self, value):
        assert value in range(128)
        if value != 0:
            if self.application().view.is_view_visible('Session'):
                direction = Live.Application.Application.View.NavDirection.left
                self.application().view.scroll_view(direction, 'Session', True)
                track = self.song().view.selected_track
                self.timed_message(2, 'T:' + track.name, False)
                if self.arm_selected_track and track.can_be_armed:
                    arm_exclusive(self.song(), track)

    @subject_slot('value')
    def _a_trk_right(self, value):
        assert value in range(128)
        if value != 0:
            if self.application().view.is_view_visible('Session'):
                direction = Live.Application.Application.View.NavDirection.right
                self.application().view.scroll_view(direction, 'Session', True)
                track = self.song().view.selected_track
                self.timed_message(2, 'T:' + track.name, False)
                if self.arm_selected_track and track.can_be_armed:
                    arm_exclusive(self.song(), track)

    @subject_slot('value')
    def _a_sel_arm(self, value):
        if value != 0:
            if self.arm_selected_track:
                self.arm_selected_track = False
                self.set_sel_arm_button.send_value(0, True)
            else:
                self.arm_selected_track = True
                self.set_sel_arm_button.send_value(127, True)

    @subject_slot('value')
    def _nav_value_left(self, value):
        assert self._device_nav_button_left != None
        assert value in range(128)
        modifier_pressed = True
        if value != 0:
            if not self.application().view.is_view_visible(
                    'Detail') or not self.application().view.is_view_visible(
                        'Detail/DeviceChain'):
                self.application().view.show_view('Detail')
                self.application().view.show_view('Detail/DeviceChain')
            else:
                direction = Live.Application.Application.View.NavDirection.left
                self.application().view.scroll_view(direction,
                                                    'Detail/DeviceChain',
                                                    not modifier_pressed)
        return

    @subject_slot('value')
    def _nav_value_right(self, value):
        assert self._device_nav_button_right != None
        assert value in range(128)
        if value != 0:
            modifier_pressed = True
            if not self.application().view.is_view_visible(
                    'Detail') or not self.application().view.is_view_visible(
                        'Detail/DeviceChain'):
                self.application().view.show_view('Detail')
                self.application().view.show_view('Detail/DeviceChain')
            else:
                direction = Live.Application.Application.View.NavDirection.right
                self.application().view.scroll_view(direction,
                                                    'Detail/DeviceChain',
                                                    not modifier_pressed)
        return

    @subject_slot('value')
    def _do_focus_navigate(self, value):
        assert self._navigate_button != None
        assert value in range(128)
        if value != 0:
            self.nav_index = (self.nav_index + 1) % len(VIEWS_ALL)
            self.application().view.focus_view(VIEWS_ALL[self.nav_index])
            self.show_message('Focus on : ' + str(VIEWS_ALL[self.nav_index]))
        return

    def focus_clip_detail(self):
        self.application().view.focus_view('Detail/Clip')

    @subject_slot('follow_song')
    def _song_follow_changed(self):
        view = self.song().view
        if view.follow_song:
            self.song_follow_button.send_value(1, True)
        else:
            self.song_follow_button.send_value(0, True)

    @subject_slot('value')
    def _do_song_follow(self, value):
        if value != 0:
            view = self.song().view
            if view.follow_song:
                view.follow_song = False
                self.song_follow_button.send_value(0, True)
            else:
                view.follow_song = True
                self.song_follow_button.send_value(1, True)

    @subject_slot('value')
    def _hold_duplicate_action(self, value):
        if value != 0:
            pass

    @subject_slot('value')
    def _hold_clear_action(self, value):
        if value != 0:
            self._mixer.enter_clear_mode()
            self._device_component.enter_clear_mode()
        else:
            self._mixer.exit_clear_mode()
            self._device_component.exit_clear_mode()

    @subject_slot('value')
    def _action_toogle_main_view(self, value):
        if value != 0:
            appv = self.application().view
            if appv.is_view_visible('Arranger'):
                appv.show_view('Session')
            else:
                appv.show_view('Arranger')

    @subject_slot('value')
    def _action_toogle_detail_view(self, value):
        if value != 0:
            appv = self.application().view
            if self.isShiftDown():
                if appv.is_view_visible('Arranger'):
                    appv.show_view('Session')
                else:
                    appv.show_view('Arranger')
            elif appv.is_view_visible('Detail/Clip'):
                appv.show_view('Detail/DeviceChain')
            else:
                appv.show_view('Detail/Clip')

    @subject_slot('re_enable_automation_enabled')
    def _on_change_reenabled(self):
        if self.song().re_enable_automation_enabled:
            self._reenable_button.turn_on()
        else:
            self._reenable_button.turn_off()

    @subject_slot('value')
    def _do_auto_reenable(self, value):
        if value != 0:
            self.song().re_enable_automation()

    def to_color_edit_mode(self, active):
        pass

    def clear_display_all(self):
        self.send_to_display('', 0)
        self.send_to_display('', 1)
        self.send_to_display('', 2)
        self.send_to_display('', 3)

    def clear_display(self, grid):
        self.send_to_display('', grid)

    def timed_message(self, grid, text, hold=False):
        if USE_DISPLAY == False:
            self.show_message(text)
        else:
            self.display_task.set_func(self.clear_display, grid)
            self.send_to_display(text, grid)
            if hold:
                self.display_task.hold()
            self.display_task.start()

    def timed_message_release(self):
        self.display_task.release()

    def update_bank_display(self):
        if USE_DISPLAY:
            name, bank = self._device._current_bank_details()
            if self._display_device_param:
                prms = len(bank)
                d1 = ''
                for i in range(4):
                    parm = bank[i]
                    if parm:
                        name = parm.name
                        d1 += name[:6] + (i < 3 and '|' or '')
                    else:
                        d1 += '      ' + (i < 3 and '|' or '')

                self.send_to_display(d1, 2)
                d1 = ''
                for i in range(4):
                    parm = bank[i + 4]
                    if parm:
                        name = parm.name
                        d1 += name[:6] + (i < 3 and '|' or '')
                    else:
                        d1 += '      ' + (i < 3 and '|' or '')

                self.send_to_display(d1, 4)
            else:
                self.timed_message(2, 'Bank: ' + name)

    def display_parameters(self, paramlist):
        if USE_DISPLAY == False:
            return

    def send_to_display(self, text, grid=0):
        if USE_DISPLAY == False:
            return
        if self._diplay_cache[grid] == text:
            return
        self._diplay_cache[grid] = text
        if len(text) > 28:
            text = text[:27]
        msgsysex = [240, 0, 0, 102, 23, 18, min(grid, 3) * 28]
        filled = text.ljust(28)
        for c in filled:
            msgsysex.append(ord(c))

        msgsysex.append(247)
        self._send_midi(tuple(msgsysex))

    def cleanup(self):
        pass

    def disconnect(self):
        self._pre_serialize()
        self.clear_display_all()
        for button, (track_index, scene_index) in self._bmatrix.iterbuttons():
            if button:
                button.send_color_direct(PColor.OFF[0])

        time.sleep(0.2)
        self._active = False
        self._suppress_send_midi = True
        super(Maschine, self).disconnect()
        return
Example #40
0
class JogWheelSection(CompoundComponent):
    def __init__(self, modeselector, editsection, *a, **k):
        super(CompoundComponent, self).__init__(*a, **k)
        self._modesel = modeselector
        self._editsection = editsection
        is_momentary = True
        self._do_push_button.subject = ButtonElement(is_momentary,
                                                     MIDI_CC_TYPE, 0, 82)
        self._do_edit_slider.subject = SliderElement(MIDI_CC_TYPE, 1, 81)
        self._do_channel_slider.subject = SliderElement(MIDI_CC_TYPE, 1, 83)
        self._do_channel_button.subject = ButtonElement(
            is_momentary, MIDI_CC_TYPE, 1, 63)
        self._do_req_quantize.subject = SliderElement(MIDI_CC_TYPE, 1, 100)
        self._do_browse.subject = SliderElement(MIDI_CC_TYPE, 1, 84)
        self._do_tempo.subject = SliderElement(MIDI_CC_TYPE, 1, 101)
        self._do_volume.subject = SliderElement(MIDI_CC_TYPE, 1, 103)
        self._do_dedicated_rec_quantize.subject = SliderElement(
            MIDI_CC_TYPE, 2, 112)
        self._do_dedicated_clip_quantize.subject = SliderElement(
            MIDI_CC_TYPE, 2, 113)
        self.set_up_function_buttons()
        self._wheel_overide = None
        self.scrub_mode = True
        self.select_arm_mode = True
        self._push_down = False
        return

    def set_up_function_buttons(self):
        is_momentary = True
        self._do_octave_button.subject = StateButton(is_momentary,
                                                     MIDI_CC_TYPE, 1, 70)
        self._do_scale_button.subject = StateButton(is_momentary, MIDI_CC_TYPE,
                                                    1, 71)
        self._do_note_button.subject = StateButton(is_momentary, MIDI_CC_TYPE,
                                                   1, 72)
        self._do_loop_mod.subject = StateButton(is_momentary, MIDI_CC_TYPE, 1,
                                                69)
        self._color_edit_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 3,
                                                114)
        self._do_color_button.subject = self._color_edit_button
        self.scrub_mode_button = StateButton(is_momentary, MIDI_CC_TYPE, 2, 53)
        self._action_scrub_mode.subject = self.scrub_mode_button
        self._action_loop_button.subject = StateButton(is_momentary,
                                                       MIDI_CC_TYPE, 2, 54)
        self._action_quant_button.subject = StateButton(
            is_momentary, MIDI_CC_TYPE, 2, 55)

    def set_overide(self, overide_callback):
        self._wheel_overide = overide_callback

    def reset_overide(self):
        self._wheel_overide = None
        if self._editsection.is_color_edit():
            self._color_edit_button.send_value(0, True)
            self._editsection.knob_pad_action(False)
            self._editsection.set_color_edit(False)
        return

    def message(self, message):
        self.canonical_parent.show_message(message)

    def use_scrub_mode(self):
        return self.scrub_mode

    def set_scrub_mode(self, value):
        self.scrub_mode = value
        self.scrub_mode_button.send_value(value and 127 or 0)

    def modifier1(self):
        return self._push_down

    def modifier2(self):
        return self._editsection.isShiftdown()

    def modifier3(self):
        return self._editsection.isAltdown()

    @subject_slot('value')
    def _do_push_button(self, value):
        if value != 0:
            self._push_down = True
        else:
            self._push_down = False
        self._modesel.handle_push(value != 0)

    @subject_slot('value')
    def _action_scrub_mode(self, value):
        if value > 0:
            self.set_scrub_mode(not self.scrub_mode)

    def _action_set_quant(self, diff):
        val = self._editsection.quantize
        self._editsection.quantize = max(1,
                                         min(len(QUANT_CONST) - 1, val + diff))
        self.canonical_parent.timed_message(
            2, 'Quantize: ' + QUANT_STRING[self._editsection.quantize], True)

    def _action_init_loop(self, diff):
        val = self._editsection.initial_clip_len
        self._editsection.initial_clip_len = max(1.0, min(64.0, val + diff))
        self.canonical_parent.timed_message(
            2, 'Init Clip Len: ' + str(self._editsection.initial_clip_len),
            True)

    @subject_slot('value')
    def _action_loop_button(self, value):
        if value > 0:
            self.canonical_parent.timed_message(
                2, 'Init Clip Len: ' + str(self._editsection.initial_clip_len),
                True)
            self.set_overide(self._action_init_loop)
        else:
            self.canonical_parent.timed_message_release()
            self.reset_overide()

    @subject_slot('value')
    def _action_quant_button(self, value):
        if value > 0:
            self.canonical_parent.timed_message(
                2, 'Quantize: ' + QUANT_STRING[self._editsection.quantize],
                True)
            self.set_overide(self._action_set_quant)
        else:
            self.canonical_parent.timed_message_release()
            self.reset_overide()

    def chg_tempo(self, diff):
        self.song().tempo = max(20, min(999, self.song().tempo + diff))
        self.canonical_parent.timed_message(
            2, 'Tempo: ' + str(round(self.song().tempo, 2)))

    @subject_slot('value')
    def _do_edit_slider(self, value):
        diff = value == 127 and -1 or 1
        if self._wheel_overide:
            self._wheel_overide(diff)
        else:
            self._modesel.navigate(diff, self.modifier1(), self.modifier2())

    @subject_slot('value')
    def _do_channel_slider(self, value):
        if self._wheel_overide:
            self._wheel_overide(value == 127 and -1 or 1)
        else:
            song = self.song()
            if self.modifier1():
                dir = value == 127 and -1 or 1
                scenes = song.scenes
                scene = song.view.selected_scene
                sindex = vindexof(scenes, scene)
                sel_scene = sindex + dir
                if sel_scene >= 0 and sel_scene < len(scenes):
                    song.view.selected_scene = scenes[sel_scene]
            else:
                if self.modifier2():
                    dir = value == 127 and Live.Application.Application.View.NavDirection.left or Live.Application.Application.View.NavDirection.right
                    self.application().view.scroll_view(
                        dir, 'Detail/DeviceChain', True)
                else:
                    dir = value == 127 and -1 or 1
                    tracks = song.tracks
                    direction = value == 127 and Live.Application.Application.View.NavDirection.left or Live.Application.Application.View.NavDirection.right
                    self.application().view.scroll_view(
                        direction, 'Session', True)
                    if self.select_arm_mode:
                        arm_exclusive(song)

    @subject_slot('value')
    def _do_channel_button(self, value):
        arm_exclusive(self.song())

    @subject_slot('value')
    def _do_browse(self, value):
        diff = value == 127 and -1 or 1
        if self._wheel_overide:
            self._wheel_overide(diff)
        else:
            step = 1.0
            if self.modifier1():
                step = 0.25
            else:
                if self.modifier2():
                    step = 4.0
            if self.scrub_mode:
                self.song().scrub_by(step * diff)
            else:
                self.song().jump_by(step * diff)

    @subject_slot('value')
    def _do_tempo(self, value):
        diff = value == 127 and -1 or 1
        if self._wheel_overide:
            self._wheel_overide(diff)
        else:
            if self.modifier1():
                self.chg_tempo(diff * 0.01)
            else:
                if self.modifier2():
                    self.chg_tempo(diff * 0.1)
                else:
                    self.chg_tempo(diff)

    @subject_slot('value')
    def _do_req_quantize(self, value):
        diff = value == 127 and -1 or 1
        if self._wheel_overide:
            self._wheel_overide(diff)
        else:
            song = self.song()
            if self.modifier2():
                swing = song.swing_amount
                song.swing_amount = max(0.0, min(1, swing + diff * 0.01))
                msg = 'Swing Amount: ' + str(int(
                    song.swing_amount * 100)) + '%'
                self.message(msg)
                self.canonical_parent.timed_message(2, msg)
            else:
                if self.modifier1():
                    quant = song.clip_trigger_quantization
                    song.clip_trigger_quantization = max(
                        0, min(13, quant + diff))
                    self.message('Clip Quantize ' +
                                 CLIQ_DESCR[song.clip_trigger_quantization])
                    self.canonical_parent.timed_message(
                        2, 'Clip Quantize: ' +
                        CLIQ_DESCR[song.clip_trigger_quantization])
                else:
                    rec_quant = song.midi_recording_quantization
                    index = QUANT_CONST.index(rec_quant) + diff
                    if index >= 0 and index < len(QUANT_CONST):
                        song.midi_recording_quantization = QUANT_CONST[index]
                        self.message(QUANT_DESCR[index])
                        self.canonical_parent.timed_message(
                            2, 'Rec Quantize: ' + QUANT_STRING[index])

    @subject_slot('value')
    def _do_dedicated_clip_quantize(self, value):
        diff = value == REL_KNOB_DOWN and -1 or 1
        song = self.song()
        quant = song.clip_trigger_quantization
        song.clip_trigger_quantization = max(0, min(13, quant + diff))
        self.message('Clip Quantize ' +
                     CLIQ_DESCR[song.clip_trigger_quantization])
        self.canonical_parent.timed_message(
            2, 'Clip Quantize: ' + CLIQ_DESCR[song.clip_trigger_quantization])

    @subject_slot('value')
    def _do_dedicated_rec_quantize(self, value):
        diff = value == REL_KNOB_DOWN and -1 or 1
        song = self.song()
        rec_quant = song.midi_recording_quantization
        index = QUANT_CONST.index(rec_quant) + diff
        if index >= 0 and index < len(QUANT_CONST):
            song.midi_recording_quantization = QUANT_CONST[index]
            self.message(QUANT_DESCR[index])
            self.canonical_parent.timed_message(
                2, 'Rec Quantize: ' + QUANT_STRING[index])

    @subject_slot('value')
    def _do_volume(self, value):
        diff = value == 127 and -1 or 1
        if self._wheel_overide:
            self._wheel_overide(diff)
        else:
            if self.modifier2():
                self.chg_cue(diff)
            else:
                self.chg_volume(diff)

    @subject_slot('value')
    def _do_color_button(self, value):
        if value > 0:
            if self._editsection.is_color_edit():
                self.reset_overide()
            else:
                self._color_edit_button.send_value(1, True)
                self._editsection.knob_pad_action(True)
                self._editsection.set_color_edit(True)

    def set_color_edit(self, active):
        if active:
            self.set_overide(self._color_change)
        else:
            self.reset_overide()

    def _color_change(self, value):
        diff = value == 127 and -1 or 1
        self._editsection.edit_colors(diff)

    @subject_slot('value')
    def _do_note_button(self, value):
        assert value in range(128)
        if value > 0:
            self.set_overide(self.canonical_parent._handle_base_note)
        else:
            self.reset_overide()

    @subject_slot('value')
    def _do_octave_button(self, value):
        assert value in range(128)
        if value > 0:
            self.set_overide(self.canonical_parent._handle_octave)
        else:
            self.reset_overide()

    @subject_slot('value')
    def _do_scale_button(self, value):
        assert value in range(128)
        if value > 0:
            self.set_overide(self.canonical_parent._handle_scale)
        else:
            self.reset_overide()

    def _handle_loop_mod(self, diff):
        factor = (self.modifier1() and 1.0 or 4.0) * diff
        if self.modifier2():
            self.canonical_parent.adjust_loop_length(factor)
        else:
            self.canonical_parent.adjust_loop_start(factor)

    @subject_slot('value')
    def _do_loop_mod(self, value):
        assert value in range(128)
        if value > 0:
            self.set_overide(self._handle_loop_mod)
        else:
            self.reset_overide()

    def chg_volume(self, diff):
        mdevice = self.song().master_track.mixer_device
        if self.modifier2():
            mdevice.volume.value = calc_new_parm(mdevice.volume, diff)
        else:
            repeat(mdevice.volume, diff)

    def chg_cue(self, diff):
        mdevice = self.song().master_track.mixer_device
        if self.modifier2():
            mdevice.cue_volume.value = calc_new_parm(mdevice.cue_volume, diff)
        else:
            repeat(mdevice.cue_volume, diff)

    def update(self):
        pass

    def refresh(self):
        pass

    def disconnect(self):
        super(JogWheelSection, self).disconnect()