예제 #1
0
    def __init__(self, note_layout = None, *a, **k):
        assert note_layout is not None
        super(InstrumentComponent, self).__init__(*a, **k)
        self._note_layout = note_layout
        self._first_note = self.page_length * 3 + self.page_offset
        self._last_page_length = self.page_length
        self._last_page_offset = self.page_offset
        self._detail_clip = None
        self._has_notes = [False] * 128
        self._has_notes_pattern = self._get_pattern(0)
        self._aftertouch_control = None
        self._aftertouch_mode = u'mono'
        self._show_notifications = True
        self.__on_detail_clip_changed.subject = self.song.view
        self.__on_detail_clip_changed()
        self._slider = SlideComponent(slideable=self, parent=self)
        self._touch_slider = SlideableTouchStripComponent(touch_slideable=self, parent=self)
        for event in (u'scale', u'root_note', u'is_in_key', u'is_fixed', u'is_horizontal', u'interval'):
            self.register_slot(self._note_layout, self._on_note_layout_changed, event)

        self._update_pattern()
    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._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()
예제 #3
0
class InstrumentComponent(PlayableComponent, Slideable, Messenger):
    __events__ = ('pattern', )
    matrix = control_matrix(PadControl)
    delete_button = ButtonControl()

    def __init__(self, note_layout=None, *a, **k):
        (super(InstrumentComponent, self).__init__)(*a, **k)
        self._note_layout = note_layout
        self._first_note = self.page_length * 3 + self.page_offset
        self._last_page_length = self.page_length
        self._last_page_offset = self.page_offset
        self._detail_clip = None
        self._has_notes = [False] * 128
        self._has_notes_pattern = self._get_pattern(0)
        self._aftertouch_control = None
        self._aftertouch_mode = 'mono'
        self._show_notifications = True
        self._InstrumentComponent__on_detail_clip_changed.subject = self.song.view
        self._InstrumentComponent__on_detail_clip_changed()
        self._slider = SlideComponent(slideable=self, parent=self)
        self._touch_slider = SlideableTouchStripComponent(touch_slideable=self,
                                                          parent=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()

    @listens('detail_clip')
    def __on_detail_clip_changed(self):
        clip = self.song.view.detail_clip
        self.set_detail_clip(clip if (liveobj_valid(clip)) and (
            clip.is_midi_clip) else None)

    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_extended(from_time=loop_start,
                                                         from_pitch=0,
                                                         time_span=loop_length,
                                                         pitch_span=128)
            for note in notes:
                self._has_notes[note.pitch] = 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
            if note is not None:
                return self._has_notes[note]
            return False
        return False

    @property
    def show_notifications(self):
        return self._show_notifications

    @show_notifications.setter
    def show_notifications(self, value):
        self._show_notifications = value

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

    @property
    def position_count(self):
        if not self._note_layout.is_in_key:
            return 139
        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]
        if self._note_layout.notes[0] == 0:
            return 0
        return len(self._note_layout.notes) - index_if(lambda n: n >= 12,
                                                       self._note_layout.notes)

    @property
    def page_offset(self):
        if self._note_layout.is_fixed:
            return 0
        return 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 min_pitch(self):
        return self.pattern[0].index

    @property
    def max_pitch(self):
        identifiers = [control.identifier for control in self.matrix]
        if len(identifiers) > 0:
            return max(identifiers)
        return 127

    @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.is_pressed:
            pitch = self._get_note_info_for_coordinate(button.coordinate).index
            if pitch:
                if self._detail_clip:
                    self._do_delete_pitch(pitch)

    @matrix.released
    def matrix(self, button):
        self._on_matrix_released(button)

    def _on_matrix_released(self, button):
        pass

    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_extended(from_time=(clip.loop_start),
                                       from_pitch=pitch,
                                       time_span=loop_length,
                                       pitch_span=1)
            self.show_notification(consts.MessageBoxText.DELETE_NOTES %
                                   note_name)

    @delete_button.pressed
    def delete_button(self, value):
        self._set_control_pads_from_script(True)

    @delete_button.released
    def delete_button(self, value):
        self._set_control_pads_from_script(False)

    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_aftertouch_mode(self, mode):
        if self._aftertouch_mode != mode:
            self._aftertouch_mode = mode
            self._update_aftertouch()

    def _align_first_note(self):
        self._first_note = self.page_offset + (
            self._first_note - self._last_page_offset) * old_div(
                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 show_pitch_range_notification(self):
        if self.is_enabled():
            if self.show_notifications:
                self.show_notification(
                    'Play {start_note} to {end_note}'.format(
                        start_note=(pitch_index_to_string(
                            self.pattern.note(0, 0).index)),
                        end_note=(pitch_index_to_string(
                            self.pattern.note(self.width - 1,
                                              self.height - 1).index))))

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

    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.height - 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_note_translations()
        self._update_led_feedback()

    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
        width = None
        height = None
        octave = old_div(first_note, self.page_length)
        offset = first_note % self.page_length - self._first_scale_note_offset(
        )
        if interval == None:
            if self._note_layout.is_in_key:
                interval = len(self._note_layout.notes)
                if self._note_layout.is_horizontal:
                    width = interval + 1
                else:
                    height = interval + 1
            else:
                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),
                              width=width,
                              height=height)

    def _update_aftertouch(self):
        if self.is_enabled():
            if self._aftertouch_control != None:
                self._aftertouch_control.send_value(self._aftertouch_mode)