def send_value(self, value, force_send=False ): if value in self.blink_colors: self.blinking = value else: self.blinking = False if value is not None: ButtonElement.send_value(self, value, force_send)
class CustomEncoderElement(RingedEncoderElement): """ CustomEncoderElement for use with LI Code that handles updating LED ring types depending on the type of parameter being controlled. """ def __init__(self, msg_type, channel, identifier, map_mode, name='', *a, **k): self._ring_mode_button = ButtonElement(False, 1, 15, identifier + 32, name=name + ' Ring_Mode_Button') super(CustomEncoderElement, self).__init__(msg_type, channel, identifier, map_mode, name=name, *a, **k) def disconnect(self): self._ring_mode_button = None super(CustomEncoderElement, self).disconnect() def set_ring_mode(self, mode): if mode in SKIP_MODES: self._ring_mode_button.send_value(FILL_VALUE, True) else: self._ring_mode_button.send_value(self._get_ring_value(mode), True) def _get_ring_value(self, mode): param = self.mapped_parameter() parent = ('MixerDevice' if type(param.canonical_parent) == Live.MixerDevice.MixerDevice else param.canonical_parent.class_name) if parent in PARAM_DICT and param.original_name in PARAM_DICT[parent]: return PARAM_DICT[parent][param.original_name] if mode == MAP_BIPOLAR: return EQ_VALUE elif mode == MAP_QUANTIZED: return WALK_VALUE return FILL_VALUE
def send_value(self, value, force_send=False ): self.current_color = value if value > 0 and value is not self.off_color: self.blinking = True else: self.blinking = False if value is not None: #super(FlashingButtonElement, self).send_value(value, force_send) ButtonElement.send_value(self, value, force_send)
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 KnobSection(CompoundComponent): _wheel_overide = None _mode = KSM_SCROLL _push_down = False scrub_mode = True alt_mode = False def __init__(self, modeselector, editsection, *a, **k): super(KnobSection, self).__init__(*a, **k) self._modesel = modeselector self._editsection = editsection self._modesel.connect_main_knob(self) is_momentary = True self.main_knob = SliderElement(MIDI_CC_TYPE, 1, 85) self.push_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 1, 87) self.volume_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 3, 110) self.swing_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 3, 111) self.tempo_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 3, 112) self.xfade_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 3, 116) self._mode_button = self.canonical_parent.create_gated_button(80, KSM_HUES[0]) self._color_edit_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 3, 113) self._set_inicliplen_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 3, 122) self._set_quantize_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 3, 126) self.do_main.subject = self.main_knob self.do_main_push.subject = self.push_button self._do_volume.subject = self.volume_button self._do_swing.subject = self.swing_button self._do_tempo.subject = self.tempo_button self._do_xfade.subject = self.xfade_button self._do_toggle_mode.subject = self._mode_button self._do_color_button.subject = self._color_edit_button self._do_mikr_cliplen.subject = self._set_inicliplen_button self._do_mikr_quantize.subject = self._set_quantize_button self._do_dedicated_rec_quantize.subject = SliderElement(MIDI_CC_TYPE, 2, 112) self._do_dedicated_clip_quantize.subject = SliderElement(MIDI_CC_TYPE, 2, 113) self._radio_buttons = ( self.volume_button, self.swing_button, self.tempo_button, self.xfade_button, self._color_edit_button) self._do_button_left.subject = ButtonElement(is_momentary, MIDI_CC_TYPE, 2, 120) self._do_button_right.subject = ButtonElement(is_momentary, MIDI_CC_TYPE, 2, 121) self.volume_button.send_value(0, True) self._mode_button.set_color(KSM_HUES[0]) self.knob_action = self._scroll_action self._prev_mode = None self._prev_action = None return def do_message(self, msg, statusbarmsg=None): if statusbarmsg == None: self.canonical_parent.show_message(msg) else: self.canonical_parent.show_message(statusbarmsg) self.canonical_parent.timed_message(2, msg) return def use_scrub_mode(self): return self.scrub_mode def set_scrub_mode(self, value): self.scrub_mode = value self.scrub_mode_button.send_value(value and 127 or 0) def set_override(self, overide_callback): if self._wheel_overide != overide_callback: self._wheel_overide = overide_callback self.volume_button.send_value(0, True) self.swing_button.send_value(0, True) self.tempo_button.send_value(0, True) self._mode_button.switch_off() self._mode = None return def reset_overide(self): if self._wheel_overide: self._wheel_overide = None return def _scroll_action(self, value): inc = value == 1 and 1 or -1 self._modesel.navigate(inc, self._push_down, self._editsection.isShiftdown()) def _do_channel_slider(self, value): song = self.song() if self._editsection.isShiftdown(): dir = value == 127 and Live.Application.Application.View.NavDirection.left or Live.Application.Application.View.NavDirection.right self.application().view.scroll_view(dir, 'Detail/DeviceChain', True) else: if self._push_down: dir = value == 127 and -1 or 1 scenes = song.scenes scene = song.view.selected_scene sindex = list(scenes).index(scene) sel_scene = sindex + dir if sel_scene >= 0 and sel_scene < len(scenes): song.view.selected_scene = scenes[sel_scene] else: direction = value == 127 and Live.Application.Application.View.NavDirection.left or Live.Application.Application.View.NavDirection.right self.application().view.scroll_view(direction, 'Session', True) if self.canonical_parent.arm_selected_track: arm_exclusive(song) def _do_transport(self, value): diff = value == 127 and -1 or 1 jump = 4.0 if self._push_down: if self._editsection.isShiftdown(): jump = 0.25 else: jump = 1.0 else: if self._editsection.isShiftdown(): jump = 0.5 if self.scrub_mode: self.song().scrub_by(jump * diff) else: self.song().jump_by(jump * diff) @subject_slot('value') def do_main(self, value): if self._wheel_overide: self._wheel_overide(value == 1 and 1 or -1, self._editsection.isShiftdown(), self._push_down) else: if self.knob_action: self.knob_action(value) def _disable_radio_button(self): for button in self._radio_buttons: button.send_value(0, True) def enter_toggle_mode(self, mode=None): self.reset_overide() self.switch_pad_wheel_edit(False) self.alt_mode = False if self._mode < KSM_SCROLL or mode != None: if mode == None: self.switch_pad_wheel_edit(False) self._mode = KSM_SCROLL else: self._mode = mode self._disable_radio_button() else: modeval = self._mode + 1 if modeval > KSM_TRANSPORT: modeval = KSM_SCROLL self._mode = modeval if self._mode == KSM_SCROLL: self.knob_action = self._scroll_action self.do_message('Main Knob -> Navigate') else: if self._mode == KSM_SELECT: self.knob_action = self._do_channel_slider self.do_message('Main Knob -> Select') else: if self._mode == KSM_TRANSPORT: self.knob_action = self._do_transport self.do_message('Main Knob -> Transport') self._mode_button.set_color(KSM_HUES[self._mode - KSM_SCROLL]) return @subject_slot('value') def _do_toggle_mode(self, value): if value > 0: if self._editsection.isShiftdown(): self.canonical_parent.toggle_nav_mode() else: self.enter_toggle_mode() def _scroll_xfade(self, value): diff = value == 127 and -1 or 1 if self._wheel_overide: self._wheel_overide(diff, self._editsection.isShiftdown(), self._push_down) else: self.chg_xfade(diff) def _scroll_volume(self, value): diff = value == 127 and -1 or 1 if self._wheel_overide: self._wheel_overide(diff, self._editsection.isShiftdown(), self._push_down) else: if self._editsection.isShiftdown(): self.chg_cue(diff) else: self.chg_volume(diff) def _scroll_tempo(self, value): diff = value == 127 and -1 or 1 if self._wheel_overide: self._wheel_overide(diff, self._editsection.isShiftdown(), self._push_down) else: if self._push_down: self.chg_tempo(diff * 0.1) else: if self._editsection.isShiftdown(): self.chg_tempo(diff * 0.01) else: self.chg_tempo(diff) def _scroll_req_quantize(self, value): diff = value == 127 and -1 or 1 if self._wheel_overide: self._wheel_overide(diff, self._editsection.isShiftdown(), self._push_down) else: song = self.song() if self._editsection.isShiftdown(): quant = song.clip_trigger_quantization song.clip_trigger_quantization = max(0, min(13, quant + diff)) self.do_message('Clip Quantize ' + CLIQ_DESCR[song.clip_trigger_quantization]) else: rec_quant = song.midi_recording_quantization index = QUANT_CONST.index(rec_quant) + diff if index >= 0 and index < len(QUANT_CONST): song.midi_recording_quantization = QUANT_CONST[index] self.do_message(QUANT_DESCR[index]) @subject_slot('value') def do_main_push(self, value): if value != 0: self.alt_mode = not self.alt_mode self._push_down = value != 0 self._modesel.handle_push(value != 0) def switch_pad_wheel_edit(self, activate): if activate: self._color_edit_button.send_value(1, True) self._editsection.set_color_edit(True) else: self._color_edit_button.send_value(0, True) self._editsection.set_color_edit(False) def invoke_color_mode(self, active): if active and self._mode != KSM_EDIT: self._prev_mode = self._mode self._to_mode(KSM_EDIT) else: if not active and self._prev_mode != None: self.switch_pad_wheel_edit(False) self._to_mode(self._prev_mode) self._prev_mode = None return @subject_slot('value') def _do_color_button(self, value, force=False): if value > 0: if self._mode != KSM_EDIT: self._prev_mode = self._mode self.reset_overide() self._to_mode(KSM_EDIT) elif self._prev_mode != None: self.switch_pad_wheel_edit(False) self._to_mode(self._prev_mode) self._prev_mode = None return def _to_mode(self, mode): button = None if mode == KSM_VOLUME: button = self.volume_button self.knob_action = self._scroll_volume self.switch_pad_wheel_edit(False) else: if mode == KSM_SWING: button = self.swing_button self.knob_action = self._scroll_req_quantize self.switch_pad_wheel_edit(False) else: if mode == KSM_TEMPO: button = self.tempo_button self.knob_action = self._scroll_tempo self.switch_pad_wheel_edit(False) else: if mode == KSM_XFADE: button = self.xfade_button self.knob_action = self._scroll_xfade self.switch_pad_wheel_edit(False) else: if mode == KSM_EDIT: self._editsection.set_color_edit(True) else: self.enter_toggle_mode(mode) self._mode = mode for rbutton in self._radio_buttons: if rbutton != button: rbutton.send_value(0) if button: self._mode_button.switch_off() button.send_value(127, True) return def _edit_color(self, value): diff = value == 127 and -1 or 1 if self._wheel_overide: self._wheel_overide(diff, self._editsection.isShiftdown(), self._push_down) else: self._editsection.edit_colors(diff) @subject_slot('value') def _do_button_left(self, value): if value != 0: self._modesel.navigate(-1, self._editsection.isShiftdown(), False) @subject_slot('value') def _do_button_right(self, value): if value != 0: self._modesel.navigate(1, self._editsection.isShiftdown(), False) @subject_slot('value') def _do_mikr_cliplen(self, value): if value == 0: self.knob_action = self._prev_action else: self._prev_action = self.knob_action self.knob_action = self._modify_init_cliplen @subject_slot('value') def _do_mikr_quantize(self, value): if value == 0: self.knob_action = self._prev_action else: self._prev_action = self.knob_action self.knob_action = self._modify_quant_grid def _modify_init_cliplen(self, value): if self._push_down: self._editsection.mod_new_initlen(value == 1 and 1 or -1) else: self._editsection.mod_new_initlen(value == 1 and 4 or -4) def _modify_quant_grid(self, value): self._editsection.mod_quant_size(value == 1 and 1 or -1) @subject_slot('value') def _do_xfade(self, value): if value > 0 and self._mode != KSM_XFADE: self.reset_overide() self._to_mode(KSM_XFADE) @subject_slot('value') def _do_volume(self, value): if value > 0 and self._mode != KSM_VOLUME: self.reset_overide() self._to_mode(KSM_VOLUME) @subject_slot('value') def _do_swing(self, value): if value > 0 and self._mode != KSM_SWING: self.reset_overide() self._to_mode(KSM_SWING) @subject_slot('value') def _do_tempo(self, value): if value > 0 and self._mode != KSM_TEMPO: self.reset_overide() self._to_mode(KSM_TEMPO) @subject_slot('value') def _do_dedicated_clip_quantize(self, value): diff = value == 127 and -1 or 1 song = self.song() quant = song.clip_trigger_quantization song.clip_trigger_quantization = max(0, min(13, quant + diff)) self.do_message('Clip Quantize ' + CLIQ_DESCR[song.clip_trigger_quantization]) @subject_slot('value') def _do_dedicated_rec_quantize(self, value): diff = value == 127 and -1 or 1 song = self.song() rec_quant = song.midi_recording_quantization index = QUANT_CONST.index(rec_quant) + diff if index >= 0 and index < len(QUANT_CONST): song.midi_recording_quantization = QUANT_CONST[index] self.do_message('Rec Quantize: ' + QUANT_STRING[index]) def chg_tempo(self, diff): self.song().tempo = max(20, min(999, self.song().tempo + diff)) self.canonical_parent.timed_message(2, 'Tempo: ' + str(round(self.song().tempo, 2))) def chg_volume(self, diff): if self._push_down: self.song().master_track.mixer_device.volume.value = calc_new_parm(self.song().master_track.mixer_device.volume, diff) else: repeat(self.song().master_track.mixer_device.volume, diff) def chg_xfade(self, diff): if self._push_down: self.song().master_track.mixer_device.crossfader.value = calc_new_parm(self.song().master_track.mixer_device.crossfader, diff) else: repeat(self.song().master_track.mixer_device.crossfader, diff) def chg_cue(self, diff): if self._push_down: self.song().master_track.mixer_device.cue_volume.value = calc_new_parm(self.song().master_track.mixer_device.cue_volume, diff) else: repeat(self.song().master_track.mixer_device.cue_volume, diff) def update(self): pass def refresh(self): pass
class AudioClipEditComponent(CompoundComponent): """ classdocs """ def __init__(self, *a, **k): super(AudioClipEditComponent, self).__init__(*a, **k) self._loop_start_slider = SliderElement(MIDI_CC_TYPE, 2, 60) self._action_loop_start.subject = self._loop_start_slider self._loop_end_slider = SliderElement(MIDI_CC_TYPE, 2, 61) self._action_loop_end.subject = self._loop_end_slider self._mark_start_slider = SliderElement(MIDI_CC_TYPE, 2, 62) self._action_mark_start.subject = self._mark_start_slider self._mark_end_slider = SliderElement(MIDI_CC_TYPE, 2, 63) self._action_mark_end.subject = self._mark_end_slider self._pitch_c_slider = SliderElement(MIDI_CC_TYPE, 2, 64) self._pitch_f_slider = SliderElement(MIDI_CC_TYPE, 2, 65) self._gain_slider = SliderElement(MIDI_CC_TYPE, 2, 66) self._action_pitch_c.subject = self._pitch_c_slider self._action_pitch_f.subject = self._pitch_f_slider self._action_gain.subject = self._gain_slider self._loop_inc_slider = SliderElement(MIDI_CC_TYPE, 2, 67) self._action_loop_inc.subject = self._loop_inc_slider self._loop_move_button = ButtonElement(False, MIDI_CC_TYPE, 2, 74) self._action_mv_loop.subject = self._loop_move_button self._loop_set_button = ButtonElement(False, MIDI_CC_TYPE, 2, 70) self._action_loop_toggle.subject = self._loop_set_button self._warp_set_button = ButtonElement(False, MIDI_CC_TYPE, 2, 71) self._action_warp_toggle.subject = self._warp_set_button self._zoom_scroll_button = ButtonElement(False, MIDI_CC_TYPE, 2, 73) self._action_scroll_mode.subject = self._zoom_scroll_button self.selected_clip_slot = None self.inc_index = 4 self.loop_inc = INC_STEPS[self.inc_index] self.start_inc = INC_STEPS[self.inc_index] self.mv_loop = False self._on_pitch_c_changed.subject = None self._on_pitch_f_changed.subject = None self._on_gain_changed.subject = None self._scroll_mode = False self.update_selected_clip() @subject_slot("value") def _action_scroll_mode(self, value): if value > 0: self._scroll_mode = True else: self._scroll_mode = False @subject_slot("value") def _action_warp_toggle(self, value): if value > 0: if self.selected_clip_slot and self.selected_clip_slot.has_clip: clip = self.selected_clip_slot.clip if clip.is_audio_clip: clip.warping = not clip.warping self._warp_set_button.send_value(clip.warping and 127 or 0, True) @subject_slot("value") def _action_loop_toggle(self, value): if value > 0: if self.selected_clip_slot and self.selected_clip_slot.has_clip: clip = self.selected_clip_slot.clip clip.looping = not clip.looping self._loop_set_button.send_value(clip.looping and 127 or 0, True) @subject_slot("value") def _action_loop_inc(self, value): if not (value == 1 and 1): inc = -1 val = self.inc_index + inc self.inc_index = val >= 0 and val < len(INC_STEPS) and val self.loop_inc = INC_STEPS[val] self.start_inc = INC_STEPS[val] self.canonical_parent.timed_message(2, "Loop Adjust: " + INC_DISP[val]) self.canonical_parent.show_message("Loop Adjust: " + INC_DISP[val]) @subject_slot("value") def _action_mv_loop(self, value): if value > 0: if self.mv_loop: self._loop_move_button.send_value(0, True) self.mv_loop = False else: self._loop_move_button.send_value(127, True) self.mv_loop = True @subject_slot("value") def _action_mark_start(self, value): if self._scroll_mode: scroll = value == 1 and 3 or 2 self.application().view.scroll_view(scroll, "Detail/Clip", False) elif not (value == 1 and 1): inc = -1 clip = self.selected_clip_slot and self.selected_clip_slot.has_clip and self.selected_clip_slot.clip ls = clip.start_marker le = clip.end_marker ls = max(0, min(le - self.start_inc, ls + inc * self.start_inc)) clip.start_marker = ls bars_to_measure(ls, clip.signature_denominator, clip.signature_numerator) self.canonical_parent.timed_message( 2, "Clip Start: " + bars_to_measure(ls, clip.signature_denominator, clip.signature_numerator) ) @subject_slot("value") def _action_mark_end(self, value): if self._scroll_mode: scroll = value == 1 and 3 or 2 self.application().view.zoom_view(scroll, "Detail/Clip", False) elif not (value == 1 and 1): inc = -1 clip = self.selected_clip_slot and self.selected_clip_slot.has_clip and self.selected_clip_slot.clip ls = clip.start_marker le = clip.end_marker le = max(ls + self.start_inc, le + inc * self.start_inc) clip.end_marker = le self.canonical_parent.timed_message( 2, "Clip End: " + bars_to_measure(le, clip.signature_denominator, clip.signature_numerator) ) @subject_slot("value") def _action_loop_start(self, value): if not (value == 1 and 1): inc = -1 if self.selected_clip_slot and self.selected_clip_slot.has_clip: clip = self.selected_clip_slot.clip ls = clip.loop_start le = clip.loop_end if self.mv_loop: diff = le - ls ls = max(0, ls + inc * self.loop_inc) clip.loop_end = inc > 0 and ls + diff clip.end_marker = ls + diff clip.loop_start = ls clip.start_marker = ls else: clip.loop_start = ls clip.start_marker = ls clip.loop_end = ls + diff clip.end_marker = ls + diff self.canonical_parent.timed_message(2, loop_str(clip)) else: ls = max(0, min(le - self.loop_inc, ls + inc * self.loop_inc)) clip.loop_start = ls self.canonical_parent.timed_message(2, loop_str(clip)) @subject_slot("value") def _action_loop_end(self, value): if not (value == 1 and 1): inc = -1 if self.selected_clip_slot and self.selected_clip_slot.has_clip: clip = self.selected_clip_slot.clip ls = clip.loop_start le = clip.loop_end le = max(ls + self.loop_inc, le + inc * self.loop_inc) clip.loop_end = le clip.end_marker = self.mv_loop and le self.canonical_parent.timed_message(2, loop_str(clip)) def update(self): pass @subject_slot("value") def _action_pitch_c(self, value): cs = self.selected_clip_slot if cs and cs.has_clip and cs.clip.is_audio_clip: cs.clip.pitch_coarse = midi_to_pitchc(value) @subject_slot("value") def _action_pitch_f(self, value): cs = self.selected_clip_slot if cs and cs.has_clip and cs.clip.is_audio_clip: cs.clip.pitch_fine = midi_to_pitchf(value) @subject_slot("value") def _action_gain(self, value): cs = self.selected_clip_slot if cs and cs.has_clip and cs.clip.is_audio_clip: cs.clip.gain = midi_to_gain(value) def _update_clip_name(self): cs = self.song().view.highlighted_clip_slot if not cs: track = self.song().view.selected_track self.canonical_parent.send_to_display("Rt Trck: " + track.name, 3) elif cs.has_clip: self.canonical_parent.send_to_display(cs.clip.is_audio_clip and "A" or "M" + ":" + cs.clip.name, 3) else: track = cs.canonical_parent index = list(track.clip_slots).index(cs) scene = self.song().scenes[index] self.canonical_parent.send_to_display("E<" + str(scene.name) + "> T:" + track.name, 3) @subject_slot("has_clip") def _on_has_clip_changed(self): self._update_clip_name() @subject_slot("name") def _on_name_changed(self): self._update_clip_name() def update_selected_clip(self): cs = self.song().view.highlighted_clip_slot if cs != self.selected_clip_slot: self.selected_clip_slot = cs self._update_clip_name() if cs and cs.has_clip and cs.clip.is_audio_clip: self._on_pitch_c_changed.subject = cs.clip self._on_pitch_f_changed.subject = cs.clip self._on_gain_changed.subject = cs.clip self._on_warp_changed.subject = cs.clip self._gain_slider.send_value(gain_to_midi(cs.clip.gain)) self._pitch_c_slider.send_value(pitchc_to_midi(cs.clip.pitch_coarse)) self._pitch_f_slider.send_value(pitchf_to_midi(cs.clip.pitch_fine)) self._warp_set_button.send_value(cs.clip.warping and 127 or 0, True) else: self._on_pitch_c_changed.subject = None self._on_pitch_f_changed.subject = None self._on_gain_changed.subject = None self._on_warp_changed.subject = None self._on_loop_changed.subject = None if cs and cs.has_clip: self._on_loop_changed.subject = cs.clip self._on_name_changed.subject = cs.clip self._loop_set_button.send_value(cs.clip.looping and 127 or 0, True) else: self._on_name_changed.subject = None self._on_loop_changed.subject = None self._on_has_clip_changed.subject = cs def on_selected_track_changed(self): self.update_selected_clip() def on_selected_scene_changed(self): self.update_selected_clip() @subject_slot("looping") def _on_loop_changed(self): cs = self.song().view.highlighted_clip_slot if cs and cs.has_clip: self._loop_set_button.send_value(cs.clip.looping and 127 or 0, True) @subject_slot("warping") def _on_warp_changed(self): cs = self.song().view.highlighted_clip_slot if cs and cs.has_clip and cs.clip.is_audio_clip: self._warp_set_button.send_value(cs.clip.warping and 127 or 0, True) @subject_slot("pitch_coarse") def _on_pitch_c_changed(self): cs = self.song().view.highlighted_clip_slot if cs and cs.has_clip and cs.clip.is_audio_clip: self._pitch_c_slider.send_value(pitchc_to_midi(cs.clip.pitch_coarse)) @subject_slot("pitch_fine") def _on_pitch_f_changed(self): cs = self.song().view.highlighted_clip_slot if cs and cs.has_clip and cs.clip.is_audio_clip: self._pitch_f_slider.send_value(pitchf_to_midi(cs.clip.pitch_fine)) @subject_slot("gain") def _on_gain_changed(self): cs = self.song().view.highlighted_clip_slot if cs and cs.has_clip and cs.clip.is_audio_clip: self._gain_slider.send_value(gain_to_midi(cs.clip.gain))
class Launchpad(ControlSurface): """ Script for Novation's Launchpad Controller """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._control_is_with_automap = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._wrote_user_byte = False self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143 matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [] for column in range(8): button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, row * 16 + column) button.name = str(column) + '_Clip_' + str(row) + '_Button' button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) self._config_button.add_value_listener(self._config_value) top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, 104 + index) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index]) for index in range(8) ] top_buttons[0].name = 'Bank_Select_Up_Button' top_buttons[1].name = 'Bank_Select_Down_Button' top_buttons[2].name = 'Bank_Select_Left_Button' top_buttons[3].name = 'Bank_Select_Right_Button' top_buttons[4].name = 'Session_Button' top_buttons[5].name = 'User1_Button' top_buttons[6].name = 'User2_Button' top_buttons[7].name = 'Mixer_Button' side_buttons[0].name = 'Vol_Button' side_buttons[1].name = 'Pan_Button' side_buttons[2].name = 'SndA_Button' side_buttons[3].name = 'SndB_Button' side_buttons[4].name = 'Stop_Button' side_buttons[5].name = 'Trk_On_Button' side_buttons[6].name = 'Solo_Button' side_buttons[7].name = 'Arm_Button' self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button) self._selector.name = 'Main_Modes' for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component(self._selector.session_component()) self._suppress_session_highlight = False def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if len(midi_bytes) == 8: if midi_bytes[1:5] == (0, 32, 41, 6): response = long(midi_bytes[5]) response += long(midi_bytes[6]) << 8 if response == Live.Application.encrypt_challenge2(self._challenge): self._on_handshake_successful() def _on_handshake_successful(self): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector.mode_index == 1: new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes, optimized = None): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): if not value in range(128): raise AssertionError enabled = self._wrote_user_byte or value == 1 self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False def _button_value(self, value): raise value in range(128) or AssertionError def _config_value(self, value): raise value in range(128) or AssertionError def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class AxiomPro(ControlSurface): """ Script for the M-Audio Axiom Pro """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): is_momentary = True self._device_selection_follows_track_selection = True self.set_pad_translations(PAD_TRANSLATIONS) self._suggested_input_port = 'HyperControl' self._suggested_output_port = 'HyperControl' self._display_on_button = ButtonElement(not is_momentary, MIDI_CC_TYPE, 15, 79) self._waiting_for_first_response = True mixer1 = DisplayingMixerComponent(0) mixer1.set_select_buttons(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 111), ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 110)) mixer1.set_mute_button(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 12)) mixer1.set_solo_button(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 13)) mixer2 = NotifyingMixerComponent(8) mixer2.set_bank_buttons(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 15), ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 14)) mixer2.master_strip().set_volume_control(SliderElement(MIDI_CC_TYPE, 15, 41)) for index in range(8): mixer2.channel_strip(index).set_volume_control(SliderElement(MIDI_CC_TYPE, 15, 33 + index)) device = PageableDeviceComponent() self.set_device_component(device) ffwd_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 115) rwd_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 114) loop_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 113) transport = TransportComponent() transport.set_stop_button(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 116)) transport.set_play_button(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 117)) transport.set_record_button(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 118)) session = SessionComponent(0, 0) transport_view_modes = TransportViewModeSelector(transport, session, ffwd_button, rwd_button, loop_button) select_button_modes = SelectButtonModeSelector(mixer2, tuple([ ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 49 + offset) for offset in range(8) ])) select_button_modes.set_mode_toggle(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 57)) self._mixer_encoder_modes = EncoderMixerModeSelector(mixer2) encoders = [] for offset in range(8): encoders.append(PeekableEncoderElement(MIDI_CC_TYPE, 15, 17 + offset, Live.MidiMap.MapMode.relative_smooth_two_compliment)) encoders[-1].set_feedback_delay(-1) mixer_or_device = MixerOrDeviceModeSelector(self._mixer_encoder_modes, device, tuple(encoders), tuple([ ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 74 + offset) for offset in range(4) ])) mixer_or_device.set_mode_toggle(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 109)) mixer_or_device.set_peek_button(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 78)) self._track_display = PhysicalDisplayElement(8, 1) self._track_display.set_clear_all_message(SYSEX_START + (16, 247)) self._track_display.set_message_parts(SYSEX_START + (17, 1, 0, 0), (247,)) self._track_display.segment(0).set_data_source(mixer1.selected_strip().track_name_data_source()) device_display = PhysicalDisplayElement(8, 1) device_display.set_message_parts(SYSEX_START + (17, 1, 0, 10), (247,)) parameter_display = PhysicalDisplayElement(16, 1) parameter_display.set_message_parts(SYSEX_START + (17, 2, 0, 0), (247,)) select_button_modes.set_mode_display(parameter_display) mixer1.set_display(parameter_display) mixer2.set_bank_display(parameter_display) page_displays = [] for index in range(4): page_displays.append(PhysicalDisplayElement(5, 1)) page_displays[-1].set_message_parts(SYSEX_START + (17, 4, index, 0), (247,)) encoder_display = PhysicalDisplayElement(80, 8) encoder_display.set_message_parts(SYSEX_START + (17, 3), (247,)) for index in range(8): pos_id = tuple() if index != 0: pos_id += (0,) if index > 3: pos_id += (index % 4, 13) else: pos_id += (index % 4, 0) encoder_display.segment(index).set_position_identifier(pos_id) mixer_or_device.set_displays(encoder_display, parameter_display, device_display, tuple(page_displays)) for component in self.components: component.set_enabled(False) def refresh_state(self): ControlSurface.refresh_state(self) self._waiting_for_first_response = True self.schedule_message(10, self._send_midi, SYSEX_START + (32, 46, 247)) def handle_sysex(self, midi_bytes): if midi_bytes[0:-2] == SYSEX_START + (32,): msg_id_byte = midi_bytes[-2] is_setup_response = msg_id_byte in (46, 38) has_sliders = msg_id_byte == 46 if is_setup_response: if self._waiting_for_first_response: self._waiting_for_first_response = False self._display_on_button.send_value(0) for component in self.components: component.set_enabled(True) self._display_on_button.send_value(127) self._send_midi(SYSEX_START + (16, 247)) self._send_midi(SYSEX_START + (17, 3, 0, 1, 65, 98, 108, 101, 116, 111, 110, 32, 76, 105, 118, 101, 32, 67, 111, 110, 116, 114, 111, 108, 32, 0, 1, 4, 83, 117, 114, 102, 97, 99, 101, 32, 118, 49, 46, 48, 46, 48, 46, 247)) self._mixer_encoder_modes.set_show_volume_page(not has_sliders) for display in self._displays: display.set_block_messages(False) self.schedule_message(25, self._refresh_displays) elif msg_id_byte == 43: self._send_midi(SYSEX_START + (16, 247)) for display in self._displays: if display is self._track_display: display.update() else: display.set_block_messages(True) def disconnect(self): ControlSurface.disconnect(self) self._send_midi(SYSEX_START + (32, 0, 247)) self._send_midi(SYSEX_START + (16, 247)) self._send_midi(SYSEX_START + (17, 3, 0, 4, 65, 98, 108, 101, 116, 111, 110, 32, 76, 105, 118, 101, 32, 67, 111, 110, 116, 114, 111, 108, 32, 0, 1, 4, 83, 117, 114, 102, 97, 99, 101, 32, 67, 108, 111, 115, 101, 100, 46, 247))
class MidiEditSection(CompoundComponent): _current_clip = None _split_value = 4 _base_note = None _gate = 1.0 _selected_note = None _note_pos = None _note_len = 0.0 _bend_val = 0 _offset = 0.0 _transpose = 0 _vel_fade = 0 _fade_mode = FADE_TIME _note_set = None _note_index = 0 def __init__(self, *a, **k): super(MidiEditSection, self).__init__(*a, **k) is_momentary = True self.split_knob = SliderElement(MIDI_CC_TYPE, 5, 70) self.gate_knob = SliderElement(MIDI_CC_TYPE, 5, 71) self.bend_knob = SliderElement(MIDI_CC_TYPE, 5, 72) self.offset_knob = SliderElement(MIDI_CC_TYPE, 5, 73) self.strech_knob = SliderElement(MIDI_CC_TYPE, 5, 74) self.fade_knob = SliderElement(MIDI_CC_TYPE, 5, 75) self.transpose_knob = SliderElement(MIDI_CC_TYPE, 5, 77) self.edit_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 5, 60) self.split_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 5, 61) self.init_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 5, 62) self.delete_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 5, 63) self.select_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 5, 64) self._do_edit_button.subject = self.edit_button self._do_split_button.subject = self.split_button self._do_delete.subject = self.delete_button self._do_init.subject = self.init_button self._do_select.subject = self.select_button self._do_split.subject = self.split_knob self._do_gate.subject = self.gate_knob self._do_bend.subject = self.bend_knob self._do_offset.subject = self.offset_knob self._do_strech.subject = self.strech_knob self._do_fade.subject = self.fade_knob self._do_transpose.subject = self.transpose_knob def do_message(self, msg, statusbarmsg=None): if statusbarmsg == None: self.canonical_parent.show_message(msg) else: self.canonical_parent.show_message(statusbarmsg) self.canonical_parent.timed_message(2, msg) return def _init_value(self): self._split_value = 4 def select_note(self, note): if self._current_clip and self._note_set: self._note_index = 0 self.set_notes(tuple(self._note_set[self._note_index])) @subject_slot('value') def _do_select(self, value): if value == 0: clip_slot = self.song().view.highlighted_clip_slot if clip_slot and clip_slot.has_clip and clip_slot.clip.is_midi_clip: clip = clip_slot.clip self._notes_changed.subject = clip self._current_clip = clip self._note_set = clip.get_notes(0.0, 0, clip.length, 127) if self._note_index == None: self._note_index = 0 else: self._note_index = (self._note_index + 1) % len(self._note_set) ls = [] selnote = self._note_set[self._note_index] ls.append(selnote) clip.remove_notes(selnote[1], selnote[0], selnote[2], 1) clip.deselect_all_notes() clip.set_notes(tuple(ls)) clip.replace_selected_notes(tuple(ls)) return @subject_slot('value') def _do_delete(self, value): if value != 0: self._selected_note = None self.edit_button.send_value(0, True) if self._current_clip: selected = self._current_clip.get_selected_notes() for note in selected: pitch = note[0] self._current_clip.remove_notes(note[1], pitch, note[2], 1) return @subject_slot('value') def _do_init(self, value): if value != 0: self._transpose = 0 self._offset = 0.0 self._bend_val = 0 self._split_value = 4 self._vel_fade = 0.0 self._gate = 1.0 self.canonical_parent.timed_message(2, 'SPLIT:' + str(self._split_value)) @subject_slot('value') def _do_edit_button(self, value): if value > 0: note = self.get_selected_note() self._transpose = 0 if note: self.edit_button.send_value(127, True) else: self.edit_button.send_value(0, True) def get_selected_note(self): clip_slot = self.song().view.highlighted_clip_slot if clip_slot and clip_slot.has_clip and clip_slot.clip.is_midi_clip: self._notes_changed.subject = clip_slot.clip self._current_clip = clip_slot.clip notes = self._current_clip.get_selected_notes() if len(notes) == 1: self._selected_note = notes[0] self._base_note = self._selected_note[0] self._note_len = self._selected_note[2] self._note_pos = self._selected_note[1] return notes[0] return def execute_transpose(self, dir): clip_slot = self.song().view.highlighted_clip_slot if clip_slot and clip_slot.has_clip and clip_slot.clip.is_midi_clip: clip = clip_slot.clip notes = clip.get_selected_notes() if len(notes) > 0: list = [] for note in notes: pitch = min(max(note[0] + dir, 0), 127) list.append((note[0] + dir, note[1], note[2], note[3], note[4])) clip.replace_selected_notes(tuple(list)) def execute_split(self): selected_note = self._selected_note if selected_note: notelen = self._note_len note = min(max(0, self._base_note + self._transpose), 127) vel = self._selected_note[3] mute = self._selected_note[4] sp = self._note_pos pos = self._note_pos if self._vel_fade < 0: endvel = vel startvel = vel - vel * abs(self._vel_fade) else: startvel = vel endvel = vel - vel * self._vel_fade list = [] divList = self.get_interval(notelen, self._split_value, self._bend_val) off = int(self._split_value * self._offset) velinc = (endvel - startvel) / self._split_value vel = startvel for index in range(self._split_value): pcl = int((index + off) % self._split_value) div = divList[pcl] notvlen = div * self._gate if notvlen > 0.0: list.append((note, pos, notvlen, vel, mute)) pos += div if self._fade_mode == FADE_EVENT: vel += velinc else: rp = (pos - sp) / notelen vel = startvel + (endvel - startvel) * rp self._current_clip.replace_selected_notes(tuple(list)) @subject_slot('value') def _do_transpose(self, value): diff = value == REL_KNOB_DOWN and -1 or 1 self.execute_transpose(diff) @subject_slot('value') def _do_fade(self, value): if self.canonical_parent.isShiftDown(): diff = value == REL_KNOB_DOWN and -0.01 or 0.01 else: diff = value == REL_KNOB_DOWN and -0.1 or 0.1 newvale = self._vel_fade + diff if newvale >= -1.0 and newvale <= 1.0: self._vel_fade = newvale self.canonical_parent.timed_message(2, 'FADE:' + str(int(round(self._vel_fade * 100, 0))) + '%') self.canonical_parent.show_message('Split Fade: ' + str(int(round(self._vel_fade * 100, 0))) + '%') self.execute_split() @subject_slot('value') def _do_strech(self, value): diff = value == REL_KNOB_DOWN and -1 or 1 if self.canonical_parent.isShiftDown(): newval = self._note_len + diff * STRECH_INC_PLS else: newval = self._note_len + diff * STRECH_INC if newval > 0: self._note_len = newval self.execute_split() self.canonical_parent.timed_message(2, 'LENGTH:' + str(self._note_len) + ' BEATS') self.canonical_parent.show_message('Set Length TO : ' + str(self._note_len) + ' beats') @subject_slot('value') def _do_offset(self, value): diff = value == REL_KNOB_DOWN and -0.01 or 0.01 newval = self._offset + diff if newval >= 0.0 and newval <= 1.0: self._offset = newval self.canonical_parent.timed_message(2, 'OFF:' + str(round(self._offset, 2))) self.canonical_parent.show_message('Split Offset : ' + str(round(self._offset, 2))) self.execute_split() @subject_slot('value') def _do_split_button(self, value): if value != 0: self.execute_split() @subject_slot('notes') def _notes_changed(self): pass @subject_slot('value') def _do_split(self, value): diff = value == REL_KNOB_DOWN and -1 or 1 newval = self._split_value + diff if newval > 0 and newval <= 128: self._split_value = newval self.canonical_parent.timed_message(2, 'SPLIT:' + str(self._split_value)) self.canonical_parent.show_message('Splits : ' + str(self._split_value)) self.execute_split() @subject_slot('value') def _do_gate(self, value): diff = value == REL_KNOB_DOWN and -0.01 or 0.01 newval = self._gate + diff if newval > 0.1 and newval <= 1.0: self._gate = newval self.canonical_parent.timed_message(2, 'GATE:' + str(int(round(self._gate * 100, 0))) + '%') self.canonical_parent.show_message('Split Gate: ' + str(int(round(self._gate * 100, 0))) + '%') self.execute_split() def get_interval__(self, notelen): div = notelen / self._split_value n = self._split_value ls = [] sum = 0 ct = self._split_value rl = notelen bv = self._bend_val / 100.0 param = abs(bv) * (self._split_value / 2) for i in range(n): div = rl / ct sz = div * (1.0 + param) ls.append(sz) ct -= 1 rl -= sz sum = 0 for v in ls: sum += v ratio = notelen / sum for i in range(len(ls)): ls[i] = ls[i] * ratio return self._bend_val < 0 and ls.reverse() or ls def get_interval(self, notelen, splits, bend_val): div = notelen / splits n = splits ls = [] param = abs(bend_val / 100.0) spl = 2 for i in range(n): sec = notelen / spl ls.append(sec) if i < n - 2: spl *= 1.0 + param acc = 0 for v in ls: acc += v ratio = notelen / acc for i in range(len(ls)): ls[i] = ls[i] * ratio return bend_val < 0.0 and ls.reverse() or ls @subject_slot('value') def _do_bend(self, value): diff = value == REL_KNOB_DOWN and -1 or 1 newval = self._bend_val + diff if newval >= -100 and newval <= 100 and self._selected_note: self._bend_val = newval self.canonical_parent.timed_message(2, 'BEND:' + str(self._bend_val) + '%') self.canonical_parent.show_message('Split Bend: ' + str(self._bend_val) + '%') list = self.get_interval(self._selected_note[2], self._split_value, self._bend_val) self.execute_split() def _on_selected_track_changed(self): debug_out(' Selected Track Changed ') def update(self): pass def refresh(self): pass
class MonoNavSection(CompoundComponent): def __init__(self, mode_selector, *a, **k): super(MonoNavSection, self).__init__(*a, **k) is_momentary = True self._modesel = mode_selector self._device_bank_registry = DeviceBankRegistry() self.volume_knob = SliderElement(MIDI_CC_TYPE, 0, 40) self.tempo_knob = SliderElement(MIDI_CC_TYPE, 0, 41) self.swing_knob = SliderElement(MIDI_CC_TYPE, 0, 42) self.mode_button = ButtonElement(True, MIDI_CC_TYPE, 2, 86) self.alt_button = ButtonElement(True, MIDI_CC_TYPE, 2, 81) self._do_left_knob.subject = self.volume_knob self._do_center_knob.subject = self.tempo_knob self._do_right_knob.subject = self.swing_knob self._do_mode_button.subject = self.mode_button self._do_alt_button.subject = self.alt_button self._mode = MODE_ADJUST self._alt_down = False def do_message(self, msg, statusbarmsg = None): if statusbarmsg == None: self.canonical_parent.show_message(msg) else: self.canonical_parent.show_message(statusbarmsg) self.canonical_parent.timed_message(2, msg) @subject_slot('value') def _do_left_knob(self, value): if not (value == 0 and -1): diff = 1 if self._mode == MODE_ADJUST: self.canonical_parent.isShiftDown() and self.chg_cue(diff) else: self.chg_volume(diff) else: self._do_channel_selection(diff) @subject_slot('value') def _do_center_knob(self, value): if not (value == 0 and -1): diff = 1 if self._mode == MODE_ADJUST: self.canonical_parent.isShiftDown() and self.chg_tempo(diff * 0.1) else: self.chg_tempo(diff) else: self._modesel.navigate(diff, False, self.canonical_parent.isShiftDown()) def chg_tempo(self, diff): self.song().tempo = max(20, min(999, self.song().tempo + diff)) self.canonical_parent.timed_message(2, 'Tempo: ' + str(round(self.song().tempo, 2))) def chg_volume(self, diff): if self._alt_down: self.song().master_track.mixer_device.volume.value = calc_new_parm(self.song().master_track.mixer_device.volume, diff) else: repeat(self.song().master_track.mixer_device.volume, diff) def chg_cue(self, diff): if self._alt_down: self.song().master_track.mixer_device.cue_volume.value = calc_new_parm(self.song().master_track.mixer_device.cue_volume, diff) else: repeat(self.song().master_track.mixer_device.cue_volume, diff) def _do_channel_selection(self, dir): song = self.song() if self._alt_down: if self.canonical_parent.isShiftDown(): if dir == 1: self.canonical_parent._device._bank_up_value(1) else: self.canonical_parent._device._bank_down_value(1) else: ndir = dir == -1 and Live.Application.Application.View.NavDirection.left or Live.Application.Application.View.NavDirection.right self.application().view.scroll_view(ndir, 'Detail/DeviceChain', True) elif self.canonical_parent.isShiftDown(): scenes = song.scenes scene = song.view.selected_scene sindex = list(scenes).index(scene) sel_scene = sindex + dir if sel_scene >= 0 and sel_scene < len(scenes): song.view.selected_scene = scenes[sel_scene] elif not (dir == -1 and Live.Application.Application.View.NavDirection.left): direction = Live.Application.Application.View.NavDirection.right self.application().view.scroll_view(direction, 'Session', True) self.canonical_parent.arm_selected_track and arm_exclusive(song) @subject_slot('value') def _do_right_knob(self, value): diff = value == 0 and -1 or 1 if self._mode == MODE_ADJUST: song = self.song() if self.canonical_parent.isShiftDown(): quant = song.clip_trigger_quantization song.clip_trigger_quantization = max(0, min(13, quant + diff)) self.do_message('Clip Quantize ' + CLIQ_DESCR[song.clip_trigger_quantization]) else: rec_quant = song.midi_recording_quantization index = QUANT_CONST.index(rec_quant) + diff if index >= 0 and index < len(QUANT_CONST): song.midi_recording_quantization = QUANT_CONST[index] self.do_message(QUANT_DESCR[index]) else: self._modesel.navigate(diff, True, self.canonical_parent.isShiftDown()) @subject_slot('value') def _do_mode_button(self, value): if value != 0: if self.canonical_parent.isShiftDown(): self.canonical_parent.toggle_nav_mode() elif self._alt_down: self.canonical_parent._do_focus_navigate(1) elif self._mode == MODE_ADJUST: self._mode = MODE_NAV self.do_message('Master Knobs control navigation') self.mode_button.send_value(1, True) elif self._mode == MODE_NAV: self._mode = MODE_ADJUST self.do_message('Master Knobs control Volume | Tempo | Quantization') self.mode_button.send_value(0, True) @subject_slot('value') def _do_alt_button(self, value): self._alt_down = value > 0 def refresh(self): pass def update(self): pass def disconnect(self): super(MonoNavSection, self).disconnect()
class NoteRepeatComponent(CompoundComponent): __module__ = __name__ __doc__ = ' Noter Repeat Handler' _knob_handler = None def __init__(self, note_repeat = None, *a, **k): super(NoteRepeatComponent, self).__init__(*a, **k) self._note_repeat = note_repeat self._adjust_cfg_value.subject = SliderElement(MIDI_CC_TYPE, 2, 105) self._note_repeat_button = ButtonElement(True, MIDI_CC_TYPE, 0, 102) self._do_note_repeat.subject = self._note_repeat_button self._cfg_adjust_button = ButtonElement(True, MIDI_CC_TYPE, 2, 106) self._cfg_adjust_button.add_value_listener(self._do_cfg_button) self._cfg_down = False self._hold_mode = False self.nr_down = False self._current_nr_button = None self._do_change_nr_1.subject = SliderElement(MIDI_CC_TYPE, 1, 76) self._do_change_nr_2.subject = SliderElement(MIDI_CC_TYPE, 1, 77) self._do_change_nr_3.subject = SliderElement(MIDI_CC_TYPE, 1, 78) self._do_change_nr_4.subject = SliderElement(MIDI_CC_TYPE, 1, 79) def createButton(ccindenfier, nr_freq): button = ButtonElement(True, MIDI_CC_TYPE, 1, ccindenfier) button.add_value_listener(self._select_value, True) button.active = False button.freq = nr_freq button.cfg = False button.hold = False return button def createCfgButton(ccindenfier, nr_freq_idx): button = ButtonElement(True, MIDI_CC_TYPE, 2, ccindenfier) button.add_value_listener(self._select_value, True) button.active = False button.fr_idx = nr_freq_idx button.freq = CFG_REPEAT_FREQUENCIES[nr_freq_idx] button.cfg = True button.hold = False return button self._cfg_buttons = [ createCfgButton(assign[0], assign[1]) for assign in CTRL_CFG_TO_FREQ ] for button in self._cfg_buttons: button.send_value(button.active and 1 or 0, True) self.nr_frq = CTRL_TO_FREQ[4][1] self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 self.buttons = [ createButton(assign[0], assign[1]) for assign in CTRL_TO_FREQ ] self.buttons[4].active = True self._previous_button = self.buttons[4] self._last_active_button = self.buttons[4] for button in self.buttons: button.send_value(button.active and 1 or 0, True) def update(self): pass def store_values(self, dict): for button, idx in zip(self._cfg_buttons, range(len(self._cfg_buttons))): dict['cofig-nr-val' + str(idx)] = button.fr_idx def recall_values(self, dict): for button, idx in zip(self._cfg_buttons, range(len(self._cfg_buttons))): key = 'cofig-nr-val' + str(idx) if key in dict: fqidx = dict[key] button.fr_idx = fqidx button.freq = CFG_REPEAT_FREQUENCIES[fqidx] def registerKnobHandler(self, handler): self._knob_handler = handler def show_note_rates(self): rates = '' for button, idx in zip(self._cfg_buttons, range(len(self._cfg_buttons))): rates += ' ' + CFG_REPEAT_DISPLAY[button.fr_idx].ljust(5) if idx < 3: rates += '|' self.canonical_parent.timed_message(2, rates) def mod_button(self, button, inc, which): cindex = button.fr_idx maxindex = len(CFG_REPEAT_FREQUENCIES) - 1 minindex = 0 if self.canonical_parent.isShiftDown(): inc *= 2 if not (cindex % 2 == 0 and maxindex): maxindex = maxindex - 1 minindex = cindex % 2 new_idx = max(minindex, min(maxindex, cindex + inc)) if new_idx != cindex: self.canonical_parent.show_message('Note Repeat Button ' + str(which) + ' : ' + CFG_REPEAT_DISPLAY[new_idx]) button.fr_idx = new_idx button.freq = CFG_REPEAT_FREQUENCIES[new_idx] self.nr_frq = button.active and CFG_REPEAT_FREQUENCIES[new_idx] self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 @subject_slot('value') def _do_change_nr_1(self, value): self.mod_button(self._cfg_buttons[0], value == 0 and -1 or 1, 1) self.show_note_rates() @subject_slot('value') def _do_change_nr_2(self, value): self.mod_button(self._cfg_buttons[1], value == 0 and -1 or 1, 2) self.show_note_rates() @subject_slot('value') def _do_change_nr_3(self, value): self.mod_button(self._cfg_buttons[2], value == 0 and -1 or 1, 3) self.show_note_rates() @subject_slot('value') def _do_change_nr_4(self, value): self.mod_button(self._cfg_buttons[3], value == 0 and -1 or 1, 4) self.show_note_rates() def _do_cfg_button(self, value): if value != 0: self._cfg_down = True button = self._current_nr_button if button and button.cfg and button.fr_idx >= 0: self.canonical_parent.show_message('Note Repeat ' + CFG_REPEAT_DISPLAY[button.fr_idx]) else: self._cfg_down = False if self._knob_handler: self._knob_handler.do_main_push(value) @subject_slot('value') def _adjust_cfg_value(self, value): button = self._current_nr_button if button and button.cfg and (self.nr_down or button.hold or self._hold_mode and button.active): if not (value == 127 and -1): inc = 1 cindex = button.fr_idx maxindex = len(CFG_REPEAT_FREQUENCIES) - 1 minindex = 0 self._cfg_down and inc *= 2 maxindex = cindex % 2 == 0 and maxindex or maxindex - 1 minindex = cindex % 2 new_idx = max(minindex, min(maxindex, cindex + inc)) self.canonical_parent.show_message('Note Repeat ' + CFG_REPEAT_DISPLAY[new_idx]) button.fr_idx = new_idx button.freq = CFG_REPEAT_FREQUENCIES[new_idx] self.nr_frq = CFG_REPEAT_FREQUENCIES[new_idx] self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 elif self._knob_handler: self._knob_handler.do_main(value) def _select_value(self, value, button): if value != 0: self._current_nr_button = button button.hold = True self.show_note_rates() if self._hold_mode: if self._previous_button == button: button.send_value(0, True) button.active = False self._last_active_button = button button.active = False self._note_repeat.repeat_rate = 32.0 self._previous_button = None elif self._previous_button == None or self._previous_button != button: button.send_value(1, True) button.active = True self.nr_frq = button.freq self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 if not self._note_repeat.enabled: self._note_repeat.enabled = True if self._previous_button != None: self._previous_button.active = False self._previous_button.send_value(0, True) self._previous_button = button else: button.send_value(1, True) button.active = True self.nr_frq = button.freq self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 if self._previous_button != None and self._previous_button != button: self._previous_button.active = False self._previous_button.send_value(0, True) self._previous_button = button else: button.hold = False @subject_slot('value') def _do_note_repeat(self, value): self.nr_down = value > 0 if self._hold_mode: if value > 0: self._note_repeat_button.send_value(0) self._note_repeat.enabled = False self._hold_mode = False if self._previous_button == None and self._last_active_button != None: self._previous_button = self._last_active_button self._last_active_button.send_value(1) self._last_active_button.active = True self.nr_frq = self._last_active_button.freq self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 elif self.canonical_parent.isShiftDown() and value > 0: self._note_repeat_button.send_value(1) self._note_repeat.enabled = True self._hold_mode = True self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 elif value == 0: self._note_repeat.enabled = False self._note_repeat_button.send_value(0) else: self._note_repeat_button.send_value(1) self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 self._note_repeat.enabled = True def disconnect(self): super(NoteRepeatComponent, self).disconnect()
class F1ColorButtonElement(ButtonElement): ' SPECIAL BUTTON CLASS THAT INTERNALLY HOLDS 3 BUTTONS FOR HANDLING RGB / HSV AND ONE BUTTON THAT HANDLES INCOMING MIDI' __module__ = __name__ """ MAIN CONSTRUCTOR """ def __init__(self, is_momentary, msg_type, identifier, hsvChannels, controlChannel, rgbColor, name): log(True, __name__) ButtonElement.__init__(self, is_momentary, msg_type, controlChannel, identifier) self.name = name + " Control" # create 3 buttons for HSV self.hueButton = ButtonElement(is_momentary, msg_type, hsvChannels[0], identifier) self.satButton = ButtonElement(is_momentary, msg_type, hsvChannels[1], identifier) self.valButton = ButtonElement(is_momentary, msg_type, hsvChannels[2], identifier) self.id = identifier # color self.hsv_fader = None rgbColor = _liveOsTools.colorsys.hex2rgb(rgbColor) hsvColor = _liveOsTools.colorsys.rgb_to_hsv(rgbColor[0] / 255.0, rgbColor[1] / 255.0, rgbColor[2] / 255.0) self.hsv_fader = HSVFader(hsvColor[0], hsvColor[1], hsvColor[2]) self.send_current_color() log(False, __name__) """ DISCONNECT """ def disconnect(self): log(__name__, "disconnect") self.hueButton = None self.hsvColor = None self.satButton = None self.hsv_fader = None """ FADE ONCE TO THAT VALUE """ def fade_to(self, hue, sat, val, time): #log(__name__, "fade_to: hue("+str(hue)+"), sat("+str(sat)+"), val("+str(val)+"), time("+str(time)+")") self.hsv_fader.add_fade(hue, sat, val, time) """ UPDATE FRAME TIME BASED """ def process(self, time_in_samples): #log(__name__, "update") #self.hsv_fader.process(time_in_samples) self.send_current_color() """ SEND COLOR """ def send_current_color(self): # send color values to buttons #log("sending: on CC: "+ str(self.id)+ " -- val: "+ str([int(self.hsv_fader.current_color[INDEX_HUE] * MIDI_RANGE),int(self.hsv_fader.current_color[INDEX_SAT] * MIDI_RANGE),int(self.hsv_fader.current_color[INDEX_VAL] * MIDI_RANGE)])) self.hueButton.send_value( int(self.hsv_fader.current_color[INDEX_HUE] * MIDI_RANGE), True) self.satButton.send_value( int(self.hsv_fader.current_color[INDEX_SAT] * MIDI_RANGE), True) self.valButton.send_value( int(self.hsv_fader.current_color[INDEX_VAL] * MIDI_RANGE), True) """ SEND BASE COLOR """ def send_base_color(self): self.hueButton.send_value( int(self.hsv_fader.get_base_color()[INDEX_HUE] * MIDI_RANGE), True) self.satButton.send_value( int(self.hsv_fader.get_base_color()[INDEX_SAT] * MIDI_RANGE), True) self.valButton.send_value( int(self.hsv_fader.get_base_color()[INDEX_VAL] * MIDI_RANGE), True) """ SET THE MAIN COLOR """ def set_base_color(self, liveRGBcolor): rgbColor = _liveOsTools.colorsys.hex2rgb(liveRGBcolor) hsvColor = _liveOsTools.colorsys.rgb_to_hsv(rgbColor[0] / 255.0, rgbColor[1] / 255.0, rgbColor[2] / 255.0) log(__name__, "new base color: rgb=" + str(rgbColor) + ", hsv=" + str(hsvColor)) self.hsv_fader.set_base_color(hsvColor[0], hsvColor[1], hsvColor[2]) self.send_base_color()
class APCmini(ControlSurface): """ Script for Novation's Launchpad Controller """ def __init__(self, c_instance): live = Live.Application.get_application() self._live_major_version = live.get_major_version() self._live_minor_version = live.get_minor_version() self._live_bugfix_version = live.get_bugfix_version() ControlSurface.__init__(self, c_instance) #self._device_selection_follows_track_selection = True with self.component_guard(): self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._control_is_with_automap = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) # Apparently this CC is used to enable feedback, ie lighting up buttons that you pressed self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._wrote_user_byte = False self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143 matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [] for column in range(8): button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, (7-row) * 8 + column) # APCmini rows are in reverse order from Launchpad button.name = str(column) + '_Clip_' + str(row) + '_Button' button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) # Control goes through here on LP, no real equivalent in APCmini (I don't think) self._config_button.add_value_listener(self._config_value) top_buttons = [ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, 64 + index) for index in range(8)] side_buttons = [ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index]) for index in range(8)] top_buttons[0].name = 'Bank_Select_Up_Button' top_buttons[1].name = 'Bank_Select_Down_Button' top_buttons[2].name = 'Bank_Select_Left_Button' top_buttons[3].name = 'Bank_Select_Right_Button' top_buttons[4].name = 'Session_Button' top_buttons[5].name = 'User1_Button' top_buttons[6].name = 'User2_Button' top_buttons[7].name = 'Mixer_Button' side_buttons[0].name = 'Vol_Button' side_buttons[1].name = 'Pan_Button' side_buttons[2].name = 'SndA_Button' side_buttons[3].name = 'SndB_Button' side_buttons[4].name = 'Stop_Button' side_buttons[5].name = 'Trk_On_Button' side_buttons[6].name = 'Solo_Button' side_buttons[7].name = 'Arm_Button' self._osd = M4LInterface() self._osd.name = "OSD" self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self._osd, self) self._selector.name = 'Main_Modes' self._do_combine() for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component(self._selector.session_component()) self._suppress_session_highlight = False self.log_message("LaunchPad95 Loaded !") def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._do_uncombine() self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None _active_instances = [] #def highlighting_session_component(self): # " Return the session component showing the ring in Live session " # return self._selector.session_component() def _combine_active_instances(): support_devices = False for instance in APCmini._active_instances: support_devices |= (instance._device_component != None) offset = 0 for instance in APCmini._active_instances: instance._activate_combination_mode(offset, support_devices) offset += instance._selector._session.width() _combine_active_instances = staticmethod(_combine_active_instances) def _activate_combination_mode(self, track_offset, support_devices): if(Settings.STEPSEQ__LINK_WITH_SESSION): self._selector._stepseq.link_with_step_offset(track_offset) if(Settings.SESSION__LINK): self._selector._session.link_with_track_offset(track_offset) def _do_combine(self): if (DO_COMBINE and (self not in APCmini._active_instances)): APCmini._active_instances.append(self) APCmini._combine_active_instances() def _do_uncombine(self): if self in APCmini._active_instances: APCmini._active_instances.remove(self) if(Settings.SESSION__LINK): self._selector._session.unlink() if(Settings.STEPSEQ__LINK_WITH_SESSION): self._selector._stepseq.unlink() APCmini._combine_active_instances() def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): # if len(midi_bytes) == 8: # if midi_bytes[1:5] == (0, 32, 41, 6): # response = long(midi_bytes[5]) # response += long(midi_bytes[6]) << 8 # if response == Live.Application.encrypt_challenge2(self._challenge): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector.mode_index == 1: if self._selector._sub_mode_index[self._selector._mode_index] > 0: # disable midi map rebuild for instrument mode to prevent light feedback errors new_channel = self._selector.channel_for_current_mode() # self.log_message(str(new_channel)) # for note in DRUM_NOTES: # self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes, optimized=None): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False # self._send_challenge() self.set_enabled(True) # Enable anyways, without checking def _send_challenge(self): for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): assert (value in range(128)) if not self._wrote_user_byte: enabled = (value == 1) self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False def _button_value(self, value): assert value in range(128) def _config_value(self, value): assert value in range(128) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class NanoKontrolLP95(ControlSurface): __module__ = __name__ __doc__ = " NanoKontrolLP95 controller script " def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) #self._suppress_session_highlight = True self._suppress_send_midi = True # Turn off rebuild MIDI map until after we're done setting up Live.Base.log(time.strftime("%d.%m.%Y %H:%M:%S", time.localtime()) + "--------------= NanoKontrolLP95 log opened =--------------") # Writes message into Live's main log file. This is a ControlSurface method. with self.component_guard(): # OBJECTS self._session = None #session object self._mixer = None #mixer object self._transport = None #transport object self._last_button_time = time.time() self._io_list_index = 0 self._setup_controls() self._setup_session_control() # Setup the session object self._setup_mixer_control() # Setup the mixer object self._session.set_mixer(self._mixer) # Bind mixer to session self._setup_transport_control() # Setup transport object self._set_mode_button() self._set_normal_mode() self._track = self.song().view.selected_track self.set_highlighting_session_component(self._session) self._flash_counter = 0 self._flash_on = True for component in self.components: component.set_enabled(True) self._suppress_send_midi = True # Turn rebuild back on, once we're done setting up Live.Base.log("NanoKontrolLP95 Loaded !") def disconnect(self): """clean things up on disconnect""" if self._cycle_button != None: self._cycle_button.remove_value_listener(self._cycle_button_value) self._clear_controls() self._transport.set_stop_button(None) self._transport.set_play_button(None) self._transport.set_rec_button(None) self._solo_buttons = None self._mute_buttons = None self._arm_buttons = None self._knobs = None self._faders = None self._ff_button = None self._rwd_button = None self._play_button = None self._stop_button = None self._rec_button = None self._track_left_button = None self._track_right_button = None self._cycle_button = None self._set_button = None self._mrk_left_button = None self._mrk_right_button = None self._session = None self._mixer = None self._transport = None ControlSurface.disconnect(self) def _setup_controls(self): self._track_left_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, track_left_btn) self._track_right_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, track_right_btn) self._cycle_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, cycle_btn) self._cycle_button_active = False self._set_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, set_btn) self._mrk_left_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, mrk_left_btn) self._mrk_right_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, mrk_right_btn) self._ff_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, ff_btn) self._rwd_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, rwd_btn) self._play_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, play_btn) self._stop_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, stop_btn) self._rec_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL, rec_btn) self._solo_buttons = [ButtonElement(True, MIDI_CC_TYPE, CHANNEL, track_solo_cc[index]) for index in range(num_tracks)] self._mute_buttons = [ButtonElement(True, MIDI_CC_TYPE, CHANNEL, track_mute_cc[index]) for index in range(num_tracks)] self._arm_buttons = [ButtonElement(True, MIDI_CC_TYPE, CHANNEL, track_arm_cc[index]) for index in range(num_tracks)] self._knobs = [SliderElement(MIDI_CC_TYPE, CHANNEL, mixer_knob_cc[index]) for index in range(num_tracks)] self._faders = [SliderElement(MIDI_CC_TYPE, CHANNEL, mixer_fader_cc[index]) for index in range(num_tracks)] def _setup_session_control(self): # CREATE SESSION, SET OFFSETS, BUTTONS NAVIGATION AND BUTTON MATRIX self._session = SpecialSessionComponent(num_tracks, num_scenes) #(num_tracks, num_scenes) self._session.set_offsets(0, 0) def _setup_mixer_control(self): #CREATE MIXER, SET OFFSET (SPECIALMIXERCOMPONENT USES SPECIALCHANNELSTRIP THAT ALLOWS US TO UNFOLD TRACKS WITH TRACK SELECT BUTTON) self._mixer = SpecialMixerComponent(self, num_tracks, 0, False, False) # 8 tracks, 2 returns, no EQ, no filters self._mixer.name = 'Mixer' self._mixer.set_track_offset(0) #Sets start point for mixer strip (offset from left) self._mixer.set_select_buttons(self._track_right_button,self._track_left_button) self._mixer.set_knobs(self._knobs) def _setup_transport_control(self): # CREATE TRANSPORT DEVICE self._transport = SpecialTransportComponent(self) self._transport.set_stop_button(self._stop_button) self._transport.set_play_button(self._play_button) self._transport.set_rec_button(self._rec_button) def connect_script_instances(self, instanciated_scripts): #Live.Base.log("connect_script_instances - Start") #Live.Base.log("connect_script_instances - self._control_surfaces()=" + str(self._control_surfaces())) if(linked): for control_surface in self._control_surfaces(): control_surface_type = str(control_surface) for sync_master in SYNC_TO_LIST: if(control_surface_type.count(sync_master)>0): control_surface_session = control_surface.highlighting_session_component() if control_surface_session: self._session.sync_to(control_surface_session) self._on_track_list_changed() break def _clear_controls(self): if (self._ff_button != None): self._ff_button.remove_value_listener(self._out_value) self._ff_button.turn_off() if (self._rwd_button != None): self._rwd_button.remove_value_listener(self._in_value) self._rwd_button.turn_off() if (self._set_button != None): self._set_button.remove_value_listener(self._monitor_value) self._set_button.remove_value_listener(self._dup_track_value) if (self._mrk_left_button != None): self._mrk_left_button.remove_value_listener(self._sub_in_value) self._mrk_left_button.remove_value_listener(self._new_midi_value) if (self._mrk_right_button != None): self._mrk_right_button.remove_value_listener(self._sub_out_value) self._mrk_right_button.remove_value_listener(self._new_audio_value) # SESSION resetsend_controls = [] self._mixer.send_controls = [] self._session.set_stop_track_clip_buttons(None) # MIXER self._mixer._set_send_nav(None, None) for track_index in range(num_tracks): strip = self._mixer.channel_strip(track_index) strip.set_solo_button(None) strip.set_mute_button(None) strip.set_arm_button(None) resetsend_controls.append(None) strip.set_select_button(None) for i in range(12): self._mixer.send_controls.append(None) strip.set_send_controls(tuple(self._mixer.send_controls)) self._mixer.set_resetsend_buttons(tuple(resetsend_controls)) self.log_message("Controls Cleared") def _set_mode_button(self): if self._cycle_button != None: self._cycle_button.remove_value_listener(self._cycle_button_value) self._cycle_button.add_value_listener(self._cycle_button_value) self._cycle_button.set_light(self._cycle_button_active) def _cycle_button_value(self, value): assert (value in range(128)) if self._cycle_button != None: if value is not 0: self._cycle_button_active = not self._cycle_button_active self._clear_controls() if self._cycle_button_active: self._set_alt_mode() else: self._set_normal_mode() self.update() def _set_normal_mode(self): for index in range(num_tracks): strip = self._mixer.channel_strip(index) strip.set_solo_button(self._solo_buttons[index]) strip.set_mute_button(self._mute_buttons[index]) strip.set_arm_button(self._arm_buttons[index]) strip.set_pan_control(self._knobs[index]) strip.set_volume_control(self._faders[index]) self._set_in_out_nav_listeners() self.show_message("IN/OUT SETUP - MUTE SOLO ARM") def _set_alt_mode(self): self._mixer._set_send_nav(self._ff_button, self._rwd_button) stop_track_controls = [] resetsend_controls = [] # SET SESSION TRACKSTOP, TRACK SELECT, RESET SEND KNOB for index in range(num_tracks): strip = self._mixer.channel_strip(index) strip.set_select_button(self._solo_buttons[index]) stop_track_controls.append(self._arm_buttons[index]) resetsend_controls.append(self._mute_buttons[index]) self._session.set_stop_track_clip_buttons(tuple(stop_track_controls)) self._mixer.set_resetsend_buttons(tuple(resetsend_controls)) self._mixer._update_send_index() self._set_create_track_listeners() self.show_message("TRACK CREATE DEL DUPE - SEL STOP RESET SEND") def _set_in_out_nav_listeners(self): if (self._ff_button != None): self._ff_button.add_value_listener(self._out_value) if (self._rwd_button != None): self._rwd_button.add_value_listener(self._in_value) if (self._set_button != None): self._set_button.add_value_listener(self._monitor_value) if (self._mrk_left_button != None): self._mrk_left_button.add_value_listener(self._sub_in_value) if (self._mrk_right_button != None): self._mrk_right_button.add_value_listener(self._sub_out_value) self.update() def _monitor_value(self, value): now = time.time() if(value is not 0): self._last_button_time = now else: song = self.song() if self._track in song.tracks: if now - self._last_button_time < LONG_PRESS: if not self._track.is_foldable: self._track.current_monitoring_state = (self._track.current_monitoring_state + 1) % 3 else: self._set_default_io() def _set_default_io(self): if self._track.has_midi_input: self._track.input_routing_type = list(self._track.available_input_routing_types)[0] if self._track.has_audio_output: if self._track.is_grouped: self._track.output_routing_type = list(self._track.available_output_routing_types)[2] else: self._track.output_routing_type = list(self._track.available_output_routing_types)[1] else: self._track.output_routing_type = list(self._track.available_output_routing_types)[-1] else: self._track.input_routing_type = list(self._track.available_input_routing_types)[-1] if self._track.is_grouped: self._track.output_routing_type = list(self._track.available_output_routing_types)[2] else: self._track.output_routing_type = list(self._track.available_output_routing_types)[1] self._track.input_routing_channel = list(self._track.available_input_routing_channels)[0] self._track.output_routing_channel = list(self._track.available_output_routing_channels)[0] self.show_message("TRACK: " + str(self._track.name) + ' INPUT - OUTPUT RESET ') def _in_value(self, value): if(value is not 0): routings = list(self._track.available_input_routing_types) current_routing = self._track.input_routing_type if current_routing in routings: new_index = (routings.index(current_routing) + 1) % len(routings) self._track.input_routing_type = routings[new_index] route = ' INPUT: ' + str(self._track.input_routing_type.display_name) self.show_message("TRACK: " + str(self._track.name) + route) self.update() def _out_value(self, value): if(value is not 0): routings = list(self._track.available_output_routing_types) current_routing = self._track.output_routing_type if current_routing in routings: new_index = (routings.index(current_routing) + 1) % len(routings) self._track.output_routing_type = routings[new_index] route = ' OUTPUT: ' + str(self._track.output_routing_type.display_name) self.show_message("TRACK: " + str(self._track.name) + route) self.update() def _sub_in_value(self, value): if(value is not 0): routings = list(self._track.available_input_routing_channels) current_routing = self._track.input_routing_channel if current_routing in routings: new_index = (routings.index(current_routing) + 1) % len(routings) self._track.input_routing_channel = routings[new_index] route = ' SUB_INPUT: ' + str(self._track.input_routing_channel.display_name) self.show_message("TRACK: " + str(self._track.name) + route) self.update() def _sub_out_value(self, value): if(value is not 0): routings = list(self._track.available_output_routing_channels) current_routing = self._track.output_routing_channel if current_routing in routings: new_index = (routings.index(current_routing) + 1) % len(routings) self._track.output_routing_channel = routings[new_index] route = ' SUB_OUTPUT: ' + str(self._track.output_routing_channel.display_name) self.show_message("TRACK: " + str(self._track.name) + route) self.update() def _on_selected_track_changed(self): # ALLOWS TO GRAB THE FIRST DEVICE OF A SELECTED TRACK IF THERE'S ANY ControlSurface._on_selected_track_changed(self) self._track = self.song().view.selected_track def update(self): ControlSurface.update(self) self._cycle_button.set_light(self._cycle_button_active) def _set_create_track_listeners(self): if (self._set_button != None): self._set_button.add_value_listener(self._dup_track_value) if (self._mrk_left_button != None): self._mrk_left_button.add_value_listener(self._new_midi_value) if (self._mrk_right_button != None): self._mrk_right_button.add_value_listener(self._new_audio_value) self.update() def _dup_track_value(self, value): now = time.time() if(value is not 0): self._last_button_time = now else: song = self.song() if self._track in song.tracks: if now - self._last_button_time < LONG_PRESS: song.duplicate_track(list(song.tracks).index(self._track)) else: song.delete_track(list(song.tracks).index(self._track)) def _new_audio_value(self, value): if(value is not 0): self._add_track(self.song().create_audio_track) def _new_midi_value(self, value): if(value is not 0): self._add_track(self.song().create_midi_track) def _add_track(self, func): song = self.song() index = list(song.tracks).index(self._track) + 1 if index < len(song.tracks) and index >0: track = song.tracks[index] if track.is_foldable or track.is_grouped: while index < len(song.tracks) and song.tracks[index].is_grouped: index += 1 func(index) @profile def update_display(self): super(NanoKontrolLP95, self).update_display() self._flash_counter = self._flash_counter + 1 if self._cycle_button_active == True: if(self._flash_counter % 2 == 0): if (self._flash_on == True): self._cycle_button.send_value(127) else: self._cycle_button.send_value(0) self._flash_on = not self._flash_on self._flash_counter = self._flash_counter % 4
class Launchpad(ControlSurface): " SCRIPT FOR NOVATION'S LAUNCHPAD CONTROLLER " " INITALIZE " def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): #self.set_suppress_rebuild_requests(True) self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = "Launchpad" self._suggested_output_port = "Launchpad" self._control_is_with_automap = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = "User_Byte_Button" self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._wrote_user_byte = False self._challenge = (Live.Application.get_random_int(0, 400000000) & 2139062143) matrix = ButtonMatrixElement() matrix.name = "Button_Matrix" """ TRACKFINDER TEST track_index = 0 for track in self.song().tracks: if track_index < 8: button_row = [] if track.is_foldable: for column in range(8): log("right one: " + str(track_index)) button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((track_index * 16) + column)) #@UndefinedVariable button.name = (((str(column) + "_Clip_") + str(track_index)) + "_Button") button_row.append(button) track_index = track_index + 1 else: for column in range(8): log("wrong one: " + str(track_index)) button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, 99, True) #@UndefinedVariable button.name = (str(column) + "_Clip_Button-DUMMY") button_row.append(button) matrix.add_row(tuple(button_row)) log("done")""" """ ORIGINAL CODE """ for row in range(8): button_row = [] for column in range(8): button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column)) button.name = (((str(column) + "_Clip_") + str(row)) + "_Button") button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) self._config_button.add_value_listener(self._config_value) top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, (104 + index)) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index]) for index in range(8) ] top_buttons[0].name = "Bank_Select_Up_Button" top_buttons[1].name = "Bank_Select_Down_Button" top_buttons[2].name = "Bank_Select_Left_Button" top_buttons[3].name = "Bank_Select_Right_Button" top_buttons[4].name = "Session_Button" top_buttons[5].name = "User1_Button" top_buttons[6].name = "User2_Button" top_buttons[7].name = "Mixer_Button" side_buttons[0].name = "Vol_Button" side_buttons[1].name = "Pan_Button" side_buttons[2].name = "SndA_Button" side_buttons[3].name = "SndB_Button" side_buttons[4].name = "Stop_Button" side_buttons[5].name = "Trk_On_Button" side_buttons[6].name = "Solo_Button" side_buttons[7].name = "Arm_Button" self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button) self._selector.name = "Main_Modes" for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component(self._selector.session_component()) self._suppress_session_highlight = False #self.set_suppress_rebuild_requests(False) " DISCONNECTOR " def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None " RETURN THE SESSION COMPONENT SHOWING THE RING IN LIVE SESSION " def highlighting_session_component(self): return self._selector.session_component() " REFRESH " def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) " SYSEX HANDLING " def handle_sysex(self, midi_bytes): if (len(midi_bytes) == 8): if (midi_bytes[1:5] == (0, 32, 41, 6)): response = long(midi_bytes[5]) response += (long(midi_bytes[6]) << 8) if (response == Live.Application.encrypt_challenge2(self._challenge)): self._suppress_send_midi = False self.set_enabled(True) " MIDI MAP " def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if (self._selector.mode_index == 1): new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) " SEND THE MIDI STUFF " def _send_midi(self, midi_bytes, optimized = None): sent_successfully = False if (not self._suppress_send_midi): sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully " UPDATE THE HARDWARE " def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() " CHALLANGE SEND " def _send_challenge(self): for index in range(4): challenge_byte = ((self._challenge >> (8 * index)) & 127) self._send_midi((176, (17 + index), challenge_byte)) " USER BYTE STUFF " def _user_byte_value(self, value): if not value in range(128): raise AssertionError enabled = self._wrote_user_byte or value == 1 self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False " BUTTON VALUE " def _button_value(self, value): assert (value in range(128)) " CONFIG VALUE " def _config_value(self, value): assert (value in range(128)) " SET THE SESSION HIGHLIGHT " def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if (not self._suppress_session_highlight): ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class LaunchMod(ControlSurface): __module__ = __name__ __doc__ = " Script for Novation's Launchpad Controller " def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self._monomod_version = 'b994' self._host_name = 'LaunchMod' self._color_type = 'Launchpad' self.hosts = [] self._timer = 0 self.set_suppress_rebuild_requests(True) self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._wrote_user_byte = False self._control_is_with_automap = False self._challenge = (Live.Application.get_random_int(0, 400000000) & 2139062143) matrix = ButtonMatrixElement() matrix.name = 'ButtonMatrix' for row in range(8): #button_row = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column)) for column in range(8) ] button_row = [ FlashingButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column), 'Button_'+str(row)+'_'+str(column), self) for column in range(8) ] matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0) self._config_button.add_value_listener(self._config_value) top_buttons = [ FlashingButtonElement(is_momentary, MIDI_CC_TYPE, 0, (104 + index), 'Top_Button'+str(index), self) for index in range(8) ] side_buttons = [ FlashingButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index], 'Side_Button'+str(index), self) for index in range(8) ] self._setup_monobridge() self._setup_monomod() self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self) self._suppress_session_highlight = False self._suppress_send_midi = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._suppress_send_midi = True self.set_suppress_rebuild_requests(False) self.log_message("--------------= LaunchMod log opened =--------------") #Create entry in log file self.refresh_state() def _setup_monobridge(self): self._monobridge = MonoBridgeElement(self) self._monobridge.name = 'MonoBridge' def _setup_monomod(self): self._host = MonomodComponent(self) self._host.name = 'Monomod_Host' self.hosts = [self._host] def disconnect(self): self._suppress_send_midi = True self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None self.log_message("--------------= LaunchMod log closed =--------------") #Create entry in log file def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if (len(midi_bytes) == 8): if (midi_bytes[1:5] == (0, 32, 41, 6)): response = long(midi_bytes[5]) response += (long(midi_bytes[6]) << 8) if (response == Live.Application.encrypt_challenge2(self._challenge)): self._suppress_send_midi = False self.set_enabled(True) #self.refresh_state() def _send_midi(self, midi_bytes): if (not self._suppress_send_midi): ControlSurface._send_midi(self, midi_bytes) def _update_hardware(self): self._suppress_send_midi = False self._config_button.send_value(40) self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = ((self._challenge >> (8 * index)) & 127) self._send_midi((176, (17 + index), challenge_byte)) def _user_byte_value(self, value): assert (value in range(128)) enabled = (value == 1) if enabled: self._config_button.send_value(40) self._control_is_with_automap = (not enabled) for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() if (not self._wrote_user_byte): self._selector.set_mode(0) self.set_enabled(enabled) else: self._wrote_user_byte = False self.request_rebuild_midi_map() def _config_value(self, value): assert (value in range(128)) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_returns = False): if (not self._suppress_session_highlight): ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_returns) def _install_forwarding(self, control): result = False if ((not self._control_is_with_automap) or (control == self._user_byte_write_button)): result = ControlSurface._install_forwarding(self, control) return result def _translate_message(self, type, from_identifier, from_channel, to_identifier, to_channel): if (not self._control_is_with_automap): ControlSurface._translate_message(self, type, from_identifier, from_channel, to_identifier, to_channel) def update_display(self): """ Live -> Script Aka on_timer. Called every 100 ms and should be used to update display relevant parts of the controller """ for message in self._scheduled_messages: message['Delay'] -= 1 if (message['Delay'] == 0): if (message['Parameter'] != None): message['Message'](message['Parameter']) else: message['Message']() del self._scheduled_messages[self._scheduled_messages.index(message)] for callback in self._timer_callbacks: callback() self._timer = (self._timer + 1) % 256 self.flash() def flash(self): #if(self.flash_status > 0): for row in range(8): if(self._selector._side_buttons[row]._flash_state > 0): self._selector._side_buttons[row].flash(self._timer) for column in range(8): button = self._selector._matrix.get_button(column, row) if(button._flash_state > 0): button.flash(self._timer) for index in range(4): if(self._selector._nav_buttons[index]._flash_state > 0): self._selector._nav_buttons[index].flash(self._timer) if(self._selector._modes_buttons[index]._flash_state > 0): self._selector._modes_buttons[index].flash(self._timer) def allow_updates(self, allow_updates): for component in self.components: component.set_allow_update(int(allow_updates!=0))
class BlockPad(ControlSurface): __module__ = __name__ __doc__ = " Script for Novation's Launchpad Controller adapted to Livid's Block controller and Monomodular by amounra " def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self._monomod_version = 'b994' self.hosts = [] self._host_name = 'BlockPad' self._color_type = 'Monochrome' self._timer = 0 is_momentary = True self._suggested_input_port = 'block (Controls)' self._suggested_output_port = 'block (Controls)' self._wrote_user_byte = False matrix = ButtonMatrixElement() for row in range(8): button_row = [ FlashingButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((column * 8) + row), 'Button_'+str(column)+'_'+str(row), self) for column in range(8) ] matrix.add_row(tuple(button_row)) knobs = [ EncoderElement(MIDI_CC_TYPE, 0, KNOB_CC[index], Live.MidiMap.MapMode.absolute) for index in range(8) ] sliders = [ EncoderElement(MIDI_CC_TYPE, 0, SLIDER_CC[index], Live.MidiMap.MapMode.absolute) for index in range(2) ] self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0) self._config_button.add_value_listener(self._config_value) top_buttons = [ FlashingButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, TOP_NOTES[index], 'Top_Button'+str(index), self) for index in range(8) ] side_buttons = [ FlashingButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index], 'Side_Button'+str(index), self) for index in range(8) ] self._setup_monobridge() self._setup_monomod() self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, tuple(knobs), tuple(sliders), self) self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self.set_enabled(True) self.log_message("--------------= BlockPad log opened =--------------") #Create entry in log file self.refresh_state() def _setup_monobridge(self): self._monobridge = MonoBridgeElement(self) self._monobridge.name = 'MonoBridge' def _setup_monomod(self): self._host = MonomodComponent(self) self._host.name = 'Monomod_Host' self.hosts = [self._host] def disconnect(self): self._suppress_send_midi = True self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None self.log_message("--------------= BlockPad log closed =--------------") #Create entry in log file def _user_byte_value(self, value): assert (value in range(128)) enabled = (value == 1) if enabled: self._config_button.send_value(40) self._control_is_with_automap = (not enabled) for control in self.controls: if isinstance(control, FlashingButtonElement): control.set_force_next_value() if (not self._wrote_user_byte): self._selector.set_mode(0) self.set_enabled(enabled) else: self._wrote_user_byte = False self.request_rebuild_midi_map() def _config_value(self, value): assert (value in range(128)) def update_display(self): """ Live -> Script Aka on_timer. Called every 100 ms and should be used to update display relevant parts of the controller """ for message in self._scheduled_messages: message['Delay'] -= 1 if (message['Delay'] == 0): if (message['Parameter'] != None): message['Message'](message['Parameter']) else: message['Message']() del self._scheduled_messages[self._scheduled_messages.index(message)] for callback in self._timer_callbacks: callback() self._timer = (self._timer + 1) % 256 if(self._timer == 0): self._selector._shift_pressed_timer = -12 self.flash() def flash(self): for row in range(8): for column in range(8): button = self._selector._matrix.get_button(column, row) if(button._flash_state > 0): button.flash(self._timer)
class AudioClipEditComponent(CompoundComponent): """ classdocs """ def __init__(self, *a, **k): super(AudioClipEditComponent, self).__init__(*a, **k) self._loop_start_slider = SliderElement(MIDI_CC_TYPE, 2, 60) self._action_loop_start.subject = self._loop_start_slider self._loop_end_slider = SliderElement(MIDI_CC_TYPE, 2, 61) self._action_loop_end.subject = self._loop_end_slider self._mark_start_slider = SliderElement(MIDI_CC_TYPE, 2, 62) self._action_mark_start.subject = self._mark_start_slider self._mark_end_slider = SliderElement(MIDI_CC_TYPE, 2, 63) self._action_mark_end.subject = self._mark_end_slider self._pitch_c_slider = SliderElement(MIDI_CC_TYPE, 2, 64) self._pitch_f_slider = SliderElement(MIDI_CC_TYPE, 2, 65) self._gain_slider = SliderElement(MIDI_CC_TYPE, 2, 66) self._action_pitch_c.subject = self._pitch_c_slider self._action_pitch_f.subject = self._pitch_f_slider self._action_gain.subject = self._gain_slider self._loop_inc_slider = SliderElement(MIDI_CC_TYPE, 2, 67) self._action_loop_inc.subject = self._loop_inc_slider self._loop_move_button = ButtonElement(False, MIDI_CC_TYPE, 2, 74) self._action_mv_loop.subject = self._loop_move_button self._loop_set_button = ButtonElement(False, MIDI_CC_TYPE, 2, 70) self._action_loop_toggle.subject = self._loop_set_button self._warp_set_button = ButtonElement(False, MIDI_CC_TYPE, 2, 71) self._action_warp_toggle.subject = self._warp_set_button self._zoom_scroll_button = ButtonElement(False, MIDI_CC_TYPE, 2, 73) self._action_scroll_mode.subject = self._zoom_scroll_button self.selected_clip_slot = None self.inc_index = 4 self.loop_inc = INC_STEPS[self.inc_index] self.start_inc = INC_STEPS[self.inc_index] self.mv_loop = False self._on_pitch_c_changed.subject = None self._on_pitch_f_changed.subject = None self._on_gain_changed.subject = None self._scroll_mode = False self.update_selected_clip() return @subject_slot('value') def _action_scroll_mode(self, value): if value > 0: self._scroll_mode = True else: self._scroll_mode = False @subject_slot('value') def _action_warp_toggle(self, value): if value > 0: if self.selected_clip_slot and self.selected_clip_slot.has_clip: clip = self.selected_clip_slot.clip if clip.is_audio_clip: clip.warping = not clip.warping self._warp_set_button.send_value(clip.warping and 127 or 0, True) @subject_slot('value') def _action_loop_toggle(self, value): if value > 0: if self.selected_clip_slot and self.selected_clip_slot.has_clip: clip = self.selected_clip_slot.clip clip.looping = not clip.looping self._loop_set_button.send_value(clip.looping and 127 or 0, True) @subject_slot('value') def _action_loop_inc(self, value): inc = value == 1 and 1 or -1 val = self.inc_index + inc if val >= 0 and val < len(INC_STEPS): self.inc_index = val self.loop_inc = INC_STEPS[val] self.start_inc = INC_STEPS[val] self.canonical_parent.timed_message(2, 'Loop Adjust: ' + INC_DISP[val]) self.canonical_parent.show_message('Loop Adjust: ' + INC_DISP[val]) @subject_slot('value') def _action_mv_loop(self, value): if value > 0: if self.mv_loop: self._loop_move_button.send_value(0, True) self.mv_loop = False else: self._loop_move_button.send_value(127, True) self.mv_loop = True @subject_slot('value') def _action_mark_start(self, value): if self._scroll_mode: scroll = value == 1 and 3 or 2 self.application().view.scroll_view(scroll, 'Detail/Clip', False) else: inc = value == 1 and 1 or -1 if self.selected_clip_slot and self.selected_clip_slot.has_clip: clip = self.selected_clip_slot.clip ls = clip.start_marker le = clip.end_marker ls = max(0, min(le - self.start_inc, ls + inc * self.start_inc)) clip.start_marker = ls bars_to_measure(ls, clip.signature_denominator, clip.signature_numerator) self.canonical_parent.timed_message(2, 'Clip Start: ' + bars_to_measure(ls, clip.signature_denominator, clip.signature_numerator)) @subject_slot('value') def _action_mark_end(self, value): if self._scroll_mode: scroll = value == 1 and 3 or 2 self.application().view.zoom_view(scroll, 'Detail/Clip', False) else: inc = value == 1 and 1 or -1 if self.selected_clip_slot and self.selected_clip_slot.has_clip: clip = self.selected_clip_slot.clip ls = clip.start_marker le = clip.end_marker le = max(ls + self.start_inc, le + inc * self.start_inc) clip.end_marker = le self.canonical_parent.timed_message(2, 'Clip End: ' + bars_to_measure(le, clip.signature_denominator, clip.signature_numerator)) @subject_slot('value') def _action_loop_start(self, value): inc = value == 1 and 1 or -1 if self.selected_clip_slot and self.selected_clip_slot.has_clip: clip = self.selected_clip_slot.clip ls = clip.loop_start le = clip.loop_end if self.mv_loop: diff = le - ls ls = max(0, ls + inc * self.loop_inc) if inc > 0: clip.loop_end = ls + diff clip.end_marker = ls + diff clip.loop_start = ls clip.start_marker = ls else: clip.loop_start = ls clip.start_marker = ls clip.loop_end = ls + diff clip.end_marker = ls + diff self.canonical_parent.timed_message(2, loop_str(clip)) else: ls = max(0, min(le - self.loop_inc, ls + inc * self.loop_inc)) clip.loop_start = ls self.canonical_parent.timed_message(2, loop_str(clip)) @subject_slot('value') def _action_loop_end(self, value): inc = value == 1 and 1 or -1 if self.selected_clip_slot and self.selected_clip_slot.has_clip: clip = self.selected_clip_slot.clip ls = clip.loop_start le = clip.loop_end le = max(ls + self.loop_inc, le + inc * self.loop_inc) clip.loop_end = le if self.mv_loop: clip.end_marker = le self.canonical_parent.timed_message(2, loop_str(clip)) def update(self): pass @subject_slot('value') def _action_pitch_c(self, value): cs = self.selected_clip_slot if cs and cs.has_clip and cs.clip.is_audio_clip: cs.clip.pitch_coarse = midi_to_pitchc(value) @subject_slot('value') def _action_pitch_f(self, value): cs = self.selected_clip_slot if cs and cs.has_clip and cs.clip.is_audio_clip: cs.clip.pitch_fine = midi_to_pitchf(value) @subject_slot('value') def _action_gain(self, value): cs = self.selected_clip_slot if cs and cs.has_clip and cs.clip.is_audio_clip: cs.clip.gain = midi_to_gain(value) def _update_clip_name(self): cs = self.song().view.highlighted_clip_slot if not cs: track = self.song().view.selected_track self.canonical_parent.send_to_display('Rt Trck: ' + track.name, 3) else: if cs.has_clip: self.canonical_parent.send_to_display((cs.clip.is_audio_clip and 'A' or 'M') + ':' + cs.clip.name, 3) else: track = cs.canonical_parent index = list(track.clip_slots).index(cs) scene = self.song().scenes[index] self.canonical_parent.send_to_display('E<' + str(scene.name) + '> T:' + track.name, 3) @subject_slot('has_clip') def _on_has_clip_changed(self): self._update_clip_name() @subject_slot('name') def _on_name_changed(self): self._update_clip_name() def update_selected_clip(self): cs = self.song().view.highlighted_clip_slot if cs != self.selected_clip_slot: self.selected_clip_slot = cs self._update_clip_name() if cs and cs.has_clip and cs.clip.is_audio_clip: self._on_pitch_c_changed.subject = cs.clip self._on_pitch_f_changed.subject = cs.clip self._on_gain_changed.subject = cs.clip self._on_warp_changed.subject = cs.clip self._gain_slider.send_value(gain_to_midi(cs.clip.gain)) self._pitch_c_slider.send_value(pitchc_to_midi(cs.clip.pitch_coarse)) self._pitch_f_slider.send_value(pitchf_to_midi(cs.clip.pitch_fine)) self._warp_set_button.send_value(cs.clip.warping and 127 or 0, True) else: self._on_pitch_c_changed.subject = None self._on_pitch_f_changed.subject = None self._on_gain_changed.subject = None self._on_warp_changed.subject = None self._on_loop_changed.subject = None if cs and cs.has_clip: self._on_loop_changed.subject = cs.clip self._on_name_changed.subject = cs.clip self._loop_set_button.send_value(cs.clip.looping and 127 or 0, True) else: self._on_name_changed.subject = None self._on_loop_changed.subject = None self._on_has_clip_changed.subject = cs return def on_selected_track_changed(self): self.update_selected_clip() def on_selected_scene_changed(self): self.update_selected_clip() @subject_slot('looping') def _on_loop_changed(self): cs = self.song().view.highlighted_clip_slot if cs and cs.has_clip: self._loop_set_button.send_value(cs.clip.looping and 127 or 0, True) @subject_slot('warping') def _on_warp_changed(self): cs = self.song().view.highlighted_clip_slot if cs and cs.has_clip and cs.clip.is_audio_clip: self._warp_set_button.send_value(cs.clip.warping and 127 or 0, True) @subject_slot('pitch_coarse') def _on_pitch_c_changed(self): cs = self.song().view.highlighted_clip_slot if cs and cs.has_clip and cs.clip.is_audio_clip: self._pitch_c_slider.send_value(pitchc_to_midi(cs.clip.pitch_coarse)) @subject_slot('pitch_fine') def _on_pitch_f_changed(self): cs = self.song().view.highlighted_clip_slot if cs and cs.has_clip and cs.clip.is_audio_clip: self._pitch_f_slider.send_value(pitchf_to_midi(cs.clip.pitch_fine)) @subject_slot('gain') def _on_gain_changed(self): cs = self.song().view.highlighted_clip_slot if cs and cs.has_clip and cs.clip.is_audio_clip: self._gain_slider.send_value(gain_to_midi(cs.clip.gain))
class F1ColorButtonElement(ButtonElement): ' SPECIAL BUTTON CLASS THAT INTERNALLY HOLDS 3 BUTTONS FOR HANDLING RGB / HSV AND ONE BUTTON THAT HANDLES INCOMING MIDI' __module__ = __name__ """ MAIN CONSTRUCTOR """ def __init__(self, is_momentary, msg_type, identifier, hsvChannels, controlChannel, rgbColor, name): log(True, __name__) ButtonElement.__init__(self, is_momentary, msg_type, controlChannel, identifier) self.name = name + " Control" # create 3 buttons for HSV self.hueButton = ButtonElement(is_momentary, msg_type, hsvChannels[0], identifier) self.satButton = ButtonElement(is_momentary, msg_type, hsvChannels[1], identifier) self.valButton = ButtonElement(is_momentary, msg_type, hsvChannels[2], identifier) self.id = identifier # color self.hsv_fader = None rgbColor = _liveOsTools.colorsys.hex2rgb(rgbColor) hsvColor = _liveOsTools.colorsys.rgb_to_hsv(rgbColor[0] / 255.0, rgbColor[1] / 255.0, rgbColor[2] / 255.0) self.hsv_fader = HSVFader(hsvColor[0], hsvColor[1], hsvColor[2]) self.send_current_color() log(False, __name__) """ DISCONNECT """ def disconnect(self): log(__name__, "disconnect") self.hueButton = None self.hsvColor = None self.satButton = None self.hsv_fader = None """ FADE ONCE TO THAT VALUE """ def fade_to(self, hue, sat, val, time): #log(__name__, "fade_to: hue("+str(hue)+"), sat("+str(sat)+"), val("+str(val)+"), time("+str(time)+")") self.hsv_fader.add_fade(hue, sat, val, time) """ UPDATE FRAME TIME BASED """ def process(self, time_in_samples): #log(__name__, "update") #self.hsv_fader.process(time_in_samples) self.send_current_color() """ SEND COLOR """ def send_current_color(self): # send color values to buttons #log("sending: on CC: "+ str(self.id)+ " -- val: "+ str([int(self.hsv_fader.current_color[INDEX_HUE] * MIDI_RANGE),int(self.hsv_fader.current_color[INDEX_SAT] * MIDI_RANGE),int(self.hsv_fader.current_color[INDEX_VAL] * MIDI_RANGE)])) self.hueButton.send_value(int(self.hsv_fader.current_color[INDEX_HUE] * MIDI_RANGE), True) self.satButton.send_value(int(self.hsv_fader.current_color[INDEX_SAT] * MIDI_RANGE), True) self.valButton.send_value(int(self.hsv_fader.current_color[INDEX_VAL] * MIDI_RANGE), True) """ SEND BASE COLOR """ def send_base_color(self): self.hueButton.send_value(int(self.hsv_fader.get_base_color()[INDEX_HUE] * MIDI_RANGE), True) self.satButton.send_value(int(self.hsv_fader.get_base_color()[INDEX_SAT] * MIDI_RANGE), True) self.valButton.send_value(int(self.hsv_fader.get_base_color()[INDEX_VAL] * MIDI_RANGE), True) """ SET THE MAIN COLOR """ def set_base_color(self, liveRGBcolor): rgbColor = _liveOsTools.colorsys.hex2rgb(liveRGBcolor) hsvColor = _liveOsTools.colorsys.rgb_to_hsv(rgbColor[0] / 255.0, rgbColor[1] / 255.0, rgbColor[2] / 255.0) log(__name__, "new base color: rgb=" + str(rgbColor) + ", hsv=" + str(hsvColor)) self.hsv_fader.set_base_color(hsvColor[0], hsvColor[1], hsvColor[2]) self.send_base_color()
class LaunchMod(ControlSurface): """ Script for Novation's Launchpad Controller """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): self._monomod_version = 'b995' self._host_name = 'LaunchMod' self._color_type = 'Launchpad' self.hosts = [] self._timer = 0 self._suppress_send_midi = True self._suppress_session_highlight = True self._suppress_highlight = False is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._control_is_with_automap = False self._user_byte_write_button = ButtonElement( is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener( self._user_byte_value) self._wrote_user_byte = False self._challenge = Live.Application.get_random_int( 0, 400000000) & 2139062143 matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [ ConfigurableButtonElement( is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column), str(column) + '_Clip_' + str(row) + '_Button', self) for column in range(8) ] matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) self._config_button.add_value_listener(self._config_value) top_button_names = [ 'Bank_Select_Up_Button', 'Bank_Select_Down_Button', 'Bank_Select_Left_Button', 'Bank_Select_Right_Button', 'Session_Button', 'User1_Button', 'User2_Button', 'Mixer_Button' ] side_button_names = [ 'Vol_Button', 'Pan_Button', 'SndA_Button', 'SndB_Button', 'Stop_Button', 'Trk_On_Button', 'Solo_Button', 'Arm_Button' ] top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, (104 + index), top_button_names[index], self) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index], side_button_names[index], self) for index in range(8) ] self._setup_monobridge() self._setup_monomod() self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self) self._selector.name = 'Main_Modes' for control in self.controls: if isinstance(control, MonoButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component( self._selector.session_component()) self._suppress_session_highlight = False self.log_message( "--------------= " + str(self._monomod_version) + " log opened =--------------") #Create entry in log file def allow_updates(self, allow_updates): for component in self.components: component.set_allow_update(int(allow_updates != 0)) def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._selector = None self._user_byte_write_button.remove_value_listener( self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if len(midi_bytes) == 8: if midi_bytes[1:5] == (0, 32, 41, 6): response = long(midi_bytes[5]) response += long(midi_bytes[6]) << 8 if response == Live.Application.encrypt_challenge2( self._challenge): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector.mode_index == 1: new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes, optimized=None): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): assert value in range(128) enabled = self._wrote_user_byte or value == 1 self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, MonoButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False def _button_value(self, value): assert value in range(128) def _config_value(self, value): assert value in range(128) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks) def _setup_m4l_interface(self): self._m4l_interface = M4LInterfaceComponent( controls=self.controls, component_guard=self.component_guard) self.get_control_names = self._m4l_interface.get_control_names self.get_control = self._m4l_interface.get_control self.grab_control = self._m4l_interface.grab_control self.release_control = self._m4l_interface.release_control """Mono overrides and additions""" def _setup_monobridge(self): self._monobridge = MonoBridgeElement(self) self._monobridge.name = 'MonoBridge' def _setup_monomod(self): self._host = MonomodComponent(self) self._host.name = 'Monomod_Host' self.hosts = [self._host] def update_display(self): ControlSurface.update_display(self) self._timer = (self._timer + 1) % 256 self.flash() def flash(self): if self._host.is_enabled(): for control in self.controls: if isinstance(control, MonoButtonElement): control.flash(self._timer)
class Launchpad(ControlSurface): """ Script for Novation's Launchpad Controller """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self.set_suppress_rebuild_requests(True) self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._control_is_with_automap = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._wrote_user_byte = False self._challenge = Live.Application.get_random_int( 0, 400000000) & 2139062143 matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [] for column in range(8): button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, row * 16 + column) button.name = str(column) + '_Clip_' + str(row) + '_Button' button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0) self._config_button.add_value_listener(self._config_value) top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, 104 + index) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index]) for index in range(8) ] top_buttons[0].name = 'Bank_Select_Up_Button' top_buttons[1].name = 'Bank_Select_Down_Button' top_buttons[2].name = 'Bank_Select_Left_Button' top_buttons[3].name = 'Bank_Select_Right_Button' top_buttons[4].name = 'Session_Button' top_buttons[5].name = 'User1_Button' top_buttons[6].name = 'User2_Button' top_buttons[7].name = 'Mixer_Button' side_buttons[0].name = 'Vol_Button' side_buttons[1].name = 'Pan_Button' side_buttons[2].name = 'SndA_Button' side_buttons[3].name = 'SndB_Button' side_buttons[4].name = 'Stop_Button' side_buttons[5].name = 'Trk_On_Button' side_buttons[6].name = 'Solo_Button' side_buttons[7].name = 'Arm_Button' self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self) self._selector.name = 'Main_Modes' self._do_combine() for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self._suppress_session_highlight = False self.set_suppress_rebuild_requests(False) self.log_message("LaunchPad85 Loaded !") def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._do_uncombine() self._selector = None self._user_byte_write_button.remove_value_listener( self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None _active_instances = [] def highlighting_session_component(self): " Return the session component showing the ring in Live session " return self._selector.session_component() def _combine_active_instances(): support_devices = False for instance in Launchpad._active_instances: support_devices |= (instance._device_component != None) offset = 0 for instance in Launchpad._active_instances: instance._activate_combination_mode(offset, support_devices) offset += instance._selector._session.width() _combine_active_instances = staticmethod(_combine_active_instances) def _activate_combination_mode(self, track_offset, support_devices): if (LINK_STEPSEQ): self._selector._stepseq.link_with_step_offset(track_offset) if (LINK_SESSION): self._selector._session.link_with_track_offset(track_offset) def _do_combine(self): if (DO_COMBINE and (self not in Launchpad._active_instances)): Launchpad._active_instances.append(self) Launchpad._combine_active_instances() def _do_uncombine(self): if self in Launchpad._active_instances: Launchpad._active_instances.remove(self) if (LINK_SESSION): self._selector._session.unlink() if (LINK_STEPSEQ): self._selector._stepseq.unlink() Launchpad._combine_active_instances() def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if len(midi_bytes) == 8: if midi_bytes[1:5] == (0, 32, 41, 6): response = long(midi_bytes[5]) response += long(midi_bytes[6]) << 8 if response == Live.Application.encrypt_challenge2( self._challenge): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector.mode_index == 1: new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): assert (value in range(128)) if (not self._wrote_user_byte): enabled = (value == 1) self._control_is_with_automap = (not enabled) self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False def _button_value(self, value): assert (value in range(128)) def _config_value(self, value): assert (value in range(128)) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class Launchpad(ControlSurface): u""" Script for Novation's Launchpad Controller """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._control_is_with_automap = False self._user_byte_write_button = ButtonElement( is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener( self._user_byte_value) self._wrote_user_byte = False self._challenge = Live.Application.get_random_int( 0, 400000000) & 2139062143 matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [] for column in range(8): button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, row * 16 + column) button.name = str(column) + '_Clip_' + str(row) + '_Button' button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) self._config_button.add_value_listener(self._config_value) top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, 104 + index) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index]) for index in range(8) ] top_buttons[0].name = 'Bank_Select_Up_Button' top_buttons[1].name = 'Bank_Select_Down_Button' top_buttons[2].name = 'Bank_Select_Left_Button' top_buttons[3].name = 'Bank_Select_Right_Button' top_buttons[4].name = 'Session_Button' top_buttons[5].name = 'User1_Button' top_buttons[6].name = 'User2_Button' top_buttons[7].name = 'Mixer_Button' side_buttons[0].name = 'Vol_Button' side_buttons[1].name = 'Pan_Button' side_buttons[2].name = 'SndA_Button' side_buttons[3].name = 'SndB_Button' side_buttons[4].name = 'Stop_Button' side_buttons[5].name = 'Trk_On_Button' side_buttons[6].name = 'Solo_Button' side_buttons[7].name = 'Arm_Button' self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button) self._selector.name = 'Main_Modes' for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component( self._selector.session_component()) self._suppress_session_highlight = False def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._selector = None self._user_byte_write_button.remove_value_listener( self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None return def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if len(midi_bytes) == 8: if midi_bytes[1:5] == (0, 32, 41, 6): response = long(midi_bytes[5]) response += long(midi_bytes[6]) << 8 if response == Live.Application.encrypt_challenge2( self._challenge): self._on_handshake_successful() def _on_handshake_successful(self): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector.mode_index == 1: new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes, optimized=None): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): assert value in range(128) if not self._wrote_user_byte: enabled = value == 1 self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False def _button_value(self, value): assert value in range(128) def _config_value(self, value): assert value in range(128) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
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 Launchpad(ControlSurface): """ Script for Novation's Launchpad Controller """ def __init__(self, c_instance): live = Live.Application.get_application() self._live_major_version = live.get_major_version() self._live_minor_version = live.get_minor_version() self._live_bugfix_version = live.get_bugfix_version() self._mk2 = Settings.DEVICE == 'Launchpad mk2' if self._mk2: self._skin = Skin('Launchpad mk2') self._side_notes = (89, 79, 69, 59, 49, 39, 29, 19) self._drum_notes = (20, 30, 31, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126) else: self._skin = Skin('Launchpad') self._side_notes = (8, 24, 40, 56, 72, 88, 104, 120) self._drum_notes = (41, 42, 43, 44, 45, 46, 47, 57, 58, 59, 60, 61, 62, 63, 73, 74, 75, 76, 77, 78, 79, 89, 90, 91, 92, 93, 94, 95, 105, 106, 107) ControlSurface.__init__(self, c_instance) with self.component_guard(): self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True if self._mk2: self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' else: self._suggested_input_port = 'Launchpad MK2' self._suggested_output_port = 'Launchpad MK2' self._control_is_with_automap = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._wrote_user_byte = False self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143 matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [] for column in range(8): if self._mk2: # for mk2 buttons are assigned "top to bottom" midi_note = (81 - (10 * row)) + column else: midi_note = row * 16 + column button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, midi_note, self._skin.off) button.name = str(column) + '_Clip_' + str(row) + '_Button' button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) self._config_button.add_value_listener(self._config_value) top_buttons = [ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, 104 + index, self._skin.off) for index in range(8)] side_buttons = [ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, self._side_notes[index], self._skin.off) for index in range(8)] top_buttons[0].name = 'Bank_Select_Up_Button' top_buttons[1].name = 'Bank_Select_Down_Button' top_buttons[2].name = 'Bank_Select_Left_Button' top_buttons[3].name = 'Bank_Select_Right_Button' top_buttons[4].name = 'Session_Button' top_buttons[5].name = 'User1_Button' top_buttons[6].name = 'User2_Button' top_buttons[7].name = 'Mixer_Button' side_buttons[0].name = 'Vol_Button' side_buttons[1].name = 'Pan_Button' side_buttons[2].name = 'SndA_Button' side_buttons[3].name = 'SndB_Button' side_buttons[4].name = 'Stop_Button' side_buttons[5].name = 'Trk_On_Button' side_buttons[6].name = 'Solo_Button' side_buttons[7].name = 'Arm_Button' self._osd = M4LInterface() self._osd.name = "OSD" self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self._osd, self, self._skin) self._selector.name = 'Main_Modes' self._do_combine() for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component(self._selector.session_component()) self._suppress_session_highlight = False self.log_message("LaunchPad95 Loaded !") def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._do_uncombine() self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False if self._mk2: self._send_midi((240, 0, 32, 41, 2, 24, 64, 247)) # launchpad mk2 needs disconnect string sent self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None _active_instances = [] #def highlighting_session_component(self): # " Return the session component showing the ring in Live session " # return self._selector.session_component() def _combine_active_instances(): support_devices = False for instance in Launchpad._active_instances: support_devices |= (instance._device_component != None) offset = 0 for instance in Launchpad._active_instances: instance._activate_combination_mode(offset, support_devices) offset += instance._selector._session.width() _combine_active_instances = staticmethod(_combine_active_instances) def _activate_combination_mode(self, track_offset, support_devices): if(Settings.STEPSEQ__LINK_WITH_SESSION): self._selector._stepseq.link_with_step_offset(track_offset) if(Settings.SESSION__LINK): self._selector._session.link_with_track_offset(track_offset) def _do_combine(self): if (DO_COMBINE and (self not in Launchpad._active_instances)): Launchpad._active_instances.append(self) Launchpad._combine_active_instances() def _do_uncombine(self): if self in Launchpad._active_instances: Launchpad._active_instances.remove(self) if(Settings.SESSION__LINK): self._selector._session.unlink() if(Settings.STEPSEQ__LINK_WITH_SESSION): self._selector._stepseq.unlink() Launchpad._combine_active_instances() def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if self._mk2: # mk2 has different challenge and params if len(midi_bytes) == 10: if midi_bytes[:7] == (240, 0, 32, 41, 2, 24, 64): response = long(midi_bytes[7]) response += long(midi_bytes[8]) << 8 if response == Live.Application.encrypt_challenge2(self._challenge): # self.log_message("Challenge Response ok") self._suppress_send_midi = False self.set_enabled(True) else: if len(midi_bytes) == 8: if midi_bytes[1:5] == (0, 32, 41, 6): response = long(midi_bytes[5]) response += long(midi_bytes[6]) << 8 if response == Live.Application.encrypt_challenge2(self._challenge): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector.mode_index == 1: if self._selector._sub_mode_index[self._selector._mode_index] > 0: # disable midi map rebuild for instrument mode to prevent light feedback errors new_channel = self._selector.channel_for_current_mode() # self.log_message(str(new_channel)) for note in self.drum_notes: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes, optimized=None): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): if self._mk2: challenge_bytes = tuple([ self._challenge >> 8 * index & 127 for index in xrange(4) ]) self._send_midi((240, 0, 32, 41, 2, 24, 64) + challenge_bytes + (247,)) else: for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): assert (value in range(128)) if not self._wrote_user_byte: enabled = (value == 1) self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False def _button_value(self, value): assert value in range(128) def _config_value(self, value): assert value in range(128) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class Axiom_OJI(AxiomPro): """ Script for the M-Audio Axiom Pro OJI version """ _browser_mode_enabled = False def __init__(self, *a, **k): #super(Axiom_OJI, self).__init__(*a, **k) ControlSurface.__init__(self, *a, **k) with self.component_guard(): is_momentary = True #self.set_pad_translations(PAD_TRANSLATIONS) self._suggested_input_port = u'HyperControl' self._suggested_output_port = u'HyperControl' self._display_on_button = ButtonElement(not is_momentary, MIDI_CC_TYPE, 15, 79) self._waiting_for_first_response = True self._mixer1 = DisplayingMixerComponent(0) self._mixer1.set_select_buttons( ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 111), ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 110)) self._mixer1.set_mute_button( ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 12)) self._mixer1.set_solo_button( ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 13)) self._mixer2 = NotifyingMixerComponent(8) self._mixer2.set_bank_buttons( ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 15), ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 14)) self._mixer2.master_strip().set_volume_control( SliderElement(MIDI_CC_TYPE, 15, 41)) for index in range(8): self._mixer2.channel_strip(index).set_volume_control( SliderElement(MIDI_CC_TYPE, 15, 33 + index)) self._device = PageableDeviceComponent( device_selection_follows_track_selection=True) self.set_device_component(self._device) self._ffwd_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 115) self._rwd_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 114) self._loop_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 113) self._transport = TransportComponent() self._transport.set_stop_button( ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 116)) self._transport.set_play_button( ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 117)) self._transport.set_record_button( ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 118, name='RecordButton')) self._session = SessionComponent(0, 0) self._transport_view_modes = TransportViewModeSelector( self._transport, self._session, self._ffwd_button, self._rwd_button, self._loop_button) self._select_button_modes = SelectButtonModeSelector( self._mixer2, tuple([ ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 49 + offset) for offset in range(8) ])) self._select_button_modes.set_mode_toggle( ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 57)) self._mixer_encoder_modes = EncoderMixerModeSelector(self._mixer2) self._encoders = [] for offset in range(8): self._encoders.append( PeekableEncoderElement( MIDI_CC_TYPE, 15, 17 + offset, Live.MidiMap.MapMode.relative_smooth_two_compliment)) self._encoders[-1].set_feedback_delay(-1) self._mixer_or_device = MixerOrDeviceModeSelector( self._mixer_encoder_modes, self._device, tuple(self._encoders), tuple([ ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 74 + offset) for offset in range(4) ])) self._mixer_or_device.set_mode_toggle( ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 109)) self._mixer_or_device.set_peek_button( ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 78)) self._track_display = PhysicalDisplayElement(8, 1) self._track_display.set_clear_all_message(SYSEX_START + (16, 247)) self._track_display.set_message_parts(SYSEX_START + (17, 1, 0, 0), (247, )) #self._track_display.segment(0).set_data_source(mixer1.selected_strip().track_name_data_source()) self._device_display = PhysicalDisplayElement(8, 1) self._device_display.set_message_parts( SYSEX_START + (17, 1, 0, 10), (247, )) self._parameter_display = PhysicalDisplayElement(16, 1) self._parameter_display.set_message_parts( SYSEX_START + (17, 2, 0, 0), (247, )) #self._select_button_modes.set_mode_display(parameter_display) #self._mixer1.set_display(parameter_display) #self._mixer2.set_bank_display(parameter_display) self._page_displays = [] for index in range(4): self._page_displays.append(PhysicalDisplayElement(5, 1)) self._page_displays[-1].set_message_parts( SYSEX_START + (17, 4, index, 0), (247, )) self._encoder_display = PhysicalDisplayElement(80, 8) self._encoder_display.set_message_parts(SYSEX_START + (17, 3), (247, )) for index in range(8): pos_id = tuple() if index != 0: pos_id += (0, ) if index > 3: pos_id += (index % 4, 13) else: pos_id += (index % 4, 0) self._encoder_display.segment(index).set_position_identifier( pos_id) self._data_sources = [ DisplayDataSource(' ') for index in range(7) ] top_display = PhysicalDisplayElement(20, 1) top_display.set_message_parts(SYSEX_START + (17, 1, 0, 0), (247, )) top_display.set_clear_all_message(SYSEX_START + (16, 247)) self._header_display = top_display self._display1 = top_display.segment(0) long_display = PhysicalDisplayElement(80, 4) long_display.set_message_parts(SYSEX_START + ( 17, 3, ), (247, )) self._display2 = long_display.segment(0) pos_id = tuple() pos_id += (0, 0) self._display2.set_position_identifier(pos_id) self._display3 = long_display.segment(1) pos_id = tuple() pos_id += (0, 1, 0) self._display3.set_position_identifier(pos_id) self._display4 = long_display.segment(2) pos_id = tuple() pos_id += (0, 2, 0) self._display4.set_position_identifier(pos_id) self._display5 = long_display.segment(3) pos_id = tuple() pos_id += (0, 3, 0) self._display5.set_position_identifier(pos_id) middle_display = PhysicalDisplayElement(20, 1) middle_display.set_message_parts(SYSEX_START + (17, 2, 0, 0), (247, )) self._display6 = middle_display.segment(0) bottom_display = PhysicalDisplayElement(20, 1) bottom_display.set_message_parts(SYSEX_START + (17, 4, 0, 0), (247, )) self._display7 = bottom_display.segment(0) self._browser_displays = [ self._display1, self._display2, self._display3, self._display4, self._display5, self._display6, self._display7 ] self._drumpads = [ ButtonElement(True, MIDI_NOTE_TYPE, 15, 81 + index, name='Pad_' + str(index)) for index in range(8) ] self._setup_m4l_interface() self.log_message('Axiom_OJI script installed') """ def __init__(self, *a, **k): super(Axiom_OJI, self).__init__(*a, **k) with self.component_guard(): #self._data_sources = (DisplayDataSource('TEST') for index in range(7)) self._display1 = PhysicalDisplayElement(16, 1) self._display1.set_message_parts(SYSEX_START + (17, 1, 0, 0), (247,)) self._header_display = self._display1 self._header_display.set_clear_all_message(SYSEX_START + (16, 247)) self._display2 = PhysicalDisplayElement(16, 1) self._display2.set_message_parts(SYSEX_START + (17, 3, 0, 0), (247,)) self._display3 = PhysicalDisplayElement(16, 1) self._display3.set_message_parts(SYSEX_START + (17, 3, 1, 0), (247,)) self._display4 = PhysicalDisplayElement(16, 1) self._display4.set_message_parts(SYSEX_START + (17, 3, 2, 0), (247,)) self._display5 = PhysicalDisplayElement(16, 1) self._display5.set_message_parts(SYSEX_START + (17, 3, 3, 0), (247,)) self._display6 = PhysicalDisplayElement(16, 1) self._display6.set_message_parts(SYSEX_START + (17, 2, 0, 0), (247,)) self._display7 = PhysicalDisplayElement(16, 1) self._display7 .set_message_parts(SYSEX_START + (17, 4, 0, 0), (247,)) self._browser_displays = [self._display1, self._display2, self._display3, self._display4, self._display5, self._display6, self._display7] """ def _setup_m4l_interface(self): self._m4l_interface = M4LInterfaceComponent( controls=self.controls, component_guard=self.component_guard, priority=10) self._m4l_interface.name = "M4LInterface" self.get_control_names = self._m4l_interface.get_control_names self.get_control = self._m4l_interface.get_control self.grab_control = self._m4l_interface.grab_control self.release_control = self._m4l_interface.release_control def handle_sysex(self, midi_bytes): if midi_bytes[0:-2] == SYSEX_START + (32, ): msg_id_byte = midi_bytes[-2] is_setup_response = msg_id_byte in (46, 38) has_sliders = msg_id_byte == 46 if is_setup_response: if self._waiting_for_first_response: self._waiting_for_first_response = False self._display_on_button.send_value(0) if not self._browser_mode_enabled: for component in self.components: component.set_enabled(True) self._display_on_button.send_value(127) self._send_midi(SYSEX_START + (16, 247)) self._send_midi(SYSEX_START + (17, 3, 0, 1, 65, 98, 108, 101, 116, 111, 110, 32, 76, 105, 118, 101, 32, 67, 111, 110, 116, 114, 111, 108, 32, 0, 1, 4, 83, 117, 114, 102, 97, 99, 101, 32, 118, 49, 46, 48, 46, 48, 46, 247)) self._mixer_encoder_modes.set_show_volume_page(not has_sliders) #if not self._browser_mode_enabled: for display in self._displays: display.set_block_messages(False) self.schedule_message(25, self._refresh_displays) #self.schedule_message(30, self._enable_browser) elif msg_id_byte == 43: self._send_midi(SYSEX_START + (16, 247)) #if not self._browser_mode_enabled: for display in self._displays: if display is self._track_display: display.update() else: display.set_block_messages(True) def _enable_browser(self): self.enable_browser_mode(True) def enable_browser_mode(self, enable): self._browser_mode_enabled = enable > 0 debug('enable_browser_mode:' + str(enable)) if enable: for component in self.components: component.set_enabled(False) self._header_display.reset() self._track_display.segment(0).set_data_source(None) for index in range(7): debug('index: ' + str(index)) self._browser_displays[index].set_data_source( self._data_sources[index]) else: self._header_display.reset() for index in range(7): debug('index: ' + str(index)) self._browser_displays[index].set_data_source(None) for component in self.components: component.set_enabled(True) def _enable_browser_mode(self, enable): self._browser_mode_enabled = enable > 0 #debug('enable_browser_mode', enable) if enable: #debug('here 1') for component in self.components: component.set_enabled(False) self._header_display.reset() #debug('here 2') #for index, display in enumerate(self._browser_displays): #debug('index:', index, 'display:', display) #self._data_sources[index].clear() #display.set_data_source(self._data_sources[index]) else: for component in self.components: component.set_enabled(True) def write_to_lcd(self, display, message): if self._browser_mode_enabled and display in range(7): self._data_sources[display].set_display_string(message) def test_browser(self): debug('test_browser') browser = self.application.browser debug('browser is:', browser) user_folders = browser.user_folders debug('user_folders are:', user_folders) for item in user_folders: #debug('item is:', item, item.name) if item.name == 'defaultPresets': inneritems = item.iter_children for inneritem in inneritems: debug('inneritem:', inneritem) if inneritem.name == 'Default.aupreset': browser.load_item(inneritem) break def load_preset(self, target=None, folder=None, directory='defaultPresets'): debug('load_preset()', target, folder, directory) if not target is None: browser = Live.Application.get_application( ).browser ##if not self.application.view.browse_mode else self.application.browser.hotswap_target user_folders = browser.user_folders for item in user_folders: if item.name == directory: if not folder is None: folder_target = None item_iterator = item.iter_children inneritems = [inneritem for inneritem in item_iterator] for inneritem in inneritems: if inneritem.name == folder: folder_target = inneritem break if folder_target: item_iterator = folder_target.iter_children inneritems = [ inneritem for inneritem in item_iterator ] for inneritem in inneritems: if isinstance(target, int): if target < len(inneritems): if inneritems[target].is_loadable: browser.load_item( inneritems[target]) break elif inneritems[target].is_folder: debug(inneritems[target], '.is_folder') innertarget = inneritems[target] innertarget_iterator = innertarget.iter_children innertargetitems = [ innertargetitem for innertargetitem in innertarget_iterator ] #debug('innertargetitems:', innertargetitems) if len(innertargetitems ) > 0 and innertargetitems[ 0].is_loadable: browser.load_item( innertargetitems[0]) break else: debug(innertargetitems[0], 'item isnt loadable 0') break else: debug(inneritems[target], 'item isnt loadable 1') break else: if inneritem.name == target: if inneritem.is_loadable: browser.load_item(inneritem) else: debug(inneritem, 'item isnt loadable 2') break else: item_iterator = item.iter_children inneritems = [inneritem for inneritem in item_iterator] for inneritem in inneritems: if isinstance(target, int): if target < len(inneritems): if inneritems[target].is_loadable: browser.load_item(inneritems[target]) break else: debug(inneritems[target], 'item isnt loadable 3') break else: if inneritem.name == target: if inneritem.is_loadable: browser.load_item(inneritem) break else: debug(inneritem, 'item isnt loadable 4') break def get_preset_names(self, folder=None, directory='defaultPresets'): #debug('get_preset_names', folder, directory) preset_names = [ 'no target', 'no target', 'no target', 'no target', 'no target', 'no target', 'no target', 'no target' ] browser = Live.Application.get_application( ).browser ##if not self.application.view.browse_mode else self.application.browser.hotswap_target user_folders = browser.user_folders for item in user_folders: if item.name == directory: if not folder is None: folder_target = None item_iterator = item.iter_children inneritems = [inneritem for inneritem in item_iterator] for inneritem in inneritems: if inneritem.name == folder: folder_target = inneritem break if folder_target: item_iterator = folder_target.iter_children preset_names = [ inneritem.name for inneritem in item_iterator ] else: item_iterator = item.iter_children preset_names = [ inneritem.name for inneritem in item_iterator ] #debug('names:', preset_names) return preset_names def write_presets_to_lcd(self, folder=None, directory='defaultPresets'): #debug('write_presets_to_lcd') preset_names = self.get_preset_names(folder, directory) #debug('names:', preset_names) for i, name in enumerate(preset_names): if i < 7: #debug('name:', i, name) self.write_to_lcd(i, name[:20])
class Launchpad(ControlSurface): """ Script for Novation's Launchpad Controller """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self.set_suppress_rebuild_requests(True) self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._control_is_with_automap = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._wrote_user_byte = False self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143 matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [] for column in range(8): button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, row * 16 + column) button.name = str(column) + '_Clip_' + str(row) + '_Button' button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0) self._config_button.add_value_listener(self._config_value) top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, 104 + index) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index]) for index in range(8) ] top_buttons[0].name = 'Bank_Select_Up_Button' top_buttons[1].name = 'Bank_Select_Down_Button' top_buttons[2].name = 'Bank_Select_Left_Button' top_buttons[3].name = 'Bank_Select_Right_Button' top_buttons[4].name = 'Session_Button' top_buttons[5].name = 'User1_Button' top_buttons[6].name = 'User2_Button' top_buttons[7].name = 'Mixer_Button' side_buttons[0].name = 'Vol_Button' side_buttons[1].name = 'Pan_Button' side_buttons[2].name = 'SndA_Button' side_buttons[3].name = 'SndB_Button' side_buttons[4].name = 'Stop_Button' side_buttons[5].name = 'Trk_On_Button' side_buttons[6].name = 'Solo_Button' side_buttons[7].name = 'Arm_Button' self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self) self._selector.name = 'Main_Modes' self._do_combine() for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self._suppress_session_highlight = False self.set_suppress_rebuild_requests(False) self.log_message("LaunchPad85 Loaded !") def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._do_uncombine() self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None _active_instances = [] def highlighting_session_component(self): " Return the session component showing the ring in Live session " return self._selector.session_component() def _combine_active_instances(): support_devices = False for instance in Launchpad._active_instances: support_devices |= (instance._device_component != None) offset = 0 for instance in Launchpad._active_instances: instance._activate_combination_mode(offset, support_devices) offset += instance._selector._session.width() _combine_active_instances = staticmethod(_combine_active_instances) def _activate_combination_mode(self, track_offset, support_devices): if(LINK_STEPSEQ): self._selector._stepseq.link_with_step_offset(track_offset) if(LINK_SESSION): self._selector._session.link_with_track_offset(track_offset) def _do_combine(self): if (DO_COMBINE and (self not in Launchpad._active_instances)): Launchpad._active_instances.append(self) Launchpad._combine_active_instances() def _do_uncombine(self): if self in Launchpad._active_instances: Launchpad._active_instances.remove(self) if(LINK_SESSION): self._selector._session.unlink() if(LINK_STEPSEQ): self._selector._stepseq.unlink() Launchpad._combine_active_instances() def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if len(midi_bytes) == 8: if midi_bytes[1:5] == (0, 32, 41, 6): response = long(midi_bytes[5]) response += long(midi_bytes[6]) << 8 if response == Live.Application.encrypt_challenge2(self._challenge): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector.mode_index == 1: new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): assert (value in range(128)) if (not self._wrote_user_byte): enabled = (value == 1) self._control_is_with_automap = (not enabled) self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False def _button_value(self, value): assert (value in range(128)) def _config_value(self, value): assert (value in range(128)) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class LaunchMod(ControlSurface): """ Script for Novation's Launchpad Controller """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): self._monomod_version = 'b995' self._host_name = 'LaunchMod' self._color_type = 'Launchpad' self.hosts = [] self._timer = 0 self._suppress_send_midi = True self._suppress_session_highlight = True self._suppress_highlight = False is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._control_is_with_automap = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._wrote_user_byte = False self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143 matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column), str(column) + '_Clip_' + str(row) + '_Button', self) for column in range(8) ] matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) self._config_button.add_value_listener(self._config_value) top_button_names = ['Bank_Select_Up_Button', 'Bank_Select_Down_Button', 'Bank_Select_Left_Button', 'Bank_Select_Right_Button', 'Session_Button', 'User1_Button', 'User2_Button', 'Mixer_Button'] side_button_names = ['Vol_Button', 'Pan_Button', 'SndA_Button', 'SndB_Button', 'Stop_Button', 'Trk_On_Button', 'Solo_Button', 'Arm_Button'] top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, (104 + index), top_button_names[index], self) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index], side_button_names[index], self) for index in range(8) ] self._setup_monobridge() self._setup_monomod() self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self) self._selector.name = 'Main_Modes' for control in self.controls: if isinstance(control, MonoButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component(self._selector.session_component()) self._suppress_session_highlight = False self.log_message("--------------= " + str(self._monomod_version) + " log opened =--------------") #Create entry in log file def allow_updates(self, allow_updates): for component in self.components: component.set_allow_update(int(allow_updates!=0)) def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if len(midi_bytes) == 8: if midi_bytes[1:5] == (0, 32, 41, 6): response = long(midi_bytes[5]) response += long(midi_bytes[6]) << 8 if response == Live.Application.encrypt_challenge2(self._challenge): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector.mode_index == 1: new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes, optimized = None): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): assert value in range(128) enabled = self._wrote_user_byte or value == 1 self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, MonoButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False def _button_value(self, value): assert value in range(128) def _config_value(self, value): assert value in range(128) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks) def _setup_m4l_interface(self): self._m4l_interface = M4LInterfaceComponent(controls=self.controls, component_guard=self.component_guard) self.get_control_names = self._m4l_interface.get_control_names self.get_control = self._m4l_interface.get_control self.grab_control = self._m4l_interface.grab_control self.release_control = self._m4l_interface.release_control """Mono overrides and additions""" def _setup_monobridge(self): self._monobridge = MonoBridgeElement(self) self._monobridge.name = 'MonoBridge' def _setup_monomod(self): self._host = MonomodComponent(self) self._host.name = 'Monomod_Host' self.hosts = [self._host] def update_display(self): ControlSurface.update_display(self) self._timer = (self._timer + 1) % 256 self.flash() def flash(self): if self._host.is_enabled(): for control in self.controls: if isinstance(control, MonoButtonElement): control.flash(self._timer)
class Launchpad(ControlSurface): _active_instances = [] def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) live = Live.Application.get_application() self._live_major_version = live.get_major_version() self._live_minor_version = live.get_minor_version() self._live_bugfix_version = live.get_bugfix_version() self._selector = None #needed because update hardware is called. self._mk2_rgb = False with self.component_guard(): self._suppress_send_midi = True self._suppress_session_highlight = True self._suggested_input_port = ("Launchpad", "Launchpad Mini", "Launchpad S", "Launchpad MK2") self._suggested_output_port = ("Launchpad", "Launchpad Mini", "Launchpad S", "Launchpad MK2") self._control_is_with_automap = False self._user_byte_write_button = None self._config_button = None self._wrote_user_byte = False self._challenge = Live.Application.get_random_int(0, 400000000) & 2139062143 self._init_done = False # caller will send challenge and we will continue as challenge is received. def init(self): #skip init if already done. if self._init_done: return self._init_done = True # second part of the __init__ after model has been identified using its challenge response if self._mk2_rgb: from SkinMK2 import make_skin self._skin = make_skin() self._side_notes = (89, 79, 69, 59, 49, 39, 29, 19) #self._drum_notes = (20, 30, 31, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126) self._drum_notes = (20, 30, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126) else: from SkinMK1 import make_skin self._skin = make_skin() self._side_notes = (8, 24, 40, 56, 72, 88, 104, 120) self._drum_notes = (41, 42, 43, 44, 45, 46, 47, 57, 58, 59, 60, 61, 62, 63, 73, 74, 75, 76, 77, 78, 79, 89, 90, 91, 92, 93, 94, 95, 105, 106, 107) with self.component_guard(): is_momentary = True self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) self._config_button.add_value_listener(self._config_value) self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = 'User_Byte_Button' self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) matrix = ButtonMatrixElement() matrix.name = 'Button_Matrix' for row in range(8): button_row = [] for column in range(8): if self._mk2_rgb: # for mk2 buttons are assigned "top to bottom" midi_note = (81 - (10 * row)) + column else: midi_note = row * 16 + column button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, midi_note, skin = self._skin, control_surface = self) button.name = str(column) + '_Clip_' + str(row) + '_Button' button_row.append(button) matrix.add_row(tuple(button_row)) top_buttons = [ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, 104 + index, skin = self._skin) for index in range(8)] side_buttons = [ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, self._side_notes[index], skin = self._skin) for index in range(8)] top_buttons[0].name = 'Bank_Select_Up_Button' top_buttons[1].name = 'Bank_Select_Down_Button' top_buttons[2].name = 'Bank_Select_Left_Button' top_buttons[3].name = 'Bank_Select_Right_Button' top_buttons[4].name = 'Session_Button' top_buttons[5].name = 'User1_Button' top_buttons[6].name = 'User2_Button' top_buttons[7].name = 'Mixer_Button' side_buttons[0].name = 'Vol_Button' side_buttons[1].name = 'Pan_Button' side_buttons[2].name = 'SndA_Button' side_buttons[3].name = 'SndB_Button' side_buttons[4].name = 'Stop_Button' side_buttons[5].name = 'Trk_On_Button' side_buttons[6].name = 'Solo_Button' side_buttons[7].name = 'Arm_Button' self._osd = M4LInterface() self._osd.name = "OSD" self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self._osd, self) self._selector.name = 'Main_Modes' self._do_combine() for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component(self._selector.session_component()) self._suppress_session_highlight = False # due to our 2 stage init, we need to rebuild midi map self.request_rebuild_midi_map() # and request update self._selector.update() if self._mk2_rgb: self.log_message("LaunchPad95 (mk2) Loaded !") else: self.log_message("LaunchPad95 Loaded !") def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._do_uncombine() if self._selector != None: self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False if self._mk2_rgb: # launchpad mk2 needs disconnect string sent self._send_midi((240, 0, 32, 41, 2, 24, 64, 247)) if self._config_button != None: self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None def _combine_active_instances(): support_devices = False for instance in Launchpad._active_instances: support_devices |= (instance._device_component != None) offset = 0 for instance in Launchpad._active_instances: instance._activate_combination_mode(offset, support_devices) offset += instance._selector._session.width() _combine_active_instances = staticmethod(_combine_active_instances) def _activate_combination_mode(self, track_offset, support_devices): if(Settings.STEPSEQ__LINK_WITH_SESSION): self._selector._stepseq.link_with_step_offset(track_offset) if(Settings.SESSION__LINK): self._selector._session.link_with_track_offset(track_offset) def _do_combine(self): if (DO_COMBINE and (self not in Launchpad._active_instances)): Launchpad._active_instances.append(self) Launchpad._combine_active_instances() def _do_uncombine(self): if self in Launchpad._active_instances: Launchpad._active_instances.remove(self) if(Settings.SESSION__LINK): self._selector._session.unlink() if(Settings.STEPSEQ__LINK_WITH_SESSION): self._selector._stepseq.unlink() Launchpad._combine_active_instances() def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): # MK2 has different challenge and params if len(midi_bytes) == 10 and midi_bytes[:7] == (240, 0, 32, 41, 2, 24, 64): response = long(midi_bytes[7]) response += long(midi_bytes[8]) << 8 if response == Live.Application.encrypt_challenge2(self._challenge): self._mk2_rgb = True self.log_message("Challenge Response ok (mk2)") self._suppress_send_midi = False self.set_enabled(True) self.init() #MK1 Challenge elif len(midi_bytes) == 8 and midi_bytes[1:5] == (0, 32, 41, 6): response = long(midi_bytes[5]) response += long(midi_bytes[6]) << 8 if response == Live.Application.encrypt_challenge2(self._challenge): self.log_message("Challenge Response ok (mk1)") self._mk2_rgb = False self.init() self._suppress_send_midi = False self.set_enabled(True) else: ControlSurface.handle_sysex(self,midi_bytes) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if self._selector!=None: if self._selector._main_mode_index==2 or self._selector._main_mode_index==1: mode = Settings.USER_MODES[ (self._selector._main_mode_index-1) * 3 + self._selector._sub_mode_index[self._selector._main_mode_index] ] #self._selector.mode_index == 1: #if self._selector._sub_mode_index[self._selector._mode_index] > 0: # disable midi map rebuild for instrument mode to prevent light feedback errors if mode != "instrument": new_channel = self._selector.channel_for_current_mode() for note in self._drum_notes: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes, optimized=None): sent_successfully = False if not self._suppress_send_midi: sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False if self._user_byte_write_button != None: self._user_byte_write_button.send_value(1) self._wrote_user_byte = True self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): # send challenge for all models to allow to detect which one is actually plugged # mk2 challenge_bytes = tuple([ self._challenge >> 8 * index & 127 for index in xrange(4) ]) self._send_midi((240, 0, 32, 41, 2, 24, 64) + challenge_bytes + (247,)) # mk1's for index in range(4): challenge_byte = self._challenge >> 8 * index & 127 self._send_midi((176, 17 + index, challenge_byte)) def _user_byte_value(self, value): assert (value in range(128)) if not self._wrote_user_byte: enabled = (value == 1) self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.force_next_send() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False def _button_value(self, value): assert value in range(128) def _config_value(self, value): assert value in range(128) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if not self._suppress_session_highlight: ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class Launchpad(ControlSurface): " SCRIPT FOR NOVATION'S LAUNCHPAD CONTROLLER " " INITALIZE " def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): #self.set_suppress_rebuild_requests(True) self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = "Launchpad" self._suggested_output_port = "Launchpad" self._control_is_with_automap = False self._user_byte_write_button = ButtonElement( is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = "User_Byte_Button" self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener( self._user_byte_value) self._wrote_user_byte = False self._challenge = (Live.Application.get_random_int(0, 400000000) & 2139062143) matrix = ButtonMatrixElement() matrix.name = "Button_Matrix" """ TRACKFINDER TEST track_index = 0 for track in self.song().tracks: if track_index < 8: button_row = [] if track.is_foldable: for column in range(8): log("right one: " + str(track_index)) button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((track_index * 16) + column)) #@UndefinedVariable button.name = (((str(column) + "_Clip_") + str(track_index)) + "_Button") button_row.append(button) track_index = track_index + 1 else: for column in range(8): log("wrong one: " + str(track_index)) button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, 99, True) #@UndefinedVariable button.name = (str(column) + "_Clip_Button-DUMMY") button_row.append(button) matrix.add_row(tuple(button_row)) log("done")""" """ ORIGINAL CODE """ for row in range(8): button_row = [] for column in range(8): button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column)) button.name = (((str(column) + "_Clip_") + str(row)) + "_Button") button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0, optimized_send_midi=False) self._config_button.add_value_listener(self._config_value) top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, (104 + index)) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index]) for index in range(8) ] top_buttons[0].name = "Bank_Select_Up_Button" top_buttons[1].name = "Bank_Select_Down_Button" top_buttons[2].name = "Bank_Select_Left_Button" top_buttons[3].name = "Bank_Select_Right_Button" top_buttons[4].name = "Session_Button" top_buttons[5].name = "User1_Button" top_buttons[6].name = "User2_Button" top_buttons[7].name = "Mixer_Button" side_buttons[0].name = "Vol_Button" side_buttons[1].name = "Pan_Button" side_buttons[2].name = "SndA_Button" side_buttons[3].name = "SndB_Button" side_buttons[4].name = "Stop_Button" side_buttons[5].name = "Trk_On_Button" side_buttons[6].name = "Solo_Button" side_buttons[7].name = "Arm_Button" self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button) self._selector.name = "Main_Modes" for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self.set_highlighting_session_component( self._selector.session_component()) self._suppress_session_highlight = False #self.set_suppress_rebuild_requests(False) " DISCONNECTOR " def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._selector = None self._user_byte_write_button.remove_value_listener( self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None " RETURN THE SESSION COMPONENT SHOWING THE RING IN LIVE SESSION " def highlighting_session_component(self): return self._selector.session_component() " REFRESH " def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) " SYSEX HANDLING " def handle_sysex(self, midi_bytes): if (len(midi_bytes) == 8): if (midi_bytes[1:5] == (0, 32, 41, 6)): response = long(midi_bytes[5]) response += (long(midi_bytes[6]) << 8) if (response == Live.Application.encrypt_challenge2( self._challenge)): self._suppress_send_midi = False self.set_enabled(True) " MIDI MAP " def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if (self._selector.mode_index == 1): new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) " SEND THE MIDI STUFF " def _send_midi(self, midi_bytes, optimized=None): sent_successfully = False if (not self._suppress_send_midi): sent_successfully = ControlSurface._send_midi(self, midi_bytes, optimized=optimized) return sent_successfully " UPDATE THE HARDWARE " def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() " CHALLANGE SEND " def _send_challenge(self): for index in range(4): challenge_byte = ((self._challenge >> (8 * index)) & 127) self._send_midi((176, (17 + index), challenge_byte)) " USER BYTE STUFF " def _user_byte_value(self, value): if not value in range(128): raise AssertionError enabled = self._wrote_user_byte or value == 1 self._control_is_with_automap = not enabled self._suppress_send_midi = self._control_is_with_automap if not self._control_is_with_automap: for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False " BUTTON VALUE " def _button_value(self, value): assert (value in range(128)) " CONFIG VALUE " def _config_value(self, value): assert (value in range(128)) " SET THE SESSION HIGHLIGHT " def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if (not self._suppress_session_highlight): ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)
class AxiomPro(ControlSurface): """ Script for the M-Audio Axiom Pro """ def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) with self.component_guard(): is_momentary = True self.set_pad_translations(PAD_TRANSLATIONS) self._suggested_input_port = 'HyperControl' self._suggested_output_port = 'HyperControl' self._display_on_button = ButtonElement(not is_momentary, MIDI_CC_TYPE, 15, 79) self._waiting_for_first_response = True mixer1 = DisplayingMixerComponent(0) mixer1.set_select_buttons(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 111), ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 110)) mixer1.set_mute_button(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 12)) mixer1.set_solo_button(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 13)) mixer2 = NotifyingMixerComponent(8) mixer2.set_bank_buttons(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 15), ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 14)) mixer2.master_strip().set_volume_control(SliderElement(MIDI_CC_TYPE, 15, 41)) for index in range(8): mixer2.channel_strip(index).set_volume_control(SliderElement(MIDI_CC_TYPE, 15, 33 + index)) device = PageableDeviceComponent(device_selection_follows_track_selection=True) self.set_device_component(device) ffwd_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 115) rwd_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 114) loop_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 113) transport = TransportComponent() transport.set_stop_button(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 116)) transport.set_play_button(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 117)) transport.set_record_button(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 118)) session = SessionComponent(0, 0) transport_view_modes = TransportViewModeSelector(transport, session, ffwd_button, rwd_button, loop_button) select_button_modes = SelectButtonModeSelector(mixer2, tuple([ ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 49 + offset) for offset in range(8) ])) select_button_modes.set_mode_toggle(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 57)) self._mixer_encoder_modes = EncoderMixerModeSelector(mixer2) encoders = [] for offset in range(8): encoders.append(PeekableEncoderElement(MIDI_CC_TYPE, 15, 17 + offset, Live.MidiMap.MapMode.relative_smooth_two_compliment)) encoders[-1].set_feedback_delay(-1) mixer_or_device = MixerOrDeviceModeSelector(self._mixer_encoder_modes, device, tuple(encoders), tuple([ ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 74 + offset) for offset in range(4) ])) mixer_or_device.set_mode_toggle(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 109)) mixer_or_device.set_peek_button(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 78)) self._track_display = PhysicalDisplayElement(8, 1) self._track_display.set_clear_all_message(SYSEX_START + (16, 247)) self._track_display.set_message_parts(SYSEX_START + (17, 1, 0, 0), (247,)) self._track_display.segment(0).set_data_source(mixer1.selected_strip().track_name_data_source()) device_display = PhysicalDisplayElement(8, 1) device_display.set_message_parts(SYSEX_START + (17, 1, 0, 10), (247,)) parameter_display = PhysicalDisplayElement(16, 1) parameter_display.set_message_parts(SYSEX_START + (17, 2, 0, 0), (247,)) select_button_modes.set_mode_display(parameter_display) mixer1.set_display(parameter_display) mixer2.set_bank_display(parameter_display) page_displays = [] for index in range(4): page_displays.append(PhysicalDisplayElement(5, 1)) page_displays[-1].set_message_parts(SYSEX_START + (17, 4, index, 0), (247,)) encoder_display = PhysicalDisplayElement(80, 8) encoder_display.set_message_parts(SYSEX_START + (17, 3), (247,)) for index in range(8): pos_id = tuple() if index != 0: pos_id += (0,) if index > 3: pos_id += (index % 4, 13) else: pos_id += (index % 4, 0) encoder_display.segment(index).set_position_identifier(pos_id) mixer_or_device.set_displays(encoder_display, parameter_display, device_display, tuple(page_displays)) for component in self.components: component.set_enabled(False) def refresh_state(self): ControlSurface.refresh_state(self) self._waiting_for_first_response = True self.schedule_message(10, self._send_midi, SYSEX_START + (32, 46, 247)) def handle_sysex(self, midi_bytes): if midi_bytes[0:-2] == SYSEX_START + (32,): msg_id_byte = midi_bytes[-2] is_setup_response = msg_id_byte in (46, 38) has_sliders = msg_id_byte == 46 if is_setup_response: if self._waiting_for_first_response: self._waiting_for_first_response = False self._display_on_button.send_value(0) for component in self.components: component.set_enabled(True) self._display_on_button.send_value(127) self._send_midi(SYSEX_START + (16, 247)) self._send_midi(SYSEX_START + (17, 3, 0, 1, 65, 98, 108, 101, 116, 111, 110, 32, 76, 105, 118, 101, 32, 67, 111, 110, 116, 114, 111, 108, 32, 0, 1, 4, 83, 117, 114, 102, 97, 99, 101, 32, 118, 49, 46, 48, 46, 48, 46, 247)) self._mixer_encoder_modes.set_show_volume_page(not has_sliders) for display in self._displays: display.set_block_messages(False) self.schedule_message(25, self._refresh_displays) elif msg_id_byte == 43: self._send_midi(SYSEX_START + (16, 247)) for display in self._displays: if display is self._track_display: display.update() else: display.set_block_messages(True) def disconnect(self): ControlSurface.disconnect(self) self._send_midi(SYSEX_START + (32, 0, 247)) self._send_midi(SYSEX_START + (16, 247)) self._send_midi(SYSEX_START + (17, 3, 0, 4, 65, 98, 108, 101, 116, 111, 110, 32, 76, 105, 118, 101, 32, 67, 111, 110, 116, 114, 111, 108, 32, 0, 1, 4, 83, 117, 114, 102, 97, 99, 101, 32, 67, 108, 111, 115, 101, 100, 46, 247))
class LaunchMod(ControlSurface): __module__ = __name__ __doc__ = " Script for Novation's Launchpad Controller " def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self._monomod_version = 'b994' self._host_name = 'LaunchMod' self._color_type = 'Launchpad' self.hosts = [] self._timer = 0 self.set_suppress_rebuild_requests(True) self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = 'Launchpad' self._suggested_output_port = 'Launchpad' self._wrote_user_byte = False self._control_is_with_automap = False self._challenge = (Live.Application.get_random_int(0, 400000000) & 2139062143) matrix = ButtonMatrixElement() matrix.name = 'ButtonMatrix' for row in range(8): #button_row = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column)) for column in range(8) ] button_row = [ FlashingButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column), 'Button_' + str(row) + '_' + str(column), self) for column in range(8) ] matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0) self._config_button.add_value_listener(self._config_value) top_buttons = [ FlashingButtonElement(is_momentary, MIDI_CC_TYPE, 0, (104 + index), 'Top_Button' + str(index), self) for index in range(8) ] side_buttons = [ FlashingButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index], 'Side_Button' + str(index), self) for index in range(8) ] self._setup_monobridge() self._setup_monomod() self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self) self._suppress_session_highlight = False self._suppress_send_midi = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._suppress_send_midi = True self.set_suppress_rebuild_requests(False) self.log_message("--------------= LaunchMod log opened =--------------" ) #Create entry in log file self.refresh_state() def _setup_monobridge(self): self._monobridge = MonoBridgeElement(self) self._monobridge.name = 'MonoBridge' def _setup_monomod(self): self._host = MonomodComponent(self) self._host.name = 'Monomod_Host' self.hosts = [self._host] def disconnect(self): self._suppress_send_midi = True self._selector = None self._user_byte_write_button.remove_value_listener( self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None self.log_message("--------------= LaunchMod log closed =--------------" ) #Create entry in log file def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if (len(midi_bytes) == 8): if (midi_bytes[1:5] == (0, 32, 41, 6)): response = long(midi_bytes[5]) response += (long(midi_bytes[6]) << 8) if (response == Live.Application.encrypt_challenge2( self._challenge)): self._suppress_send_midi = False self.set_enabled(True) #self.refresh_state() def _send_midi(self, midi_bytes): if (not self._suppress_send_midi): ControlSurface._send_midi(self, midi_bytes) def _update_hardware(self): self._suppress_send_midi = False self._config_button.send_value(40) self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = ((self._challenge >> (8 * index)) & 127) self._send_midi((176, (17 + index), challenge_byte)) def _user_byte_value(self, value): assert (value in range(128)) enabled = (value == 1) if enabled: self._config_button.send_value(40) self._control_is_with_automap = (not enabled) for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() if (not self._wrote_user_byte): self._selector.set_mode(0) self.set_enabled(enabled) else: self._wrote_user_byte = False self.request_rebuild_midi_map() def _config_value(self, value): assert (value in range(128)) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_returns=False): if (not self._suppress_session_highlight): ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_returns) def _install_forwarding(self, control): result = False if ((not self._control_is_with_automap) or (control == self._user_byte_write_button)): result = ControlSurface._install_forwarding(self, control) return result def _translate_message(self, type, from_identifier, from_channel, to_identifier, to_channel): if (not self._control_is_with_automap): ControlSurface._translate_message(self, type, from_identifier, from_channel, to_identifier, to_channel) def update_display(self): """ Live -> Script Aka on_timer. Called every 100 ms and should be used to update display relevant parts of the controller """ for message in self._scheduled_messages: message['Delay'] -= 1 if (message['Delay'] == 0): if (message['Parameter'] != None): message['Message'](message['Parameter']) else: message['Message']() del self._scheduled_messages[ self._scheduled_messages.index(message)] for callback in self._timer_callbacks: callback() self._timer = (self._timer + 1) % 256 self.flash() def flash(self): #if(self.flash_status > 0): for row in range(8): if (self._selector._side_buttons[row]._flash_state > 0): self._selector._side_buttons[row].flash(self._timer) for column in range(8): button = self._selector._matrix.get_button(column, row) if (button._flash_state > 0): button.flash(self._timer) for index in range(4): if (self._selector._nav_buttons[index]._flash_state > 0): self._selector._nav_buttons[index].flash(self._timer) if (self._selector._modes_buttons[index]._flash_state > 0): self._selector._modes_buttons[index].flash(self._timer) def allow_updates(self, allow_updates): for component in self.components: component.set_allow_update(int(allow_updates != 0))
class NoteRepeatComponent(CompoundComponent): """ Noter Repeat Handler""" __module__ = __name__ _knob_handler = None def __init__(self, note_repeat=None, *a, **k): super(NoteRepeatComponent, self).__init__(*a, **k) self._note_repeat = note_repeat self._adjust_cfg_value.subject = SliderElement(MIDI_CC_TYPE, 2, 105) self._note_repeat_button = ButtonElement(True, MIDI_CC_TYPE, 0, 102) self._do_note_repeat.subject = self._note_repeat_button self._cfg_adjust_button = ButtonElement(True, MIDI_CC_TYPE, 2, 106) self._cfg_adjust_button.add_value_listener(self._do_cfg_button) self._cfg_down = False self._hold_mode = False self.nr_down = False self._current_nr_button = None self._do_change_nr_1.subject = SliderElement(MIDI_CC_TYPE, 1, 76) self._do_change_nr_2.subject = SliderElement(MIDI_CC_TYPE, 1, 77) self._do_change_nr_3.subject = SliderElement(MIDI_CC_TYPE, 1, 78) self._do_change_nr_4.subject = SliderElement(MIDI_CC_TYPE, 1, 79) def createButton(ccindenfier, nr_freq): button = ButtonElement(True, MIDI_CC_TYPE, 1, ccindenfier) button.add_value_listener(self._select_value, True) button.active = False button.freq = nr_freq button.cfg = False button.hold = False return button def createCfgButton(ccindenfier, nr_freq_idx): button = ButtonElement(True, MIDI_CC_TYPE, 2, ccindenfier) button.add_value_listener(self._select_value, True) button.active = False button.fr_idx = nr_freq_idx button.freq = CFG_REPEAT_FREQUENCIES[nr_freq_idx] button.cfg = True button.hold = False return button self._cfg_buttons = [ createCfgButton(assign[0], assign[1]) for assign in CTRL_CFG_TO_FREQ ] for button in self._cfg_buttons: button.send_value(button.active and 1 or 0, True) self.nr_frq = CTRL_TO_FREQ[4][1] self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 self.buttons = [ createButton(assign[0], assign[1]) for assign in CTRL_TO_FREQ ] self.buttons[4].active = True self._previous_button = self.buttons[4] self._last_active_button = self.buttons[4] for button in self.buttons: button.send_value(button.active and 1 or 0, True) return def update(self): pass def store_values(self, dict): for button, idx in zip(self._cfg_buttons, range(len(self._cfg_buttons))): dict['cofig-nr-val' + str(idx)] = button.fr_idx def recall_values(self, dict): for button, idx in zip(self._cfg_buttons, range(len(self._cfg_buttons))): key = 'cofig-nr-val' + str(idx) if key in dict: fqidx = dict[key] button.fr_idx = fqidx button.freq = CFG_REPEAT_FREQUENCIES[fqidx] def registerKnobHandler(self, handler): self._knob_handler = handler def show_note_rates(self): rates = '' for button, idx in zip(self._cfg_buttons, range(len(self._cfg_buttons))): rates += ' ' + CFG_REPEAT_DISPLAY[button.fr_idx].ljust(5) if idx < 3: rates += '|' self.canonical_parent.timed_message(2, rates) def mod_button(self, button, inc, which): cindex = button.fr_idx maxindex = len(CFG_REPEAT_FREQUENCIES) - 1 minindex = 0 if self.canonical_parent.isShiftDown(): inc *= 2 maxindex = cindex % 2 == 0 and maxindex or maxindex - 1 minindex = cindex % 2 new_idx = max(minindex, min(maxindex, cindex + inc)) if new_idx != cindex: self.canonical_parent.show_message('Note Repeat Button ' + str(which) + ' : ' + CFG_REPEAT_DISPLAY[new_idx]) button.fr_idx = new_idx button.freq = CFG_REPEAT_FREQUENCIES[new_idx] if button.active: self.nr_frq = CFG_REPEAT_FREQUENCIES[new_idx] self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 @subject_slot('value') def _do_change_nr_1(self, value): self.mod_button(self._cfg_buttons[0], value == REL_KNOB_DOWN and -1 or 1, 1) self.show_note_rates() @subject_slot('value') def _do_change_nr_2(self, value): self.mod_button(self._cfg_buttons[1], value == REL_KNOB_DOWN and -1 or 1, 2) self.show_note_rates() @subject_slot('value') def _do_change_nr_3(self, value): self.mod_button(self._cfg_buttons[2], value == REL_KNOB_DOWN and -1 or 1, 3) self.show_note_rates() @subject_slot('value') def _do_change_nr_4(self, value): self.mod_button(self._cfg_buttons[3], value == REL_KNOB_DOWN and -1 or 1, 4) self.show_note_rates() def _do_cfg_button(self, value): if value != 0: self._cfg_down = True button = self._current_nr_button if button and button.cfg and button.fr_idx >= 0: self.canonical_parent.show_message( 'Note Repeat ' + CFG_REPEAT_DISPLAY[button.fr_idx]) else: self._cfg_down = False if self._knob_handler: self._knob_handler.do_main_push(value) @subject_slot('value') def _adjust_cfg_value(self, value): button = self._current_nr_button if button and button.cfg and (self.nr_down or button.hold or self._hold_mode and button.active): inc = value == 127 and -1 or 1 cindex = button.fr_idx maxindex = len(CFG_REPEAT_FREQUENCIES) - 1 minindex = 0 if self._cfg_down: inc *= 2 maxindex = cindex % 2 == 0 and maxindex or maxindex - 1 minindex = cindex % 2 new_idx = max(minindex, min(maxindex, cindex + inc)) self.canonical_parent.show_message('Note Repeat ' + CFG_REPEAT_DISPLAY[new_idx]) button.fr_idx = new_idx button.freq = CFG_REPEAT_FREQUENCIES[new_idx] self.nr_frq = CFG_REPEAT_FREQUENCIES[new_idx] self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 else: if self._knob_handler: self._knob_handler.do_main(value) def _select_value(self, value, button): if value != 0: self._current_nr_button = button button.hold = True self.show_note_rates() if self._hold_mode: if self._previous_button == button: button.send_value(0, True) button.active = False self._last_active_button = button button.active = False self._note_repeat.repeat_rate = 32.0 self._previous_button = None elif self._previous_button == None or self._previous_button != button: button.send_value(1, True) button.active = True self.nr_frq = button.freq self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 if not self._note_repeat.enabled: self._note_repeat.enabled = True if self._previous_button != None: self._previous_button.active = False self._previous_button.send_value(0, True) self._previous_button = button else: button.send_value(1, True) button.active = True self.nr_frq = button.freq self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 if self._previous_button != None and self._previous_button != button: self._previous_button.active = False self._previous_button.send_value(0, True) self._previous_button = button else: button.hold = False return @subject_slot('value') def _do_note_repeat(self, value): self.nr_down = value > 0 if self._hold_mode: if value > 0: self._note_repeat_button.send_value(0) self._note_repeat.enabled = False self._hold_mode = False if self._previous_button == None and self._last_active_button != None: self._previous_button = self._last_active_button self._last_active_button.send_value(1) self._last_active_button.active = True self.nr_frq = self._last_active_button.freq self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 else: if self.canonical_parent.isShiftDown() and value > 0: self._note_repeat_button.send_value(1) self._note_repeat.enabled = True self._hold_mode = True self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 else: if value == 0: self._note_repeat.enabled = False self._note_repeat_button.send_value(0) else: self._note_repeat_button.send_value(1) self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0 self._note_repeat.enabled = True return def disconnect(self): super(NoteRepeatComponent, self).disconnect()
class Launchpad(ControlSurface): " Script for Novation's Launchpad Controller " def __init__(self, c_instance): ControlSurface.__init__(self, c_instance) self.set_suppress_rebuild_requests(True) self._suppress_send_midi = True self._suppress_session_highlight = True is_momentary = True self._suggested_input_port = "Launchpad" self._suggested_output_port = "Launchpad" self._control_is_with_automap = False self._user_byte_write_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 16) self._user_byte_write_button.name = "User_Byte_Button" self._user_byte_write_button.send_value(1) self._user_byte_write_button.add_value_listener(self._user_byte_value) self._wrote_user_byte = False self._challenge = (Live.Application.get_random_int(0, 400000000) & 2139062143) matrix = ButtonMatrixElement() matrix.name = "Button_Matrix" for row in range(8): button_row = [] for column in range(8): button = ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, ((row * 16) + column)) button.name = (((str(column) + "_Clip_") + str(row)) + "_Button") button_row.append(button) matrix.add_row(tuple(button_row)) self._config_button = ButtonElement(is_momentary, MIDI_CC_TYPE, 0, 0) self._config_button.add_value_listener(self._config_value) top_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_CC_TYPE, 0, (104 + index)) for index in range(8) ] side_buttons = [ ConfigurableButtonElement(is_momentary, MIDI_NOTE_TYPE, 0, SIDE_NOTES[index]) for index in range(8) ] top_buttons[0].name = "Bank_Select_Up_Button" top_buttons[1].name = "Bank_Select_Down_Button" top_buttons[2].name = "Bank_Select_Left_Button" top_buttons[3].name = "Bank_Select_Right_Button" top_buttons[4].name = "Session_Button" top_buttons[5].name = "User1_Button" top_buttons[6].name = "User2_Button" top_buttons[7].name = "Mixer_Button" side_buttons[0].name = "Vol_Button" side_buttons[1].name = "Pan_Button" side_buttons[2].name = "SndA_Button" side_buttons[3].name = "SndB_Button" side_buttons[4].name = "Stop_Button" side_buttons[5].name = "Trk_On_Button" side_buttons[6].name = "Solo_Button" side_buttons[7].name = "Arm_Button" self._selector = MainSelectorComponent(matrix, tuple(top_buttons), tuple(side_buttons), self._config_button, self) self._selector.name = "Main_Modes" for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.add_value_listener(self._button_value) self._suppress_session_highlight = False self.set_suppress_rebuild_requests(False) self.log_message("LaunchPad85 Loaded !") def disconnect(self): self._suppress_send_midi = True for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.remove_value_listener(self._button_value) self._selector = None self._user_byte_write_button.remove_value_listener(self._user_byte_value) self._config_button.remove_value_listener(self._config_value) ControlSurface.disconnect(self) self._suppress_send_midi = False self._config_button.send_value(32) self._config_button.send_value(0) self._config_button = None self._user_byte_write_button.send_value(0) self._user_byte_write_button = None def highlighting_session_component(self): " Return the session component showing the ring in Live session " return self._selector.session_component() def refresh_state(self): ControlSurface.refresh_state(self) self.schedule_message(5, self._update_hardware) def handle_sysex(self, midi_bytes): if (len(midi_bytes) == 8): if (midi_bytes[1:5] == (0, 32, 41, 6)): response = long(midi_bytes[5]) response += (long(midi_bytes[6]) << 8) if (response == Live.Application.encrypt_challenge2(self._challenge)): self._suppress_send_midi = False self.set_enabled(True) def build_midi_map(self, midi_map_handle): ControlSurface.build_midi_map(self, midi_map_handle) if (self._selector.mode_index == 1): new_channel = self._selector.channel_for_current_mode() for note in DRUM_NOTES: self._translate_message(MIDI_NOTE_TYPE, note, 0, note, new_channel) def _send_midi(self, midi_bytes): sent_successfully = False if (not self._suppress_send_midi): sent_successfully = ControlSurface._send_midi(self, midi_bytes) return sent_successfully def _update_hardware(self): self._suppress_send_midi = False self._wrote_user_byte = True self._user_byte_write_button.send_value(1) self._suppress_send_midi = True self.set_enabled(False) self._suppress_send_midi = False self._send_challenge() def _send_challenge(self): for index in range(4): challenge_byte = ((self._challenge >> (8 * index)) & 127) self._send_midi((176, (17 + index), challenge_byte)) def _user_byte_value(self, value): assert (value in range(128)) if (not self._wrote_user_byte): enabled = (value == 1) self._control_is_with_automap = (not enabled) self._suppress_send_midi = self._control_is_with_automap if (not self._control_is_with_automap): for control in self.controls: if isinstance(control, ConfigurableButtonElement): control.set_force_next_value() self._selector.set_mode(0) self.set_enabled(enabled) self._suppress_send_midi = False else: self._wrote_user_byte = False def _button_value(self, value): assert (value in range(128)) def _config_value(self, value): assert (value in range(128)) def _set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks): if (not self._suppress_session_highlight): ControlSurface._set_session_highlight(self, track_offset, scene_offset, width, height, include_return_tracks)