Example #1
0
 def __init__(self, display_label = ' ', display_seg_start = 0, encoder_touch_delay = 0, *a, **k):
     super(ValueComponentBase, self).__init__(*a, **k)
     encoder = EncoderControl(touch_event_delay=encoder_touch_delay)
     encoder.touched = ValueComponentBase.__on_encoder_touched
     encoder.released = ValueComponentBase.__on_encoder_released
     encoder.value = ValueComponentBase.__on_encoder_value
     self.add_control('encoder', encoder)
     self._display = self.register_component(self.create_display_component(display_label=display_label, display_seg_start=display_seg_start))
     self._display.set_enabled(False)
Example #2
0
 def __init__(self, display_label = u' ', display_seg_start = 0, encoder_touch_delay = 0, *a, **k):
     super(ValueComponentBase, self).__init__(*a, **k)
     encoder = EncoderControl(touch_event_delay=encoder_touch_delay)
     encoder.touched = ValueComponentBase.__on_encoder_touched
     encoder.released = ValueComponentBase.__on_encoder_released
     encoder.value = ValueComponentBase.__on_encoder_value
     self.add_control(u'encoder', encoder)
     self._display = self.register_component(self.create_display_component(display_label=display_label, display_seg_start=display_seg_start))
     self._display.set_enabled(False)
Example #3
0
class AudioClipSettingsControllerComponent(Component):
    """'
    Component for managing settings of an audio clip
    """
    __module__ = __name__
    warp_mode_encoder = StepEncoderControl()
    transpose_encoder = StepEncoderControl()
    detune_encoder = EncoderControl()
    gain_encoder = EncoderControl()
    shift_button = ButtonControl()

    def __init__(self, *a, **k):
        super(AudioClipSettingsControllerComponent, self).__init__(*a, **k)
        self._audio_clip_model = self.register_disconnectable(
            AudioClipSettingsModel())

    def _get_clip(self):
        return self._audio_clip_model.clip

    def _set_clip(self, clip):
        self._audio_clip_model.clip = clip
        self._update_encoder_enabled_state()
        self._on_clip_changed()

    clip = property(_get_clip, _set_clip)

    def _update_encoder_enabled_state(self):
        enabled = liveobj_valid(self.clip)
        self.warp_mode_encoder.enabled = self.transpose_encoder.enabled = self.detune_encoder.enabled = self.gain_encoder.enabled = enabled

    @warp_mode_encoder.value
    def warp_mode_encoder(self, value, encoder):
        self._on_clip_warp_mode_value(value)

    def _on_clip_warp_mode_value(self, value):
        self._audio_clip_model.warp_mode = value

    @transpose_encoder.value
    def transpose_encoder(self, value, encoder):
        self._on_transpose_encoder_value(value)

    def _on_transpose_encoder_value(self, value):
        self._audio_clip_model.set_clip_pitch_coarse(
            value, self.shift_button.is_pressed)

    @detune_encoder.value
    def detune_encoder(self, value, encoder):
        self._on_detune_encoder_value(value)

    def _on_detune_encoder_value(self, value):
        self._audio_clip_model.set_clip_pitch_fine(
            value, self.shift_button.is_pressed)

    @gain_encoder.value
    def gain_encoder(self, value, encoder):
        self._audio_clip_model.set_clip_gain(value,
                                             self.shift_button.is_pressed)
Example #4
0
class ViewControlComponent(Component):
    vertical_encoder = EncoderControl()
    horizontal_encoder = EncoderControl()

    @vertical_encoder.value
    def vertical_encoder(self, value, _):
        direction = NavDirection.up if value < 0 else NavDirection.down
        self.application.view.scroll_view(direction, '', False)

    @horizontal_encoder.value
    def horizontal_encoder(self, value, _):
        direction = NavDirection.left if value < 0 else NavDirection.right
        self.application.view.scroll_view(direction, '', False)
Example #5
0
class TransportComponent(TransportComponentBase):
    play_button = ButtonControl(color=u'Transport.PlayOff')
    jump_encoder = EncoderControl()
    loop_start_encoder = EncoderControl()

    def __init__(self, *a, **k):
        super(TransportComponent, self).__init__(*a, **k)
        self.__on_signature_numerator_changed.subject = self.song
        self.__on_signature_denominator_changed.subject = self.song
        self._session_record_toggle = ToggleComponent(u'session_record',
                                                      self.song,
                                                      parent=self)
        self._calculate_distance_to_move()

    def set_play_button(self, button):
        self.play_button.set_control_element(button)

    def set_session_record_button(self, button):
        self._session_record_toggle.set_toggle_button(button)

    @play_button.pressed
    def play_button(self, _):
        self.song.start_playing()

    @jump_encoder.value
    def jump_encoder(self, value, _):
        self.song.jump_by(value * self._distance_to_move)

    @loop_start_encoder.value
    def loop_start_encoder(self, value, _):
        self.song.loop_start = max(
            0.0, self.song.loop_start + value * self._distance_to_move)

    @listens(u'signature_numerator')
    def __on_signature_numerator_changed(self):
        self._calculate_distance_to_move()

    @listens(u'signature_denominator')
    def __on_signature_denominator_changed(self):
        self._calculate_distance_to_move()

    def _calculate_distance_to_move(self):
        self._distance_to_move = old_div(4.0, self.song.signature_denominator
                                         ) * self.song.signature_numerator * 64

    def _update_button_states(self):
        super(TransportComponent, self)._update_button_states()
        self.play_button.color = u'Transport.PlayOn' if self.song.is_playing else u'Transport.PlayOff'

    def _update_stop_button_color(self):
        self.stop_button.color = u'Transport.PlayOff' if self.song.is_playing else u'Transport.PlayOn'
Example #6
0
class NoteSettingBase(ControlManager):
    __events__ = (u'setting_changed', )
    attribute_index = -1
    encoder = EncoderControl()

    def __init__(self, grid_resolution=None, *a, **k):
        super(NoteSettingBase, self).__init__(*a, **k)
        self._min_max_value = None
        self._grid_resolution = grid_resolution
        return

    def encoder_value_to_attribute(self, value):
        raise NotImplementedError

    @property
    def step_length(self):
        if self._grid_resolution:
            return self._grid_resolution.step_length
        return 1.0

    def set_min_max(self, min_max_value):
        self._min_max_value = min_max_value

    @encoder.value
    def encoder(self, value, _):
        self._on_encoder_value_changed(value)

    def _on_encoder_value_changed(self, value):
        self.notify_setting_changed(self.attribute_index, self.encoder_value_to_attribute(value))
Example #7
0
class SessionNavigationComponent(SessionNavigationComponentBase):
    scene_encoder = EncoderControl()

    @scene_encoder.value
    def scene_encoder(self, value, _):
        if value > 0:
            if self._vertical_banking.can_scroll_up():
                self._vertical_banking.scroll_up()
        elif self._vertical_banking.can_scroll_down():
            self._vertical_banking.scroll_down()
Example #8
0
class SessionComponent(SessionComponentBase):
    scene_encoder = EncoderControl()

    @scene_encoder.value
    def scene_encoder(self, value, _):
        factor = 1 if value < 0 else -1
        new_scene_index = factor + list(self.song.scenes).index(
            self.song.view.selected_scene)
        if new_scene_index in range(len(self.song.scenes)):
            self.song.view.selected_scene = self.song.scenes[new_scene_index]
class ScrollComponent(ScrollComponentBase):
    scroll_encoder = EncoderControl()

    @scroll_encoder.value
    def scroll_encoder(self, value, _):
        scroll_step = nop
        if value > 0 and self.can_scroll_down():
            scroll_step = self._do_scroll_down
        elif value < 0 and self.can_scroll_up():
            scroll_step = self._do_scroll_up
        scroll_step()
def add_scroll_encoder(component):
    scroll_encoder = EncoderControl()

    @scroll_encoder.value
    def scroll_encoder(component, value, _):
        if value < 0:
            if component.can_scroll_up():
                component.scroll_up()
        elif component.can_scroll_down():
            component.scroll_down()

    component.add_control('scroll_encoder', scroll_encoder)
class ArrangementComponent(Component):
    set_or_delete_cue_button = ButtonControl()
    jump_encoder = EncoderControl()

    @set_or_delete_cue_button.pressed
    def set_or_delete_cue_button(self, _):
        if self.application.view.focused_document_view == 'Arranger':
            self.song.set_or_delete_cue()

    @jump_encoder.value
    def jump_encoder(self, value, _):
        step = -1.0 if value < 0 else 1.0
        if self.song.is_playing:
            step *= 4.0
        self.song.jump_by(step)
Example #12
0
class SessionComponent(SessionComponentBase):
    selected_scene_launch_button = ButtonControl()
    scene_encoder = EncoderControl()

    @scene_encoder.value
    def scene_encoder(self, value, _):
        factor = 1 if value < 0 else -1
        new_scene_index = factor + list(self.song.scenes).index(
            self.song.view.selected_scene)
        if new_scene_index in range(len(self.song.scenes)):
            self.song.view.selected_scene = self.song.scenes[new_scene_index]

    @selected_scene_launch_button.released_immediately
    def selected_scene_launch_button(self, _):
        self.song.view.selected_scene.fire()
Example #13
0
class TransportComponent(TransportComponentBase):
    capture_midi_button = ButtonControl(color=u'Recording.On',
                                        pressed_color=u'Recording.Off')
    shift_button = ButtonControl(color=u'DefaultButton.Off',
                                 pressed_color=u'DefaultButton.On')
    prev_cue_button = ButtonControl(color=u'DefaultButton.Off',
                                    pressed_color=u'DefaultButton.On')
    next_cue_button = ButtonControl(color=u'DefaultButton.Off',
                                    pressed_color=u'DefaultButton.On')
    scroll_encoder = EncoderControl()

    def __init__(self, *a, **k):
        super(TransportComponent, self).__init__(*a, **k)
        self.__on_can_capture_midi_changed.subject = self.song
        self.__on_can_capture_midi_changed()

    @scroll_encoder.value
    def scroll_encoder(self, value, _):
        factor = 1 if value > 0 else -1
        if self.shift_button.is_pressed:
            self.song.tempo = clamp(self.song.tempo + factor, 20, 999)
        else:
            self.song.jump_by(factor)

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

    @prev_cue_button.pressed
    def prev_cue_button(self, _):
        if self.song.can_jump_to_prev_cue:
            self.song.jump_to_prev_cue()

    @next_cue_button.pressed
    def next_cue_button(self, _):
        if self.song.can_jump_to_next_cue:
            self.song.jump_to_next_cue()

    @listens(u'can_capture_midi')
    def __on_can_capture_midi_changed(self):
        self.capture_midi_button.enabled = self.song.can_capture_midi
Example #14
0
class ValueComponentBase(CompoundComponent):
    """
    Component to control one continuous property with a infinite
    touch-sensitive encoder. You can optionally give it a display and
    a button such that the value will be displayed while its pressed.
    """
    def create_display_component(self, *a, **k):
        raise NotImplementedError

    encoder = EncoderControl()

    def __init__(self, display_label=' ', display_seg_start=0, *a, **k):
        super(ValueComponentBase, self).__init__(*a, **k)
        self._display = self.register_component(
            self.create_display_component(display_label=display_label,
                                          display_seg_start=display_seg_start))
        self._display.set_enabled(False)

    @property
    def display(self):
        return self._display

    @encoder.touched
    def encoder(self, encoder):
        self._update_display_state()

    @encoder.released
    def encoder(self, encoder):
        self._update_display_state()

    @encoder.value
    def encoder(self, value, encoder):
        self._on_value(value)

    def _on_value(self, value):
        pass

    def _update_display_state(self):
        self._display.set_enabled(self.encoder.is_touched)
Example #15
0
class LoopSettingsControllerComponent(LoopSettingsControllerComponentBase):
    __events__ = ('looping', 'loop_parameters', 'zoom')
    zoom_encoder = MappedControl()
    zoom_touch_encoder = EncoderControl()
    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()
        self.__on_loop_position_value_changed.subject = self._looping_settings[
            0]
        self.__on_loop_length_value_changed.subject = self._looping_settings[1]
        self.__on_start_offset_value_changed.subject = self._looping_settings[
            2]
        self.__on_start_value_changed.subject = self._non_looping_settings[0]
        self.__on_end_value_changed.subject = self._non_looping_settings[1]

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

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

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

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

    @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):
        if self.waveform_navigation is not None:
            self.waveform_navigation.reset_focus_and_animation()
        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()
        self.notify_waveform_navigation()

    @listens('value')
    def __on_loop_position_value_changed(self):
        if self.waveform_navigation is not None and self._loop_model.looping:
            self.waveform_navigation.change_object(
                self.waveform_navigation.loop_start_focus)

    @listens('value')
    def __on_loop_length_value_changed(self):
        if self.waveform_navigation is not None and self._loop_model.looping:
            self.waveform_navigation.change_object(
                self.waveform_navigation.loop_end_focus)

    @listens('value')
    def __on_start_offset_value_changed(self):
        if self.waveform_navigation is not None and self._loop_model.looping:
            self.waveform_navigation.change_object(
                self.waveform_navigation.start_marker_focus)

    @listens('value')
    def __on_start_value_changed(self):
        if self.waveform_navigation is not None and not self._loop_model.looping:
            self.waveform_navigation.change_object(
                self.waveform_navigation.start_marker_focus)

    @listens('value')
    def __on_end_value_changed(self):
        if self.waveform_navigation is not None and not self._loop_model.looping:
            self.waveform_navigation.change_object(
                self.waveform_navigation.loop_end_focus)

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

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

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

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

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

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

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

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

    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.zoom_touch_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()
Example #16
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()
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
Example #18
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()