class MaschineTransport(TransportComponent): """ this override changes the following from the default behavious: - reverses the stop button color behaviour - reverses tap_tempo_button lighting behaviour """ # ? consider adding follow_song + observer tap_tempo_button = ButtonControl(color='DefaultButton.Off', pressed_color='DefaultButton.On') def __init__(self, *a, **k): super(MaschineTransport, self).__init__(*a, **k) def _update_stop_button_color(self): self.stop_button.color = 'DefaultButton.Off' if self.song.is_playing else 'DefaultButton.On' @tap_tempo_button.pressed def tap_tempo_button(self, _): if not self._end_undo_step_task.is_running: self.song.begin_undo_step() self._end_undo_step_task.restart() self.song.tap_tempo() def set_tap_tempo_button(self, button): self.tap_tempo_button.set_control_element(button)
class MaschineScrollComponent(Component): __module__ = __name__ __events__ = (u'scroll', ) button = ButtonControl(color='TrackNavigation.TrackNotSelected', repeat=True) @button.pressed def button(self, button): self.notify_scroll()
class MaschineDeviceOrganizer(Component): move_backward_button = ButtonControl(color='DefaultButton.Off') move_forward_button = ButtonControl(color='DefaultButton.Off') # todo: safe guard index range def __init__(self, *a, **k): super(MaschineDeviceOrganizer, self).__init__(*a, **k) self._device = None def set_device(self, device): self._device = device def set_move_backward_button(self, button): self.move_backward_button.set_control_element(button) def set_move_forward_button(self, button): self.move_forward_button.set_control_element(button) @move_backward_button.pressed def _on_move_backward_button_pressed(self, button): self.move_backward() @move_forward_button.pressed def _on_move_forward_button_pressed(self, button): self.move_forward() def move_backward(self): parent = self._device.canonical_parent device_index = list(parent.devices).index(self._device) if device_index > 0: self.song.move_device(self._device, parent, device_index - 1) def move_forward(self): parent = self._device.canonical_parent device_index = list(parent.devices).index(self._device) if device_index < len(parent.devices) - 1: self.song.move_device(self._device, parent, device_index + 2)
class MaschineTrackSelectionEnabler(Component): """ this object wraps the track selection matrix. it enable and disable the selection matrix so that it act like a layer of selection buttons when enabled. and release the matrix to go back to playable mode when disabled """ # ? if in the future, if track provider will dynamically change # ? track provider should be observed for changes and updates selection_matrix_button = ButtonControl(color='DefaultButton.Off') @depends(info_display=None) def __init__(self, info_display=None, track_provider=None, *a, **k): assert info_display is not None assert track_provider is not None self._info_display = info_display super(MaschineTrackSelectionEnabler, self).__init__(*a, **k) self._track_provider = track_provider self.selection_matrix = MaschineTrackSelection( info_display=info_display, track_provider=track_provider, parent=self, name='Selection_Matrix', is_enabled=False) def set_select_buttons(self, buttons): self.selection_matrix.set_select_buttons(buttons) def set_selection_matrix_button(self, button): self.selection_matrix_button.set_control_element(button) @selection_matrix_button.pressed def _on_selection_matrix_button_pressed(self, button): self._toggle_selection_matrix() @selection_matrix_button.released_delayed def _on_selection_matrix_button_released_delated(self, button): self._toggle_selection_matrix() def set_track_provider(self, new_provider): if self._track_provider != new_provider: self._track_provider = new_provider def _toggle_selection_matrix(self): enabled = self.selection_matrix.is_enabled() self._set_selection_matrix_enabled(False if enabled else True) def _set_selection_matrix_enabled(self, enabled): self.selection_matrix.set_enabled(enabled) self.selection_matrix_button.color = 'DefaultButton.On' if enabled else 'DefaultButton.Off'
class MaschineView(Component): view_button = ButtonControl(color='DefaultButton.Off', pressed_color='DefaultButton.On') @depends(info_display=None) def __init__(self, info_display=None, *a, **k): assert info_display is not None self._info_display = info_display super(MaschineView, self).__init__(*a, **k) @view_button.pressed def _on_view_toggled(self, button): self.toggle_main_view() def toggle_main_view(self): view = '' app_view = self.application.view if app_view.is_view_visible('Session'): self.show_view('Arranger') view = 'arranger' else: self.show_view('Session') view = 'session' self._display_message_on_maschine(view) def show_view(self, view): assert view in VIEWS app_view = self.application.view try: if view == 'Detail/DeviceChain' or 'Detail/Clip': if not app_view.is_view_visible('Detail'): app_view.show_view('Detail') if not app_view.is_view_visible(view): app_view.show_view(view) except RuntimeError: pass def _display_message_on_maschine(self, view): self._tasks.clear() message = 'In {} View'.format(view) display_task = partial(self._info_display.display_message_on_maschine, message, 3) clear_task = partial(self._info_display.clear_display, 3) self._tasks.add( task.sequence(task.run(display_task), task.wait(1.5), task.run(clear_task)))
class MaschineTrackNavigator(Component): """ long presses scroll through the track list, short presses will select the next track. only the latter option was available when using the track scroller directly. """ track_scroller_type = BasicTrackScroller master_track_button = ButtonControl(color='DefaultButton.On', pressed_color='DefaultButton.Off') @depends(info_display=None) def __init__(self, info_display=None, *a, **k): assert info_display is not None self._info_display = info_display super(MaschineTrackNavigator, self).__init__(*a, **k) self._track_scroller = ScrollComponent(self.track_scroller_type(), parent=self) song = self.song view = song.view self.register_slot(song, self.__on_selected_track_changed, 'visible_tracks') self.register_slot(song, self.__on_selected_track_changed, 'return_tracks') self.register_slot(view, self.__on_selected_track_changed, 'selected_track') self.__on_selected_track_changed() self._update_master_track_button() @property def master_track(self): return self.song.master_track def set_previous_track_button(self, button): self._track_scroller.set_scroll_up_button(button) def set_next_track_button(self, button): self._track_scroller.set_scroll_down_button(button) def set_master_track_button(self, button): self.master_track_button.set_control_element(button) @master_track_button.pressed def _on_master_track_button_pressed(self, button): self.select_master_track() def select_master_track(self): selected_track = self.song.view.selected_track if selected_track != self.master_track: self.song.view.selected_track = self.master_track self._update_master_track_button() def _update_master_track_button(self): selected_track = self.song.view.selected_track self.master_track_button.color = 'DefaultButton.On' if selected_track == self.master_track else 'DefaultButton.Off' def __on_selected_track_changed(self): track = self.song.view.selected_track if liveobj_valid(track): if self.is_enabled(): self._track_scroller.update() self.__on_name_changed.subject = track self._update_master_track_button() self._display_track_name(track) if not track.devices: self._info_display.clear_display(1) self._info_display.clear_display(3) @listens('name') def __on_name_changed(self): track = self.song.view.selected_track self._display_track_name(track) def _display_track_name(self, track): if track in self.song.visible_tracks: self._info_display.display_message_on_maschine( 'Track - {} '.format(track.name), 2) elif track in self.song.return_tracks: self._info_display.display_message_on_maschine( 'Return - {}'.format(track.name), 2) else: self._info_display.display_message_on_maschine( 'Master Track Selected', 2)
class MaschineNoteRepeatEnabler(Component): __module__ = __name__ """ this is a wrapper for the note repeat component. it gives a button to toggle the note repeat component. the wrapper takes care of automatically disabling the note repeat component when an audio track is selected (including master and return tracks) """ note_repeat_button = ButtonControl(color='DefaultButton.Off') @depends(info_display=None) def __init__(self, info_display=None, note_repeat=None, *a, **k): assert info_display is not None self._info_display = info_display super(MaschineNoteRepeatEnabler, self).__init__(*a, **k) self.note_repeat_component = MaschineNoteRepeat(note_repeat=note_repeat, name='Note_Repeat', parent=self, is_enabled=False) self.__on_selected_track_changed.subject = self.song.view @listens('selected_track') def __on_selected_track_changed(self): self.note_repeat_button.enabled = not self.song.view.selected_track.has_audio_input self.note_repeat_component.set_enabled(False) self.note_repeat_button.color = 'DefaultButton.Off' def set_select_buttons(self, buttons): self.note_repeat_component.set_select_buttons(buttons) def set_note_repeat_button(self, button): self.note_repeat_button.set_control_element(button) def set_note_repeat(self, note_repeat): self.note_repeat_component.set_note_repeat(note_repeat) @note_repeat_button.pressed def _on_note_repeat_button_pressed(self, button): self._toggle_note_repeat() @note_repeat_button.released_delayed def _on_note_repeat_button_released_delayed(self, button): self._toggle_note_repeat() def _toggle_note_repeat(self): enabled = self.note_repeat_component.is_enabled() self._set_note_repeat_enabled(False if enabled else True) rate = self.note_repeat_component._get_internal_repeat_rate_index() self.note_repeat_component.selected_rate = rate self._display_message_on_maschine(enabled) def _display_message_on_maschine(self, enabled): message = 'Note Repeate is {}'.format('Off' if enabled else 'Active') display_task = partial(self._info_display.display_message_on_maschine, message, 3) clear_task = partial(self._info_display.clear_display, 3) self._tasks.add(task.sequence(task.run(display_task), task.wait(2.5), task.run(clear_task))) # ? maybe consider saving and restoring note repeat on track selection ?? def _restore_note_repeat_enabled_state(self): return self._set_note_repeat_enabled(self._get_note_repeat_enabled()) def _set_note_repeat_enabled(self, is_enabled): self.note_repeat_component.set_enabled(is_enabled) self.song.view.selected_track.set_data('maschine-note-repeat-enabled', is_enabled) self.note_repeat_button.color = 'DefaultButton.On' if is_enabled else 'DefaultButton.Off' def _get_note_repeat_enabled(self): return self.song.view.selected_track.get_data('maschine-note-repeat-rate', False)
class MaschineTrackSelection(MaschineTrackListerComponent): previous_track_page_button = ButtonControl(color='DefaultButton.Off') next_track_page_button = ButtonControl(color='DefaultButton.Off') @depends(info_display=None) def __init__(self, info_display=None, track_provider=None, *a, **k): assert info_display is not None self._info_display = info_display self._track_list = track_provider super(MaschineTrackSelection, self).__init__(track_provider=self._track_list, *a, **k) self.register_disconnectable(self._track_list) self.__on_selected_track_changed.subject = self.song.view self.__on_selected_track_changed() self._update_select_buttons() def _color_for_button(self, button_index, is_selected): tracks = self.track_provider.tracks color = self.color_class_name if button_index > len(self.track_provider.tracks): return color + '.NoTrack' if len(self.song.return_tracks) > 0 and tracks[button_index + self.track_offset] in self.song.return_tracks: return color + '.ReturnTrackSelected' if is_selected else color + '.ReturnTrackNotSelected' elif tracks[button_index + self.track_offset] == self.song.master_track: return color + '.MasterTrackSelected' if is_selected else color + '.MasterTrackNotSelected' else: return color + '.TrackSelected' if is_selected else color + '.TrackNotSelected' @property def selected_track(self): return self.track_provider.selected_track @listens('tracks') def __on_tracks_changed(self): self._update_select_buttons() self._scroll_overlay.update_scroll_buttons() @listens('selected_track') def __on_selected_track_changed(self): current_track = self.song.view.selected_track self._update_track_provider(current_track) # self._update_track_offset() self.__on_name_changed.subject = self.selected_track self._display_track_name(self.selected_track) if not self.selected_track.devices: self._info_display.clear_display(1) self._info_display.clear_display(3) @listens('name') def __on_name_changed(self): track = self.selected_track self._display_track_name(track) @previous_track_page_button.pressed def _on_previous_track_page_button_pressed(self, button): self.select_previous_track_page() @next_track_page_button.pressed def _on_next_track_page_button_pressed(self, button): self.select_next_track_page() def can_scroll_left(self): return self.track_offset > 0 def can_scroll_right(self): tracks = self._track_provider.tracks return self.track_offset < len(tracks) - self._num_visible_tracks def scroll_left(self): offset = max(0, self.track_offset - self._num_visible_tracks) self.track_offset = offset def scroll_right(self): offset = self.track_offset + self._num_visible_tracks self.track_offset = offset def link_selection(self): track = self.track_provider.tracks[self.track_offset] self._select_track(track) def select_next_track_page(self): if self.can_scroll_right(): self.scroll_right() self.link_selection() def select_previous_track_page(self): if self.can_scroll_left(): self.scroll_left() self.link_selection() def _select_track(self, track): if track and track != self.song.view.selected_track: self.song.view.selected_track = track self._update_track_provider(track) def _update_track_provider(self, track): self._track_list.selected_track = track # def _update_track_offset(self): # # offset = self.track_offset # tracks = self.track_provider.tracks # index = tracks.index(self.track_provider.selected_track) # new_offset = index / self._num_visible_tracks # print(new_offset) # self.track_offset = new_offset # self._create_track_slots() def _display_track_name(self, track): if track in self.song.visible_tracks: self._info_display.display_message_on_maschine('Track - {} '.format(track.name), 2) elif track in self.song.return_tracks: self._info_display.display_message_on_maschine('Return - {}'.format(track.name), 2) else: self._info_display.display_message_on_maschine('Master Track Selected', 2) def _on_select_button_pressed(self, button): if button.index > len(self.track_provider.tracks): return self._select_track(self.tracks[button.index].track)
class MaschineTrackCreation(Component): midi_track_button = ButtonControl(color='DefaultButton.Off', pressed_color='DefaultButton.On') audio_track_button = ButtonControl(color='DefaultButton.Off', pressed_color='DefaultButton.On') return_track_button = ButtonControl(color='DefaultButton.Off', pressed_color='DefaultButton.On') @depends(info_display=None) def __init__(self, info_display=None, *a, **k): assert info_display is not None self._info_display = info_display super(MaschineTrackCreation, self).__init__(*a, **k) @property def selected_track(self): return self.song.view.selected_track @property def master_track(self): return self.song.master_track @property def return_tracks(self): return self.song.return_tracks @midi_track_button.pressed def _on_midi_track_button_pressed(self, _): self._create_new_midi_track() @audio_track_button.pressed def _on_audio_track_button_pressed(self, _): self._create_new_audio_track() @return_track_button.pressed def _on_return_track_button_pressed(self, _): self._create_new_return_track() def _create_new_midi_track(self): track = self.selected_track master = self.master_track if track == master or track in list(self.return_tracks): self.song.create_midi_track(-1) else: self.song.create_midi_track() self._display_message_on_maschine('midi') def _create_new_audio_track(self): track = self.selected_track master = self.master_track if track == master or track in list(self.return_tracks): self.song.create_audio_track(-1) else: self.song.create_audio_track() self._display_message_on_maschine('audio') def _create_new_return_track(self): if len(self.song.return_tracks) < 12: self.song.create_return_track() self._display_message_on_maschine('return') else: self._info_display.clear_display(3) self._tasks.clear() message = 'Only 12 sends allowed' display_task = partial(self._info_display.display_message_on_maschine, message, 3) clear_display = partial(self._info_display.clear_display, 3) self._tasks.add(task.sequence(task.run(display_task), task.wait(1), task.run(clear_display))) def _display_message_on_maschine(self, track_type): self._tasks.clear() self._info_display.clear_display(3) message = 'Created New {} Track'.format(track_type) display_task = partial(self._info_display.display_message_on_maschine, message, 3) clear_display = partial(self._info_display.clear_display, 3) self._tasks.add(task.sequence(task.run(display_task), task.wait(1), task.run(clear_display)))
class MaschineKeyboard(MaschinePadMixin, PlayableComponent, ScrollComponent): __events__ = ('scale', 'root_note') next_scale_button = ButtonControl(color='DefaultButton.Off', pressed_color='DefaultButton.On') previous_scale_button = ButtonControl(color='DefaultButton.Off', pressed_color='DefaultButton.On') next_key_button = ButtonControl(color='DefaultButton.Off', pressed_color='DefaultButton.On') previous_key_button = ButtonControl(color='DefaultButton.Off', pressed_color='DefaultButton.On') @depends(info_display=None) def __init__(self, translation_channel, info_display=None, *a, **k): assert info_display is not None self._info_display = info_display super(MaschineKeyboard, self).__init__(*a, **k) self._translation_channel = translation_channel self._scale = None self._root_note = self.song.root_note self._start_note = 36 self.__on_selected_track_changed.subject = self.song.view self.__on_root_note_changed.subject = self.song self.__on_scale_name_changed.subject = self.song self.__on_selected_track_changed() self._scale = self._get_scale_from_name(self.song.scale_name) @next_scale_button.pressed def _on_next_scale_button_pressed(self, button): self.scroll_scales(1) @previous_scale_button.pressed def _on_previous_scale_button_pressed(self, button): self.scroll_scales(-1) @next_key_button.pressed def _on_next_key_button_pressed(self, button): self.scroll_keys(1) @previous_key_button.pressed def _on_previous_key_button_pressed(self, button): self.scroll_keys(-1) def scroll_keys(self, offset): index = clamp(self.root_note + offset, 0, 12) if index in range(12): self.root_note = index self._move_start_note(offset) self._display_scale_and_key_info() def scroll_scales(self, offset): if self._scale: scale_index = clamp( SCALES.index(self.scale) + offset, 0, len(SCALES)) self.scale = SCALES[scale_index] or SCALES[0] self._update_note_translations() self._update_led_feedback() self._display_scale_and_key_info() @property def root_note(self): return self._root_note @root_note.setter def root_note(self, root_note): self.song.root_note = root_note self._root_note = root_note @property def scale(self): return self._scale @scale.setter def scale(self, scale): self._song.scale_name = scale.name self._scale = scale @listens('root_note') def __on_root_note_changed(self): self.notify_root_note(self.song.root_note) @listens('scale_name') def __on_scale_name_changed(self): self._scale = self._get_scale_from_name(self.song.scale_name) self.notify_scale(self._scale) @listens('selected_track') def __on_selected_track_changed(self): track = self.song.view.selected_track self.__on_devices_changed.subject = track if not self._has_instrument(): self._turn_matarix_buttons_off() else: self._update_led_feedback() @listens('devices') def __on_devices_changed(self): if not self._has_instrument(): self._turn_matarix_buttons_off() else: self._update_led_feedback() def _get_scale_from_name(self, name): return find_if(lambda scale: scale.name == name, SCALES) or SCALES[0] def _has_instrument(self): track = self.song.view.selected_track if track.has_midi_input and track.devices: for device in track.devices: if device.type == Live.Device.DeviceType.instrument and not device.can_have_drum_pads: return True else: return False def _update_button_color(self, button): if not self._has_instrument(): self._turn_matarix_buttons_off() else: button.color = ('Keyboard.{}').format( 'Natural' if (button.identifier - self.root_note) % 12 in self.scale.notes else 'Sharp') def _turn_matarix_buttons_off(self): for button in self.matrix: button.color = 'DefaultButton.Off' def can_scroll_up(self): return self._start_note < MAX_START_NOTE def can_scroll_down(self): return self._start_note > 0 def scroll_up(self): if self.can_scroll_up(): self._move_start_note(12) def scroll_down(self): if self.can_scroll_down(): self._move_start_note(-12) def _move_start_note(self, factor): self._start_note += factor self._update_note_translations() self._release_all_pads() def _note_translation_for_button(self, button): row, column = button.coordinate inverted_row = self.matrix.height - row - 1 return (inverted_row * self.matrix.width + column + self._start_note, self._translation_channel) def _release_all_pads(self): for pad in self.matrix: if pad.is_pressed: pad._release_button() def _display_scale_and_key_info(self): self._tasks.clear() self._info_display.clear_display(3) message = 'Key - {} | Scale - {}'.format(NOTE_NAMES[self.root_note], self.scale.name) display_task = partial(self._info_display.display_message_on_maschine, message, 3) clear_task = partial(self._info_display.clear_display, 3) self._tasks.add( task.sequence(task.run(display_task), task.wait(1.5), task.run(clear_task)))
class MaschineDevice(DeviceComponent): __events__ = (u'bank', ) previous_bank_button = ButtonControl(color='DefaultButton.On', pressed_color='DefaultButton.Off') next_bank_button = ButtonControl(color='DefaultButton.On', pressed_color='DefaultButton.Off') bypass_device_button = ButtonControl(color='DefaultButton.Off') reset_parameters_button = ButtonControl(color='DefaultButton.Off') randomize_parameters_button = ButtonControl(color='DefaultButton.Off') @depends(info_display=None) def __init__(self, info_display=None, *a, **k): assert info_display is not None self._info_display = info_display self._quantized_parameters = {} super(MaschineDevice, self).__init__(*a, **k) self.__on_bank_changed.subject = self._device_bank_registry self.update_bank_buttons() @property def device_is_active(self): return self.device() and self.device().parameters[0].value != 0 @listens('is_active') def __on_is_active_changed(self): self.update_bypass_button() if self.device(): message = '{} - {}'.format( 'Activated' if self.device_is_active else 'Bypassed', self.device().name) self._display_temprary_message_on_maschine(message, 1) @listens('device_bank') def __on_bank_changed(self, device, bank): if device == self.device(): self._set_bank_index(bank) self.update_bank_buttons() self.notify_bank() self._display_message_on_maschine() def set_previous_bank_button(self, button): self.previous_bank_button.set_control_element(button) def set_next_bank_button(self, button): self.next_bank_button.set_control_element(button) def set_bypass_device_button(self, button): self.bypass_device_button.set_control_element(button) def set_reset_parameters_button(self, button): self.reset_parameters_button.set_control_element(button) def set_randomize_parameters_button(self, button): self.randomize_parameters_button.set_control_element(button) @previous_bank_button.pressed def _on_previous_bank_button_pressed(self, button): self.select_previous_bank() @next_bank_button.pressed def _on_next_bank_button_pressed(self, button): self.select_next_bank() @bypass_device_button.pressed def _on_bypass_device_button_pressed(self, button): self.toggle_device_active_state() self.update_bypass_button() @reset_parameters_button.pressed def _on_reset_parameters_button_pressed(self, button): self.reset_device_parameters() @randomize_parameters_button.pressed def _on_randomize_parameters_button_pressed(self, button): self.randomize_device_parameters() def select_previous_bank(self): self._scroll_banks(-1) def select_next_bank(self): self._scroll_banks(1) def toggle_device_active_state(self): if self.device(): parameter = self.device().parameters[0] if parameter.is_enabled: self.device().parameters[ 0].value = False if self.device_is_active else True def reset_device_parameters(self): if self.device() is None: return parameters = filter( lambda param: param.is_enabled and param.state == 0, self.device().parameters) for i in range(0, len(parameters)): p = parameters[i] if not p.is_quantized: p.value = p.default_value else: if p.name in self._quantized_parameters: p.value = self._quantized_parameters[p.name] # self._display_temprary_message_on_maschine('Device Reset to defaults', 3) def randomize_device_parameters(self): if self.device() is None: return parameters = filter( lambda param: param.is_enabled and param.state == 0, self.device().parameters[1:]) for p in parameters: if not p.is_quantized: value = random.random() scaled_value = p.min + (value * (p.max - p.min)) if in_range(scaled_value, p.min, p.max): p.value = scaled_value def _scroll_banks(self, offset): if self._bank: bank_index = clamp(self._bank.index + offset, 0, self._bank.bank_count()) self._device_bank_registry.set_device_bank(self.device(), bank_index) self._set_bank_index(bank_index) def update_bypass_button(self): self.bypass_device_button.enabled = self.device() is not None self.bypass_device_button.color = 'DefaultButton.Off' if self.device_is_active else 'DefaultButton.On' def update_bank_buttons(self): self.previous_bank_button.enabled = self._bank is not None and self._bank.index > 0 self.next_bank_button.enabled = self._bank is not None and self._bank.index + 1 < self._bank.bank_count( ) def _set_device(self, device): super(MaschineDevice, self)._set_device(device) self.__on_is_active_changed.subject = device self.update_bank_buttons() self.update_bypass_button() if device: self._collect_device_quantized_parameters(device) self._display_message_on_maschine() def _collect_device_quantized_parameters(self, device): if not device.can_have_chains: parameters = device.parameters for parameter in parameters: if parameter.is_enabled and parameter.is_quantized: self._quantized_parameters[ parameter.name] = parameter.value def _create_parameter_info(self, parameter, name): parameter_info = ParameterInfo(parameter=parameter, name=name, default_encoder_sensitivity=1.0, fine_grain_encoder_sensitivity=0.1) return parameter_info def _display_temprary_message_on_maschine(self, message, display_index): self._tasks.clear() self._info_display.clear_display(display_index) temp_display = partial(self._info_display.display_message_on_maschine, message, display_index) clear_display = partial(self._info_display.clear_display, display_index) self._tasks.add( task.sequence(task.run(temp_display), task.wait(2.5), task.run(clear_display), task.run(self._display_message_on_maschine))) def _display_message_on_maschine(self): if self.device(): self._info_display.clear_display(1) message = '{} - {}'.format(self.device().name or '', self._bank.name or '') self._info_display.display_message_on_maschine(message, 1)
class MaschineDeviceNavigation(DeviceNavigationComponent): """ this object will observe the device chain on the selected track. it enables the users to: - select devices from the device chain for control - move devices backward and forward in the device chain - move devices in and out of a rack (when one exists in the device chain) - remove the selected device from the device chain. """ # todo: paging buttons should page through by 8 and not 1 # todo: add device resetting to default state # todo: may be add parameter randomizer too? next_device_button = ButtonControl( color='ItemNavigation.ItemNotSelected', pressed_color='ItemNavigation.ItemSelected') previous_device_button = ButtonControl( color='ItemNavigation.ItemNotSelected', pressed_color='ItemNavigation.ItemSelected') next_chain_button = ButtonControl( color='ItemNavigation.ItemNotSelected', pressed_color='ItemNavigation.ItemSelected') previous_chain_button = ButtonControl( color='ItemNavigation.ItemNotSelected', pressed_color='ItemNavigation.ItemSelected') next_page_button = ButtonControl(color='DefaultButton.Off', pressed_color='DefaultButton.On') previous_page_button = ButtonControl(color='DefaultButton.Off', pressed_color='DefaultButton.On') remove_device_button = ButtonControl(color='DefaultButton.Off', pressed_color='DefaultButton.On') collapse_device_button = ButtonControl(color='DefaultButton.Off') @depends(info_display=None) def __init__(self, info_display=None, device_component=None, item_provider=None, *a, **k): assert info_display is not None self._info_display = info_display super(MaschineDeviceNavigation, self).__init__(device_component=device_component, item_provider=item_provider, *a, **k) self.__on_appointed_device_changed.subject = self.song self._device_organizer = MaschineDeviceOrganizer( parent=self, name='Device_Organizer') @property def first_device(self): index = self._get_selected_device_index() return index == 0 @property def last_device(self): index = self._get_selected_device_index() return index == len(self.item_provider.items) - 1 @listens(u'appointed_device') def __on_appointed_device_changed(self): device = self.song.appointed_device self._device_organizer.set_device(device) self._info_display.clear_display(3) def set_next_device_button(self, button): self.next_device_button.set_control_element(button) def set_previous_device_button(self, button): self.previous_device_button.set_control_element(button) def set_next_chain_button(self, button): self.next_chain_button.set_control_element(button) def set_previous_chain_button(self, button): self.previous_chain_button.set_control_element(button) def set_remove_device_button(self, button): self.remove_device_button.set_control_element(button) def set_move_backward_button(self, button): self._device_organizer.set_move_backward_button(button) def set_move_forward_button(self, button): self._device_organizer.set_move_forward_button(button) def set_collapse_device_button(self, button): self.collapse_device_button.set_control_element(button) def set_next_page_button(self, button): self.next_page_button.set_control_element(button) def set_previous_page_button(self, button): self.previous_page_button.set_control_element(button) @previous_device_button.pressed def _on_previous_device_button_pressed(self, _): self.select_previous_device() @next_device_button.pressed def _on_next_device_button_pressed(self, _): self.select_next_device() @remove_device_button.pressed def _on_remove_device_button_pressed(self, _): self.remove_selected_device() @collapse_device_button.pressed def _on_collapse_device_button_pressed(self, button): self.toggle_device_collapsed_state() @next_chain_button.pressed def _on_next_chain_button_pressed(self, button): self.select_next_chain_in_rack() @previous_chain_button.pressed def _on_previous_chain_button_pressed(self, button): self.select_previous_chain_in_rack() @next_page_button.pressed def _on_next_page_button_pressed(self, button): self.select_next_device_page() @previous_page_button.pressed def _on_previous_page_button_pressed(self, button): self.select_previous_device_page() def select_next_device(self): index = self._get_selected_device_index() target_device = None if not self.last_device and self._is_valid_index(index): target_device = self.item_provider.items[index + 1][0] self._select_item(target_device) self._show_selected_item() def select_previous_device(self): index = self._get_selected_device_index() target_device = None if not self.first_device and self._is_valid_index(index): target_device = self.item_provider.items[index - 1][0] self._select_item(target_device) self._show_selected_item() def select_next_chain_in_rack(self): device = self.selected_item if device: rack = is_rack(device) target_chain = None if rack and not is_empty_rack(device): chains = list(device.chains) selected_chain = device.view.selected_chain index = clamp( chains.index(selected_chain) + 1, 0, len(chains) - 1) target_chain = chains[index] device.view.selected_chain = target_chain self._select_item(device) def select_previous_chain_in_rack(self): device = self.selected_item if device: rack = is_rack(device) target_chain = None if rack and not is_empty_rack(device): chains = list(device.chains) selected_chain = device.view.selected_chain index = clamp(chains.index(selected_chain) - 1, 0, len(chains)) target_chain = chains[index] device.view.selected_chain = target_chain self._select_item(device) def remove_selected_device(self): if self.selected_item is not None: parent = self.selected_item.canonical_parent index = list(parent.devices).index(self.selected_item) parent.delete_device(index) self.update_items() def toggle_device_collapsed_state(self): device = self.selected_item if device: rack = is_rack(device) if rack: # if len(list(device.chains)) > 1: # showing_chains = device.is_showing_chains # device.is_showing_chains = False if showing_chains else True showing_devices = device.view.is_showing_chain_devices device.view.is_showing_chain_devices = False if showing_devices else True else: collapsed = device.view.is_collapsed device.view.is_collapsed = False if collapsed else True def _is_valid_index(self, index): return 0 <= index < len(self.item_provider.items) def _get_selected_device_index(self): return index_if(lambda i: i[0] == self.selected_item, self.item_provider.items) def _color_for_button(self, button_index, is_selected): device = self.items[button_index].item if device and is_rack(device): return self.color_class_name + '.RackItemSelected' if is_selected else self.color_class_name + '.RackItemNotSelected' else: return self.color_class_name + '.ItemSelected' if is_selected else self.color_class_name + '.ItemNotSelected' def can_scroll_left(self): return self.item_offset > 0 def can_scroll_right(self): devices = self._item_provider.items return self.item_offset < len(devices) - self._num_visible_items def scroll_left(self): offset = max(0, self.item_offset - self._num_visible_items) self.item_offset = offset def scroll_right(self): offset = self.item_offset + self._num_visible_items self.item_offset = offset def link_selection(self): device = self._item_provider.items[self.item_offset][0] self._select_item(device) def select_next_device_page(self): if self.can_scroll_right(): self.scroll_right() self.link_selection() def select_previous_device_page(self): if self.can_scroll_left(): self.scroll_left() self.link_selection()