コード例 #1
0
class ToggledTaggedSetting(TaggedSettingsComponent, ChannelizedSettingsBase):

    _set_attribute_tag_model = lambda self, a: str(a)

    split_toggle = ToggleButtonControl(
        toggled_color='MonoInstrument.SplitModeOnValue',
        untoggled_color='DefaultButton.Off')
    seq_toggle = ToggleButtonControl(
        toggled_color='MonoInstrument.SequencerModeOnValue',
        untoggled_color='DefaultButton.Off')

    def __init__(self, *a, **k):
        super(ToggledTaggedSetting, self).__init__(value_dict=[
            'none',
            'seq',
            'split',
        ],
                                                   *a,
                                                   **k)

    @split_toggle.toggled
    def split_toggle(self, toggled, button):
        self.value = 'none' if self.value == 'split' else 'split'
        self.update()

    @seq_toggle.toggled
    def seq_toggle(self, toggled, button):
        self.value = 'none' if self.value == 'seq' else 'seq'
        self.update()

    def _update_controls(self):
        self.split_toggle.is_toggled = bool(self.value is 'split')
        self.seq_toggle.is_toggled = bool(self.value is 'seq')
コード例 #2
0
class ViewToggleComponent(Component):
    detail_view_toggle_button = ToggleButtonControl()
    main_view_toggle_button = ToggleButtonControl()

    def __init__(self, *a, **k):
        super(ViewToggleComponent, self).__init__(*a, **k)
        self.__on_detail_view_visibility_changed.subject = self.application.view
        self.__on_main_view_visibility_changed.subject = self.application.view
        self.__on_detail_view_visibility_changed()
        self.__on_main_view_visibility_changed()

    @detail_view_toggle_button.toggled
    def detail_view_toggle_button(self, is_toggled, _):
        self._show_or_hide_view(is_toggled, b'Detail')

    @main_view_toggle_button.toggled
    def main_view_toggle_button(self, is_toggled, _):
        self._show_or_hide_view(is_toggled, b'Session')

    def _show_or_hide_view(self, show_view, view_name):
        if show_view:
            self.application.view.show_view(view_name)
        else:
            self.application.view.hide_view(view_name)

    @listens(b'is_view_visible', b'Detail')
    def __on_detail_view_visibility_changed(self):
        self.detail_view_toggle_button.is_toggled = self.application.view.is_view_visible(
            b'Detail')

    @listens(b'is_view_visible', b'Session')
    def __on_main_view_visibility_changed(self):
        self.main_view_toggle_button.is_toggled = self.application.view.is_view_visible(
            b'Session')
コード例 #3
0
class FixedLengthSettingComponent(Component):
    length_option_buttons = control_list(RadioButtonControl, checked_color='Option.Selected', unchecked_color='Option.Unselected', control_count=len(LENGTH_OPTIONS))
    fixed_length_toggle_button = ToggleButtonControl(toggled_color='Option.On', untoggled_color='Option.Off')
    legato_launch_toggle_button = ToggleButtonControl(toggled_color='FixedLength.PhraseAlignedOn', untoggled_color='FixedLength.PhraseAlignedOff')
    label_display_line = TextDisplayControl(LENGTH_LABELS)
    option_display_line = TextDisplayControl(LENGTH_OPTION_NAMES)

    def __init__(self, fixed_length_setting=None, *a, **k):
        assert fixed_length_setting is not None
        super(FixedLengthSettingComponent, self).__init__(*a, **k)
        self._fixed_length_setting = fixed_length_setting
        self.length_option_buttons.connect_property(fixed_length_setting, 'selected_index')
        self.fixed_length_toggle_button.connect_property(fixed_length_setting, 'enabled')
        self.legato_launch_toggle_button.connect_property(fixed_length_setting, 'legato_launch')
        self.__on_setting_selected_index_changes.subject = fixed_length_setting
        self.__on_setting_selected_index_changes(fixed_length_setting.selected_index)
        return

    @listens('selected_index')
    def __on_setting_selected_index_changes(self, index):
        self._update_option_display()

    def _update_option_display(self):
        for index, option_name in enumerate(LENGTH_OPTION_NAMES):
            if index == self._fixed_length_setting.selected_index:
                prefix = consts.CHAR_SELECT if 1 else ' '
                self.option_display_line[index] = prefix + option_name
コード例 #4
0
class ViewToggleComponent(Component):
    detail_view_toggle_button = ToggleButtonControl(untoggled_color=u'View.DetailOff', toggled_color=u'View.DetailOn')
    main_view_toggle_button = ToggleButtonControl(untoggled_color=u'View.MainOff', toggled_color=u'View.MainOn')
    clip_view_toggle_button = ToggleButtonControl(untoggled_color=u'View.ClipOff', toggled_color=u'View.ClipOn')
    browser_view_toggle_button = ToggleButtonControl(untoggled_color=u'View.BrowserOff', toggled_color=u'View.BrowserOn')

    def __init__(self, *a, **k):
        super(ViewToggleComponent, self).__init__(*a, **k)
        self.__on_detail_view_visibility_changed.subject = self.application.view
        self.__on_main_view_visibility_changed.subject = self.application.view
        self.__on_clip_view_visibility_changed.subject = self.application.view
        self.__on_browser_view_visibility_changed.subject = self.application.view
        self.__on_detail_view_visibility_changed()
        self.__on_main_view_visibility_changed()
        self.__on_clip_view_visibility_changed()
        self.__on_browser_view_visibility_changed()

    @detail_view_toggle_button.toggled
    def detail_view_toggle_button(self, is_toggled, _):
        self._show_or_hide_view(is_toggled, u'Detail')

    @main_view_toggle_button.toggled
    def main_view_toggle_button(self, is_toggled, _):
        self._show_or_hide_view(is_toggled, u'Session')

    @clip_view_toggle_button.toggled
    def clip_view_toggle_button(self, is_toggled, _):
        self._show_or_hide_view(is_toggled, u'Detail/Clip')

    @browser_view_toggle_button.toggled
    def browser_view_toggle_button(self, is_toggled, _):
        self._show_or_hide_view(is_toggled, u'Browser')

    def _show_or_hide_view(self, show_view, view_name):
        if show_view:
            self.application.view.show_view(view_name)
        else:
            self.application.view.hide_view(view_name)

    @listens(u'is_view_visible', u'Detail')
    def __on_detail_view_visibility_changed(self):
        self.detail_view_toggle_button.is_toggled = self.application.view.is_view_visible(u'Detail')

    @listens(u'is_view_visible', u'Session')
    def __on_main_view_visibility_changed(self):
        self.main_view_toggle_button.is_toggled = self.application.view.is_view_visible(u'Session')

    @listens(u'is_view_visible', u'Detail/Clip')
    def __on_clip_view_visibility_changed(self):
        self.clip_view_toggle_button.is_toggled = self.application.view.is_view_visible(u'Detail/Clip')

    @listens(u'is_view_visible', u'Browser')
    def __on_browser_view_visibility_changed(self):
        self.browser_view_toggle_button.is_toggled = self.application.view.is_view_visible(u'Browser')
コード例 #5
0
class ProfilingSettingsComponent(Component):
    show_qml_stats_button = ToggleButtonControl()
    show_usb_stats_button = ToggleButtonControl()
    show_realtime_ipc_stats_button = ToggleButtonControl()

    def __init__(self, settings = None, *a, **k):
        raise settings is not None or AssertionError
        super(ProfilingSettingsComponent, self).__init__(*a, **k)
        self.show_qml_stats_button.connect_property(settings, 'show_qml_stats')
        self.show_usb_stats_button.connect_property(settings, 'show_usb_stats')
        self.show_realtime_ipc_stats_button.connect_property(settings, 'show_realtime_ipc_stats')
        return
コード例 #6
0
class TransportComponent(TransportComponentBase):
    play_button = ToggleButtonControl(toggled_color='Transport.PlayOn',
                                      untoggled_color='Transport.PlayOff')
    capture_midi_button = ButtonControl()

    def __init__(self, *a, **k):
        (super(TransportComponent, self).__init__)(*a, **k)
        self._metronome_toggle.view_transform = lambda v: 'Transport.MetronomeOn' if v else 'Transport.MetronomeOff'
        self._TransportComponent__on_can_capture_midi_changed.subject = self.song
        self._TransportComponent__on_can_capture_midi_changed()

    @play_button.toggled
    def _on_play_button_toggled(self, is_toggled, _):
        self.song.is_playing = is_toggled
        if is_toggled:
            self.song.current_song_time = 0.0

    @capture_midi_button.pressed
    def capture_midi_button(self, _):
        try:
            if self.song.can_capture_midi:
                self.song.capture_midi()
        except RuntimeError:
            pass

    @listens('can_capture_midi')
    def __on_can_capture_midi_changed(self):
        self.capture_midi_button.color = 'Transport.Capture{}'.format(
            'On' if self.song.can_capture_midi else 'Off')

    def _update_button_states(self):
        super(TransportComponent, self)._update_button_states()
        self.continue_playing_button.color = 'Transport.Continue{}'.format(
            'Off' if self.song.is_playing else 'On')
コード例 #7
0
class MasterTrackComponent(Component):
    toggle_button = ToggleButtonControl()

    def __init__(self, tracks_provider=None, *a, **k):
        raise tracks_provider is not None or AssertionError
        super(MasterTrackComponent, self).__init__(*a, **k)
        self._tracks_provider = tracks_provider
        self.__on_selected_item_changed.subject = self._tracks_provider
        self._previous_selection = self._tracks_provider.selected_item
        self._update_button_state()

    @listens('selected_item')
    def __on_selected_item_changed(self, *a):
        self._update_button_state()
        if not self._is_on_master():
            self._previous_selection = self._tracks_provider.selected_item

    def _update_button_state(self):
        self.toggle_button.is_toggled = self._is_on_master()

    @toggle_button.toggled
    def toggle_button(self, toggled, button):
        if toggled:
            self._previous_selection = self._tracks_provider.selected_item
            self._tracks_provider.selected_item = self.song.master_track
        else:
            self._tracks_provider.selected_item = self._previous_selection
        self._update_button_state()

    def _is_on_master(self):
        return self._tracks_provider.selected_item == self.song.master_track
コード例 #8
0
class DeviceComponent(DeviceComponentBase):
    prev_bank_button = ButtonControl(color=u'Action.Off',
                                     pressed_color=u'Action.On')
    next_bank_button = ButtonControl(color=u'Action.Off',
                                     pressed_color=u'Action.On')
    bank_name_display = TextDisplayControl()
    device_lock_button = ToggleButtonControl()

    def __init__(self, toggle_lock=None, *a, **k):
        super(DeviceComponent, self).__init__(*a, **k)
        assert toggle_lock is not None
        self._toggle_lock = toggle_lock
        self.__on_is_locked_to_device_changed.subject = self._device_provider
        self.__on_is_locked_to_device_changed()

    @prev_bank_button.pressed
    def prev_bank_button(self, _):
        self._scroll_bank(-1)

    @next_bank_button.pressed
    def next_bank_button(self, _):
        self._scroll_bank(1)

    @device_lock_button.toggled
    def device_lock_button(self, toggled, _):
        self._toggle_lock()
        self._update_device_lock_button()

    def _create_parameter_info(self, parameter, name):
        return ParameterInfo(parameter=parameter,
                             name=name,
                             default_encoder_sensitivity=1.0)

    def _set_bank_index(self, index):
        super(DeviceComponent, self)._set_bank_index(index)
        self._update_bank_name_display()

    def _scroll_bank(self, offset):
        bank = self._bank
        if bank:
            new_index = clamp(bank.index + offset, 0, bank.bank_count() - 1)
            self._device_bank_registry.set_device_bank(self.device(),
                                                       new_index)

    def _update_bank_name_display(self):
        bank_name = u''
        device = self.device()
        if liveobj_valid(device):
            device_bank_names = self._banking_info.device_bank_names(device)
            if device_bank_names:
                bank_name = device_bank_names[
                    self._device_bank_registry.get_device_bank(device)]
        self.bank_name_display[0] = bank_name

    def _update_device_lock_button(self):
        self.device_lock_button.is_toggled = self._device_provider.is_locked_to_device

    @listens(u'is_locked_to_device')
    def __on_is_locked_to_device_changed(self):
        self._update_device_lock_button()
コード例 #9
0
class ExperimentalSettingsComponent(Component):
    new_waveform_navigation_button = ToggleButtonControl()

    def __init__(self, settings=None, *a, **k):
        raise settings is not None or AssertionError
        super(ExperimentalSettingsComponent, self).__init__(*a, **k)
        self.new_waveform_navigation_button.connect_property(
            settings, 'new_waveform_navigation')
コード例 #10
0
class ActionsComponent(Component):
    u"""
    Simple component that provides undo/redo, record quantization toggle
    and clip quantization handling.
    """
    undo_button = ButtonControl(**ACTION_BUTTON_COLORS)
    redo_button = ButtonControl(color=u'Misc.Shift',
                                pressed_color=u'Misc.ShiftOn',
                                disabled_color=u'DefaultButton.Disabled')
    quantization_on_button = ToggleButtonControl(untoggled_color=u'Misc.Shift',
                                                 toggled_color=u'Misc.ShiftOn')

    def __init__(self, *a, **k):
        self.suppressing_control_notifications = BooleanContext()
        super(ActionsComponent, self).__init__(*a, **k)
        self._record_quantization = RecordingQuantization.rec_q_sixtenth
        self._on_record_quantization_changed_in_live.subject = self.song
        self._on_record_quantization_changed_in_live()
        self._metronome_toggle = ToggleComponent(u'metronome', self.song)

    def control_notifications_enabled(self):
        return self.is_enabled() and not self.suppressing_control_notifications

    def quantize_clip(self, clip):
        raise isinstance(clip, Live.Clip.Clip) or AssertionError
        clip.quantize(self._record_quantization, 1.0)

    @undo_button.pressed
    def undo_button(self, button):
        if self.song.can_undo:
            self.song.undo()

    @redo_button.pressed
    def redo_button(self, button):
        if self.song.can_redo:
            self.song.redo()

    @quantization_on_button.toggled
    def quantization_on_button(self, is_toggled, button):
        self._record_quantization_on = is_toggled
        self.song.midi_recording_quantization = self._record_quantization if self._record_quantization_on else RecordingQuantization.rec_q_no_q

    @listens(u'midi_recording_quantization')
    def _on_record_quantization_changed_in_live(self):
        quant_value = self.song.midi_recording_quantization
        quant_on = quant_value != RecordingQuantization.rec_q_no_q
        if quant_on:
            self._record_quantization = quant_value
        self._record_quantization_on = quant_on
        with self.suppressing_control_notifications():
            self.quantization_on_button.is_toggled = quant_on

    def set_metronome_button(self, button):
        self._metronome_toggle.set_toggle_button(button)

    def update(self):
        super(ActionsComponent, self).update()
        self._metronome_toggle.update()
コード例 #11
0
class SessionRecordingComponent(SessionRecordingComponentBase):
    mpc_automation_toggle = ToggleButtonControl(toggled_color=u'Automation.On', untoggled_color=u'Automation.Off')

    def _on_session_automation_record_changed(self):
        super(SessionRecordingComponent, self)._on_session_automation_record_changed()
        self.mpc_automation_toggle.is_toggled = self.song.session_automation_record

    @mpc_automation_toggle.toggled
    def mpc_automation_toggle(self, is_toggled, _):
        self.song.session_automation_record = is_toggled
コード例 #12
0
class TouchStripPitchModComponent(Component, Messenger):
    touch_strip_toggle = ToggleButtonControl()

    def __init__(self, *a, **k):
        super(TouchStripPitchModComponent, self).__init__(*a, **k)
        self._touch_strip = None
        self._touch_strip_indication = None
        return

    def set_touch_strip(self, control):
        self._touch_strip = control
        self._update_touch_strip()

    def _update_touch_strip(self):
        if self._touch_strip:
            self._touch_strip.behaviour = MODWHEEL_BEHAVIOUR if self.touch_strip_toggle.is_toggled else DEFAULT_BEHAVIOUR

    @touch_strip_toggle.toggled
    def touch_strip_toggle(self, toggled, button):
        self._update_touch_strip()
        self._update_touch_strip_indication()
        self.show_notification(
            consts.MessageBoxText.TOUCHSTRIP_MODWHEEL_MODE
            if toggled else consts.MessageBoxText.TOUCHSTRIP_PITCHBEND_MODE)

    def set_touch_strip_indication(self, control):
        self._touch_strip_indication = control
        self._update_touch_strip_indication()

    def _update_touch_strip_indication(self):
        if self._touch_strip_indication:
            self._touch_strip_indication.set_mode(TouchStripModes.CUSTOM_FREE)
            self._touch_strip_indication.send_state([
                TouchStripStates.STATE_FULL
                if 1 else TouchStripStates.STATE_HALF
                for _ in xrange(self._touch_strip_indication.state_count)
                if self.touch_strip_toggle.is_toggled
            ])

    def update(self):
        super(TouchStripPitchModComponent, self).update()
        if self.is_enabled():
            self._update_touch_strip()
            self._update_touch_strip_indication()
コード例 #13
0
ファイル: channelized_settings.py プロジェクト: aumhaa/m4m8
class ToggledChannelizedSettingsComponent(ChannelizedSettingsBase):


	toggle_button = ToggleButtonControl()

	def __init__(self, toggled_color = 'DefaultButton.On', untoggled_color = 'DefaultButton.Off', *a, **k):
		super(ToggledChannelizedSettingsComponent, self).__init__(value_dict = [False, True], *a, **k)
		self.toggle_button.toggled_color = toggled_color
		self.toggle_button.untoggled_color = untoggled_color
	

	@toggle_button.toggled
	def toggle_button(self, toggled, button):
		self.index = int(toggled)
		self.update()
	

	def _update_controls(self):
		self.toggle_button.is_toggled = bool(self._values[self._channel])
コード例 #14
0
class DisplayDebugSettingsComponent(Component):
    show_row_spaces_button = ToggleButtonControl()
    show_row_margins_button = ToggleButtonControl()
    show_row_middle_button = ToggleButtonControl()
    show_button_spaces_button = ToggleButtonControl()
    show_unlit_button_button = ToggleButtonControl()
    show_lit_button_button = ToggleButtonControl()

    def __init__(self, settings = None, *a, **k):
        assert settings is not None
        super(DisplayDebugSettingsComponent, self).__init__(*a, **k)
        self.show_row_spaces_button.connect_property(settings, u'show_row_spaces')
        self.show_row_margins_button.connect_property(settings, u'show_row_margins')
        self.show_row_middle_button.connect_property(settings, u'show_row_middle')
        self.show_button_spaces_button.connect_property(settings, u'show_button_spaces')
        self.show_unlit_button_button.connect_property(settings, u'show_unlit_button')
        self.show_lit_button_button.connect_property(settings, u'show_lit_button')
コード例 #15
0
class QuantizationComponent(Component):
    quantization_toggle_button = ToggleButtonControl(
        untoggled_color='Quantization.Off', toggled_color='Quantization.On')

    def __init__(self, *a, **k):
        super(QuantizationComponent, self).__init__(*a, **k)
        self._record_quantization = RecordingQuantization.rec_q_sixtenth
        self.__on_record_quantization_changed.subject = self.song
        self.__on_record_quantization_changed()

    def quantize_clip(self, clip):
        clip.quantize(self._record_quantization, 1.0)

    @quantization_toggle_button.toggled
    def quantization_toggle_button(self, is_toggled, _):
        self.song.midi_recording_quantization = self._record_quantization if is_toggled else RecordingQuantization.rec_q_no_q

    @listens('midi_recording_quantization')
    def __on_record_quantization_changed(self):
        quantization_on = self.song.midi_recording_quantization != RecordingQuantization.rec_q_no_q
        if quantization_on:
            self._record_quantization = self.song.midi_recording_quantization
        self.quantization_toggle_button.is_toggled = quantization_on
コード例 #16
0
class LoopSettingsControllerComponent(LoopSettingsControllerComponentBase):
    __events__ = ('looping', 'loop_parameters', 'zoom')
    zoom_encoder = MappedControl()
    loop_button = ToggleButtonControl(toggled_color='Clip.Option',
                                      untoggled_color='Clip.OptionDisabled')

    def __init__(self, zoom_handler=None, *a, **k):
        super(LoopSettingsControllerComponent, self).__init__(*a, **k)
        self._looping_settings = [
            LoopSetting(name=PARAMETERS_LOOPED[0],
                        parent=self._loop_model,
                        source_property='position'),
            LoopSetting(name=PARAMETERS_LOOPED[1],
                        parent=self._loop_model,
                        use_length_conversion=True,
                        source_property='loop_length'),
            LoopSetting(name=PARAMETERS_LOOPED[2],
                        parent=self._loop_model,
                        source_property='start_marker')
        ]
        self._non_looping_settings = [
            LoopSetting(name=PARAMETERS_NOT_LOOPED[0],
                        parent=self._loop_model,
                        source_property='loop_start'),
            LoopSetting(name=PARAMETERS_NOT_LOOPED[1],
                        parent=self._loop_model,
                        source_property='loop_end')
        ]
        for setting in self._looping_settings + self._non_looping_settings:
            self.register_disconnectable(setting)

        self._zoom_handler = self.register_disconnectable(
            zoom_handler or ClipZoomHandling())
        self._processed_zoom_requests = 0
        self.__on_looping_changed.subject = self._loop_model
        self.__on_looping_changed()

    @loop_button.toggled
    def loop_button(self, toggled, button):
        self._loop_model.looping = toggled

    @property
    def looping(self):
        return self._loop_model.looping if self.clip else False

    @property
    def loop_parameters(self):
        if not liveobj_valid(self.clip):
            return []
        parameters = self._looping_settings if self.looping else self._non_looping_settings
        return [self.zoom] + parameters if self.zoom else parameters

    @property
    def zoom(self):
        return getattr(self.clip, 'zoom', None) if liveobj_valid(
            self.clip) else None

    @listenable_property
    def processed_zoom_requests(self):
        return self._processed_zoom_requests

    @listens('is_recording')
    def __on_is_recording_changed(self):
        recording = False
        if liveobj_valid(self._loop_model.clip):
            recording = self._loop_model.clip.is_recording
            self._looping_settings[1].recording = recording
            self._non_looping_settings[1].recording = recording

    @listens('looping')
    def __on_looping_changed(self):
        self._update_and_notify()

    def _update_loop_button(self):
        self.loop_button.enabled = liveobj_valid(self.clip)
        if liveobj_valid(self.clip):
            self.loop_button.is_toggled = self._loop_model.looping

    def _on_clip_changed(self):
        self._update_and_notify()
        self.__on_is_recording_changed.subject = self._loop_model.clip
        self.__on_is_recording_changed()
        self._zoom_handler.set_parameter_host(self._loop_model.clip)
        self._connect_encoder()

    def _update_and_notify(self):
        self._update_loop_button()
        self.notify_looping()
        self.notify_loop_parameters()
        self.notify_zoom()

    def _connect_encoder(self):
        self.zoom_encoder.mapped_parameter = self.zoom

    def set_zoom_encoder(self, encoder):
        self.zoom_encoder.set_control_element(encoder)
        self._connect_encoder()

    def request_zoom(self, zoom_factor):
        self._zoom_handler.request_zoom(zoom_factor)
        self._processed_zoom_requests += 1
        self.notify_processed_zoom_requests()
コード例 #17
0
class BrowserComponent(Component, Messenger):
    __events__ = (u'loaded', u'close')
    NUM_ITEMS_PER_COLUMN = 6
    NUM_VISIBLE_BROWSER_LISTS = 7
    NUM_COLUMNS_IN_EXPANDED_LIST = 3
    EXPAND_LIST_TIME = 1.5
    REVEAL_PREVIEW_LIST_TIME = 0.2
    MIN_TIME = 0.6
    MAX_TIME = 1.4
    MIN_TIME_TEXT_LENGTH = 30
    MAX_TIME_TEXT_LENGTH = 70
    up_button = ButtonControl(repeat=True)
    down_button = ButtonControl(repeat=True)
    right_button = ButtonControl(repeat=True, **NAVIGATION_COLORS)
    left_button = ButtonControl(repeat=True, **NAVIGATION_COLORS)
    back_button = ButtonControl(**NAVIGATION_COLORS)
    open_button = ButtonControl(**NAVIGATION_COLORS)
    load_button = ButtonControl(**NAVIGATION_COLORS)
    close_button = ButtonControl()
    prehear_button = ToggleButtonControl(toggled_color=u'Browser.Option', untoggled_color=u'Browser.OptionDisabled')
    scroll_encoders = control_list(StepEncoderControl, num_steps=10, control_count=NUM_VISIBLE_BROWSER_LISTS)
    scroll_focused_encoder = StepEncoderControl(num_steps=10)
    scrolling = listenable_property.managed(False)
    horizontal_navigation = listenable_property.managed(False)
    list_offset = listenable_property.managed(0)
    can_enter = listenable_property.managed(False)
    can_exit = listenable_property.managed(False)
    context_color_index = listenable_property.managed(-1)
    context_text = listenable_property.managed(u'')

    @depends(commit_model_changes=None, selection=None)
    def __init__(self, preferences = dict(), commit_model_changes = None, selection = None, main_modes_ref = None, *a, **k):
        assert commit_model_changes is not None
        super(BrowserComponent, self).__init__(*a, **k)
        self._lists = []
        self._browser = Live.Application.get_application().browser
        self._current_hotswap_target = self._browser.hotswap_target
        self._updating_root_items = BooleanContext()
        self._focused_list_index = 0
        self._commit_model_changes = commit_model_changes
        self._preferences = preferences
        self._expanded = False
        self._unexpand_with_scroll_encoder = False
        self._delay_preview_list = BooleanContext()
        self._selection = selection
        self._main_modes_ref = main_modes_ref if main_modes_ref is not None else nop
        self._load_neighbour_overlay = LoadNeighbourOverlayComponent(parent=self, is_enabled=False)
        self._content_filter_type = None
        self._content_hotswap_target = None
        self._preview_list_task = self._tasks.add(task.sequence(task.wait(self.REVEAL_PREVIEW_LIST_TIME), task.run(self._replace_preview_list_by_task))).kill()
        self._update_root_items()
        self._update_navigation_buttons()
        self._update_context()
        self.prehear_button.is_toggled = preferences.setdefault(u'browser_prehear', True)
        self._on_selected_track_color_index_changed.subject = self.song.view
        self._on_selected_track_name_changed.subject = self.song.view
        self._on_detail_clip_name_changed.subject = self.song.view
        self._on_hotswap_target_changed.subject = self._browser
        self._on_load_next.subject = self._load_neighbour_overlay
        self._on_load_previous.subject = self._load_neighbour_overlay
        self._on_focused_item_changed.subject = self
        self.register_slot(self, self.notify_focused_item, u'focused_list_index')

        def auto_unexpand():
            self.expanded = False
            self._update_list_offset()

        self._unexpand_task = self._tasks.add(task.sequence(task.wait(self.EXPAND_LIST_TIME), task.run(auto_unexpand))).kill()

    @up_button.pressed
    def up_button(self, button):
        with self._delay_preview_list():
            self.focused_list.select_index_with_offset(-1)
        self._update_auto_expand()
        self._update_scrolling()
        self._update_horizontal_navigation()

    @up_button.released
    def up_button(self, button):
        self._finish_preview_list_task()
        self._update_scrolling()

    @down_button.pressed
    def down_button(self, button):
        with self._delay_preview_list():
            self.focused_list.select_index_with_offset(1)
        self._update_auto_expand()
        self._update_scrolling()
        self._update_horizontal_navigation()

    @down_button.released
    def down_button(self, button):
        self._finish_preview_list_task()
        self._update_scrolling()

    @right_button.pressed
    def right_button(self, button):
        if self._expanded and self._can_auto_expand() and self._focused_list_index > 0:
            self.focused_list.select_index_with_offset(self.NUM_ITEMS_PER_COLUMN)
            self._update_scrolling()
            self.horizontal_navigation = True
        elif not self._enter_selected_item():
            self._update_auto_expand()

    @right_button.released
    def right_button(self, button):
        self._update_scrolling()

    @left_button.pressed
    def left_button(self, button):
        if self._expanded and self._focused_list_index > 0 and self.focused_list.selected_index >= self.NUM_ITEMS_PER_COLUMN:
            self.focused_list.select_index_with_offset(-self.NUM_ITEMS_PER_COLUMN)
            self._update_scrolling()
            self.horizontal_navigation = True
        else:
            self._exit_selected_item()

    @left_button.released
    def left_button(self, button):
        self._update_scrolling()

    @open_button.pressed
    def open_button(self, button):
        self._enter_selected_item()

    @back_button.pressed
    def back_button(self, button):
        self._exit_selected_item()

    @scroll_encoders.touched
    def scroll_encoders(self, encoder):
        list_index = self._get_list_index_for_encoder(encoder)
        if list_index is not None:
            try:
                if self._focus_list_with_index(list_index, crop=False):
                    self._unexpand_with_scroll_encoder = True
                    self._prehear_selected_item()
                if self.focused_list.selected_item.is_loadable and encoder.index == self.scroll_encoders.control_count - 1:
                    self._update_list_offset()
                self._on_encoder_touched()
            except CannotFocusListError:
                pass

    @scroll_encoders.released
    def scroll_encoders(self, encoders):
        self._on_encoder_released()

    @scroll_encoders.value
    def scroll_encoders(self, value, encoder):
        list_index = self._get_list_index_for_encoder(encoder)
        if list_index is not None:
            try:
                if self._focus_list_with_index(list_index):
                    self._unexpand_with_scroll_encoder = True
                self._on_encoder_value(value)
            except CannotFocusListError:
                pass

    @scroll_focused_encoder.value
    def scroll_focused_encoder(self, value, encoder):
        self._on_encoder_value(value)

    @scroll_focused_encoder.touched
    def scroll_focused_encoder(self, encoder):
        self._on_encoder_touched()

    @scroll_focused_encoder.released
    def scroll_focused_encoder(self, encoder):
        self._on_encoder_released()

    def _on_encoder_value(self, value):
        with self._delay_preview_list():
            self.focused_list.select_index_with_offset(value)
        first_visible_list_focused = self.focused_list_index == self.list_offset
        if self.expanded and first_visible_list_focused:
            self.expanded = False
            self._unexpand_with_scroll_encoder = True
        elif not first_visible_list_focused and not self.expanded and self._can_auto_expand():
            self._update_auto_expand()
            self._unexpand_with_scroll_encoder = True
        self._update_scrolling()
        self._update_horizontal_navigation()

    def _on_encoder_touched(self):
        self._unexpand_task.kill()
        self._update_scrolling()
        self._update_horizontal_navigation()

    def _on_encoder_released(self):
        any_encoder_touched = any(map(lambda e: e.is_touched, self.scroll_encoders)) or self.scroll_focused_encoder.is_touched
        if not any_encoder_touched and self._unexpand_with_scroll_encoder:
            self._unexpand_task.restart()
        self._update_scrolling()

    def _get_list_index_for_encoder(self, encoder):
        if self.expanded:
            if encoder.index == 0:
                return self.list_offset
            return self.list_offset + 1
        index = self.list_offset + encoder.index
        if self.focused_list_index + 1 == index and self.should_widen_focused_item:
            index = self.focused_list_index
        if 0 <= index < len(self._lists):
            return index
        else:
            return None

    @load_button.pressed
    def load_button(self, button):
        self._load_selected_item()

    @prehear_button.toggled
    def prehear_button(self, toggled, button):
        if toggled:
            self._prehear_selected_item()
        else:
            self._browser.stop_preview()
        self._preferences[u'browser_prehear'] = toggled
        self.notify_prehear_enabled()

    @close_button.pressed
    def close_button(self, button):
        self.notify_close()

    @listenable_property
    def lists(self):
        return self._lists

    @listenable_property
    def focused_list_index(self):
        return self._focused_list_index

    @listenable_property
    def prehear_enabled(self):
        return self.prehear_button.is_toggled

    @property
    def focused_list(self):
        return self._lists[self._focused_list_index]

    @listenable_property
    def focused_item(self):
        return self.focused_list.selected_item

    @listenable_property
    def expanded(self):
        return self._expanded

    @property
    def load_neighbour_overlay(self):
        return self._load_neighbour_overlay

    @listenable_property
    def should_widen_focused_item(self):
        return self.focused_item.is_loadable and not self.focused_item.is_device

    @property
    def context_display_type(self):
        return u'custom_button'

    def disconnect(self):
        super(BrowserComponent, self).disconnect()
        self._lists = []
        self._commit_model_changes = None

    @expanded.setter
    def expanded(self, expanded):
        if self._expanded != expanded:
            self._expanded = expanded
            self._unexpand_with_scroll_encoder = False
            self._update_navigation_buttons()
            if len(self._lists) > self._focused_list_index + 1:
                self._lists[self._focused_list_index + 1].limit = self.num_preview_items
            self.notify_expanded()

    @listens(u'selected_track.color_index')
    def _on_selected_track_color_index_changed(self):
        if self.is_enabled():
            self._update_context()
            self._update_navigation_buttons()

    @listens(u'selected_track.name')
    def _on_selected_track_name_changed(self):
        if self.is_enabled():
            self._update_context()

    @listens(u'detail_clip.name')
    def _on_detail_clip_name_changed(self):
        if self.is_enabled():
            self._update_context()

    @listens(u'hotswap_target')
    def _on_hotswap_target_changed(self):
        if self.is_enabled():
            if not self._switched_to_empty_pad():
                self._update_root_items()
                self._update_context()
                self._update_list_offset()
                self._update_load_neighbour_overlay_visibility()
            else:
                self._load_neighbour_overlay.set_enabled(False)
        self._current_hotswap_target = self._browser.hotswap_target

    @listens(u'focused_item')
    def _on_focused_item_changed(self):
        self.notify_should_widen_focused_item()

    @property
    def browse_for_audio_clip(self):
        main_modes = self._main_modes_ref()
        if main_modes is None:
            return False
        has_midi_support = self.song.view.selected_track.has_midi_input
        return not has_midi_support and u'clip' in main_modes.active_modes

    def _switched_to_empty_pad(self):
        hotswap_target = self._browser.hotswap_target
        is_browsing_drumpad = isinstance(hotswap_target, Live.DrumPad.DrumPad)
        was_browsing_pad = isinstance(self._current_hotswap_target, Live.DrumPad.DrumPad)
        return is_browsing_drumpad and was_browsing_pad and len(hotswap_target.chains) == 0

    def _focus_list_with_index(self, index, crop = True):
        u"""
        Focus the list with the given index.
        Raises CannotFocusListError if the operation fails.
        Returns True if a new list was focused and False if it was already focused.
        """
        if self._focused_list_index != index:
            if self._finish_preview_list_task():
                if index >= len(self._lists):
                    raise CannotFocusListError()
            assert 0 <= index < len(self._lists)
            self._on_focused_selection_changed.subject = None
            if self._focused_list_index > index and crop:
                for l in self._lists[self._focused_list_index:]:
                    l.selected_index = -1

            self._focused_list_index = index
            self.focused_list.limit = -1
            if self.focused_list.selected_index == -1:
                self.focused_list.selected_index = 0
            self.notify_focused_list_index()
            self._on_focused_selection_changed.subject = self.focused_list
            if crop:
                self._crop_browser_lists(self._focused_list_index + 2)
            if self._focused_list_index == len(self._lists) - 1:
                self._replace_preview_list()
            self._load_neighbour_overlay.set_enabled(False)
            self._update_navigation_buttons()
            return True
        return False

    @listens(u'selected_index')
    def _on_focused_selection_changed(self):
        if self._delay_preview_list and not self.focused_item.is_loadable:
            self._preview_list_task.restart()
        else:
            self._replace_preview_list()
        self._update_navigation_buttons()
        self._prehear_selected_item()
        self._load_neighbour_overlay.set_enabled(False)
        self.notify_focused_item()

    def _get_actual_item(self, item):
        contained_item = getattr(item, u'contained_item', None)
        if contained_item is not None:
            return contained_item
        return item

    def _previous_can_be_loaded(self):
        return self.focused_list.selected_index > 0 and self.focused_list.items[self.focused_list.selected_index - 1].is_loadable

    def _next_can_be_loaded(self):
        items = self.focused_list.items
        return self.focused_list.selected_index < len(items) - 1 and items[self.focused_list.selected_index + 1].is_loadable

    @listens(u'load_next')
    def _on_load_next(self):
        self.focused_list.selected_index += 1
        self._load_selected_item()

    @listens(u'load_previous')
    def _on_load_previous(self):
        self.focused_list.selected_index -= 1
        self._load_selected_item()

    def _update_load_neighbour_overlay_visibility(self):
        self._load_neighbour_overlay.set_enabled(liveobj_valid(self._browser.hotswap_target) and (self._next_can_be_loaded() or self._previous_can_be_loaded()) and not self.focused_list.selected_item.is_device)

    def _load_selected_item(self):
        focused_list = self.focused_list
        self._update_load_neighbour_overlay_visibility()
        self._update_navigation_buttons()
        item = self._get_actual_item(focused_list.selected_item)
        self._load_item(item)
        self.notify_loaded()

    def _show_load_notification(self, item):
        notification_text = self._make_notification_text(item)
        text_length = len(notification_text)
        notification_time = self.MIN_TIME
        if text_length > self.MIN_TIME_TEXT_LENGTH:
            if text_length > self.MAX_TIME_TEXT_LENGTH:
                notification_time = self.MAX_TIME
            else:
                notification_time = self.MIN_TIME + (self.MAX_TIME - self.MIN_TIME) * old_div(float(text_length - self.MIN_TIME_TEXT_LENGTH), self.MAX_TIME_TEXT_LENGTH - self.MIN_TIME_TEXT_LENGTH)
        self.show_notification(notification_text, notification_time=notification_time)
        self._commit_model_changes()

    def _make_notification_text(self, browser_item):
        return u'Loading %s' % browser_item.name

    def _load_item(self, item):
        self._show_load_notification(item)
        if liveobj_valid(self._browser.hotswap_target):
            if isinstance(item, PluginPresetBrowserItem):
                self._browser.hotswap_target.selected_preset_index = item.preset_index
            else:
                self._browser.load_item(item)
                self._content_hotswap_target = self._browser.hotswap_target
        else:
            with self._insert_right_of_selected():
                self._browser.load_item(item)

    @contextmanager
    def _insert_right_of_selected(self):
        DeviceInsertMode = Live.Track.DeviceInsertMode
        device_to_select = get_selection_for_new_device(self._selection)
        if device_to_select:
            self._selection.selected_object = device_to_select
        selected_track_view = self.song.view.selected_track.view
        selected_track_view.device_insert_mode = DeviceInsertMode.selected_right
        yield
        selected_track_view.device_insert_mode = DeviceInsertMode.default

    def _prehear_selected_item(self):
        if self.prehear_button.is_toggled and not self._updating_root_items:
            self._browser.stop_preview()
            item = self._get_actual_item(self.focused_list.selected_item)
            if item and item.is_loadable and isinstance(item, Live.Browser.BrowserItem):
                self._browser.preview_item(item)

    def _stop_prehear(self):
        if self.prehear_button.is_toggled and not self._updating_root_items:
            self._browser.stop_preview()

    def _update_navigation_buttons(self):
        focused_list = self.focused_list
        self.up_button.enabled = focused_list.selected_index > 0
        self.down_button.enabled = focused_list.selected_index < len(focused_list.items) - 1
        selected_item_loadable = self.focused_list.selected_item.is_loadable
        can_exit = self._focused_list_index > 0
        assume_can_enter = self._preview_list_task.is_running and not selected_item_loadable
        can_enter = self._focused_list_index < len(self._lists) - 1 or assume_can_enter
        self.back_button.enabled = can_exit
        self.open_button.enabled = can_enter
        self.load_button.enabled = selected_item_loadable
        self._load_neighbour_overlay.can_load_previous = self._previous_can_be_loaded()
        self._load_neighbour_overlay.can_load_next = self._next_can_be_loaded()
        context_button_color = IndexedColor.from_live_index(self.context_color_index, DISPLAY_BUTTON_SHADE_LEVEL) if self.context_color_index > -1 else u'Browser.Navigation'
        self.load_button.color = context_button_color
        self.close_button.color = context_button_color
        self._load_neighbour_overlay.load_next_button.color = context_button_color
        self._load_neighbour_overlay.load_previous_button.color = context_button_color
        if not self._expanded:
            self.left_button.enabled = self.back_button.enabled
            self.right_button.enabled = can_enter or self._can_auto_expand()
        else:
            num_columns = int(ceil(old_div(float(len(self.focused_list.items)), self.NUM_ITEMS_PER_COLUMN)))
            last_column_start_index = (num_columns - 1) * self.NUM_ITEMS_PER_COLUMN
            self.left_button.enabled = self._focused_list_index > 0
            self.right_button.enabled = can_enter or self.focused_list.selected_index < last_column_start_index
        self.can_enter = can_enter
        self.can_exit = can_exit

    def _update_scrolling(self):
        self.scrolling = self.up_button.is_pressed or self.down_button.is_pressed or self.scroll_focused_encoder.is_touched or any(map(lambda e: e.is_touched, self.scroll_encoders)) or self.right_button.is_pressed and self._expanded or self.left_button.is_pressed and self._expanded

    def _update_horizontal_navigation(self):
        self.horizontal_navigation = self.right_button.is_pressed or self.left_button.is_pressed

    def _update_context(self):
        selected_track = self.song.view.selected_track
        clip = self.song.view.detail_clip
        if self.browse_for_audio_clip and clip:
            self.context_text = clip.name
        elif liveobj_valid(self._browser.hotswap_target):
            self.context_text = self._browser.hotswap_target.name
        else:
            self.context_text = selected_track.name
        selected_track_color_index = selected_track.color_index
        self.context_color_index = selected_track_color_index if selected_track_color_index is not None else -1

    def _enter_selected_item(self):
        item_entered = False
        self._finish_preview_list_task()
        new_index = self._focused_list_index + 1
        if 0 <= new_index < len(self._lists):
            self._focus_list_with_index(new_index)
            self._unexpand_task.kill()
            self._update_list_offset()
            self._update_auto_expand()
            self._prehear_selected_item()
            item_entered = True
        return item_entered

    def _exit_selected_item(self):
        item_exited = False
        try:
            self._focus_list_with_index(self._focused_list_index - 1)
            self._update_list_offset()
            self._update_auto_expand()
            self._stop_prehear()
            item_exited = True
        except CannotFocusListError:
            pass

        return item_exited

    def _can_auto_expand(self):
        return len(self.focused_list.items) > self.NUM_ITEMS_PER_COLUMN * 2 and self.focused_list.selected_item.is_loadable and getattr(self.focused_list.selected_item, u'contained_item', None) == None

    def _update_auto_expand(self):
        self.expanded = self._can_auto_expand()
        self._update_list_offset()

    def _update_list_offset(self):
        if self.expanded:
            self.list_offset = max(0, self.focused_list_index - 1)
        else:
            offset = len(self._lists)
            if self.focused_list.selected_item.is_loadable:
                offset += 1
            self.list_offset = max(0, offset - self.NUM_VISIBLE_BROWSER_LISTS)

    def _replace_preview_list_by_task(self):
        self._replace_preview_list()
        self._update_navigation_buttons()

    def _finish_preview_list_task(self):
        if self._preview_list_task.is_running:
            self._replace_preview_list_by_task()
            return True
        return False

    def _replace_preview_list(self):
        self._preview_list_task.kill()
        self._crop_browser_lists(self._focused_list_index + 1)
        selected_item = self.focused_list.selected_item
        children_iterator = selected_item.iter_children
        if len(children_iterator) > 0:
            enable_wrapping = getattr(selected_item, u'enable_wrapping', True) and self.focused_list.items_wrapped
            self._append_browser_list(children_iterator=children_iterator, limit=self.num_preview_items, enable_wrapping=enable_wrapping)

    def _append_browser_list(self, children_iterator, limit = -1, enable_wrapping = True):
        l = BrowserList(item_iterator=children_iterator, item_wrapper=self._wrap_item if enable_wrapping else nop, limit=limit)
        l.items_wrapped = enable_wrapping
        self._lists.append(l)
        self.register_disconnectable(l)
        self.notify_lists()

    def _crop_browser_lists(self, length):
        num_items_to_crop = len(self._lists) - length
        for _ in range(num_items_to_crop):
            l = self._lists.pop()
            self.unregister_disconnectable(l)

        if num_items_to_crop > 0:
            self.notify_lists()

    def _make_root_browser_items(self):
        filter_type = self._browser.filter_type
        hotswap_target = self._browser.hotswap_target
        if liveobj_valid(hotswap_target):
            filter_type = filter_type_for_hotswap_target(hotswap_target, default=filter_type)
        return make_root_browser_items(self._browser, filter_type)

    def _content_cache_is_valid(self):
        return self._content_filter_type == self._browser.filter_type and not liveobj_changed(self._content_hotswap_target, self._browser.hotswap_target)

    def _invalidate_content_cache(self):
        self._content_hotswap_target = None
        self._content_filter_type = None

    def _update_content_cache(self):
        self._content_filter_type = self._browser.filter_type
        self._content_hotswap_target = self._browser.hotswap_target

    def _update_root_items(self):
        if not self._content_cache_is_valid():
            self._update_content_cache()
            with self._updating_root_items():
                self._on_focused_selection_changed.subject = None
                self._crop_browser_lists(0)
                self._append_browser_list(children_iterator=self._make_root_browser_items())
                self._focused_list_index = 0
                self.focused_list.selected_index = 0
                self._select_hotswap_target()
                self._on_focused_selection_changed.subject = self.focused_list
                self._on_focused_selection_changed()

    def _select_hotswap_target(self, list_index = 0):
        if list_index < len(self._lists):
            l = self._lists[list_index]
            l.access_all = True
            children = l.items
            i = index_if(lambda i: i.is_selected, children)
            if i < len(children):
                self._focused_list_index = list_index
                l.selected_index = i
                self._replace_preview_list()
                self._select_hotswap_target(list_index + 1)

    @property
    def num_preview_items(self):
        if self._expanded:
            return self.NUM_ITEMS_PER_COLUMN * self.NUM_COLUMNS_IN_EXPANDED_LIST
        return 6

    def update(self):
        super(BrowserComponent, self).update()
        self._invalidate_content_cache()
        if self.is_enabled():
            self._update_root_items()
            self._update_context()
            self._update_list_offset()
            self._update_load_neighbour_overlay_visibility()
            self._update_navigation_buttons()
            self.expanded = False
            self._update_list_offset()
        else:
            self._stop_prehear()
            self.list_offset = 0

    def _wrap_item(self, item):
        if item.is_device:
            return self._wrap_device_item(item)
        if self._is_hotswap_target_plugin(item):
            return self._wrap_hotswapped_plugin_item(item)
        return item

    def _wrap_device_item(self, item):
        u"""
        Create virtual folder around items that can be loaded AND have children, to avoid
        having two actions on an item (open and load).
        """
        wrapped_loadable = WrappedLoadableBrowserItem(name=item.name, is_loadable=True, contained_item=item)
        return FolderBrowserItem(name=item.name, is_loadable=True, is_device=True, contained_item=item, wrapped_loadable=wrapped_loadable, icon=u'browser_arrowcontent.svg')

    def _is_hotswap_target_plugin(self, item):
        return isinstance(self._browser.hotswap_target, Live.PluginDevice.PluginDevice) and isinstance(item, Live.Browser.BrowserItem) and self._browser.relation_to_hotswap_target(item) == Live.Browser.Relation.equal

    def _wrap_hotswapped_plugin_item(self, item):
        return PluginBrowserItem(name=item.name, vst_device=self._browser.hotswap_target)
コード例 #18
0
class SimpleDeviceParameterComponent(Component):
    bank_select_buttons = FixedRadioButtonGroup(control_count=8,
      unchecked_color='Mode.Device.Bank.Available',
      checked_color='Mode.Device.Bank.Selected')
    device_lock_button = ToggleButtonControl()

    @depends(device_provider=None)
    def __init__(self, device_provider=None, device_bank_registry=None, toggle_lock=None, use_parameter_banks=False, *a, **k):
        (super(SimpleDeviceParameterComponent, self).__init__)(*a, **k)
        self._toggle_lock = toggle_lock
        self._use_parameter_banks = use_parameter_banks
        self._device = None
        self._banks = []
        self._bank_index = 0
        self._parameter_controls = None
        self._empty_control_slots = self.register_disconnectable(EventObject())
        self._device_bank_registry = device_bank_registry
        self._device_provider = device_provider
        self._SimpleDeviceParameterComponent__on_provided_device_changed.subject = device_provider
        self._SimpleDeviceParameterComponent__on_provided_device_changed()
        if toggle_lock:
            self._SimpleDeviceParameterComponent__on_is_locked_to_device_changed.subject = self._device_provider
            self._SimpleDeviceParameterComponent__on_is_locked_to_device_changed()

    @bank_select_buttons.checked
    def bank_select_buttons(self, button):
        self._on_bank_select_button_checked(button)

    def _on_bank_select_button_checked(self, button):
        self.bank_index = button.index

    @bank_select_buttons.value
    def bank_select_buttons(self, value, _):
        if not value:
            self._on_bank_select_button_released()

    def _on_bank_select_button_released(self):
        pass

    @device_lock_button.toggled
    def device_lock_button(self, *_):
        self._on_device_lock_button_toggled()

    def _on_device_lock_button_toggled(self):
        self._toggle_lock()
        self._update_device_lock_button()

    @property
    def bank_index(self):
        if self._use_parameter_banks:
            return self._bank_index
        return 0

    @bank_index.setter
    def bank_index(self, value):
        self._bank_index = self._clamp_to_bank_size(value)
        if self._device_bank_registry:
            self._device_bank_registry.set_device_bank(self._device, self._bank_index)
        self.update()

    def _clamp_to_bank_size(self, value):
        return clamp(value, 0, self.num_banks - 1)

    @property
    def selected_bank(self):
        if self.num_banks:
            return self._banks[(self._bank_index or 0)]
        return []

    @property
    def num_banks(self):
        return len(self._banks)

    def set_parameter_controls(self, controls):
        for control in self._parameter_controls or []:
            release_control(control)

        self._parameter_controls = controls
        self.update()

    @listens('device')
    def __on_provided_device_changed(self):
        for control in self._parameter_controls or []:
            release_control(control)

        self._device = self._device_provider.device
        self._SimpleDeviceParameterComponent__on_bank_changed.subject = self._device_bank_registry
        if self._device_bank_registry:
            self._bank_index = self._device_bank_registry.get_device_bank(self._device)
            self.update()
        else:
            self.bank_index = 0

    @listens('device_bank')
    def __on_bank_changed(self, device, bank):
        if device == self._device:
            self.bank_index = bank

    @listens('is_locked_to_device')
    def __on_is_locked_to_device_changed(self):
        self._update_device_lock_button()

    def update(self):
        super(SimpleDeviceParameterComponent, self).update()
        if self.is_enabled():
            self._update_parameter_banks()
            self._update_bank_select_buttons()
            self._empty_control_slots.disconnect()
            if liveobj_valid(self._device):
                self._connect_parameters()
            else:
                self._disconnect_parameters()
        else:
            self._disconnect_parameters()

    def _disconnect_parameters(self):
        for control in self._parameter_controls or []:
            release_control(control)
            self._empty_control_slots.register_slot(control, nop, 'value')

    def _connect_parameters(self):
        for control, parameter in zip_longest(self._parameter_controls or [], self.selected_bank):
            if liveobj_valid(control):
                if liveobj_valid(parameter):
                    control.connect_to(parameter)
                else:
                    control.release_parameter()
                    self._empty_control_slots.register_slot(control, nop, 'value')

    def _update_parameter_banks(self):
        if liveobj_valid(self._device):
            if self._use_parameter_banks:
                self._banks = parameter_banks(self._device)
            else:
                self._banks = [
                 best_of_parameter_bank(self._device)]
        else:
            self._banks = []
        self._bank_index = self._clamp_to_bank_size(self._bank_index)

    def _update_bank_select_buttons(self):
        self.bank_select_buttons.active_control_count = self.num_banks
        if self.bank_index < self.num_banks:
            self.bank_select_buttons[self.bank_index].is_checked = True

    def _update_device_lock_button(self):
        self.device_lock_button.is_toggled = self._device_provider.is_locked_to_device
コード例 #19
0
class ScalesComponent(Component):
    __events__ = ('close',)
    navigation_colors = dict(color='Scales.Navigation', disabled_color='Scales.NavigationDisabled')
    up_button = ButtonControl(repeat=True, **navigation_colors)
    down_button = ButtonControl(repeat=True, **navigation_colors)
    right_button = ButtonControl(repeat=True, **navigation_colors)
    left_button = ButtonControl(repeat=True, **navigation_colors)
    root_note_buttons = control_list(RadioButtonControl, control_count=len(ROOT_NOTES), checked_color='Scales.OptionOn', unchecked_color='Scales.OptionOff')
    in_key_toggle_button = ToggleButtonControl(toggled_color='Scales.OptionOn', untoggled_color='Scales.OptionOn')
    fixed_toggle_button = ToggleButtonControl(toggled_color='Scales.OptionOn', untoggled_color='Scales.OptionOff')
    scale_encoders = control_list(StepEncoderControl)
    close_button = ButtonControl(color='Scales.Close')
    horizontal_navigation = listenable_property.managed(False)
    NUM_DISPLAY_ROWS = 4
    NUM_DISPLAY_COLUMNS = int(ceil(float(len(SCALES)) / NUM_DISPLAY_ROWS))

    def __init__(self, note_layout = None, *a, **k):
        raise note_layout is not None or AssertionError
        super(ScalesComponent, self).__init__(*a, **k)
        self._note_layout = note_layout
        self._scale_list = list(SCALES)
        self._scale_name_list = map(lambda m: m.name, self._scale_list)
        self._selected_scale_index = -1
        self._selected_root_note_index = -1
        self.in_key_toggle_button.connect_property(note_layout, 'is_in_key')
        self.fixed_toggle_button.connect_property(note_layout, 'is_fixed')
        self.__on_root_note_changed.subject = self._note_layout
        self.__on_scale_changed.subject = self._note_layout
        self.__on_root_note_changed(note_layout.root_note)
        self.__on_scale_changed(note_layout.scale)

    def _set_selected_scale_index(self, index):
        index = clamp(index, 0, len(self._scale_list) - 1)
        self._note_layout.scale = self._scale_list[index]

    @down_button.pressed
    def down_button(self, button):
        self._update_horizontal_navigation()
        self._set_selected_scale_index(self._selected_scale_index + 1)

    @up_button.pressed
    def up_button(self, button):
        self._update_horizontal_navigation()
        self._set_selected_scale_index(self._selected_scale_index - 1)

    @left_button.pressed
    def left_button(self, button):
        self._update_horizontal_navigation()
        self._set_selected_scale_index(self._selected_scale_index - self.NUM_DISPLAY_ROWS)

    @right_button.pressed
    def right_button(self, button):
        self._update_horizontal_navigation()
        self._set_selected_scale_index(self._selected_scale_index + self.NUM_DISPLAY_ROWS)

    @root_note_buttons.pressed
    def root_note_buttons(self, button):
        self._note_layout.root_note = ROOT_NOTES[button.index]

    @listens('root_note')
    def __on_root_note_changed(self, root_note):
        self._selected_root_note_index = list(ROOT_NOTES).index(root_note)
        self.root_note_buttons.checked_index = self._selected_root_note_index
        self.notify_selected_root_note_index()

    @property
    def root_note_names(self):
        return [ NOTE_NAMES[note] for note in ROOT_NOTES ]

    @listenable_property
    def selected_root_note_index(self):
        return self._selected_root_note_index

    @scale_encoders.value
    def scale_encoders(self, value, encoder):
        self._update_horizontal_navigation()
        self._set_selected_scale_index(self._selected_scale_index + value)

    @property
    def scale_names(self):
        return self._scale_name_list

    @listenable_property
    def selected_scale_index(self):
        return self._selected_scale_index

    @listens('scale')
    def __on_scale_changed(self, scale):
        index = self._scale_list.index(scale) if scale in self._scale_list else -1
        if index != self._selected_scale_index:
            self._selected_scale_index = index
            self.up_button.enabled = index > 0
            self.left_button.enabled = index > 0
            self.down_button.enabled = index < len(self._scale_list) - 1
            self.right_button.enabled = index < len(self._scale_list) - 1
            self.notify_selected_scale_index()

    @close_button.pressed
    def close_button(self, button):
        self.notify_close()

    @property
    def note_layout(self):
        return self._note_layout

    def _update_horizontal_navigation(self):
        self.horizontal_navigation = self.right_button.is_pressed or self.left_button.is_pressed
コード例 #20
0
class SimpleDeviceParameterComponent(Component):
    prev_bank_button = ButtonControl()
    next_bank_button = ButtonControl()
    device_lock_button = ToggleButtonControl()
    device_on_off_button = ToggleButtonControl()
    device_name_display = DisplayControl()

    @depends(device_provider=None)
    def __init__(self,
                 device_provider=None,
                 device_bank_registry=None,
                 toggle_lock=None,
                 *a,
                 **k):
        super(SimpleDeviceParameterComponent, self).__init__(*a, **k)
        self._toggle_lock = toggle_lock
        self._device = None
        self._banks = []
        self._bank_index = 0
        self._parameter_controls = None
        self._empty_control_slots = self.register_disconnectable(EventObject())
        self._device_bank_registry = device_bank_registry
        self._device_provider = device_provider
        self._device_name_slot = self.register_slot(
            None, self._update_device_name_display, u'name')
        self.__on_provided_device_changed.subject = device_provider
        self.__on_provided_device_changed()
        self._display_bank_name_task = self._tasks.add(
            task.sequence(task.run(self._display_bank_name),
                          task.wait(BANK_NAME_DISPLAY_DURATION),
                          task.run(self._update_device_name_display)))
        self._display_bank_name_task.kill()
        if toggle_lock:
            self.__on_is_locked_to_device_changed.subject = self._device_provider
            self.__on_is_locked_to_device_changed()

    @device_lock_button.toggled
    def device_lock_button(self, *_):
        self._on_device_lock_button_toggled()

    @device_on_off_button.toggled
    def device_on_off_button(self, is_toggled, _):
        parameter = self._on_off_parameter()
        if parameter is not None:
            parameter.value = float(is_toggled)

    def _on_device_lock_button_toggled(self):
        self._toggle_lock()
        self._update_device_lock_button()

    @prev_bank_button.pressed
    def prev_bank_button(self, _):
        self.bank_index = self._bank_index - 1

    @next_bank_button.pressed
    def next_bank_button(self, _):
        self.bank_index = self._bank_index + 1

    @property
    def bank_index(self):
        return self._bank_index

    @bank_index.setter
    def bank_index(self, value):
        prev_bank_index = self._bank_index
        self._bank_index = self._clamp_to_bank_size(value)
        if prev_bank_index != self._bank_index:
            self._display_bank_name_task.restart()
        if self._device_bank_registry:
            self._device_bank_registry.set_device_bank(self._device,
                                                       self._bank_index)
        self.update()

    def _clamp_to_bank_size(self, value):
        return clamp(value, 0, self.num_banks - 1)

    @property
    def selected_bank(self):
        if self.num_banks:
            return self._banks[self._bank_index or 0]
        return []

    @property
    def num_banks(self):
        return len(self._banks)

    def set_parameter_controls(self, controls):
        for control in self._parameter_controls or []:
            release_control(control)

        self._parameter_controls = controls
        self.update()

    @listens(u'device')
    def __on_provided_device_changed(self):
        for control in self._parameter_controls or []:
            release_control(control)

        self._device = self._device_provider.device
        self.__on_bank_changed.subject = self._device_bank_registry
        if self._device_bank_registry:
            self._bank_index = self._device_bank_registry.get_device_bank(
                self._device)
            self.update()
        else:
            self.bank_index = 0
        self._device_name_slot.subject = self._device
        self._update_device_name_display()

    @listens(u'device_bank')
    def __on_bank_changed(self, device, bank):
        if device == self._device:
            self.bank_index = bank

    @listens(u'is_locked_to_device')
    def __on_is_locked_to_device_changed(self):
        self._update_device_lock_button()

    @listens(u'value')
    def __on_device_on_off_changed(self):
        self._update_device_on_off_button()

    def update(self):
        super(SimpleDeviceParameterComponent, self).update()
        if self.is_enabled():
            self._update_parameter_banks()
            self._update_bank_navigation_buttons()
            self._empty_control_slots.disconnect()
            self.__on_device_on_off_changed.subject = self._on_off_parameter()
            self._update_device_on_off_button()
            if liveobj_valid(self._device):
                self._connect_parameters()
            else:
                self._disconnect_parameters()
        else:
            self._disconnect_parameters()

    def _disconnect_parameters(self):
        for control in self._parameter_controls or []:
            release_control(control)
            self._empty_control_slots.register_slot(control, nop, u'value')

    def _connect_parameters(self):
        for control, parameter in izip_longest(self._parameter_controls or [],
                                               self.selected_bank):
            if liveobj_valid(control):
                if liveobj_valid(parameter):
                    control.connect_to(parameter)
                else:
                    control.release_parameter()
                    self._empty_control_slots.register_slot(
                        control, nop, u'value')

    def _on_off_parameter(self):
        if liveobj_valid(self._device):
            for p in self._device.parameters:
                if p.name.startswith(u'Device On') and liveobj_valid(
                        p) and p.is_enabled:
                    return p

    def _update_parameter_banks(self):
        if liveobj_valid(self._device):
            self._banks = parameter_banks(self._device)
        else:
            self._banks = []
        self._bank_index = self._clamp_to_bank_size(self._bank_index)

    def _update_bank_navigation_buttons(self):
        self.prev_bank_button.enabled = self.bank_index > 0
        self.next_bank_button.enabled = self.bank_index < self.num_banks - 1

    def _update_device_lock_button(self):
        self.device_lock_button.is_toggled = self._device_provider.is_locked_to_device

    def _update_device_on_off_button(self):
        parameter = self._on_off_parameter()
        self.device_on_off_button.enabled = parameter is not None
        if parameter is not None:
            self.device_on_off_button.is_toggled = parameter.value > 0

    def _update_device_name_display(self):
        self.device_name_display.message = self._device.name if liveobj_valid(
            self._device) else u' - '

    def _display_bank_name(self):
        names = parameter_bank_names(self._device)
        if self.bank_index < len(names):
            self.device_name_display.message = names[self.bank_index]
コード例 #21
0
ファイル: actions.py プロジェクト: lzref/ComradeEncoders
class ActionsComponent(Component):
    actions_display = TextDisplayControl(segments=ACTION_NAMES)
    actions_color_fields = control_list(ColorSysexControl, len(ACTION_NAMES))
    actions_selection_fields = control_list(BinaryControl, len(ACTION_NAMES))
    undo_button = ButtonControl(color='Action.Available')
    redo_button = ButtonControl(color='Action.Available')
    capture_midi_button = ButtonControl()
    metronome_button = ToggleButtonControl(
        toggled_color='Transport.MetronomeOn',
        untoggled_color='Transport.MetronomeOff')

    def __init__(self, *a, **k):
        super(ActionsComponent, self).__init__(*a, **k)
        self.__on_can_capture_midi_changed.subject = self.song
        self.__on_can_capture_midi_changed()
        self.actions_color_fields[
            METRONOME_DISPLAY_INDEX].color = 'Transport.MetronomeOn'
        self.actions_color_fields[
            UNDO_DISPLAY_INDEX].color = 'Action.Available'
        self.actions_color_fields[
            REDO_DISPLAY_INDEX].color = 'Action.Available'
        self.__on_metronome_changed.subject = self.song
        self.__on_metronome_changed()

    @property
    def capture_midi_display(self):
        return self.actions_display[CAPTURE_DISPLAY_INDEX]

    @capture_midi_display.setter
    def capture_midi_display(self, string):
        self.actions_display[CAPTURE_DISPLAY_INDEX] = string

    @property
    def capture_midi_color_field(self):
        return self.actions_color_fields[CAPTURE_DISPLAY_INDEX]

    @property
    def capture_midi_selection_field(self):
        return self.actions_selection_fields[CAPTURE_DISPLAY_INDEX]

    @undo_button.pressed
    def undo_button(self, _):
        if self.song.can_undo:
            self.song.undo()

    @redo_button.pressed
    def redo_button(self, _):
        if self.song.can_redo:
            self.song.redo()

    @capture_midi_button.pressed
    def capture_midi_button(self, _):
        try:
            self.song.capture_midi()
        except RuntimeError:
            pass

    @metronome_button.toggled
    def metronome_button(self, toggled, _):
        self.song.metronome = toggled

    @listens('can_capture_midi')
    def __on_can_capture_midi_changed(self):
        self._update_capture_midi_controls()

    @listens('metronome')
    def __on_metronome_changed(self):
        self._update_metronome_controls()

    def _update_capture_midi_controls(self):
        can_capture_midi = self.song.can_capture_midi
        self.capture_midi_button.enabled = can_capture_midi
        self.capture_midi_display = 'capture' if can_capture_midi else ''
        self.capture_midi_color_field.color = 'DefaultButton.On' if can_capture_midi else 'DefaultButton.Disabled'
        self.capture_midi_selection_field.is_on = can_capture_midi

    def _update_metronome_controls(self):
        metronome = self.song.metronome
        self.metronome_button.is_toggled = metronome
        self.actions_selection_fields[
            METRONOME_DISPLAY_INDEX].is_on = metronome
コード例 #22
0
ファイル: channelized_settings.py プロジェクト: aumhaa/m4m8
class ScrollingChannelizedSettingsComponent(ChannelizedSettingsBase):


	up_button = ButtonControl(repeat=True)
	down_button = ButtonControl(repeat=True)
	bank_up_button = ButtonControl(repeat=True)
	bank_down_button = ButtonControl(repeat=True)
	shift_toggle = ToggleButtonControl()

	def __init__(self, bank_increment = 16, on_color = 'DefaultButton.On', off_color = 'DefaultButton.Off', bank_on_color = 'DefaultButton.On', bank_off_color = 'DefaultButton.Off', *a, **k):
		super(ScrollingChannelizedSettingsComponent, self).__init__(*a, **k)
		self._bank_increment = bank_increment
		self.up_button.color = on_color
		self.up_button.disabled_color = off_color
		self.down_button.color = on_color
		self.down_button.disabled_color = off_color
		self.shift_toggle.toggled_color = on_color
		self.shift_toggle.untoggled_color = off_color
	

	@up_button.pressed
	def up_button(self, button):
		if self.shift_toggle.is_toggled:
			value = self._values[self._channel]
			self.index = clamp(value+self._bank_increment, 0, (self._range-1))
		else:
			value = self._values[self._channel]
			self.index = clamp(value+1, 0, (self._range-1))
	

	@down_button.pressed
	def down_button(self, button):
		if self.shift_toggle.is_toggled:
			value = self._values[self._channel]
			self.index = clamp(value-self._bank_increment, 0, (self._range-1))
		else:
			value = self._values[self._channel]
			self.index = clamp(value-1, 0, (self._range-1))
	

	@bank_up_button.pressed
	def bank_up_button(self, button):
		value = self._values[self._channel]
		self.index = clamp(value+self._bank_increment, 0, (self._range-1))
	

	@bank_down_button.pressed
	def bank_down_button(self, button):
		value = self._values[self._channel]
		self.index = clamp(value-self._bank_increment, 0, (self._range-1))
	

	def _update_controls(self):
		at_beginning = self.index == 0
		at_end = self.index == (self._range - 1)
		self.up_button.enabled = not at_end
		self.down_button.enabled = not at_beginning
		self.bank_up_button.enabled = not at_end
		self.bank_down_button.enabled = not at_beginning
	

	def buttons_are_pressed(self):
		return self.up_button.is_pressed or self.down_button.is_pressed or self.bank_up_button.is_pressed or self.bank_down_button.is_pressed
コード例 #23
0
class QuantizationSettingsComponent(Component):
    swing_amount_encoder = EncoderControl()
    quantize_to_encoder = StepEncoderControl()
    quantize_amount_encoder = EncoderControl()
    record_quantization_encoder = StepEncoderControl()
    record_quantization_toggle_button = ToggleButtonControl(
        toggled_color='Recording.FixedLengthRecordingOn',
        untoggled_color='Recording.FixedLengthRecordingOff')
    quantize_amount = listenable_property.managed(1.0)
    quantize_to_index = listenable_property.managed(DEFAULT_QUANTIZATION_INDEX)
    record_quantization_index = listenable_property.managed(
        DEFAULT_QUANTIZATION_INDEX)

    def __init__(self, *a, **k):
        super(QuantizationSettingsComponent, self).__init__(*a, **k)
        self.__on_swing_amount_changed.subject = self.song
        self.__on_record_quantization_changed.subject = self.song
        self.__on_record_quantization_changed()

    @property
    def quantize_to(self):
        return QUANTIZATION_OPTIONS[self.quantize_to_index]

    @listenable_property
    def swing_amount(self):
        return self.song.swing_amount

    @listenable_property
    def record_quantization_enabled(self):
        return self.record_quantization_toggle_button.is_toggled

    @property
    def quantization_option_names(self):
        return QUANTIZATION_NAMES

    @swing_amount_encoder.value
    def swing_amount_encoder(self, value, encoder):
        self.song.swing_amount = clamp(self.song.swing_amount + value * 0.5,
                                       0.0, 0.5)

    @staticmethod
    def _clamp_quantization_index(index):
        return clamp(index, 0, len(QUANTIZATION_OPTIONS) - 1)

    @quantize_to_encoder.value
    def quantize_to_encoder(self, value, encoder):
        self.quantize_to_index = self._clamp_quantization_index(
            self.quantize_to_index + value)

    @quantize_amount_encoder.value
    def quantize_amount_encoder(self, value, encoder):
        self.quantize_amount = clamp(self.quantize_amount + value, 0.0, 1.0)

    @record_quantization_encoder.value
    def record_quantization_encoder(self, value, encoder):
        self.record_quantization_index = self._clamp_quantization_index(
            self.record_quantization_index + value)
        self._update_record_quantization()

    @record_quantization_toggle_button.toggled
    def record_quantization_toggle_button(self, value, button):
        self._update_record_quantization()

    @listens('swing_amount')
    def __on_swing_amount_changed(self):
        self.notify_swing_amount()

    @listens('midi_recording_quantization')
    def __on_record_quantization_changed(self):
        quant_value = self.song.midi_recording_quantization
        quant_on = quant_value != RecordingQuantization.rec_q_no_q
        if quant_value in QUANTIZATION_OPTIONS:
            self.record_quantization_index = QUANTIZATION_OPTIONS.index(
                quant_value)
        self.record_quantization_toggle_button.is_toggled = quant_on
        self.notify_record_quantization_enabled(quant_on)

    def _update_record_quantization(self):
        index = QUANTIZATION_OPTIONS[self.record_quantization_index]
        self.song.midi_recording_quantization = index if self.record_quantization_toggle_button.is_toggled else RecordingQuantization.rec_q_no_q
コード例 #24
0
class InstrumentComponent(PlayableComponent, CompoundComponent, Slideable,
                          Messenger):
    """
    Class that sets up the button matrix as a piano, using different
    selectable layouts for the notes.
    """
    __events__ = ('pattern', )
    touch_strip_toggle = ToggleButtonControl()
    matrix = control_matrix(PadControl, pressed_color='Instrument.NoteAction')

    def __init__(self, note_layout=None, *a, **k):
        raise note_layout is not None or AssertionError
        super(InstrumentComponent, self).__init__(*a, **k)
        self._note_layout = note_layout
        self._delete_button = None
        self._first_note = self.page_length * 3 + self.page_offset
        self._last_page_length = self.page_length
        self._delete_button = None
        self._last_page_offset = self.page_offset
        self._touch_strip = None
        self._touch_strip_indication = None
        self._detail_clip = None
        self._has_notes = [False] * 128
        self._has_notes_pattern = self._get_pattern(0)
        self._aftertouch_control = None
        self._slider = self.register_component(SlideComponent(self))
        self._touch_slider = self.register_component(
            SlideableTouchStripComponent(self))
        for event in ('scale', 'root_note', 'is_in_key', 'is_fixed',
                      'is_horizontal', 'interval'):
            self.register_slot(self._note_layout, self._on_note_layout_changed,
                               event)

        self._update_pattern()

    def set_detail_clip(self, clip):
        if clip != self._detail_clip:
            self._detail_clip = clip
            self._on_clip_notes_changed.subject = clip
            self._on_loop_start_changed.subject = clip
            self._on_loop_end_changed.subject = clip
            self._on_clip_notes_changed()

    @listens('notes')
    def _on_clip_notes_changed(self):
        if self._detail_clip:
            self._has_notes = [False] * 128
            loop_start = self._detail_clip.loop_start
            loop_length = self._detail_clip.loop_end - loop_start
            notes = self._detail_clip.get_notes(loop_start, 0, loop_length,
                                                128)
            for note in notes:
                self._has_notes[note[0]] = True

        self.notify_contents()

    @listens('loop_start')
    def _on_loop_start_changed(self):
        self._on_loop_selection_changed()

    @listens('loop_end')
    def _on_loop_end_changed(self):
        self._on_loop_selection_changed()

    def _on_loop_selection_changed(self):
        self._on_clip_notes_changed()

    def contents(self, index):
        if self._detail_clip:
            note = self._has_notes_pattern[index].index
            return self._has_notes[note] if note is not None else False
        return False

    @property
    def page_length(self):
        return len(
            self._note_layout.notes) if self._note_layout.is_in_key else 12

    @property
    def position_count(self):
        if not self._note_layout.is_in_key:
            return 139
        else:
            offset = self.page_offset
            octaves = 11 if self._note_layout.notes[0] < 8 else 10
            return offset + len(self._note_layout.notes) * octaves

    def _first_scale_note_offset(self):
        if not self._note_layout.is_in_key:
            return self._note_layout.notes[0]
        elif self._note_layout.notes[0] == 0:
            return 0
        else:
            return len(self._note_layout.notes) - index_if(
                lambda n: n >= 12, self._note_layout.notes)

    @property
    def page_offset(self):
        return 0 if self._note_layout.is_fixed else self._first_scale_note_offset(
        )

    def _get_position(self):
        return self._first_note

    def _set_position(self, note):
        self._first_note = note
        self._update_pattern()
        self._update_matrix()
        self.notify_position()

    position = property(_get_position, _set_position)

    @property
    def pattern(self):
        return self._pattern

    @matrix.pressed
    def matrix(self, button):
        self._on_matrix_pressed(button)

    def _on_matrix_pressed(self, button):
        if self._delete_button and self._delete_button.is_pressed():
            pitch = self._get_note_info_for_coordinate(button.coordinate).index
            if pitch and self._detail_clip:
                self._do_delete_pitch(pitch)

    def _do_delete_pitch(self, pitch):
        clip = self._detail_clip
        if clip:
            note_name = pitch_index_to_string(pitch)
            loop_length = clip.loop_end - clip.loop_start
            clip.remove_notes(clip.loop_start, pitch, loop_length, 1)
            self.show_notification(consts.MessageBoxText.DELETE_NOTES %
                                   note_name)

    @listens('value')
    def _on_delete_value(self, value):
        self._set_control_pads_from_script(bool(value))

    @touch_strip_toggle.toggled
    def touch_strip_toggle(self, toggled, button):
        self._update_touch_strip()
        self._update_touch_strip_indication()
        self.show_notification(
            consts.MessageBoxText.TOUCHSTRIP_MODWHEEL_MODE
            if toggled else consts.MessageBoxText.TOUCHSTRIP_PITCHBEND_MODE)

    def set_touch_strip(self, control):
        self._touch_strip = control
        self._update_touch_strip()

    def _update_touch_strip(self):
        if self._touch_strip:
            self._touch_strip.behaviour = MODWHEEL_BEHAVIOUR if self.touch_strip_toggle.is_toggled else DEFAULT_BEHAVIOUR

    def set_touch_strip_indication(self, control):
        self._touch_strip_indication = control
        self._update_touch_strip_indication()

    def _update_touch_strip_indication(self):
        if self._touch_strip_indication:
            self._touch_strip_indication.set_mode(TouchStripModes.CUSTOM_FREE)
            self._touch_strip_indication.send_state([
                (TouchStripStates.STATE_FULL
                 if self.touch_strip_toggle.is_toggled else
                 TouchStripStates.STATE_HALF)
                for _ in xrange(self._touch_strip_indication.state_count)
            ])

    def set_note_strip(self, strip):
        self._touch_slider.set_scroll_strip(strip)

    def set_octave_strip(self, strip):
        self._touch_slider.set_page_strip(strip)

    def set_octave_up_button(self, button):
        self._slider.set_scroll_page_up_button(button)

    def set_octave_down_button(self, button):
        self._slider.set_scroll_page_down_button(button)

    def set_scale_up_button(self, button):
        self._slider.set_scroll_up_button(button)

    def set_scale_down_button(self, button):
        self._slider.set_scroll_down_button(button)

    def set_aftertouch_control(self, control):
        self._aftertouch_control = control
        self._update_aftertouch()

    def set_delete_button(self, button):
        self._delete_button = button
        self._on_delete_value.subject = button
        self._set_control_pads_from_script(button and button.is_pressed())

    def _align_first_note(self):
        self._first_note = self.page_offset + (
            self._first_note - self._last_page_offset) * float(
                self.page_length) / float(self._last_page_length)
        if self._first_note >= self.position_count:
            self._first_note -= self.page_length
        self._last_page_length = self.page_length
        self._last_page_offset = self.page_offset

    def _on_note_layout_changed(self, _):
        self._update_scale()

    def _update_scale(self):
        self._align_first_note()
        self._update_pattern()
        self._update_matrix()
        self.notify_position_count()
        self.notify_position()
        self.notify_contents()

    def update(self):
        super(InstrumentComponent, self).update()
        if self.is_enabled():
            self._update_matrix()
            self._update_aftertouch()
            self._update_touch_strip()
            self._update_touch_strip_indication()

    def _update_pattern(self):
        self._pattern = self._get_pattern()
        self._has_notes_pattern = self._get_pattern(0)
        self.notify_pattern()

    def _invert_and_swap_coordinates(self, coordinates):
        return (coordinates[1], self.width - 1 - coordinates[0])

    def _get_note_info_for_coordinate(self, coordinate):
        x, y = self._invert_and_swap_coordinates(coordinate)
        return self.pattern.note(x, y)

    def _update_button_color(self, button):
        note_info = self._get_note_info_for_coordinate(button.coordinate)
        button.color = 'Instrument.' + note_info.color

    def _button_should_be_enabled(self, button):
        return self._get_note_info_for_coordinate(
            button.coordinate).index != None

    def _note_translation_for_button(self, button):
        note_info = self._get_note_info_for_coordinate(button.coordinate)
        return (note_info.index, note_info.channel)

    def _set_button_control_properties(self, button):
        super(InstrumentComponent, self)._set_button_control_properties(button)
        button.sensitivity_profile = 'default' if self._takeover_pads else 'instrument'

    def _update_matrix(self):
        self._update_control_from_script()
        self._update_led_feedback()
        self._update_note_translations()

    def _get_pattern(self, first_note=None):
        if first_note is None:
            first_note = int(round(self._first_note))
        interval = self._note_layout.interval
        notes = self._note_layout.notes
        octave = first_note / self.page_length
        offset = first_note % self.page_length - self._first_scale_note_offset(
        )
        if interval == None:
            interval = 8
        elif not self._note_layout.is_in_key:
            interval = [0, 2, 4, 5, 7, 9, 10, 11][interval]
        if self._note_layout.is_horizontal:
            steps = [1, interval]
            origin = [offset, 0]
        else:
            steps = [interval, 1]
            origin = [0, offset]
        return MelodicPattern(steps=steps,
                              scale=notes,
                              origin=origin,
                              root_note=octave * 12,
                              chromatic_mode=not self._note_layout.is_in_key)

    def _update_aftertouch(self):
        if self.is_enabled() and self._aftertouch_control != None:
            self._aftertouch_control.send_value('mono')
コード例 #25
0
class BrowserComponent(Component):
    u"""
    Component for controlling the Live library browser.  It has 4
    browsing columns that are controlled by encoders and state
    buttons.  The contents of these lists are provided by a browser
    model -- see BrowserModel and derivatives.
    """
    __events__ = (u'load_item',)
    NUM_COLUMNS = 4
    COLUMN_SIZE = 4
    enter_button = ButtonControl(**consts.SIDE_BUTTON_COLORS)
    exit_button = ButtonControl(**consts.SIDE_BUTTON_COLORS)
    shift_button = ButtonControl()
    prehear_button = ToggleButtonControl(toggled_color=u'Browser.Prehear', untoggled_color=u'Browser.PrehearOff')

    def __init__(self, browser = None, make_browser_model = None, preferences = dict(), *a, **k):
        super(BrowserComponent, self).__init__(*a, **k)
        assert make_browser_model is not None
        self._browser = browser or self.application.browser
        self._browser_model = EmptyBrowserModel(browser=self._browser)
        self._make_browser_model = make_browser_model
        num_data_sources = self.NUM_COLUMNS * self.COLUMN_SIZE
        self._data_sources = map(DisplayDataSource, (u'',) * num_data_sources)
        self._last_loaded_item = None
        self._default_item_formatter = DefaultItemFormatter()
        self._list_components = [ ListComponent(parent=self) for _ in xrange(self.NUM_COLUMNS) ]
        for i, component in enumerate(self._list_components):
            component.do_trigger_action = lambda item: self._do_load_item(item)
            component.last_action_item = lambda : self._last_loaded_item
            component.item_formatter = partial(self._item_formatter, i)

        self._preferences = preferences
        self._select_buttons = []
        self._state_buttons = []
        self._encoder_controls = []
        self._on_selected_item.replace_subjects(self._list_components)
        self._on_list_item_action.replace_subjects(self._list_components)
        self._on_hotswap_target_changed.subject = self._browser
        self._on_filter_type_changed.subject = self._browser
        self._on_browser_full_refresh.subject = self._browser
        self._scroll_offset = 0
        self._max_scroll_offset = 0
        self._max_hierarchy = 0
        self._last_filter_type = None
        self._skip_next_preselection = False
        self._browser_model_dirty = True
        self._on_content_lists_changed()
        self.prehear_button.is_toggled = preferences.setdefault(u'browser_prehear', True)
        self._last_selected_item = None

    def disconnect(self):
        self._last_selected_item = None
        super(BrowserComponent, self).disconnect()

    def set_display_line1(self, display):
        self.set_display_line_with_index(display, 0)

    def set_display_line2(self, display):
        self.set_display_line_with_index(display, 1)

    def set_display_line3(self, display):
        self.set_display_line_with_index(display, 2)

    def set_display_line4(self, display):
        self.set_display_line_with_index(display, 3)

    def set_display_line_with_index(self, display, index):
        if display:
            sources = self._data_sources[index::self.COLUMN_SIZE]
            display.set_data_sources(sources)

    def set_select_buttons(self, buttons):
        for button in buttons or []:
            if button:
                button.reset()

        self._on_select_matrix_value.subject = buttons or None
        self._select_buttons = buttons
        buttons = buttons or (None, None, None, None, None, None, None, None)
        for component, button in izip(self._list_components, buttons[1::2]):
            self._set_button_if_enabled(component, u'action_button', button)

        for component, button in izip(self._list_components, buttons[::2]):
            if self.shift_button.is_pressed:
                self._set_button_if_enabled(component, u'prev_page_button', button)
                self._set_button_if_enabled(component, u'select_prev_button', None)
            else:
                self._set_button_if_enabled(component, u'prev_page_button', None)
                self._set_button_if_enabled(component, u'select_prev_button', button)

    def set_state_buttons(self, buttons):
        for button in buttons or []:
            if button:
                button.reset()

        self._on_state_matrix_value.subject = buttons or None
        self._state_buttons = buttons
        buttons = buttons or (None, None, None, None, None, None, None, None)
        for component, button in izip(self._list_components, buttons[::2]):
            if self.shift_button.is_pressed:
                self._set_button_if_enabled(component, u'next_page_button', button)
                self._set_button_if_enabled(component, u'select_next_button', None)
            else:
                self._set_button_if_enabled(component, u'next_page_button', None)
                self._set_button_if_enabled(component, u'select_next_button', button)

        for button in buttons[1::2]:
            if button and self.is_enabled():
                button.set_light(u'DefaultButton.Disabled')

    @shift_button.value
    def shift_button(self, value, control):
        self.set_select_buttons(self._select_buttons)
        self.set_state_buttons(self._state_buttons)

    def _set_button_if_enabled(self, component, name, button):
        control = getattr(component, name)
        if component.is_enabled(explicit=True):
            control.set_control_element(button)
        else:
            control.set_control_element(None)
            if button and self.is_enabled():
                button.set_light(u'DefaultButton.Disabled')

    def set_encoder_controls(self, encoder_controls):
        if encoder_controls:
            num_active_lists = len(self._browser_model.content_lists) - self._scroll_offset
            num_assignable_lists = min(num_active_lists, len(encoder_controls) / 2)
            index = 0
            for component in self._list_components[:num_assignable_lists - 1]:
                component.encoders.set_control_element(encoder_controls[index:index + 2])
                index += 2

            self._list_components[num_assignable_lists - 1].encoders.set_control_element(encoder_controls[index:])
        else:
            for component in self._list_components:
                component.encoders.set_control_element([])

        self._encoder_controls = encoder_controls

    def update(self):
        super(BrowserComponent, self).update()
        if self.is_enabled():
            self.set_state_buttons(self._state_buttons)
            self.set_select_buttons(self._select_buttons)
            self._update_browser_model()
        else:
            self._browser.stop_preview()

    def reset_load_memory(self):
        self._update_load_memory(None)

    def _do_load_item(self, item):
        self.do_load_item(item)
        self._update_load_memory(item)
        self._skip_next_preselection = True

        def reset_skip_next_preselection():
            self._skip_next_preselection = False

        self._tasks.add(task.run(reset_skip_next_preselection))

    def _update_load_memory(self, item):
        self._last_loaded_item = item
        for component in self._list_components:
            component.update()

    def do_load_item(self, item):
        item.action()
        self.notify_load_item(item.content)

    def back_to_top(self):
        self._set_scroll_offset(0)

    def _set_scroll_offset(self, offset):
        self._scroll_offset = offset
        self._on_content_lists_changed()
        scrollable_list = self._list_components[-1].scrollable_list
        if scrollable_list:
            scrollable_list.request_notify_item_activated()

    def _update_navigation_button_state(self):
        self.exit_button.enabled = self._scroll_offset > 0
        self.enter_button.enabled = self._scroll_offset < self._max_scroll_offset

    def _shorten_item_name(self, shortening_limit, list_index, item_name):
        u"""
        Creates the name of an item shortened by removing words from the parents name
        """

        def is_short_enough(item_name):
            return len(item_name) <= 9

        content_lists = self._browser_model.content_lists
        parent_lists = reversed(content_lists[max(0, list_index - 3):list_index])
        for content_list in parent_lists:
            if is_short_enough(item_name):
                break
            parent_name = unicode(content_list.selected_item)
            stems = split_stem(parent_name)
            for stem in stems:
                short_name = make_stem_cleaner(stem)(item_name)
                short_name = full_strip(short_name)
                item_name = short_name if len(short_name) > 4 else item_name
                if is_short_enough(item_name):
                    break

        if len(item_name) >= shortening_limit and item_name[-1] == consts.CHAR_ELLIPSIS:
            return item_name[:-1]
        return item_name

    def _item_formatter(self, depth, index, item, action_in_progress):
        display_string = u''
        separator_length = len(self._data_sources[self.COLUMN_SIZE * depth].separator)
        shortening_limit = 16 - separator_length
        if item:
            item_name = u'Loading...' if action_in_progress else self._shorten_item_name(shortening_limit, depth + self._scroll_offset, unicode(item))
            display_string = consts.CHAR_SELECT if item and item.is_selected else u' '
            display_string += item_name
            if depth == len(self._list_components) - 1 and item.is_selected and self._scroll_offset < self._max_hierarchy:
                display_string = string.ljust(display_string, consts.DISPLAY_LENGTH / 4 - 1)
                shortening_limit += 1
                display_string = display_string[:shortening_limit] + consts.CHAR_ARROW_RIGHT
            if depth == 0 and self._scroll_offset > 0:
                prefix = consts.CHAR_ARROW_LEFT if index == 0 else u' '
                display_string = prefix + display_string
        return display_string[:shortening_limit + 1]

    @enter_button.pressed
    def enter_button(self, control):
        self._set_scroll_offset(min(self._max_scroll_offset, self._scroll_offset + 1))

    @exit_button.pressed
    def exit_button(self, control):
        self._set_scroll_offset(max(0, self._scroll_offset - 1))

    @prehear_button.toggled
    def prehear_button(self, toggled, button):
        if not toggled:
            self._browser.stop_preview()
        elif self._last_selected_item is not None:
            self._last_selected_item.preview()
        self._preferences[u'browser_prehear'] = toggled

    @listens(u'hotswap_target')
    def _on_hotswap_target_changed(self):
        if not self._skip_next_preselection:
            self._set_scroll_offset(0)
        self._update_browser_model()

    @listens(u'filter_type')
    def _on_filter_type_changed(self):
        self._update_browser_model()

    @listens(u'full_refresh')
    def _on_browser_full_refresh(self):
        self._browser_model_dirty = True

    def _update_browser_model(self):
        if self.is_enabled():
            self._do_update_browser_model()

    def _create_browser_model_of_type(self, filter_type):
        self._last_filter_type = filter_type
        new_model = self._make_browser_model(self._browser, filter_type)
        if self._browser_model and self._browser_model.can_be_exchanged(new_model) and new_model.can_be_exchanged(self._browser_model):
            self._browser_model.exchange_model(new_model)
            new_model.disconnect()
        else:
            self.disconnect_disconnectable(self._browser_model)
            self._browser_model = self.register_disconnectable(new_model)
            self._on_content_lists_changed.subject = self._browser_model
            self._on_selection_updated.subject = self._browser_model
        self._browser_model.update_content()

    def _do_update_browser_model(self):
        filter_type = filter_type_for_browser(self._browser)
        if filter_type != self._last_filter_type:
            self._create_browser_model_of_type(filter_type)
        elif self._browser_model_dirty:
            self._browser_model.update_content()
        elif not self._skip_next_preselection:
            self._browser_model.update_selection()
        self._skip_next_preselection = False
        self._browser_model_dirty = False

    @listens_group(u'item_action')
    def _on_list_item_action(self, item, _):
        self.notify_load_item(item.content)

    @listens_group(u'selected_item')
    def _on_selected_item(self, item, _):
        if item is not None and self.prehear_button.is_toggled:
            item.preview()
        self._last_selected_item = item

    @listens(u'selection_updated')
    def _on_selection_updated(self, index):
        more_content_available = len(self._browser_model.content_lists) > self.NUM_COLUMNS + self._scroll_offset
        required_scroll_offset = index - (self.NUM_COLUMNS - 1)
        if more_content_available and required_scroll_offset > self._scroll_offset:
            self._set_scroll_offset(self._scroll_offset + 1)
            self._browser_model.update_selection()

    @listens(u'content_lists')
    def _on_content_lists_changed(self):
        self._last_selected_item = None
        components = self._list_components
        contents = self._browser_model.content_lists[self._scroll_offset:]
        messages = self._browser_model.empty_list_messages
        scroll_depth = len(self._browser_model.content_lists) - len(self._list_components)
        self._max_scroll_offset = max(0, scroll_depth + 2)
        self._max_hierarchy = max(0, scroll_depth)
        for component, content, message in izip_longest(components, contents, messages):
            if component != None:
                component.scrollable_list = content
                component.empty_list_message = message

        active_lists = len(contents)
        num_head = clamp(active_lists - 1, 0, self.NUM_COLUMNS - 1)
        head = components[:num_head]
        last = components[num_head:]

        def set_data_sources_with_separator(component, sources, separator):
            for source in sources:
                source.separator = separator

            component.set_data_sources(sources)
            component.set_enabled(True)

        for idx, component in enumerate(head):
            offset = idx * self.COLUMN_SIZE
            sources = self._data_sources[offset:offset + self.COLUMN_SIZE]
            set_data_sources_with_separator(component, sources, u'|')

        if last:
            offset = num_head * self.COLUMN_SIZE
            scrollable_list = last[0].scrollable_list
            if scrollable_list and find_if(lambda item: item.content.is_folder, scrollable_list.items):
                sources = self._data_sources[offset:offset + self.COLUMN_SIZE]
                map(DisplayDataSource.clear, self._data_sources[offset + self.COLUMN_SIZE:])
            else:
                sources = self._data_sources[offset:]
            set_data_sources_with_separator(last[0], sources, u'')
            for component in last[1:]:
                component.set_enabled(False)

        self.set_select_buttons(self._select_buttons)
        self.set_state_buttons(self._state_buttons)
        self.set_encoder_controls(self._encoder_controls)
        self._update_navigation_button_state()

    @listens(u'value')
    def _on_select_matrix_value(self, value, *_):
        pass

    @listens(u'value')
    def _on_state_matrix_value(self, value, *_):
        pass

    @listens(u'value')
    def _on_encoder_matrix_value(self, value, *_):
        pass
コード例 #26
0
class ScalesComponent(Component):
    __events__ = ('close', )
    root_note_buttons = control_list(RadioButtonControl,
                                     control_count=len(ROOT_NOTES),
                                     checked_color='Scales.OptionOn',
                                     unchecked_color='Scales.OptionOff')
    in_key_toggle_button = ToggleButtonControl(
        toggled_color='Scales.OptionOn', untoggled_color='Scales.OptionOn')
    fixed_toggle_button = ToggleButtonControl(
        toggled_color='Scales.OptionOn', untoggled_color='Scales.OptionOff')
    scale_encoders = control_list(StepEncoderControl)
    close_button = ButtonControl(color='Scales.Close')

    def __init__(self, note_layout=None, *a, **k):
        raise note_layout is not None or AssertionError
        super(ScalesComponent, self).__init__(*a, **k)
        self._note_layout = note_layout
        self._scale_list = list(SCALES)
        self._scale_name_list = map(lambda m: m.name, self._scale_list)
        self._selected_scale_index = -1
        self._selected_root_note_index = -1
        self.in_key_toggle_button.connect_property(note_layout, 'is_in_key')
        self.fixed_toggle_button.connect_property(note_layout, 'is_fixed')
        self.__on_root_note_changed.subject = self._note_layout
        self.__on_scale_changed.subject = self._note_layout
        self.__on_root_note_changed(note_layout.root_note)
        self.__on_scale_changed(note_layout.scale)

    @root_note_buttons.pressed
    def root_note_buttons(self, button):
        self._note_layout.root_note = ROOT_NOTES[button.index]

    @listens('root_note')
    def __on_root_note_changed(self, root_note):
        self._selected_root_note_index = list(ROOT_NOTES).index(root_note)
        self.root_note_buttons.checked_index = self._selected_root_note_index
        self.notify_selected_root_note_index()

    @property
    def root_note_names(self):
        return [NOTE_NAMES[note] for note in ROOT_NOTES]

    @listenable_property
    def selected_root_note_index(self):
        return self._selected_root_note_index

    @scale_encoders.value
    def scale_encoders(self, value, encoder):
        index = clamp(self._selected_scale_index + value, 0,
                      len(self._scale_list) - 1)
        self._note_layout.scale = self._scale_list[index]

    @property
    def scale_names(self):
        return self._scale_name_list

    @listenable_property
    def selected_scale_index(self):
        return self._selected_scale_index

    @listens('scale')
    def __on_scale_changed(self, scale):
        index = self._scale_list.index(
            scale) if scale in self._scale_list else -1
        if index != self._selected_scale_index:
            self._selected_scale_index = index
            self.notify_selected_scale_index()

    @close_button.pressed
    def close_button(self, button):
        self.notify_close()

    @property
    def note_layout(self):
        return self._note_layout
コード例 #27
0
class ScalesComponent(Component):
    navigation_colors = dict(color=b'Scales.Navigation',
                             disabled_color=b'Scales.NavigationDisabled')
    up_button = ButtonControl(repeat=True, **navigation_colors)
    down_button = ButtonControl(repeat=True, **navigation_colors)
    right_button = ButtonControl(repeat=True, **navigation_colors)
    left_button = ButtonControl(repeat=True, **navigation_colors)
    root_note_buttons = control_list(RadioButtonControl,
                                     control_count=len(ROOT_NOTES),
                                     checked_color=b'Scales.OptionOn',
                                     unchecked_color=b'Scales.OptionOff')
    in_key_toggle_button = ToggleButtonControl(
        toggled_color=b'Scales.OptionOn', untoggled_color=b'Scales.OptionOn')
    fixed_toggle_button = ToggleButtonControl(
        toggled_color=b'Scales.OptionOn', untoggled_color=b'Scales.OptionOff')
    scale_encoders = control_list(StepEncoderControl)
    layout_encoder = StepEncoderControl()
    direction_encoder = StepEncoderControl()
    horizontal_navigation = listenable_property.managed(False)
    NUM_DISPLAY_ROWS = 4
    NUM_DISPLAY_COLUMNS = int(ceil(float(len(SCALES)) / NUM_DISPLAY_ROWS))

    def __init__(self, note_layout=None, *a, **k):
        assert note_layout is not None
        super(ScalesComponent, self).__init__(*a, **k)
        self._note_layout = note_layout
        self._scale_list = list(SCALES)
        self._scale_name_list = map(lambda m: m.name, self._scale_list)
        self._selected_scale_index = -1
        self._selected_root_note_index = -1
        self._layouts = (Layout(b'4ths',
                                3), Layout(b'3rds',
                                           2), Layout(b'Sequential', None))
        self._selected_layout_index = 0
        self.in_key_toggle_button.connect_property(note_layout, b'is_in_key')
        self.fixed_toggle_button.connect_property(note_layout, b'is_fixed')
        self.__on_root_note_changed.subject = self._note_layout
        self.__on_scale_changed.subject = self._note_layout
        self.__on_interval_changed.subject = self._note_layout
        self.__on_root_note_changed(note_layout.root_note)
        self.__on_scale_changed(note_layout.scale)
        self.__on_interval_changed(self._note_layout.interval)
        return

    def _set_selected_scale_index(self, index):
        index = clamp(index, 0, len(self._scale_list) - 1)
        self._note_layout.scale = self._scale_list[index]

    @down_button.pressed
    def down_button(self, button):
        self._update_horizontal_navigation()
        self._set_selected_scale_index(self._selected_scale_index + 1)

    @up_button.pressed
    def up_button(self, button):
        self._update_horizontal_navigation()
        self._set_selected_scale_index(self._selected_scale_index - 1)

    @left_button.pressed
    def left_button(self, button):
        self._update_horizontal_navigation()
        self._set_selected_scale_index(self._selected_scale_index -
                                       self.NUM_DISPLAY_ROWS)

    @right_button.pressed
    def right_button(self, button):
        self._update_horizontal_navigation()
        self._set_selected_scale_index(self._selected_scale_index +
                                       self.NUM_DISPLAY_ROWS)

    @root_note_buttons.pressed
    def root_note_buttons(self, button):
        self._note_layout.root_note = ROOT_NOTES[button.index]

    @listens(b'root_note')
    def __on_root_note_changed(self, root_note):
        self._selected_root_note_index = list(ROOT_NOTES).index(root_note)
        self.root_note_buttons.checked_index = self._selected_root_note_index
        self.notify_selected_root_note_index()

    @property
    def root_note_names(self):
        return [NOTE_NAMES[note] for note in ROOT_NOTES]

    @listenable_property
    def selected_root_note_index(self):
        return self._selected_root_note_index

    @scale_encoders.value
    def scale_encoders(self, value, encoder):
        self._update_horizontal_navigation()
        self._set_selected_scale_index(self._selected_scale_index + value)

    @property
    def scale_names(self):
        return self._scale_name_list

    @listenable_property
    def selected_scale_index(self):
        return self._selected_scale_index

    @listens(b'scale')
    def __on_scale_changed(self, scale):
        index = self._scale_list.index(
            scale) if scale in self._scale_list else -1
        if index != self._selected_scale_index:
            self._selected_scale_index = index
            self.up_button.enabled = index > 0
            self.left_button.enabled = index > 0
            self.down_button.enabled = index < len(self._scale_list) - 1
            self.right_button.enabled = index < len(self._scale_list) - 1
            self.notify_selected_scale_index()

    @layout_encoder.value
    def layout_encoder(self, value, encoder):
        index = clamp(self._selected_layout_index + value, 0,
                      len(self._layouts) - 1)
        self.selected_layout_index = index

    @property
    def layout_names(self):
        return [layout.name for layout in self._layouts]

    @listenable_property
    def selected_layout_index(self):
        return self._selected_layout_index

    @selected_layout_index.setter
    def selected_layout_index(self, index):
        if index != self._selected_layout_index:
            self._selected_layout_index = index
            interval = self._layouts[index].interval
            self._note_layout.interval = interval
            self.notify_selected_layout_index()

    @direction_encoder.value
    def direction_encoder(self, value, encoder):
        self._note_layout.is_horizontal = value < 0

    @property
    def note_layout(self):
        return self._note_layout

    def _update_horizontal_navigation(self):
        self.horizontal_navigation = self.right_button.is_pressed or self.left_button.is_pressed

    @listens(b'interval')
    def __on_interval_changed(self, interval):
        index = index_if(lambda layout: layout.interval == interval,
                         self._layouts)
        self.selected_layout_index = index
コード例 #28
0
class LoopSettingsControllerComponent(LoopSettingsControllerComponentBase):
    __events__ = (u'looping', u'loop_parameters', u'zoom', u'clip')
    ZOOM_DEFAULT_SENSITIVITY = MappedSensitivitySettingControl.DEFAULT_SENSITIVITY
    ZOOM_FINE_SENSITIVITY = MappedSensitivitySettingControl.FINE_SENSITIVITY
    zoom_encoder = MappedSensitivitySettingControl()
    zoom_touch_encoder = EncoderControl()
    loop_button = ToggleButtonControl(toggled_color='Clip.Option',
                                      untoggled_color='Clip.OptionDisabled')
    crop_button = ButtonControl(color='Clip.Action')

    def __init__(self, *a, **k):
        super(LoopSettingsControllerComponent, self).__init__(*a, **k)
        self._looping_settings = [
            LoopSetting(name=PARAMETERS_LOOPED[0],
                        parent=self._loop_model,
                        source_property='position'),
            LoopSetting(name=PARAMETERS_LOOPED[1],
                        parent=self._loop_model,
                        use_length_conversion=True,
                        source_property='loop_length'),
            LoopSetting(name=PARAMETERS_LOOPED[2],
                        parent=self._loop_model,
                        source_property='start_marker')
        ]
        self._non_looping_settings = [
            LoopSetting(name=PARAMETERS_NOT_LOOPED[0],
                        parent=self._loop_model,
                        source_property='loop_start'),
            LoopSetting(name=PARAMETERS_NOT_LOOPED[1],
                        parent=self._loop_model,
                        source_property='loop_end')
        ]
        for setting in self._looping_settings + self._non_looping_settings:
            self.register_disconnectable(setting)

        self.__on_looping_changed.subject = self._loop_model
        self.__on_looping_changed()

    def update(self):
        super(LoopSettingsControllerComponent, self).update()
        if self.is_enabled():
            self.notify_timeline_navigation()
            self.notify_clip()

    @loop_button.toggled
    def loop_button(self, toggled, button):
        self._loop_model.looping = toggled

    @crop_button.pressed
    def crop_button(self, button):
        if liveobj_valid(self.clip):
            self.clip.crop()

    @property
    def looping(self):
        if self.clip:
            return self._loop_model.looping
        return False

    @property
    def loop_parameters(self):
        if not liveobj_valid(self.clip):
            return []
        parameters = self._looping_settings if self.looping else self._non_looping_settings
        if self.zoom:
            return [self.zoom] + parameters
        return parameters

    @property
    def zoom(self):
        if liveobj_valid(self.clip):
            return getattr(self.clip, 'zoom', None)
        else:
            return

    @listenable_property
    def timeline_navigation(self):
        if liveobj_valid(self.clip):
            return getattr(self.clip, 'timeline_navigation', None)
        else:
            return

    @listens('is_recording')
    def __on_is_recording_changed(self):
        self._update_recording_state()

    @listens('is_overdubbing')
    def __on_is_overdubbing_changed(self):
        self._update_recording_state()

    def _update_recording_state(self):
        clip = self._loop_model.clip
        if liveobj_valid(clip):
            recording = clip.is_recording and not clip.is_overdubbing
            self._looping_settings[1].recording = recording
            self._non_looping_settings[1].recording = recording

    @listens('looping')
    def __on_looping_changed(self):
        self._update_and_notify()

    def _update_loop_button(self):
        self.loop_button.enabled = liveobj_valid(self.clip)
        if liveobj_valid(self.clip):
            self.loop_button.is_toggled = self._loop_model.looping

    def _on_clip_changed(self):
        if self.timeline_navigation is not None:
            self.timeline_navigation.reset_focus_and_animation()
        self._update_and_notify()
        self.__on_is_recording_changed.subject = self._loop_model.clip
        self.__on_is_overdubbing_changed.subject = self._loop_model.clip
        self._update_recording_state()
        self.crop_button.enabled = liveobj_valid(
            self.clip) and self.clip.is_midi_clip
        self._connect_encoder()
        if self.is_enabled():
            self.notify_timeline_navigation()
        return

    def _on_clip_start_marker_touched(self):
        if self.timeline_navigation is not None:
            self.timeline_navigation.touch_object(
                self.timeline_navigation.start_marker_focus)
        return

    def _on_clip_position_touched(self):
        if self.timeline_navigation is not None:
            self.timeline_navigation.touch_object(
                self.timeline_navigation.loop_start_focus)
        return

    def _on_clip_end_touched(self):
        if self.timeline_navigation is not None:
            self.timeline_navigation.touch_object(
                self.timeline_navigation.loop_end_focus)
        return

    def _on_clip_start_marker_released(self):
        if self.timeline_navigation is not None:
            self.timeline_navigation.release_object(
                self.timeline_navigation.start_marker_focus)
        return

    def _on_clip_position_released(self):
        if self.timeline_navigation is not None:
            self.timeline_navigation.release_object(
                self.timeline_navigation.loop_start_focus)
        return

    def _on_clip_end_released(self):
        if self.timeline_navigation is not None:
            self.timeline_navigation.release_object(
                self.timeline_navigation.loop_end_focus)
        return

    @zoom_touch_encoder.touched
    def zoom_touch_encoder(self, encoder):
        if self.timeline_navigation is not None:
            self.timeline_navigation.touch_object(
                self.timeline_navigation.zoom_focus)
        return

    @zoom_touch_encoder.released
    def zoom_touch_encoder(self, encoder):
        if self.timeline_navigation is not None:
            self.timeline_navigation.release_object(
                self.timeline_navigation.zoom_focus)
        return

    def _update_and_notify(self):
        self._update_loop_button()
        self.notify_looping()
        self.notify_loop_parameters()
        self.notify_zoom()

    def _connect_encoder(self):
        self.zoom_encoder.mapped_parameter = self.zoom
        self.zoom_encoder.update_sensitivities(self.ZOOM_DEFAULT_SENSITIVITY,
                                               self.ZOOM_FINE_SENSITIVITY)

    def set_zoom_encoder(self, encoder):
        self.zoom_encoder.set_control_element(encoder)
        self.zoom_touch_encoder.set_control_element(encoder)
        self._connect_encoder()
コード例 #29
0
class TransportComponent(TransportComponentBase):
    tempo_control = InputControl()
    tempo_display = TextDisplayControl()
    play_button = ButtonControl()
    stop_button = ButtonControl()
    shift_button = ButtonControl()
    tui_metronome_button = ToggleButtonControl()
    metronome_color_control = ButtonControl()
    follow_song_button = ButtonControl()
    clip_trigger_quantization_control = SendReceiveValueControl()
    clip_trigger_quantization_button_row = control_list(
        RadioButtonControl, len(RADIO_BUTTON_GROUP_QUANTIZATION_VALUES))
    clip_trigger_quantization_color_controls = control_list(
        ColorSysexControl, len(RADIO_BUTTON_GROUP_QUANTIZATION_VALUES))
    jump_backward_button = ButtonControl()
    jump_forward_button = ButtonControl()
    loop_start_display = TextDisplayControl()
    loop_length_display = TextDisplayControl()
    arrangement_position_display = TextDisplayControl()
    arrangement_position_control = EncoderControl()
    loop_start_control = EncoderControl()
    loop_length_control = EncoderControl()
    tui_arrangement_record_button = ToggleButtonControl()

    def __init__(self, *a, **k):
        super(TransportComponent, self).__init__(*a, **k)
        song = self.song
        self._cached_num_beats_in_bar = num_beats_in_bar(song)
        self.__on_song_tempo_changed.subject = song
        self.__on_song_tempo_changed()
        self.__on_metronome_changed.subject = song
        self.__on_metronome_changed()
        self.__on_clip_trigger_quantization_changed.subject = song
        self.__on_clip_trigger_quantization_changed()
        self.__on_follow_song_changed.subject = song.view
        self.__on_follow_song_changed()
        self.__on_signature_numerator_changed.subject = song
        self.__on_signature_denominator_changed.subject = song
        self.__on_loop_start_changed.subject = song
        self.__on_loop_start_changed()
        self.__on_loop_length_changed.subject = song
        self.__on_loop_length_changed()
        self.__on_arrangement_position_changed.subject = song
        self.__on_arrangement_position_changed()
        self.__on_record_mode_changed.subject = song
        self.__on_record_mode_changed()

    def set_tempo_control(self, control):
        self.tempo_control.set_control_element(control)

    @listens(b'tempo')
    def __on_song_tempo_changed(self):
        self.tempo_display[0] = (b'{0:.2f}').format(self.song.tempo)

    @tempo_control.value
    def tempo_control(self, value, _):
        self.song.tempo = clamp(float((b'').join(imap(chr, value[2:]))),
                                TEMPO_MIN, TEMPO_MAX)

    @play_button.pressed
    def play_button(self, _):
        song = self.song
        song.is_playing = not song.is_playing

    @stop_button.pressed
    def stop_button(self, _):
        self.song.stop_playing()
        if self.shift_button.is_pressed:
            self.song.current_song_time = 0.0

    @tui_metronome_button.toggled
    def tui_metronome_button(self, toggled, _):
        self.song.metronome = toggled

    @follow_song_button.pressed
    def follow_song_button(self, _):
        view = self.song.view
        view.follow_song = not view.follow_song

    @clip_trigger_quantization_control.value
    def clip_trigger_quantization_control(self, value, _):
        if is_valid_launch_quantize_value(value):
            self.song.clip_trigger_quantization = value

    @clip_trigger_quantization_button_row.checked
    def clip_trigger_quantization_button_row(self, button):
        self.song.clip_trigger_quantization = RADIO_BUTTON_GROUP_QUANTIZATION_VALUES[
            button.index]

    def _apply_value_to_arrangement_property(self, property_name, value):
        factor = 0.25 if self.shift_button.is_pressed else 1.0
        delta = factor * sign(value)
        old_value = getattr(self.song, property_name)
        setattr(self.song, property_name, max(0.0, old_value + delta))

    @arrangement_position_control.value
    def arrangement_position_control(self, value, _):
        self._apply_value_to_arrangement_property(b'current_song_time', value)

    @loop_start_control.value
    def loop_start_control(self, value, _):
        self._apply_value_to_arrangement_property(b'loop_start', value)

    @loop_length_control.value
    def loop_length_control(self, value, _):
        self._apply_value_to_arrangement_property(b'loop_length', value)

    @jump_backward_button.pressed
    def jump_backward_button(self, _):
        self.song.jump_by(self._cached_num_beats_in_bar * -1)

    @jump_forward_button.pressed
    def jump_forward_button(self, _):
        self.song.jump_by(self._cached_num_beats_in_bar)

    @tui_arrangement_record_button.toggled
    def tui_arrangement_record_button(self, toggled, _):
        self.song.record_mode = toggled

    def _update_button_states(self):
        self._update_play_button_color()
        self._update_continue_playing_button_color()
        self._update_stop_button_color()

    def _update_play_button_color(self):
        raise NotImplementedError

    def _update_continue_playing_button_color(self):
        self.continue_playing_button.color = b'Transport.PlayOn' if self.song.is_playing else b'Transport.PlayOff'

    def _update_stop_button_color(self):
        self.stop_button.color = b'Transport.StopOff' if self.song.is_playing else b'Transport.StopOn'

    @listens(b'metronome')
    def __on_metronome_changed(self):
        self._update_tui_metronome_button()
        self._update_metronome_color_control()

    @listens(b'follow_song')
    def __on_follow_song_changed(self):
        self.follow_song_button.color = b'DefaultButton.On' if self.song.view.follow_song else b'DefaultButton.Off'

    @listens(b'clip_trigger_quantization')
    def __on_clip_trigger_quantization_changed(self):
        self._update_clip_trigger_quantization_control()
        self._update_clip_trigger_quantization_color_controls()

    @listens(b'signature_numerator')
    def __on_signature_numerator_changed(self):
        self._cached_num_beats_in_bar = num_beats_in_bar(self.song)

    @listens(b'signature_denominator')
    def __on_signature_denominator_changed(self):
        self._cached_num_beats_in_bar = num_beats_in_bar(self.song)

    def _update_clip_trigger_quantization_control(self):
        self.clip_trigger_quantization_control.value = int(
            self.song.clip_trigger_quantization)

    def _update_clip_trigger_quantization_color_controls(self):
        quantization = self.song.clip_trigger_quantization
        for index, control in enumerate(
                self.clip_trigger_quantization_color_controls):
            control.color = b'DefaultButton.On' if RADIO_BUTTON_GROUP_QUANTIZATION_VALUES[
                index] == quantization else b'DefaultButton.Off'

    def _update_tui_metronome_button(self):
        self.tui_metronome_button.is_toggled = self.song.metronome

    def _update_metronome_color_control(self):
        self.metronome_color_control.color = b'Transport.MetronomeOn' if self.song.metronome else b'Transport.MetronomeOff'

    def _update_tui_arrangement_record_button(self):
        self.tui_arrangement_record_button.is_toggled = self.song.record_mode

    @listens(b'loop_start')
    def __on_loop_start_changed(self):
        loop_start_time = self.song.get_beats_loop_start()
        self.loop_start_display[0] = format_beat_time(loop_start_time)

    @listens(b'loop_length')
    def __on_loop_length_changed(self):
        loop_length_time = self.song.get_beats_loop_length()
        self.loop_length_display[0] = format_beat_time(loop_length_time)

    @listens(b'current_song_time')
    def __on_arrangement_position_changed(self):
        song_time = self.song.get_current_beats_song_time()
        self.arrangement_position_display[0] = format_beat_time(song_time)

    @listens(b'record_mode')
    def __on_record_mode_changed(self):
        self._update_tui_arrangement_record_button()
コード例 #30
0
class UserComponent(UserComponentBase):
    user_mode_toggle_button = ToggleButtonControl()

    @user_mode_toggle_button.toggled
    def user_mode_toggle_button(self, toggled, button):
        self.toggle_mode()