Exemplo n.º 1
0
class OptimizedOwnershipHandler(ElementOwnershipHandler):
    """
    Control element ownership handler that delays notification of
    ownership changes and minimizes the number of actual owernship
    changes that are delivered.
    """

    def __init__(self, *a, **k):
        super(OptimizedOwnershipHandler, self).__init__(*a, **k)
        self._ownership_changes = {}
        self._sequence_number = 0

    def handle_ownership_change(self, control, client, status):
        if (control, client, not status) in self._ownership_changes:
            del self._ownership_changes[control, client, not status]
        else:
            self._ownership_changes[control, client, status] = self._sequence_number
        self._sequence_number += 1

    @depends(log_message=const(print_message), traceback=const(traceback))
    def commit_ownership_changes(self, log_message = None, traceback = None):
        notify = super(OptimizedOwnershipHandler, self).handle_ownership_change
        while self._ownership_changes:
            notifications = sorted(self._ownership_changes.iteritems(), key=second)
            self._ownership_changes.clear()
            for (control, client, status), _ in notifications:
                try:
                    notify(control, client, status)
                except Exception:
                    log_message('Error when trying to give control:', control.name)
                    traceback.print_exc()

        self._ownership_changes.clear()
        self._sequence_number = 0
Exemplo n.º 2
0
 def __init__(self, *a, **k):
     super(TransportComponent, self).__init__(*a, **k)
     self._ffwd_button = None
     self._rwd_button = None
     self._tap_tempo_button = None
     self._tempo_control = None
     self._tempo_fine_control = None
     self._song_position_control = None
     self._rwd_task = Task.Task()
     self._ffwd_task = Task.Task()
     self._fine_tempo_needs_pickup = True
     self._prior_fine_tempo_value = -1
     self._end_undo_step_task = self._tasks.add(
         Task.sequence(Task.wait(1.5), Task.run(self.song().end_undo_step)))
     self._end_undo_step_task.kill()
     song = self.song()
     self._loop_toggle, self._punch_in_toggle, self._punch_out_toggle, self._record_toggle, self._play_toggle, self._stop_toggle, self._nudge_down_toggle, self._nudge_up_toggle, self._metronome_toggle, self._session_record_toggle, self.arrangement_overdub_toggle, self._overdub_toggle = self.register_components(
         ToggleComponent('loop', song),
         ToggleComponent('punch_in', song, is_momentary=True),
         ToggleComponent('punch_out', song, is_momentary=True),
         ToggleComponent('record_mode', song),
         ToggleComponent('is_playing', song, model_transform=const(True)),
         ToggleComponent('is_playing',
                         song,
                         model_transform=const(False),
                         view_transform=const(False)),
         ToggleComponent('nudge_down', song, is_momentary=True),
         ToggleComponent('nudge_up', song, is_momentary=True),
         ToggleComponent('metronome', song),
         ToggleComponent('session_record', song),
         ToggleComponent('arrangement_overdub', song),
         ToggleComponent('overdub', song))
 def __init__(self, c_instance=None, publish_self=True, *a, **k):
     """ Define and Initialize standard behavior """
     super(ControlSurface, self).__init__(*a, **k)
     if not c_instance:
         raise AssertionError
         self.canonical_parent = None
         publish_self and publish_control_surface(self)
     self._c_instance = c_instance
     self.log_message('Initialising...')
     self._pad_translations = None
     self._suggested_input_port = str('')
     self._suggested_output_port = str('')
     self._components = []
     self._displays = []
     self.controls = []
     self._highlighting_session_component = None
     self._device_component = None
     self._device_selection_follows_track_selection = False
     self._forwarding_long_identifier_registry = {}
     self._forwarding_registry = {}
     self._is_sending_scheduled_messages = BooleanContext()
     self._remaining_scheduled_messages = []
     self._task_group = Task.TaskGroup(auto_kill=False)
     self._in_build_midi_map = BooleanContext()
     self._suppress_requests_counter = 0
     self._rebuild_requests_during_suppression = 0
     self._enabled = True
     self._in_component_guard = BooleanContext()
     self._accumulate_midi_messages = BooleanContext()
     self._midi_message_dict = {}
     self._midi_message_list = []
     self._midi_message_count = 0
     self._control_surface_injector = inject(
         parent_task_group=const(self._task_group),
         show_message=const(self.show_message),
         log_message=const(self.log_message),
         register_component=const(self._register_component),
         register_control=const(self._register_control),
         request_rebuild_midi_map=const(self.request_rebuild_midi_map),
         send_midi=const(self._send_midi),
         song=self.song).everywhere()
     with self.setting_listener_caller():
         self.song().add_visible_tracks_listener(
             self._on_track_list_changed)
         self.song().add_scenes_listener(self._on_scene_list_changed)
         self.song().view.add_selected_track_listener(
             self._on_selected_track_changed)
         self.song().view.add_selected_scene_listener(
             self._on_selected_scene_changed)
     return
 def __init__(self, play_toggle_model_transform = const(True), *a, **k):
     super(TransportComponent, self).__init__(*a, **k)
     self._ffwd_button = None
     self._rwd_button = None
     self._tap_tempo_button = None
     self._tempo_control = None
     self._tempo_fine_control = None
     self._song_position_control = None
     self._rwd_task = Task.Task()
     self._ffwd_task = Task.Task()
     self._fine_tempo_needs_pickup = True
     self._prior_fine_tempo_value = -1
     self._end_undo_step_task = self._tasks.add(Task.sequence(Task.wait(1.5), Task.run(self.song().end_undo_step)))
     self._end_undo_step_task.kill()
     song = self.song()
     self._loop_toggle, self._punch_in_toggle, self._punch_out_toggle, self._record_toggle, self._play_toggle, self._stop_toggle, self._nudge_down_toggle, self._nudge_up_toggle, self._metronome_toggle, self._session_record_toggle, self.arrangement_overdub_toggle, self._overdub_toggle = self.register_components(ToggleComponent('loop', song), ToggleComponent('punch_in', song, is_momentary=True), ToggleComponent('punch_out', song, is_momentary=True), ToggleComponent('record_mode', song), ToggleComponent('is_playing', song, model_transform=play_toggle_model_transform), ToggleComponent('is_playing', song, model_transform=const(False), view_transform=const(False)), ToggleComponent('nudge_down', song, is_momentary=True), ToggleComponent('nudge_up', song, is_momentary=True), ToggleComponent('metronome', song), ToggleComponent('session_record', song), ToggleComponent('arrangement_overdub', song), ToggleComponent('overdub', song))
Exemplo n.º 5
0
 def __init__(self, c_instance, publish_self=True, *a, **k):
     """ Define and Initialize standard behavior """
     super(ControlSurface, self).__init__(*a, **k)
     self.canonical_parent = None
     if publish_self:
         if isinstance(__builtins__, dict):
             if CS_LIST_KEY not in __builtins__.keys():
                 __builtins__[CS_LIST_KEY] = []
             __builtins__[CS_LIST_KEY].append(self)
         else:
             if not hasattr(__builtins__, CS_LIST_KEY):
                 setattr(__builtins__, CS_LIST_KEY, [])
             cs_list = getattr(__builtins__, CS_LIST_KEY)
             cs_list.append(self)
             setattr(__builtins__, CS_LIST_KEY, cs_list)
     self._c_instance = c_instance
     self._pad_translations = None
     self._suggested_input_port = str('')
     self._suggested_output_port = str('')
     self._components = []
     self._displays = []
     self.controls = []
     self._highlighting_session_component = None
     self._device_component = None
     self._device_selection_follows_track_selection = False
     self._forwarding_long_identifier_registry = {}
     self._forwarding_registry = {}
     self._is_sending_scheduled_messages = BooleanContext()
     self._remaining_scheduled_messages = []
     self._task_group = Task.TaskGroup(auto_kill=False)
     self._in_build_midi_map = BooleanContext()
     self._suppress_requests_counter = 0
     self._rebuild_requests_during_suppression = 0
     self._enabled = True
     self._in_component_guard = BooleanContext()
     self._accumulate_midi_messages = BooleanContext()
     self._midi_message_dict = {}
     self._midi_message_list = []
     self._midi_message_count = 0
     self._control_surface_injector = inject(
         parent_task_group=const(self._task_group),
         show_message=const(self.show_message),
         register_component=const(self._register_component),
         register_control=const(self._register_control),
         request_rebuild_midi_map=const(self.request_rebuild_midi_map),
         send_midi=const(self._send_midi),
         song=self.song).everywhere()
     with self.setting_listener_caller():
         self.song().add_visible_tracks_listener(
             self._on_track_list_changed)
         self.song().add_scenes_listener(self._on_scene_list_changed)
         self.song().view.add_selected_track_listener(
             self._on_selected_track_changed)
         self.song().view.add_selected_scene_listener(
             self._on_selected_scene_changed)
 def __init__(self, c_instance, publish_self = True, *a, **k):
     """ Define and Initialize standard behavior """
     super(ControlSurface, self).__init__(*a, **k)
     self.canonical_parent = None
     if publish_self:
         if isinstance(__builtins__, dict):
             if CS_LIST_KEY not in __builtins__.keys():
                 __builtins__[CS_LIST_KEY] = []
             __builtins__[CS_LIST_KEY].append(self)
         else:
             if not hasattr(__builtins__, CS_LIST_KEY):
                 setattr(__builtins__, CS_LIST_KEY, [])
             cs_list = getattr(__builtins__, CS_LIST_KEY)
             cs_list.append(self)
             setattr(__builtins__, CS_LIST_KEY, cs_list)
     self._c_instance = c_instance
     self._pad_translations = None
     self._suggested_input_port = str('')
     self._suggested_output_port = str('')
     self._components = []
     self._displays = []
     self.controls = []
     self._highlighting_session_component = None
     self._device_component = None
     self._device_selection_follows_track_selection = False
     self._forwarding_long_identifier_registry = {}
     self._forwarding_registry = {}
     self._is_sending_scheduled_messages = BooleanContext()
     self._remaining_scheduled_messages = []
     self._task_group = Task.TaskGroup(auto_kill=False)
     self._in_build_midi_map = BooleanContext()
     self._suppress_requests_counter = 0
     self._rebuild_requests_during_suppression = 0
     self._enabled = True
     self._in_component_guard = BooleanContext()
     self._accumulate_midi_messages = BooleanContext()
     self._midi_message_dict = {}
     self._midi_message_list = []
     self._midi_message_count = 0
     self._control_surface_injector = inject(parent_task_group=const(self._task_group), show_message=const(self.show_message), register_component=const(self._register_component), register_control=const(self._register_control), request_rebuild_midi_map=const(self.request_rebuild_midi_map), send_midi=const(self._send_midi), song=self.song).everywhere()
     with self.setting_listener_caller():
         self.song().add_visible_tracks_listener(self._on_track_list_changed)
         self.song().add_scenes_listener(self._on_scene_list_changed)
         self.song().view.add_selected_track_listener(self._on_selected_track_changed)
         self.song().view.add_selected_scene_listener(self._on_selected_scene_changed)
 def __init__(self, c_instance = None, publish_self = True, *a, **k):
     """ Define and Initialize standard behavior """
     super(ControlSurface, self).__init__(*a, **k)
     if not c_instance:
         raise AssertionError
         self.canonical_parent = None
         publish_self and publish_control_surface(self)
     self._c_instance = c_instance
     self.log_message('Initialising...')
     self._pad_translations = None
     self._suggested_input_port = str('')
     self._suggested_output_port = str('')
     self._components = []
     self._displays = []
     self.controls = []
     self._highlighting_session_component = None
     self._device_component = None
     self._device_selection_follows_track_selection = False
     self._forwarding_long_identifier_registry = {}
     self._forwarding_registry = {}
     self._is_sending_scheduled_messages = BooleanContext()
     self._remaining_scheduled_messages = []
     self._task_group = Task.TaskGroup(auto_kill=False)
     self._in_build_midi_map = BooleanContext()
     self._suppress_requests_counter = 0
     self._rebuild_requests_during_suppression = 0
     self._enabled = True
     self._in_component_guard = BooleanContext()
     self._accumulate_midi_messages = BooleanContext()
     self._midi_message_dict = {}
     self._midi_message_list = []
     self._midi_message_count = 0
     self._control_surface_injector = inject(parent_task_group=const(self._task_group), show_message=const(self.show_message), log_message=const(self.log_message), register_component=const(self._register_component), register_control=const(self._register_control), request_rebuild_midi_map=const(self.request_rebuild_midi_map), send_midi=const(self._send_midi), song=self.song).everywhere()
     with self.setting_listener_caller():
         self.song().add_visible_tracks_listener(self._on_track_list_changed)
         self.song().add_scenes_listener(self._on_scene_list_changed)
         self.song().view.add_selected_track_listener(self._on_selected_track_changed)
         self.song().view.add_selected_scene_listener(self._on_selected_scene_changed)
     return
 def on_nested_control_element_value(self, value, sender):
     x, y = self._button_coordinates[sender]
     raise self._buttons[y][x] or AssertionError
     is_momentary = getattr(sender, 'is_momentary', const(None))()
     self.notify_value(value, x, y, is_momentary)
 def __init__(self, *a, **k):
     super(OptimizedControlSurface, self).__init__(*a, **k)
     self._optimized_ownership_handler = OptimizedOwnershipHandler()
     injecting = inject(
         element_ownership_handler=const(self._optimized_ownership_handler))
     self._ownership_handler_injector = injecting.everywhere()
 class ProxiedInterface(NotifyingControlElement.ProxiedInterface):
     send_value = nop
     receive_value = nop
     use_default_message = nop
     set_channel = nop
     message_channel = const(None)
Exemplo n.º 11
0
 def on_nested_control_element_value(self, value, sender):
     x, y = self._button_coordinates[sender]
     raise self._buttons[y][x] or AssertionError
     is_momentary = getattr(sender, 'is_momentary', const(None))()
     self.notify_value(value, x, y, is_momentary)
 def nested_display_resource_factory(self, display):
     wrapper = ClientWrapper(wrap=lambda c: (display, c), unwrap=partial(maybe(second)))
     return const(ProxyResource(proxied_resource=self._central_resource, client_wrapper=wrapper))
Exemplo n.º 13
0
class TaskGroup(Task):
    auto_kill = True
    auto_remove = True
    loop = False

    def __init__(self,
                 tasks=[],
                 auto_kill=None,
                 auto_remove=None,
                 loop=None,
                 *a,
                 **k):
        super(TaskGroup, self).__init__(*a, **k)
        if auto_kill is not None:
            self.auto_kill = auto_kill
        if auto_remove is not None:
            self.auto_remove = auto_remove
        if loop is not None:
            self.loop = loop
        self._tasks = []
        for task in tasks:
            self.add(task)

    def clear(self):
        for t in self._tasks:
            t._set_parent(None)

        self._tasks = []
        super(TaskGroup, self).clear()

    @depends(log_message=const(print_message), traceback=const(traceback))
    def do_update(self, timer, log_message=None, traceback=None):
        super(TaskGroup, self).do_update(timer)
        for task in self._tasks:
            if not task.is_killed:
                try:
                    task.update(timer)
                except Exception:
                    task.kill()
                    log_message('Error when executing task')
                    traceback.print_exc()

        if self.auto_remove:
            self._tasks = remove_if(lambda t: t.is_killed, self._tasks)
        all_killed = len(filter(lambda t: t.is_killed,
                                self._tasks)) == self.count
        if self.auto_kill and all_killed:
            self.kill()
        elif self.loop and all_killed:
            self.restart()

    def add(self, task):
        task = totask(task)
        task._set_parent(self)
        self._tasks.append(task)
        if self.is_killed:
            super(TaskGroup, self).restart()
        return task

    def remove(self, task):
        self._tasks.remove(task)
        task._set_parent(None)

    def find(self, task):
        return find_if(lambda t: t._task_equivalent(task), self._tasks)

    def restart(self):
        super(TaskGroup, self).restart()
        for x in self._tasks:
            x.restart()

    @property
    def count(self):
        return len(self._tasks)
 class ProxiedInterface(ControlElement.ProxiedInterface):
     set_num_segments = nop
     set_data_sources = nop
     segment = const(LogicalDisplaySegment(1, nop))
 def nested_display_resource_factory(self, display):
     wrapper = ClientWrapper(wrap=lambda c: (display, c),
                             unwrap=partial(maybe(second)))
     return const(
         ProxyResource(proxied_resource=self._central_resource,
                       client_wrapper=wrapper))
Exemplo n.º 16
0
class ControlElement(Disconnectable):
    """
    Base class for all classes representing control elements on a
    control surface
    """

    class ProxiedInterface(object):
        """
        Declaration of the interface to be used when the
        ControlElement is wrapped in any form of Proxy object.
        """
        send_midi = nop

        def __init__(self, outer = None, *a, **k):
            super(ControlElement.ProxiedInterface, self).__init__(*a, **k)
            self._outer = outer

        @property
        def outer(self):
            return self._outer

    @lazy_attribute
    def proxied_interface(self):
        return self.ProxiedInterface(outer=self)

    canonical_parent = None
    name = ''
    optimized_send_midi = True
    _has_resource = False
    _resource_type = StackingResource
    _has_task_group = False

    @depends(send_midi=None, register_control=None)
    def __init__(self, name = '', resource_type = None, optimized_send_midi = None, send_midi = None, register_control = None, *a, **k):
        super(ControlElement, self).__init__(*a, **k)
        self._send_midi = send_midi
        self.name = name
        if resource_type is not None:
            self._resource_type = resource_type
        if optimized_send_midi is not None:
            self.optimized_send_midi = optimized_send_midi
        register_control(self)

    def disconnect(self):
        self.reset()
        super(ControlElement, self).disconnect()

    def send_midi(self, message):
        raise message != None or AssertionError
        return self._send_midi(message, optimized=self.optimized_send_midi)

    def clear_send_cache(self):
        pass

    def reset(self):
        raise NotImplementedError

    @property
    def resource(self):
        return self._resource

    @lazy_attribute
    def _resource(self):
        self._has_resource = True
        return self._resource_type(self._on_resource_received, self._on_resource_lost)

    @lazy_attribute
    @depends(parent_task_group=Task.TaskGroup)
    def _tasks(self, parent_task_group = None):
        tasks = parent_task_group.add(Task.TaskGroup())
        self._has_task_group = True
        return tasks

    def _on_resource_received(self, client, *a, **k):
        self.notify_ownership_change(client, True)

    def _on_resource_lost(self, client):
        self.notify_ownership_change(client, False)

    @depends(element_ownership_handler=const(ElementOwnershipHandler()))
    def notify_ownership_change(self, client, grabbed, element_ownership_handler = None):
        element_ownership_handler.handle_ownership_change(self, client, grabbed)
Exemplo n.º 17
0
class InputControlElement(NotifyingControlElement):
    """
    Base class for all classes representing control elements on a controller
    """
    __subject_events__ = (SubjectEvent(name='value', signal=InputSignal, override=True),)
    _input_signal_listener_count = 0
    num_delayed_messages = 1
    send_depends_on_forwarding = True

    @depends(request_rebuild_midi_map=const(nop))
    def __init__(self, msg_type = None, channel = None, identifier = None, sysex_identifier = None, request_rebuild_midi_map = None, *a, **k):
        raise msg_type in MIDI_MSG_TYPES or AssertionError
        raise in_range(channel, 0, 16) or channel is None or AssertionError
        raise in_range(identifier, 0, 128) or identifier is None or AssertionError
        raise msg_type != MIDI_SYSEX_TYPE or channel == None or AssertionError
        raise msg_type != MIDI_SYSEX_TYPE or identifier == None or AssertionError
        raise msg_type == MIDI_SYSEX_TYPE or sysex_identifier == None or AssertionError
        super(InputControlElement, self).__init__(*a, **k)
        self._request_rebuild = request_rebuild_midi_map
        self._msg_type = msg_type
        self._msg_channel = channel
        self._msg_identifier = identifier
        self._msg_sysex_identifier = sysex_identifier
        self._original_channel = channel
        self._original_identifier = identifier
        self._needs_takeover = True
        self._is_mapped = True
        self._is_being_forwarded = True
        self._delayed_messages = []
        self._force_next_send = False
        self._mapping_feedback_delay = 0
        self._mapping_sensitivity = 1.0
        self._send_delayed_messages_task = self._tasks.add(Task.run(self._send_delayed_messages))
        self._send_delayed_messages_task.kill()
        self._parameter_to_map_to = None
        self._in_parameter_gesture = False
        self._last_sent_message = None
        self._report_input = False
        self._report_output = False

    def message_type(self):
        return self._msg_type

    def message_channel(self):
        return self._msg_channel

    def message_identifier(self):
        return self._msg_identifier

    def message_sysex_identifier(self):
        return self._msg_sysex_identifier

    def message_map_mode(self):
        raise NotImplementedError

    def _get_mapping_sensitivity(self):
        return self._mapping_sensitivity

    def _set_mapping_sensitivity(self, sensitivity):
        self._mapping_sensitivity = sensitivity

    mapping_sensitivity = property(_get_mapping_sensitivity, _set_mapping_sensitivity)

    def force_next_send(self):
        """
        Enforces sending the next value regardless of wether the
        control is mapped to the script.
        """
        self._force_next_send = True

    def set_channel(self, channel):
        if not self._msg_type != MIDI_SYSEX_TYPE:
            raise AssertionError
            raise in_range(channel, 0, 16) or channel == None or AssertionError
            self._msg_channel = self._msg_channel != channel and channel
            self._request_rebuild()

    def set_identifier(self, identifier):
        if not self._msg_type != MIDI_SYSEX_TYPE:
            raise AssertionError
            raise in_range(identifier, 0, 128) or identifier == None or AssertionError
            self._msg_identifier = self._msg_identifier != identifier and identifier
            self._request_rebuild()

    def set_needs_takeover(self, needs_takeover):
        raise self.message_type() != MIDI_NOTE_TYPE or AssertionError
        self._needs_takeover = needs_takeover

    def set_feedback_delay(self, delay):
        raise delay >= -1 or AssertionError
        self._mapping_feedback_delay = delay

    def needs_takeover(self):
        raise self.message_type() != MIDI_NOTE_TYPE or AssertionError
        return self._needs_takeover

    def use_default_message(self):
        if (self._msg_channel, self._msg_identifier) != (self._original_channel, self._original_identifier):
            self._msg_channel = self._original_channel
            self._msg_identifier = self._original_identifier
            self._request_rebuild()

    def _mapping_feedback_values(self):
        value_map = tuple()
        if self._mapping_feedback_delay != 0:
            if self._msg_type != MIDI_PB_TYPE:
                value_map = tuple(range(128))
            else:
                value_pairs = []
                for value in xrange(16384):
                    value_pairs.append((value >> 7 & 127, value & 127))

                value_map = tuple(value_pairs)
        return value_map

    def install_connections(self, install_translation, install_mapping, install_forwarding):
        self._send_delayed_messages_task.kill()
        self._is_mapped = False
        self._is_being_forwarded = False
        if self._msg_channel != self._original_channel or self._msg_identifier != self._original_identifier:
            install_translation(self._msg_type, self._original_identifier, self._original_channel, self._msg_identifier, self._msg_channel)
        if self._parameter_to_map_to != None:
            self._is_mapped = install_mapping(self, self._parameter_to_map_to, self._mapping_feedback_delay, self._mapping_feedback_values())
        if self.script_wants_forwarding():
            self._is_being_forwarded = install_forwarding(self)
            if self._is_being_forwarded and self.send_depends_on_forwarding:
                self._send_delayed_messages_task.restart()

    def script_wants_forwarding(self):
        """
        Returns wether the script wants to receive receive the values,
        otherwise, the control will be mapped to the track.
        
        Subclasses that overload this should _request_rebuild()
        whenever the property changes.
        """
        return self._input_signal_listener_count > 0 or self._report_input

    def begin_gesture(self):
        """
        Begins a modification on the input control element,
        meaning that we should consider the next flow of input data as
        a consistent gesture from the user.
        """
        if self._parameter_to_map_to and not self._in_parameter_gesture:
            self._in_parameter_gesture = True
            self._parameter_to_map_to.begin_gesture()

    def end_gesture(self):
        """
        Ends a modification of the input control element. See
        begin_gesture.
        """
        if self._parameter_to_map_to and self._in_parameter_gesture:
            self._in_parameter_gesture = False
            self._parameter_to_map_to.end_gesture()

    def connect_to(self, parameter):
        """ parameter is a Live.Device.DeviceParameter """
        if not parameter != None:
            raise AssertionError
            self._parameter_to_map_to = self._parameter_to_map_to != parameter and parameter
            self._request_rebuild()

    def release_parameter(self):
        if self._parameter_to_map_to != None:
            self.end_gesture()
            self._parameter_to_map_to = None
            self._request_rebuild()

    def mapped_parameter(self):
        return self._parameter_to_map_to

    def _status_byte(self, channel):
        status_byte = channel
        if self._msg_type == MIDI_NOTE_TYPE:
            status_byte += MIDI_NOTE_ON_STATUS
        elif self._msg_type == MIDI_CC_TYPE:
            status_byte += MIDI_CC_STATUS
        elif self._msg_type == MIDI_PB_TYPE:
            status_byte += MIDI_PB_STATUS
        else:
            raise NotImplementedError
        return status_byte

    def identifier_bytes(self):
        """
        Returns a list with all the MIDI message prefixes that
        identify this control element.
        """
        if self._msg_type == MIDI_PB_TYPE:
            return ((self._status_byte(self._msg_channel),),)
        elif self._msg_type == MIDI_SYSEX_TYPE:
            return (self.message_sysex_identifier(),)
        elif self._msg_type == MIDI_NOTE_TYPE:
            return ((self._status_byte(self._msg_channel), self.message_identifier()), (self._status_byte(self._msg_channel) - 16, self.message_identifier()))
        else:
            return ((self._status_byte(self._msg_channel), self.message_identifier()),)

    def _send_delayed_messages(self):
        self.clear_send_cache()
        for value, channel in self._delayed_messages:
            self._do_send_value(value, channel=channel)

        self._delayed_messages[:] = []

    def send_value(self, value, force_send = False, channel = None):
        value = int(value)
        self._verify_value(value)
        if force_send or self._force_next_send:
            self._do_send_value(value, channel)
        elif self.send_depends_on_forwarding and not self._is_being_forwarded or self._send_delayed_messages_task.is_running:
            first = 1 - self.num_delayed_messages
            self._delayed_messages = self._delayed_messages[first:] + [(value, channel)]
        elif (value, channel) != self._last_sent_message:
            self._do_send_value(value, channel)
        self._force_next_send = False

    def _do_send_value(self, value, channel = None):
        data_byte1 = self._original_identifier
        data_byte2 = value
        status_byte = self._status_byte(channel or self._original_channel)
        if self._msg_type == MIDI_PB_TYPE:
            data_byte1 = value & 127
            data_byte2 = value >> 7 & 127
        if self.send_midi((status_byte, data_byte1, data_byte2)):
            self._last_sent_message = (value, channel)
            if self._report_output:
                is_input = True
                self._report_value(value, not is_input)
        self._delayed_value_to_send = None
        self._delayed_channel = None

    def clear_send_cache(self):
        self._last_sent_message = None

    def reset(self):
        """ Send 0 to reset motorized faders and turn off LEDs """
        self.send_value(0)

    def receive_value(self, value):
        self._verify_value(value)
        self._last_sent_message = None
        self.notify_value(value)
        if self._report_input:
            is_input = True
            self._report_value(value, is_input)

    def set_report_values(self, report_input, report_output):
        """
        Set boolean values report_input and report_output enabling
        debug information.
        """
        self._report_input = report_input
        self._report_output = report_output

    def _verify_value(self, value):
        upper_bound = self._msg_type < MIDI_SYSEX_TYPE and (16384 if self._msg_type == MIDI_PB_TYPE else 128)
        if not in_range(value, 0, upper_bound):
            raise AssertionError

    def _report_value(self, value, is_input):
        self._verify_value(value)
        message = str(self.__class__.__name__) + ' ('
        if self._msg_type == MIDI_NOTE_TYPE:
            message += 'Note ' + str(self._msg_identifier) + ', '
        elif self._msg_type == MIDI_CC_TYPE:
            message += 'CC ' + str(self._msg_identifier) + ', '
        else:
            message += 'PB '
        message += 'Chan. ' + str(self._msg_channel)
        message += ') '
        message += 'received value ' if is_input else 'sent value '
        message += str(value)
        debug_print(message)

    @property
    def _last_sent_value(self):
        return self._last_sent_message[0] if self._last_sent_message else -1
 def __init__(self, *a, **k):
     super(OptimizedControlSurface, self).__init__(*a, **k)
     self._optimized_ownership_handler = OptimizedOwnershipHandler()
     injecting = inject(element_ownership_handler=const(self._optimized_ownership_handler))
     self._ownership_handler_injector = injecting.everywhere()