Exemplo n.º 1
0
	def doStuff(self,x):

		try:
			data, addr = self.sock.recvfrom(64) # buffer size is 1024 byte
			
			addressPattern = data[:data.find("\0")]

			#data = data.split(',i')
			#v = struct.unpack('>i',data[1][2:(2+4)])[0]
			data = data.split(',ii')
			v = struct.unpack('>ii',data[1][1:])

			track_number = v[0]
			midi_cc = v[1]

			b = SliderElement(1, 0, midi_cc)
			song = self.song()

			if (addressPattern == "/pan"):
				b.connect_to(song.tracks[track_number].mixer_device.panning)
			elif (addressPattern == "/volume"):
				b.connect_to(song.tracks[track_number].mixer_device.volume)
			self.show_message(addressPattern)
		except socket.error:
			pass
Exemplo n.º 2
0
class MaschineMk2(Maschine):
    """Control Script for Maschine Studio"""
    __module__ = __name__
    _gated_buttons = []

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

    def create_pad_button(self, scene_index, track_index, color_source):
        return PadColorButton(True, 0, scene_index, track_index, color_source)

    def create_gated_button(self, identifier, hue):
        button = GatedColorButton(True, MIDI_CC_TYPE, identifier, hue)
        self._gated_buttons.append(button)
        return button

    def _init_maschine(self):
        self._jogwheel = KnobSection(self._modeselect, self._editsection)
        self._note_repeater.registerKnobHandler(self._jogwheel)
        self._mixer.set_touch_mode(2)
        self._device_component.set_touch_mode(2)
        self.prehear_knob = SliderElement(MIDI_CC_TYPE, 0, 41)
        self.prehear_knob.connect_to(
            self.song().master_track.mixer_device.cue_volume)

    def to_color_edit_mode(self, active):
        if self._editsection.is_color_edit() != active:
            self._jogwheel.invoke_color_mode(active and 1 or 0)

    def use_layered_buttons(self):
        return True

    def _final_init(self):
        debug_out('########## LIVE 9 MASCHINE Mk2 V 2.02 ############# ')

    def _click_measure(self):
        pass

    def apply_preferences(self):
        super(MaschineMk2, self).apply_preferences()
        pref_dict = self._pref_dict
        if 'color_mode' in pref_dict:
            value = pref_dict['color_mode']
            self._session.set_color_mode(value)
            self._session._c_mode_button.send_value(value == True and 127 or 0,
                                                    True)
        else:
            self._session.set_color_mode(False)
            self._session._c_mode_button.send_value(0, True)

    def store_preferences(self):
        super(MaschineMk2, self).store_preferences()
        self._pref_dict['color_mode'] = self._session.is_color_mode()

    def preferences_name(self):
        return 'MaschineMk2'

    def cleanup(self):
        for button in self._gated_buttons:
            button.switch_off()
Exemplo n.º 3
0
    def doStuff(self, x):

        try:
            data, addr = self.sock.recvfrom(64)  # buffer size is 1024 byte

            addressPattern = data[:data.find("\0")]

            #data = data.split(',i')
            #v = struct.unpack('>i',data[1][2:(2+4)])[0]
            data = data.split(',ii')
            v = struct.unpack('>ii', data[1][1:])

            track_number = v[0]
            midi_cc = v[1]

            b = SliderElement(1, 0, midi_cc)
            song = self.song()

            if (addressPattern == "/pan"):
                b.connect_to(song.tracks[track_number].mixer_device.panning)
            elif (addressPattern == "/volume"):
                b.connect_to(song.tracks[track_number].mixer_device.volume)
            self.show_message(addressPattern)
        except socket.error:
            pass
Exemplo n.º 4
0
 def connect_to(self, parameter):
     """ Overrides standard so that mapping type can be properly updated on parameter
     assignment.  Also handles setting up observers. """
     self._on_parameter_changed.subject = None
     self._on_parameter_name_changed.subject = None
     SliderElement.connect_to(self, parameter)
     self._smoother.set_parameter(self._parameter_to_map_to)
     self._on_parameter_changed.subject = self._parameter_to_map_to
     self._on_parameter_name_changed.subject = self._parameter_to_map_to
     self._update_mapping_type()
     return
Exemplo n.º 5
0
    def connect_to(self, param):
        if self._parameter_to_map_to is not None:
            try:
                self._parameter_to_map_to.remove_value_listener(self._on_value_changed)
            except:
                pass

        SliderElement.connect_to(self, param)
        if param is not None:
            if not self._parameter_to_map_to.value_has_listener(self._on_value_changed):
                self._parameter_to_map_to.add_value_listener(self._on_value_changed)
        self._send_param_val(True)
Exemplo n.º 6
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
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)
Exemplo n.º 8
0
class HyperionChan(CompoundComponent):
    def __init__(self, hyperion, mod_num, *a, **kw):
        super(HyperionChan, self).__init__(*a, **kw)

        self.hyperion = hyperion
        self.mod_num = mod_num

        self._track_selector_encoder = EncoderElement(MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 0x15, Live.MidiMap.MapMode.relative_smooth_binary_offset) # Right encoder on Hyperion
        self._track_selector_encoder.add_value_listener(self._on_track_selector_encoder)

        self._fader = SliderElement(MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 0x25)
        self._pots = [
            EncoderElement(MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 0x1E + num, Live.MidiMap.MapMode.absolute, name='Pot{}'.format(num))
            for num in range(8)
        ]
        self._btns = [
            ButtonElement(True, MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 1 + num, name='Btn{}'.format(num))
            for num in range(8)
        ]
        self._enc_right_btn = ButtonElement(True, MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 10, name='EncRightBtn')
        self._enc_right_btn.add_value_listener(self._on_enc_right_btn)

        #self._cs = ChannelStripComponent()
        #self._cs.set_volume_control(self._fader)
        self._vu_slider = SliderElement(MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 60)

        self._vu = VUMeter(self)

        self._track = None

        tracks = self._get_all_tracks(self.hyperion.song().tracks)
        if len(tracks) > self.mod_num:
            self._bind_to_track(tracks[self.mod_num])
        else:
            self._bind_to_track(None)

    def log(self, msg, *args):
        self.hyperion.log_message(('HyperionChan({}): ' + msg).format(self.mod_num, *args))

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

        self._enc_right_btn.remove_value_listener(self._on_enc_right_btn)

    def _get_parent_by_type(self, obj, parent_type):
        if not obj.canonical_parent:
            return
        if isinstance(obj.canonical_parent, parent_type):
            return obj.canonical_parent
        return self._get_parent_by_type(obj.canonical_parent, parent_type)

    def _on_enc_right_btn(self, value):
        if value and self._track:
            self.log('type {}',type(self._track))

            song = self.hyperion.song()
            if isinstance(self._track, Live.Track.Track):
                song.view.selected_track = self._track
            elif isinstance(self._track, Live.DrumChain.DrumChain):
                parent_track = self._get_parent_by_type(self._track, Live.Track.Track)

                song.view.selected_track = parent_track
                try:
                    song.view.selected_chain = self._track
                except:
                    try:
                        song.view.selected_track = parent_track
                        song.view.selected_chain = self._track.canonical_parent.canonical_parent
                        self._track.canonical_parent.view.selected_chain = self._track
                    except:
                        pass

    def _get_track_mapper_device(self, track):
        for device in track.devices:
            if device.name == 'MultiMapper16 V2.0':
                return device

    def _get_all_tracks(self, all_tracks):
        got_tracks = []

        for cur_track in all_tracks:
            if isinstance(cur_track, (Live.Track.Track, Live.DrumChain.DrumChain)):
                got_tracks.append(cur_track)

            devices = list(cur_track.devices)
            if len(devices) and isinstance(devices[0], Live.RackDevice.RackDevice):
                got_tracks.extend(self._get_all_tracks(devices[0].chains))

        return [track for track in got_tracks if self._get_track_mapper_device(track)]

    def _on_track_selector_encoder(self, value):
        direction = 1 if value > 64 else -1

        tracks = self._get_all_tracks(self.hyperion.song().tracks)

        # for t in tracks:
        #     self.log('AAAAA {}', t.name)

        try:
            cur_track_idx = tracks.index(self._track)
        except ValueError:
            self.log('track disappeared :(')
            self._bind_to_track(tracks[0])
        else:
            cur_track_idx += direction
            if cur_track_idx == len(tracks):
                cur_track_idx = 0
            if cur_track_idx == -1:
                cur_track_idx = len(tracks) - 1
            self._bind_to_track(tracks[cur_track_idx])

    def _bind_to_track(self, track):
        if self._track:
            #self._cs.set_track(None)
            self._fader.release_parameter()
            [pot.release_parameter() for pot in self._pots]
            [btn.release_parameter() for btn in self._btns]

            self._track.remove_name_listener(self._on_name_changed)

            self._track = None

        if not track:
            return

        self.log('binding to {}', track.name)
        self._track = track

        self._fader.connect_to(track.mixer_device.volume)

        mapper_dev = self._get_track_mapper_device(track)
        for num in range(8):
            self._pots[num].connect_to(mapper_dev.parameters[3 + num]) # MacroA0 MacroA1 etc
            self._btns[num].connect_to(mapper_dev.parameters[11 + num]) # MacroB0 MacroB1 etc

        # self._cs.set_track(track)

        if getattr(self._track, 'has_audio_output', False) and hasattr(self._track, 'add_output_meter_left_listener'):
            self._vu.set_vu_meter(track, self._vu_slider)
        else:
            self._vu.set_vu_meter(None, None)

        self._track.add_name_listener(self._on_name_changed)
        self._on_name_changed()

    def _on_name_changed(self):
        self.hyperion.sysex.set_title(self.mod_num, self._track.name if self._track else '-')
class 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()
class MaschineMk1(Maschine):
    """Control Script for Maschine Studio"""
    __module__ = __name__

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

    def create_pad_button(self, scene_index, track_index, color_source):
        return PadButton(True, 0, scene_index, track_index)

    def create_gated_button(self, identifier, hue):
        return MonoGatedButton(True, MIDI_CC_TYPE, identifier)

    def _init_maschine(self):
        self._jogwheel = KnobSection(self._modeselect, self._editsection)
        self._note_repeater.registerKnobHandler(self._jogwheel)
        self._mixer.set_touch_mode(2)
        self._device_component.set_touch_mode(2)
        self._set_up_machine_knobs()
        self._nav_section = MonoNavSection(self._modeselect)

    def _set_up_machine_knobs(self):
        master_track = self.song().master_track
        self.prehear_knob = SliderElement(MIDI_CC_TYPE, 0, 43)
        self.prehear_knob.connect_to(
            self.song().master_track.mixer_device.cue_volume)
        self._stop_tap_button = ButtonElement(True, MIDI_CC_TYPE, 1, 111)
        self._do_combined_stop_tap.subject = self._stop_tap_button
        self._undo__redo_button = ButtonElement(True, MIDI_CC_TYPE, 2, 85)
        self._do_undo_redo.subject = self._undo__redo_button

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

    @subject_slot('value')
    def _do_undo_redo(self, value):
        if value:
            if 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'))

    def _final_init(self):
        debug_out('########## LIVE 9 MASCHINE Mk1 V 2.01 ############# ')

    def is_monochrome(self):
        return True

    def _click_measure(self):
        pass

    def preferences_name(self):
        return 'MaschineMk1'

    def apply_preferences(self):
        super(MaschineMk1, self).apply_preferences()
        pref_dict = self._pref_dict
        self._session.set_step_advance(1)

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

    def update_display(self):
        with self.component_guard():
            with self._is_sending_scheduled_messages():
                self._task_group.update(0.1)
            self._modeselect.notify_mono(self.blink_state)
            self.blink_state = (self.blink_state + 1) % 4
            self.display_task.tick()
            self.update_undo_redo(False)
Exemplo n.º 11
0
class MaschineStudio(Maschine):
    """Control Script for Maschine Studio"""
    __module__ = __name__
    _gated_buttons = []

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

    def create_pad_button(self, scene_index, track_index, color_source):
        return PadColorButton(True, 0, scene_index, track_index, color_source)

    def create_gated_button(self, identifier, hue):
        button = GatedColorButton(True, MIDI_CC_TYPE, identifier, hue)
        self._gated_buttons.append(button)
        return button

    def _init_maschine(self):
        self._jogwheel = JogWheelSection(self._modeselect, self._editsection)
        self.prehear_knob = SliderElement(MIDI_CC_TYPE, 0, 41)
        self.prehear_knob.connect_to(
            self.song().master_track.mixer_device.cue_volume)
        self._device_component.set_touch_mode(2)

    def _final_init(self):
        debug_out('########## LIVE 9 MASCHINE STUDIO V 2.02 ############# ')

    def _click_measure(self):
        pass

    def preferences_name(self):
        return 'MaschineStudio'

    def apply_preferences(self):
        super(MaschineStudio, self).apply_preferences()
        pref_dict = self._pref_dict
        if 'use_scrub' in pref_dict:
            self._jogwheel.set_scrub_mode(pref_dict['use_scrub'])
        else:
            self._jogwheel.set_scrub_mode(True)
        if 'color_mode' in pref_dict:
            value = pref_dict['color_mode']
            self._session.set_color_mode(value)
            self._session._c_mode_button.send_value(value == True and 127 or 0,
                                                    True)
        else:
            self._session.set_color_mode(False)
            self._session._c_mode_button.send_value(0, True)

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

    def store_preferences(self):
        super(MaschineStudio, self).store_preferences()
        self._pref_dict['use_scrub'] = self._jogwheel.use_scrub_mode()
        self._pref_dict['color_mode'] = self._session.is_color_mode()

    @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()