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