def __init__(self, mixer):
     ModeSelectorComponent.__init__(self)
     self._mixer = mixer
     self._controls = None
     self._page_names = ('Vol', 'Pan', 'SendA', 'SendB', 'SendC')
     self._page_name_sources = None
     self._current_page_data_source = DisplayDataSource()
     self._parameter_sources = [DisplayDataSource() for index in range(8)]
     self._show_volume_page = False
     self._mixer.set_update_callback(self._mixer_assignments_changed)
Пример #2
0
 def __init__(self, *a, **k):
     (super(PageableDeviceComponent, self).__init__)(*a, **k)
     self._parameter_value_data_source = DisplayDataSource()
     self._parameter_name_data_sources = []
     self._page_name_data_sources = []
     self._page_index = [0, 0, 0, 0]
     for new_index in range(8):
         self._parameter_name_data_sources.append(DisplayDataSource())
         self._page_name_data_sources.append(DisplayDataSource())
         self._parameter_name_data_sources[(-1)].set_display_string(' - ')
         self._page_name_data_sources[(-1)].set_display_string(' - ')
 def _setup_name_display(self):
     self._name_display = PhysicalDisplayElement(16, 1)
     self._name_display.name = 'Display'
     self._name_display.set_message_parts(SYSEX_START + (8, ), (247, ))
     self._name_display_data_source = DisplayDataSource()
     self._name_display.segment(0).set_data_source(
         self._name_display_data_source)
    def __init__(self, *a, **k):
        (super(BestBankDeviceComponent, self).__init__)(*a, **k)
        new_banks = {}
        new_bank_names = {}
        self._device_banks = DEVICE_DICT
        self._device_bank_names = BANK_NAME_DICT
        self._device_best_banks = DEVICE_BOB_DICT
        for device_name, current_banks in self._device_banks.items():
            if len(current_banks) > 1:
                current_banks = self._device_best_banks[device_name] + current_banks
                new_bank_names[device_name] = (BOP_BANK_NAME,) + self._device_bank_names[device_name]
            else:
                new_banks[device_name] = current_banks

        self._device_banks = new_banks
        self._device_bank_names = new_bank_names
        self._bank_name_data_source = DisplayDataSource()
 def _setup_displays(self):
     self._name_display = PhysicalDisplayElement(12, 1)
     self._name_display.name = 'Name_Display'
     self._name_display.set_message_parts(SYSEX_START + (21, ), (0, 247))
     self._name_display.set_clear_all_message(CLEAR_NAME)
     self._name_display_data_source = DisplayDataSource()
     self._name_display.segment(0).set_data_source(
         self._name_display_data_source)
     self._value_display = NumericalDisplayElement(3, 1)
     self._value_display.name = 'Value_Display'
     self._value_display.set_message_parts(SYSEX_START + (20, 48), (0, 247))
     self._value_display.set_clear_all_message(CLEAR_VALUE)
     self._value_display_data_source = DisplayDataSource()
     self._value_display.segment(0).set_data_source(
         self._value_display_data_source)
     self._bank_display = NumericalDisplayElement(3, 1)
     self._bank_display.name = 'Bank_Display'
     self._bank_display.set_message_parts(SYSEX_START + (19, ), (0, 247))
     self._bank_display.set_clear_all_message(CLEAR_BANK)
     self._bank_display_data_source = DisplayDataSource()
     self._bank_display.segment(0).set_data_source(
         self._bank_display_data_source)
     self._pad_display = NumericalDisplayElement(2, 1)
     self._pad_display.name = 'Pad_Display'
     self._pad_display.set_message_parts(SYSEX_START + (18, ), (0, 247))
     self._pad_display.set_clear_all_message(CLEAR_PAD)
     self._pad_display_data_source = DisplayDataSource()
     self._pad_display.segment(0).set_data_source(
         self._pad_display_data_source)
    def _create_display(self):
        self._display_line1, self._display_line2 = DisplayElement(
            16, 1), DisplayElement(16, 1)
        for index, display_line in enumerate(
            (self._display_line1, self._display_line2)):
            display_line.set_message_parts(SETUP_MSG_PREFIX + (4, 0, 96),
                                           SETUP_MSG_SUFFIX)
            display_line.segment(0).set_position_identifier((index + 1, ))

        def adjust_null_terminated_string(string, width):
            return string.ljust(width, ' ') + '\x00'

        self._display_line1_data_source, self._display_line2_data_source = DisplayDataSource(
            adjust_string_fn=adjust_null_terminated_string), DisplayDataSource(
                adjust_string_fn=adjust_null_terminated_string)
        self._display_line1.segment(0).set_data_source(
            self._display_line1_data_source)
        self._display_line2.segment(0).set_data_source(
            self._display_line2_data_source)
        self._display_line1_data_source.set_display_string('KeyLab')
        self._display_line2_data_source.set_display_string('Ableton Live')
    def page_name_data_source(self, index):
        if self._page_name_sources == None:
            self._page_name_sources = []
            offset = 0
            if not self._show_volume_page:
                offset = 1
            for idx in range(4):
                self._page_name_sources.append(DisplayDataSource())
                self._page_name_sources[idx].set_display_string(
                    self._page_names[(idx + offset)])

        return self._page_name_sources[index]
Пример #8
0
    def __init__(self, mixer_modes, device, encoders, page_buttons):
        ModeSelectorComponent.__init__(self)
        self._mixer_modes = mixer_modes
        self._device = device
        self._encoders = encoders
        self._page_buttons = page_buttons
        self._peek_button = None
        self._encoders_display = None
        self._value_display = None
        self._device_display = None
        self._page_displays = None
        self._device_dummy_source = DisplayDataSource()
        self._parameter_source = DisplayDataSource()
        self._device_dummy_source.set_display_string('Mixer')
        self._clean_value_display_in = -1
        self._must_update_encoder_display = False
        self._register_timer_callback(self._on_timer)
        identify_sender = True
        for encoder in self._encoders:
            encoder.add_value_listener(self._parameter_value, identify_sender)

        self.set_mode(0)
class KeyLab(ArturiaControlSurface):
    def __init__(self, *a, **k):
        (super(KeyLab, self).__init__)(*a, **k)
        with self.component_guard():
            self._create_controls()
            self._create_display()
            self._create_device()
            self._create_drums()
            self._create_transport()
            self._create_session()
            self._create_session_recording()
            self._create_mixer()

    def _create_controls(self):
        self._device_encoders = ButtonMatrixElement(rows=[[
            EncoderElement(MIDI_CC_TYPE,
                           ENCODER_CHANNEL,
                           identifier, (Live.MidiMap.MapMode.
                                        relative_smooth_binary_offset),
                           name=('Device_Encoder_%d_%d' %
                                 (col_index, row_index)))
            for col_index, identifier in enumerate(row)
        ] for row_index, row in enumerate((ENCODER_MSG_IDS[:4],
                                           ENCODER_MSG_IDS[4:8]))])
        self._horizontal_scroll_encoder = EncoderElement(
            MIDI_CC_TYPE,
            ENCODER_CHANNEL, (ENCODER_MSG_IDS[(-2)]),
            (Live.MidiMap.MapMode.relative_smooth_binary_offset),
            name='Horizontal_Scroll_Encoder')
        self._vertical_scroll_encoder = EncoderElement(
            MIDI_CC_TYPE,
            ENCODER_CHANNEL, (ENCODER_MSG_IDS[(-1)]),
            (Live.MidiMap.MapMode.relative_smooth_binary_offset),
            name='Vertical_Scroll_Encoder')
        self._volume_sliders = ButtonMatrixElement(rows=[[
            SliderElement(MIDI_CC_TYPE, ENCODER_CHANNEL, identifier)
            for identifier in SLIDER_MSG_IDS[:-1]
        ]])
        self._master_slider = SliderElement(MIDI_CC_TYPE, ENCODER_CHANNEL,
                                            SLIDER_MSG_IDS[(-1)])

        def make_keylab_button(name):
            button = ButtonElement(True,
                                   MIDI_CC_TYPE,
                                   0, (get_button_identifier_by_name(name)),
                                   name=(name.title()))
            return button

        for button_name in list(BUTTON_HARDWARE_AND_MESSAGE_IDS.keys()):
            setattr(self, '_' + button_name, make_keylab_button(button_name))

        self._pads = ButtonMatrixElement(rows=[[
            ButtonElement(True,
                          MIDI_CC_TYPE,
                          PAD_CHANNEL, (col_index + row_offset),
                          name=('Pad_%d_%d' % (col_index, row_index)))
            for col_index in range(4)
        ] for row_index, row_offset in enumerate(range(48, 35, -4))])

    def _create_display(self):
        self._display_line1, self._display_line2 = DisplayElement(
            16, 1), DisplayElement(16, 1)
        for index, display_line in enumerate(
            (self._display_line1, self._display_line2)):
            display_line.set_message_parts(SETUP_MSG_PREFIX + (4, 0, 96),
                                           SETUP_MSG_SUFFIX)
            display_line.segment(0).set_position_identifier((index + 1, ))

        def adjust_null_terminated_string(string, width):
            return string.ljust(width, ' ') + '\x00'

        self._display_line1_data_source, self._display_line2_data_source = DisplayDataSource(
            adjust_string_fn=adjust_null_terminated_string), DisplayDataSource(
                adjust_string_fn=adjust_null_terminated_string)
        self._display_line1.segment(0).set_data_source(
            self._display_line1_data_source)
        self._display_line2.segment(0).set_data_source(
            self._display_line2_data_source)
        self._display_line1_data_source.set_display_string('KeyLab')
        self._display_line2_data_source.set_display_string('Ableton Live')

    def _create_device(self):
        self._device = DeviceComponent(
            name='Device',
            is_enabled=False,
            layer=Layer(parameter_controls=(self._device_encoders)),
            device_selection_follows_track_selection=True)
        self._device.set_enabled(True)
        self.set_device_component(self._device)
        self._device_navigation = DeviceNavigationComponent(
            name='Device_Navigation',
            is_enabled=False,
            layer=Layer(device_nav_left_button=(self._device_left_button),
                        device_nav_right_button=(self._device_right_button)))
        self._device_navigation.set_enabled(True)

    def _create_drums(self):
        self._drums = DrumRackComponent(name='Drums',
                                        is_enabled=False,
                                        layer=Layer(pads=(self._pads)))
        self._drums.set_enabled(True)

    def _create_transport(self):
        self._transport = TransportComponent(
            name='Transport',
            is_enabled=False,
            layer=Layer(play_button=(self._play_button),
                        stop_button=(self._stop_button),
                        record_button=(self._record_button),
                        loop_button=(self._loop_button)))
        self._transport.set_enabled(True)

    def _create_session(self):
        self._session = SessionComponent(
            num_tracks=8,
            num_scenes=1,
            name='Session',
            is_enabled=False,
            layer=Layer(
                select_next_button=(self._scene_down_button),
                select_prev_button=(self._scene_up_button),
                selected_scene_launch_button=(self._scene_launch_button),
                stop_all_clips_button=(self._stop_all_clips_button),
                scene_select_encoder=(self._vertical_scroll_encoder)))
        self._session.set_enabled(True)

    def _create_session_recording(self):
        self._session_recording = SessionRecordingComponent(
            (ClipCreator()), (ViewControlComponent()),
            name='Session_Recording',
            is_enabled=False,
            layer=Layer(record_button=(self._session_record_button)))
        self._session_recording.set_enabled(True)

    def _create_mixer(self):
        self._mixer = MixerComponent(
            num_tracks=(self._volume_sliders.width()),
            name='Mixer',
            is_enabled=False,
            layer=Layer(
                volume_controls=(self._volume_sliders),
                track_select_encoder=(self._horizontal_scroll_encoder)))
        self._mixer.master_strip().layer = Layer(
            volume_control=(self._master_slider))
        self._mixer.set_enabled(True)

    def _collect_setup_messages(self):
        for hardware_id, identifier in zip(ENCODER_HARDWARE_IDS,
                                           ENCODER_MSG_IDS):
            self._setup_hardware_encoder(hardware_id, identifier,
                                         ENCODER_CHANNEL)

        for hardware_id, identifier in zip(SLIDER_HARDWARE_IDS,
                                           SLIDER_MSG_IDS):
            self._setup_hardware_slider(hardware_id, identifier,
                                        ENCODER_CHANNEL)

        for hardware_id, identifier in BUTTON_HARDWARE_AND_MESSAGE_IDS.values(
        ):
            self._setup_hardware_button(hardware_id, identifier)

        for hardware_id, identifier in zip(PAD_HARDWARE_IDS, PAD_MSG_IDS):
            self._setup_hardware_pad(hardware_id, identifier)

    def _setup_hardware_encoder(self, hardware_id, identifier, channel=0):
        self._set_encoder_cc_msg_type(hardware_id, is_relative=True)
        self._set_identifier(hardware_id, identifier)
        self._set_channel(hardware_id, channel)

    def _setup_hardware_button(self, hardware_id, identifier, channel=0, **k):
        self._set_encoder_cc_msg_type(hardware_id)
        self._set_identifier(hardware_id, identifier)
        self._set_channel(hardware_id, channel)
        self._set_value_minimum(hardware_id)
        self._set_value_maximum(hardware_id)

    def _setup_hardware_pad(self,
                            hardware_id,
                            identifier,
                            channel=PAD_CHANNEL):
        self._set_pad_note_msg_type(hardware_id)
        self._set_identifier(hardware_id, identifier)
        self._set_channel(hardware_id, channel)

    def _set_pad_note_msg_type(self, hardware_id):
        self._collect_setup_message(MODE_PROPERTY, hardware_id, PAD_NOTE_MODE)

    def _setup_hardware(self):
        for msg in self._messages_to_send:
            self._tasks.add(
                Task.sequence(partial(self._send_midi, msg),
                              Task.wait(MESSAGE_DELAY)))

        self._messages_to_send = []
class EncoderMixerModeSelector(ModeSelectorComponent):
    def __init__(self, mixer):
        ModeSelectorComponent.__init__(self)
        self._mixer = mixer
        self._controls = None
        self._page_names = ('Vol', 'Pan', 'SendA', 'SendB', 'SendC')
        self._page_name_sources = None
        self._current_page_data_source = DisplayDataSource()
        self._parameter_sources = [DisplayDataSource() for index in range(8)]
        self._show_volume_page = False
        self._mixer.set_update_callback(self._mixer_assignments_changed)

    def disconnect(self):
        for button in self._modes_buttons:
            button.remove_value_listener(self._mode_value)

        self._mixer = None
        self._controls = None
        self._page_names = None
        self._page_name_sources = None
        self._current_page_data_source = None
        self._parameter_sources = None
        ModeSelectorComponent.disconnect(self)

    def set_modes_buttons(self, buttons):
        identify_sender = True
        for button in self._modes_buttons:
            button.remove_value_listener(self._mode_value)

        self._modes_buttons = []
        if buttons != None:
            for button in buttons:
                self._modes_buttons.append(button)
                button.add_value_listener(self._mode_value, identify_sender)

        self.set_mode(0)
        self.update()

    def set_controls(self, controls):
        self._controls = controls
        self.set_mode(0)
        self.update()

    def set_show_volume_page(self, show):
        if show != self._show_volume_page:
            self._show_volume_page = show
            if self._page_name_sources != None:
                offset = 0
                if not self._show_volume_page:
                    offset = 1
                for idx in range(4):
                    self._page_name_sources[idx].set_display_string(
                        self._page_names[(idx + offset)])

            self.update()

    def page_name_data_source(self, index):
        if self._page_name_sources == None:
            self._page_name_sources = []
            offset = 0
            if not self._show_volume_page:
                offset = 1
            for idx in range(4):
                self._page_name_sources.append(DisplayDataSource())
                self._page_name_sources[idx].set_display_string(
                    self._page_names[(idx + offset)])

        return self._page_name_sources[index]

    def parameter_data_source(self, index):
        return self._mixer.channel_strip(index).track_name_data_source()

    def current_page_data_source(self):
        return self._current_page_data_source

    def number_of_modes(self):
        return 4

    def update(self):
        super(EncoderMixerModeSelector, self).update()
        if self.is_enabled():
            if self._controls != None:
                mode = self._mode_index
                if not self._show_volume_page:
                    mode += 1
                self._current_page_data_source.set_display_string(
                    self._page_names[mode])
                for index in range(len(self._controls)):
                    self._controls[index].release_parameter()
                    self._mixer.channel_strip(
                        index).track_name_data_source().update()
                    self._mixer.channel_strip(index).set_pan_control(None)
                    self._mixer.channel_strip(index).set_send_controls(
                        (None, None, None))
                    if self._show_volume_page:
                        self._mixer.channel_strip(index).set_volume_control(
                            None)
                    if mode == 0:
                        self._mixer.channel_strip(index).set_volume_control(
                            self._controls[index])
                    else:
                        if mode == 1:
                            self._mixer.channel_strip(index).set_pan_control(
                                self._controls[index])
                        else:
                            if mode == 2:
                                self._mixer.channel_strip(
                                    index).set_send_controls(
                                        (self._controls[index], None, None))
                            else:
                                if mode == 3:
                                    self._mixer.channel_strip(
                                        index).set_send_controls(
                                            (None, self._controls[index],
                                             None))
                                else:
                                    if mode == 4:
                                        self._mixer.channel_strip(
                                            index).set_send_controls(
                                                (None, None,
                                                 self._controls[index]))
                                    else:
                                        print('Invalid mode index')

    def _mixer_assignments_changed(self):
        self.update()
Пример #11
0
class PageableDeviceComponent(DeviceComponent):
    def __init__(self, *a, **k):
        (super(PageableDeviceComponent, self).__init__)(*a, **k)
        self._parameter_value_data_source = DisplayDataSource()
        self._parameter_name_data_sources = []
        self._page_name_data_sources = []
        self._page_index = [0, 0, 0, 0]
        for new_index in range(8):
            self._parameter_name_data_sources.append(DisplayDataSource())
            self._page_name_data_sources.append(DisplayDataSource())
            self._parameter_name_data_sources[(-1)].set_display_string(' - ')
            self._page_name_data_sources[(-1)].set_display_string(' - ')

    def disconnect(self):
        self._parameter_value_data_source = None
        self._parameter_name_data_sources = None
        self._page_name_data_sources = None
        DeviceComponent.disconnect(self)

    def set_device(self, device):
        DeviceComponent.set_device(self, device)
        if self._device == None:
            for source in self._parameter_name_data_sources:
                source.set_display_string(' - ')

            for source in self._page_name_data_sources:
                source.set_display_string(' - ')

    def set_bank_buttons(self, buttons):
        DeviceComponent.set_bank_buttons(self, buttons)

    def set_parameter_controls(self, controls):
        if self._parameter_controls != None:
            for control in self._parameter_controls:
                if self._device != None:
                    control.release_parameter()

        self._parameter_controls = controls
        if self._parameter_controls != None:
            for control in self._parameter_controls:
                pass

        self.update()

    def parameter_value_data_source(self):
        return self._parameter_value_data_source

    def parameter_name_data_source(self, index):
        return self._parameter_name_data_sources[index]

    def page_name_data_source(self, index):
        return self._page_name_data_sources[index]

    def _bank_value(self, value, button):
        if self.is_enabled():
            if not button.is_momentary() or value is not 0:
                bank = list(self._bank_buttons).index(button)
                if self._device != None:
                    if bank != self._bank_index:
                        self._bank_index = bank
                    else:
                        self._page_index[bank] += 1
                    self.update()

    def _assign_parameters(self):
        if self._device.class_name in list(SPECIAL_DEVICE_DICT.keys()):
            self._PageableDeviceComponent__assign_parameters_special()
        elif self._device.class_name in list(DEVICE_DICT.keys()):
            self._PageableDeviceComponent__assign_parameters_normal()
        else:
            self._PageableDeviceComponent__assign_parameters_plugin()
        self._parameter_value_data_source.set_display_string('')
        for index in range(len(self._parameter_controls)):
            if self._parameter_controls[index].mapped_parameter() != None:
                self._parameter_name_data_sources[index].set_display_string(
                    self._parameter_controls[index].mapped_parameter().name)
            else:
                self._parameter_name_data_sources[index].set_display_string(
                    ' - ')

    def __assign_parameters_special(self):
        banks = SPECIAL_DEVICE_DICT[self._device.class_name]
        bank_names = SPECIAL_NAME_DICT[self._device.class_name]
        pages = banks[self._bank_index]
        self._page_index[self._bank_index] %= len(pages)
        self._bank_name = bank_names[self._bank_index][self._page_index[
            self._bank_index]]
        page = pages[self._page_index[self._bank_index]]
        for index in range(len(self._parameter_controls)):
            parameter = get_parameter_by_name(self._device, page[index])
            if parameter != None:
                self._parameter_controls[index].connect_to(parameter)
            else:
                self._parameter_controls[index].release_parameter()

        for index in range(len(self._page_name_data_sources)):
            if index < len(bank_names):
                page_names = bank_names[index]
                if index == self._bank_index:
                    self._page_name_data_sources[index].set_display_string(
                        page_names[((self._page_index[index] + 1) %
                                    len(page_names))])
                else:
                    self._page_name_data_sources[index].set_display_string(
                        page_names[(self._page_index[index] %
                                    len(page_names))])
            else:
                self._page_name_data_sources[index].set_display_string(' - ')

    def __assign_parameters_normal(self):
        self._page_index[self._bank_index] = 0
        banks = DEVICE_DICT[self._device.class_name]
        bank_names = []
        if len(banks) > self._bank_index:
            if self._device.class_name in list(BANK_NAME_DICT.keys()):
                if len(BANK_NAME_DICT[self._device.class_name]) > 1:
                    bank_names = BANK_NAME_DICT[self._device.class_name]
            bank = banks[self._bank_index]
            if self._bank_index in range(len(bank_names)):
                self._bank_name = bank_names[self._bank_index]
            else:
                self._bank_name = 'Bank ' + str(self._bank_index + 1)
            for index in range(len(self._parameter_controls)):
                parameter = get_parameter_by_name(self._device, bank[index])
                if parameter != None:
                    self._parameter_controls[index].connect_to(parameter)
                else:
                    self._parameter_controls[index].release_parameter()

        for index in range(len(self._page_name_data_sources)):
            if index < len(bank_names):
                self._page_name_data_sources[index].set_display_string(
                    bank_names[index])
            else:
                self._page_name_data_sources[index].set_display_string(' - ')

    def __assign_parameters_plugin(self):
        num_controls = len(self._parameter_controls)
        num_banks = min(8, number_of_parameter_banks(self._device))
        num_double_pages = 0
        num_double_pages_before = 0
        parameters_to_use = self._device.parameters[1:]
        self._bank_name = 'Bank ' + str(self._bank_index + 1)
        if num_banks > 4:
            num_double_pages = num_banks - 4
        if self._bank_index < num_double_pages:
            self._page_index[self._bank_index] %= 2
            num_double_pages_before = self._bank_index
        else:
            self._page_index[self._bank_index] = 0
            num_double_pages_before = num_double_pages
        if self._bank_index + num_double_pages_before < num_banks:
            bank_offset = (self._bank_index +
                           num_double_pages_before) * num_controls
            page_offset = bank_offset + self._page_index[
                self._bank_index] * num_controls
            for control in self._parameter_controls:
                if page_offset < len(parameters_to_use):
                    control.connect_to(parameters_to_use[page_offset])
                else:
                    control.release_parameter()
                page_offset += 1

            bank_names = []
            parameter_offset = 0
            for index in range(4):
                display_string = ' - '
                if index < num_banks:
                    if index < num_double_pages:
                        add_offset_before = (index == self._bank_index) and (
                            self._page_index[index]
                            == 0) or (index != self._bank_index
                                      and self._page_index[index] != 0)
                        if add_offset_before:
                            parameter_offset += num_controls
                        display_string = str(parameter_offset + 1).rjust(
                            2) + '-' + str(parameter_offset +
                                           num_controls).rjust(2)
                        add_offset_before or parameter_offset += num_controls
                    else:
                        display_string = str(parameter_offset + 1).rjust(
                            2) + '-' + str(parameter_offset +
                                           num_controls).rjust(2)
                self._page_name_data_sources[index].set_display_string(
                    display_string)
                parameter_offset += num_controls
Пример #12
0
class MixerOrDeviceModeSelector(ModeSelectorComponent):
    def __init__(self, mixer_modes, device, encoders, page_buttons):
        ModeSelectorComponent.__init__(self)
        self._mixer_modes = mixer_modes
        self._device = device
        self._encoders = encoders
        self._page_buttons = page_buttons
        self._peek_button = None
        self._encoders_display = None
        self._value_display = None
        self._device_display = None
        self._page_displays = None
        self._device_dummy_source = DisplayDataSource()
        self._parameter_source = DisplayDataSource()
        self._device_dummy_source.set_display_string('Mixer')
        self._clean_value_display_in = -1
        self._must_update_encoder_display = False
        self._register_timer_callback(self._on_timer)
        identify_sender = True
        for encoder in self._encoders:
            encoder.add_value_listener(self._parameter_value, identify_sender)

        self.set_mode(0)

    def disconnect(self):
        self._unregister_timer_callback(self._on_timer)
        self._mixer_modes = None
        self._device = None
        self._encoders = None
        self._page_buttons = None
        self._encoders_display = None
        self._value_display = None
        self._device_display = None
        self._page_displays = None
        self._device_dummy_source = None
        self._parameter_source = None
        ModeSelectorComponent.disconnect(self)

    def set_displays(self, encoders_display, value_display, device_display,
                     page_displays):
        self._encoders_display = encoders_display
        self._value_display = value_display
        self._device_display = device_display
        self._page_displays = page_displays
        if self._value_display != None:
            self._value_display.segment(0).set_data_source(
                self._parameter_source)
        self.update()

    def set_peek_button(self, button):
        if self._peek_button != button:
            if self._peek_button != None:
                self._peek_button.remove_value_listener(self._peek_value)
            self._peek_button = button
            if self._peek_button != None:
                self._peek_button.add_value_listener(self._peek_value)
            self.update()

    def number_of_modes(self):
        return 2

    def update(self):
        super(MixerOrDeviceModeSelector, self).update()
        if self.is_enabled():
            if self._mode_index == 0:
                self._device.set_parameter_controls(None)
                self._device.set_bank_buttons(None)
                self._mixer_modes.set_controls(self._encoders)
                self._mixer_modes.set_modes_buttons(self._page_buttons)
                if self._device_display != None:
                    self._device_display.segment(0).set_data_source(
                        self._mixer_modes.current_page_data_source())
                    self._device_display.update()
                if self._encoders_display != None:
                    for index in range(len(self._encoders)):
                        self._encoders_display.segment(index).set_data_source(
                            self._mixer_modes.parameter_data_source(index))

                    self._encoders_display.update()
                if self._page_displays != None:
                    for index in range(len(self._page_displays)):
                        self._page_displays[index].segment(0).set_data_source(
                            self._mixer_modes.page_name_data_source(index))
                        self._page_displays[index].update()

            elif self._mode_index == 1:
                self._mixer_modes.set_controls(None)
                self._mixer_modes.set_modes_buttons(None)
                self._device.set_parameter_controls(self._encoders)
                self._device.set_bank_buttons(self._page_buttons)
                if self._device_display != None:
                    self._device_display.segment(0).set_data_source(
                        self._device.device_name_data_source())
                    self._device_display.update()
                if self._encoders_display != None:
                    for index in range(len(self._encoders)):
                        self._encoders_display.segment(index).set_data_source(
                            self._device.parameter_name_data_source(index))

                    self._encoders_display.update()
                if self._page_displays != None:
                    for index in range(len(self._page_displays)):
                        self._page_displays[index].segment(0).set_data_source(
                            self._device.page_name_data_source(index))
                        self._page_displays[index].update()

            else:
                print('Invalid mode index')

    def _parameter_value(self, value, control):
        if self.is_enabled():
            parameter = control.mapped_parameter()
            if parameter != None:
                self._parameter_source.set_display_string(parameter.name +
                                                          ': ' +
                                                          parameter.__str__())
            else:
                self._parameter_source.set_display_string('<unmapped>')
            self._clean_value_display_in = 20

    def _on_timer(self):
        if self._clean_value_display_in > 0:
            self._clean_value_display_in -= 1
            if self._clean_value_display_in == 0:
                self._parameter_source.set_display_string('')
                self._clean_value_display_in = -1
        if self._must_update_encoder_display:
            self._encoders_display.update()
            self._must_update_encoder_display = False

    def _peek_value(self, value):
        new_peek_mode = value != 0
        peek_changed = False
        for encoder in self._encoders:
            if new_peek_mode != encoder.get_peek_mode():
                encoder.set_peek_mode(new_peek_mode)
                peek_changed = True

        if peek_changed:
            if self._encoders_display != None:
                self._must_update_encoder_display = True
class Axiom_DirectLink(ControlSurface):
    def __init__(self, c_instance):
        ControlSurface.__init__(self, c_instance)
        with self.component_guard():
            self.set_pad_translations(PAD_TRANSLATIONS)
            self._suggested_input_port = 'DirectLink'
            self._suggested_output_port = 'DirectLink'
            self._waiting_for_first_response = True
            self._has_sliders = True
            self._current_midi_map = None
            self._display_reset_delay = -1
            self._shift_pressed = False
            self._shift_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15,
                                               13)
            self._master_slider = SliderElement(MIDI_CC_TYPE, 15, 41)
            self._next_nav_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE,
                                                  15, 111)
            self._prev_nav_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE,
                                                  15, 110)
            self._device_bank_buttons = None
            self._device_navigation = None
            self._shift_button.name = 'Shift_Button'
            self._master_slider.name = 'Master_Volume_Control'
            self._next_nav_button.name = 'Next_Track_Button'
            self._prev_nav_button.name = 'Prev_Track_Button'
            self._master_slider.add_value_listener((self._slider_value),
                                                   identify_sender=True)
            self._shift_button.add_value_listener(self._shift_value)
            self._setup_mixer()
            self._setup_transport_and_session()
            self._setup_device()
            self._setup_display()
            for component in self.components:
                component.set_enabled(False)

    def refresh_state(self):
        ControlSurface.refresh_state(self)
        self._waiting_for_first_response = True
        self.schedule_message(3, self._send_midi, SYSEX_START + (32, 46, 247))

    def handle_sysex(self, midi_bytes):
        if midi_bytes[0:-2] == SYSEX_START + (32, ):
            if midi_bytes[(-2)] != 0:
                self._has_sliders = midi_bytes[(-2)] & 8 != 0
                if self._waiting_for_first_response:
                    self._waiting_for_first_response = False
                    self.schedule_message(1, self._show_startup_message)
                    for component in self.components:
                        component.set_enabled(True)

                if self._has_sliders:
                    self._mixer.master_strip().set_volume_control(
                        self._master_slider)
                    self._mixer.update()
                else:
                    self._mixer.master_strip().set_volume_control(None)
                    self._mixer.selected_strip().set_volume_control(
                        self._master_slider)
                self.request_rebuild_midi_map()

    def disconnect(self):
        self._display_data_source.set_display_string('  ')
        self._shift_button.remove_value_listener(self._shift_value)
        self._inst_button.remove_value_listener(self._inst_value)
        for encoder in self._encoders:
            encoder.remove_value_listener(self._encoder_value)

        for slider in tuple(self._sliders) + (self._master_slider, ):
            slider.remove_value_listener(self._slider_value)

        for button in tuple(
                self._strip_buttons) + (self._selected_mute_solo_button, ):
            button.remove_value_listener(self._mixer_button_value)

        for button in self._device_bank_buttons:
            button.remove_value_listener(self._device_bank_value)

        self._encoders = None
        self._sliders = None
        self._strip_buttons = None
        self._master_slider = None
        self._current_midi_map = None
        self._selected_mute_solo_button = None
        self._inst_button = None
        self._shift_button = None
        self._device_navigation = None
        self._display = None
        ControlSurface.disconnect(self)
        self._send_midi(SYSEX_START + (32, 0, 247))

    def build_midi_map(self, midi_map_handle):
        self._current_midi_map = midi_map_handle
        ControlSurface.build_midi_map(self, midi_map_handle)

    def update_display(self):
        ControlSurface.update_display(self)
        if self._display_reset_delay >= 0:
            self._display_reset_delay -= 1
            if self._display_reset_delay == -1:
                self._show_current_track_name()

    def _setup_mixer(self):
        self._selected_mute_solo_button = ButtonElement(
            IS_MOMENTARY, MIDI_CC_TYPE, 15, 12)
        mute_solo_flip_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15,
                                              57)
        self._strip_buttons = []
        self._selected_mute_solo_button.name = 'Selected_Mute_Button'
        mute_solo_flip_button.name = 'Mute_Solo_Flip_Button'
        self._selected_mute_solo_button.add_value_listener(
            (self._mixer_button_value), identify_sender=True)
        self._mixer = ShiftableMixerComponent(8)
        self._mixer.name = 'Mixer'
        self._mixer.set_shift_button(self._shift_button)
        self._mixer.set_selected_mute_solo_button(
            self._selected_mute_solo_button)
        self._mixer.set_select_buttons(self._next_nav_button,
                                       self._prev_nav_button)
        self._mixer.selected_strip().name = 'Selected_Channel_Strip'
        self._mixer.master_strip().name = 'Master_Channel_Strip'
        self._mixer.master_strip().set_volume_control(self._master_slider)
        self._sliders = []
        for index in range(8):
            strip = self._mixer.channel_strip(index)
            strip.name = 'Channel_Strip_' + str(index)
            strip.set_invert_mute_feedback(True)
            self._sliders.append(SliderElement(MIDI_CC_TYPE, 15, 33 + index))
            self._sliders[(-1)].name = str(index) + '_Volume_Control'
            self._sliders[(-1)].set_feedback_delay(-1)
            self._sliders[(-1)].add_value_listener((self._slider_value),
                                                   identify_sender=True)
            strip.set_volume_control(self._sliders[(-1)])
            self._strip_buttons.append(
                ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 49 + index))
            self._strip_buttons[(-1)].name = str(index) + '_Mute_Button'
            self._strip_buttons[(-1)].add_value_listener(
                (self._mixer_button_value), identify_sender=True)

        self._mixer.set_strip_mute_solo_buttons(tuple(self._strip_buttons),
                                                mute_solo_flip_button)

    def _setup_transport_and_session(self):
        ffwd_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 115)
        rwd_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 114)
        loop_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 113)
        play_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 117)
        stop_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 116)
        rec_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 118)
        ffwd_button.name = 'FFwd_Button'
        rwd_button.name = 'Rwd_Button'
        loop_button.name = 'Loop_Button'
        play_button.name = 'Play_Button'
        stop_button.name = 'Stop_Button'
        rec_button.name = 'Record_Button'
        transport = ShiftableTransportComponent()
        transport.name = 'Transport'
        transport.set_shift_button(self._shift_button)
        transport.set_stop_button(stop_button)
        transport.set_play_button(play_button)
        transport.set_record_button(rec_button)
        pads = []
        for index in range(len(PAD_TRANSLATIONS)):
            pads.append(
                ButtonElement(IS_MOMENTARY, MIDI_NOTE_TYPE, 15,
                              PAD_TRANSLATIONS[index][2]))
            pads[(-1)].name = 'Pad_' + str(index)

        self._session = ShiftableSessionComponent(8, 0)
        self._session.name = 'Session_Control'
        self._session.selected_scene().name = 'Selected_Scene'
        self._session.set_mixer(self._mixer)
        self._session.set_shift_button(self._shift_button)
        self._session.set_clip_slot_buttons(tuple(pads))
        transport_view_modes = TransportViewModeSelector(
            transport, self._session, ffwd_button, rwd_button, loop_button)
        transport_view_modes.name = 'Transport_View_Modes'

    def _setup_device(self):
        self._encoders = []
        for offset in range(8):
            self._encoders.append(
                PeekableEncoderElement(
                    MIDI_CC_TYPE, 15, 17 + offset,
                    Live.MidiMap.MapMode.relative_smooth_two_compliment))
            self._encoders[(-1)].set_feedback_delay(-1)
            self._encoders[(-1)].add_value_listener((self._encoder_value),
                                                    identify_sender=True)
            self._encoders[(-1)].name = 'Device_Control_' + str(offset)

        prev_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 14)
        next_bank_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 15)
        prev_bank_button.name = 'Device_Bank_Down_Button'
        next_bank_button.name = 'Device_Bank_Up_Button'
        device = BestBankDeviceComponent(
            device_selection_follows_track_selection=True)
        device.name = 'Device_Component'
        self.set_device_component(device)
        device.set_parameter_controls(tuple(self._encoders))
        device.set_bank_nav_buttons(prev_bank_button, next_bank_button)
        self._device_bank_buttons = (prev_bank_button, next_bank_button)
        prev_bank_button.add_value_listener(self._device_bank_value)
        next_bank_button.add_value_listener(self._device_bank_value)
        self._inst_button = ButtonElement(IS_MOMENTARY, MIDI_CC_TYPE, 15, 109)
        self._inst_button.name = 'Inst_Button'
        self._inst_button.add_value_listener(self._inst_value)
        self._device_navigation = DetailViewCntrlComponent()
        self._device_navigation.name = 'Device_Navigation_Component'

    def _setup_display(self):
        self._display = PhysicalDisplayElement(5, 1)
        self._display.name = 'Display'
        self._display.set_message_parts(SYSEX_START + (17, 1, 0, 0), (247, ))
        self._display_data_source = DisplayDataSource()
        self._display.segment(0).set_data_source(self._display_data_source)

    def _on_selected_track_changed(self):
        ControlSurface._on_selected_track_changed(self)
        self._show_current_track_name()

    def _shift_value(self, value):
        self._shift_pressed = value > 0
        for encoder in self._encoders:
            encoder.set_peek_mode(self._shift_pressed)

        if self._shift_pressed:
            self._mixer.set_select_buttons(None, None)
            self._session.set_track_bank_buttons(self._next_nav_button,
                                                 self._prev_nav_button)
            self._device_component.set_bank_nav_buttons(None, None)
            self._device_navigation.set_device_nav_buttons(
                self._device_bank_buttons[0], self._device_bank_buttons[1])
        else:
            self._session.set_track_bank_buttons(None, None)
            self._mixer.set_select_buttons(self._next_nav_button,
                                           self._prev_nav_button)
            self._device_navigation.set_device_nav_buttons(None, None)
            self._device_component.set_bank_nav_buttons(
                self._device_bank_buttons[0], self._device_bank_buttons[1])
        self.request_rebuild_midi_map()

    def _encoder_value(self, value, sender):
        if self._device_component.is_enabled():
            display_string = ' - '
            if sender.mapped_parameter() != None:
                display_string = sender.mapped_parameter().name
            self._display_data_source.set_display_string(display_string)
            self._set_display_data_source(self._display_data_source)
            self._display_reset_delay = STANDARD_DISPLAY_DELAY

    def _slider_value(self, value, sender):
        if self._mixer.is_enabled():
            display_string = ' - '
            if sender.mapped_parameter() != None:
                master = self.song().master_track
                tracks = self.song().tracks
                returns = self.song().return_tracks
                track = None
                if sender == self._master_slider:
                    if self._has_sliders:
                        track = master
                    else:
                        track = self.song().view.selected_track
                else:
                    track = self._mixer.channel_strip(
                        self._sliders.index(sender))._track
                if track == master:
                    display_string = 'Ma'
                else:
                    pass
                if track in tracks:
                    display_string = str(list(tracks).index(track) + 1)
                elif track in returns:
                    display_string = str(
                        chr(ord('A') + list(returns).index(track)))
                else:
                    pass
                display_string += ' Vol'
            self._display_data_source.set_display_string(display_string)
            self._set_display_data_source(self._display_data_source)
            self._display_reset_delay = STANDARD_DISPLAY_DELAY

    def _mixer_button_value(self, value, sender):
        if self._mixer.is_enabled():
            if value > 0:
                strip = None
                if sender == self._selected_mute_solo_button:
                    strip = self._mixer.selected_strip()
                else:
                    strip = self._mixer.channel_strip(
                        self._strip_buttons.index(sender))
                if strip != None:
                    self._set_display_data_source(
                        strip.track_name_data_source())
                else:
                    self._display_data_source.set_display_string(' - ')
                    self._set_display_data_source(self._display_data_source)
                self._display_reset_delay = STANDARD_DISPLAY_DELAY

    def _device_bank_value(self, value):
        if self._device_component.is_enabled():
            if value > 0:
                data_source = self._device_component.bank_name_data_source()
                if self._shift_pressed:
                    data_source = self._device_component.device_name_data_source(
                    )
                self._set_display_data_source(data_source)
                self._display_reset_delay = STANDARD_DISPLAY_DELAY

    def _inst_value(self, value):
        if value > 0:
            if self._device_component.is_enabled():
                if self.song().view.selected_track.view.select_instrument():
                    self._set_display_data_source(
                        self._device_component.device_name_data_source())
                    self._display_reset_delay = STANDARD_DISPLAY_DELAY

    def _show_current_track_name(self):
        if self._display != None:
            if self._mixer != None:
                self._set_display_data_source(
                    self._mixer.selected_strip().track_name_data_source())

    def _show_startup_message(self):
        self._display.display_message('LIVE')
        self._display_reset_delay = INITIAL_DISPLAY_DELAY

    def _set_display_data_source(self, data_source):
        self._display.segment(0).set_data_source(data_source)
        data_source.update()
class BestBankDeviceComponent(DeviceComponent):

    def __init__(self, *a, **k):
        (super(BestBankDeviceComponent, self).__init__)(*a, **k)
        new_banks = {}
        new_bank_names = {}
        self._device_banks = DEVICE_DICT
        self._device_bank_names = BANK_NAME_DICT
        self._device_best_banks = DEVICE_BOB_DICT
        for device_name, current_banks in self._device_banks.items():
            if len(current_banks) > 1:
                current_banks = self._device_best_banks[device_name] + current_banks
                new_bank_names[device_name] = (BOP_BANK_NAME,) + self._device_bank_names[device_name]
            else:
                new_banks[device_name] = current_banks

        self._device_banks = new_banks
        self._device_bank_names = new_bank_names
        self._bank_name_data_source = DisplayDataSource()

    def disconnect(self):
        self._bank_name_data_source = None
        DeviceComponent.disconnect(self)

    def bank_name_data_source(self):
        return self._bank_name_data_source

    def _bank_up_value(self, value):
        DeviceComponent._bank_up_value(self, value)
        self._update_bank_display()

    def _bank_down_value(self, value):
        DeviceComponent._bank_down_value(self, value)
        self._update_bank_display()

    def _update_bank_display(self):
        if self.is_enabled():
            bank_name = ''
            if self._device != None:
                if self._bank_name != '<No Bank>':
                    bank_name = self._bank_name
                    if bank_name in (BOP_BANK_NAME, 'Bank 1'):
                        bank_name = 'Home'
            self._bank_name_data_source.set_display_string(bank_name)

    def _is_banking_enabled(self):
        return True

    def _number_of_parameter_banks(self):
        result = 0
        if self._device != None:
            if self._device.class_name in list(self._device_banks.keys()):
                result = len(self._device_banks[self._device.class_name])
            else:
                result = DeviceComponent._number_of_parameter_banks(self)
        return result

    def _parameter_banks(self):
        return parameter_banks(self._device, self._device_banks)

    def _parameter_bank_names(self):
        return parameter_bank_names(self._device, self._device_bank_names)
class Axiom_AIR_25_49_61(ControlSurface):
    def __init__(self, c_instance):
        ControlSurface.__init__(self, c_instance)
        self._alt_device_component = None
        with self.component_guard():
            self.set_pad_translations(PAD_TRANSLATIONS)
            self._suggested_input_port = 'HyperControl'
            self._suggested_output_port = 'HyperControl'
            self._single_fader_button_modes = None
            self._has_faders = True
            self._display_reset_delay = -1
            self._hc_byte = HC_BYTE
            self._waiting_for_first_response = True
            self._setup_controls()
            self._setup_displays()
            self._setup_mixer()
            self._setup_session()
            self._setup_transport()
            self._setup_device()
            self._setup_modes()
            self._drum_group_midi_button = None
            self._drum_group_hyper_button = None
            for component in self.components:
                component.set_enabled(False)

    def disconnect(self):
        self._scheduled_messages = []
        for encoder in self._encoders:
            encoder.remove_value_listener(self._encoder_value)

        for fader in self._faders:
            fader.remove_value_listener(self._fader_value)

        for fader_button in self._fader_buttons:
            fader_button.remove_value_listener(self._fader_button_value)

        self._master_fader.remove_value_listener(self._fader_value)
        self._master_fader_button.remove_value_listener(
            self._fader_button_value)
        self._select_button.remove_value_listener(self._select_button_value)
        self._identify_button.remove_value_listener(self._identify_value)
        self._fader_group_midi_button.remove_value_listener(
            self._midi_button_value)
        self._fader_group_mix_button.remove_value_listener(
            self._hyper_button_value)
        self._fader_group_fx_button.remove_value_listener(
            self._hyper_button_value)
        self._encoder_group_midi_button.remove_value_listener(
            self._midi_button_value)
        self._encoder_group_mix_button.remove_value_listener(
            self._hyper_button_value)
        self._encoder_group_fx_button.remove_value_listener(
            self._hyper_button_value)
        if self._drum_group_midi_button != None:
            self._drum_group_midi_button.remove_value_listener(
                self._midi_button_value)
        if self._drum_group_hyper_button != None:
            self._drum_group_hyper_button.remove_value_listener(
                self._hyper_button_value)
        self._alt_device_component = None
        self._name_display = None
        self._value_display = None
        self._bank_display = None
        self._pad_display = None
        self._name_display_data_source = None
        self._value_display_data_source = None
        self._bank_display_data_source = None
        self._pad_display_data_source = None
        self._select_button = None
        self._left_button = None
        self._right_button = None
        self._up_button = None
        self._down_button = None
        self._loop_button = None
        self._ffwd_button = None
        self._rwd_button = None
        self._play_button = None
        self._stop_button = None
        self._rec_button = None
        self._master_fader_button = None
        self._fader_buttons = None
        self._faders = None
        self._encoders = None
        self._drum_pads = None
        self._identify_button = None
        self._main_group_hyper_button = None
        self._main_group_track_button = None
        self._main_group_fx_button = None
        self._encoder_group_midi_button = None
        self._encoder_group_mix_button = None
        self._encoder_group_fx_button = None
        self._fader_group_mode_button = None
        self._fader_group_midi_button = None
        self._fader_group_mix_button = None
        self._fader_group_fx_button = None
        self._drum_group_midi_button = None
        self._drum_group_roll_button = None
        self._drum_group_hyper_button = None
        self._mixer_for_encoders = None
        self._mixer_for_faders = None
        self._device_for_encoders = None
        self._device_for_faders = None
        self._transport = None
        self._session = None
        ControlSurface.disconnect(self)
        self._send_midi(SYSEX_START + DISABLE_HYPERCONTROL)

    def refresh_state(self):
        ControlSurface.refresh_state(self)
        self.schedule_message(5, self._send_midi, IDENTITY_REQUEST)

    def handle_sysex(self, midi_bytes):
        if midi_bytes[0:10] == AXIOM_AIR_RESPONSE:
            if midi_bytes[12:15] < AXIOM_REV4_RESPONSE:
                self.schedule_message(1, self._send_midi,
                                      SYSEX_START + ENGAGE_HYPERCONTROL)
                self.schedule_message(2, self._send_midi,
                                      SYSEX_START + CLEAR_ALL)
                self.schedule_message(3, self._name_display.display_message,
                                      'Firmware')
                self.schedule_message(13, self._name_display.display_message,
                                      'Update')
                self.schedule_message(23, self._name_display.display_message,
                                      'Required')
                self.schedule_message(33, self._send_midi,
                                      SYSEX_START + DISABLE_HYPERCONTROL)
            elif midi_bytes[12:15] >= AXIOM_REV4_RESPONSE:
                if self._waiting_for_first_response == True:
                    self._waiting_for_first_response = False
                    self._has_faders = midi_bytes[10] != 50
                    self.schedule_message(1, self._send_midi,
                                          SYSEX_START + ENGAGE_HYPERCONTROL)
                    self.schedule_message(2, self._send_midi,
                                          SYSEX_START + SPECIAL_HYPERCONTROL)
                    self.schedule_message(3, self._complete_setup)
                else:
                    self._display_reset_delay = 0
        elif midi_bytes[0:8] == REQUEST_HYPERCONTROL:
            self.schedule_message(5, self._send_midi, IDENTITY_REQUEST)

    def update_display(self):
        ControlSurface.update_display(self)
        if self._display_reset_delay >= 0:
            self._display_reset_delay -= 1
            if self._display_reset_delay == -1:
                self._set_displays_to_default()

    def _on_selected_track_changed(self):
        ControlSurface._on_selected_track_changed(self)
        self._display_reset_delay = 0

    def restore_bank(self, bank_index):
        ControlSurface.restore_bank(self, bank_index)
        if self._alt_device_component != None:
            self._alt_device_component.restore_bank(bank_index)

    def set_appointed_device(self, device):
        ControlSurface.set_appointed_device(self, device)
        with self.component_guard():
            if self._alt_device_component != None:
                self._alt_device_component.set_device(device)

    def set_alt_device_component(self, device_component):
        self._alt_device_component = device_component

    def _update_device_selection(self):
        track = self.song().view.selected_track
        device_to_select = track.view.selected_device
        if device_to_select == None:
            if len(track.devices) > 0:
                device_to_select = track.devices[0]
        if device_to_select != None:
            self.song().view.select_device(device_to_select)
        self._device_component.set_device(device_to_select)
        if self._alt_device_component != None:
            self._alt_device_component.set_device(device_to_select)

    def _setup_controls(self):
        self._left_button = create_button(99, 'Left_Button')
        self._right_button = create_button(100, 'Right_Button')
        self._up_button = create_button(101, 'Up_Button')
        self._down_button = create_button(102, 'Down_Button')
        self._loop_button = create_button(113, 'Loop_Button')
        self._rwd_button = create_button(114, 'Rwd_Button')
        self._ffwd_button = create_button(115, 'FFwd_Button')
        self._stop_button = create_button(116, 'Stop_Button')
        self._play_button = create_button(117, 'Play_Button')
        self._rec_button = create_button(118, 'Record_Button')
        self._select_button = ConfigurableButtonElement(
            IS_MOMENTARY, MIDI_CC_TYPE, GLOBAL_CHANNEL, 98)
        self._select_button.name = 'Select_Button'
        self._select_button.add_value_listener(self._select_button_value)
        self._main_group_hyper_button = create_configurable_button(
            104, 'Fader_Group_HyperControl_Button', 2, 14)
        self._main_group_track_button = create_configurable_button(
            105, 'Main_Group_Track_Button', 2, 11)
        self._main_group_fx_button = create_configurable_button(
            106, 'Main_Group_Inst_FX_Button', 2, 11)
        self._identify_button = create_configurable_button(
            97, 'Identify_Button', 2, 16)
        self._identify_button.add_value_listener(self._identify_value)
        self._fader_buttons = []
        for index in range(8):
            self._fader_buttons.append(
                create_configurable_button(49 + index,
                                           'Fader_Button_%d' % index))
            self._fader_buttons[(-1)].add_value_listener(
                (self._fader_button_value), identify_sender=True)

        self._faders = []
        for index in range(8):
            self._faders.append(create_slider(33 + index, 'Fader_%d' % index))
            self._faders[(-1)].add_value_listener((self._fader_value),
                                                  identify_sender=True)

        self._master_fader_button = create_configurable_button(
            57, 'Master_Fader_Button')
        self._master_fader_button.add_value_listener(
            (self._fader_button_value), identify_sender=True)
        self._master_fader = create_slider(41, 'Master_Fader')
        self._master_fader.add_value_listener((self._fader_value),
                                              identify_sender=True)
        self._fader_group_mode_button = create_configurable_button(
            61, 'Fader_Group_Mode_Button')
        self._fader_group_midi_button = create_configurable_button(
            60, 'Fader_Group_MIDI_Button')
        self._fader_group_midi_button.add_value_listener(
            (self._midi_button_value), identify_sender=True)
        self._fader_group_mix_button = create_configurable_button(
            58, 'Fader_Group_Mix_Button', 0, 1)
        self._fader_group_mix_button.add_value_listener(
            (self._hyper_button_value), identify_sender=True)
        self._fader_group_fx_button = create_configurable_button(
            59, 'Fader_Group_Inst_FX_Button', 0, -1)
        self._fader_group_fx_button.add_value_listener(
            (self._hyper_button_value), identify_sender=True)
        self._encoders = []
        for index in range(8):
            self._encoders.append(
                create_encoder(17 + index, 'Encoder_%d' % index))
            self._encoders[(-1)].add_value_listener((self._encoder_value),
                                                    identify_sender=True)

        self._encoder_group_midi_button = create_configurable_button(
            27, 'Encoder_Group_MIDI_Button', 0, 72)
        self._encoder_group_midi_button.add_value_listener(
            (self._midi_button_value), identify_sender=True)
        self._encoder_group_mix_button = create_configurable_button(
            25, 'Encoder_Group_Mix_Button', 0, 72)
        self._encoder_group_mix_button.add_value_listener(
            (self._hyper_button_value), identify_sender=True)
        self._encoder_group_fx_button = create_configurable_button(
            26, 'Encoder_Group_Inst_FX_Button', 0, 72)
        self._encoder_group_fx_button.add_value_listener(
            (self._hyper_button_value), identify_sender=True)

    def _setup_drum_pads(self):
        self._drum_pads = []
        num_pads = 12 if self._has_faders else 16
        for index in range(8):
            self._drum_pads.append(
                create_configurable_button(81 + index, 'Pad_%d' % index, 0, 0,
                                           MIDI_CC_TYPE))

        for index in range(num_pads - 8):
            self._drum_pads.append(
                ConfigurableButtonElement(IS_MOMENTARY, MIDI_NOTE_TYPE,
                                          GLOBAL_CHANNEL - 1, 81 + index,
                                          GLOBAL_SEND_CHANNEL, 8,
                                          MIDI_CC_TYPE))
            self._drum_pads[(-1)].name = 'Pad_' + str(index + 8)

        self._drum_group_midi_button = create_configurable_button(
            91, 'Drum_Group_MIDI_Button', 2, -2)
        self._drum_group_midi_button.add_value_listener(
            (self._midi_button_value), identify_sender=True)
        self._drum_group_roll_button = create_configurable_button(
            90, 'Drum_Group_Roll_Button', -1)
        self._drum_group_hyper_button = create_configurable_button(
            89, 'Drum_Group_HyperControl_Button', 2, 2)
        self._drum_group_hyper_button.add_value_listener(
            (self._hyper_button_value), identify_sender=True)

    def _setup_displays(self):
        self._name_display = PhysicalDisplayElement(12, 1)
        self._name_display.name = 'Name_Display'
        self._name_display.set_message_parts(SYSEX_START + (21, ), (0, 247))
        self._name_display.set_clear_all_message(CLEAR_NAME)
        self._name_display_data_source = DisplayDataSource()
        self._name_display.segment(0).set_data_source(
            self._name_display_data_source)
        self._value_display = NumericalDisplayElement(3, 1)
        self._value_display.name = 'Value_Display'
        self._value_display.set_message_parts(SYSEX_START + (20, 48), (0, 247))
        self._value_display.set_clear_all_message(CLEAR_VALUE)
        self._value_display_data_source = DisplayDataSource()
        self._value_display.segment(0).set_data_source(
            self._value_display_data_source)
        self._bank_display = NumericalDisplayElement(3, 1)
        self._bank_display.name = 'Bank_Display'
        self._bank_display.set_message_parts(SYSEX_START + (19, ), (0, 247))
        self._bank_display.set_clear_all_message(CLEAR_BANK)
        self._bank_display_data_source = DisplayDataSource()
        self._bank_display.segment(0).set_data_source(
            self._bank_display_data_source)
        self._pad_display = NumericalDisplayElement(2, 1)
        self._pad_display.name = 'Pad_Display'
        self._pad_display.set_message_parts(SYSEX_START + (18, ), (0, 247))
        self._pad_display.set_clear_all_message(CLEAR_PAD)
        self._pad_display_data_source = DisplayDataSource()
        self._pad_display.segment(0).set_data_source(
            self._pad_display_data_source)

    def _setup_mixer(self):
        self._mixer_for_encoders = SpecialMixerComponent(
            self._name_display, self._value_display, 8)
        self._mixer_for_encoders.name = 'Mixer_for_encoders'
        self._mixer_for_faders = SpecialMixerComponent(self._name_display,
                                                       self._value_display, 8)
        self._mixer_for_faders.name = 'Mixer_for_faders'

    def _setup_session(self):
        self._session = SpecialSessionComponent(8, 0)
        self._session.name = 'Session_Control'
        self._session.selected_scene().name = 'Selected_Scene'
        self._session.set_mixer(self._mixer_for_encoders)
        self._session.set_alt_mixer(self._mixer_for_faders)
        self._session.add_offset_listener(self._update_bank_value)

    def _setup_transport(self):
        self._transport = TransportComponent()
        self._transport.name = 'Transport'
        self._transport.set_stop_button(self._stop_button)
        self._transport.set_play_button(self._play_button)
        self._transport.set_record_button(self._rec_button)
        transport_view_modes = TransportViewModeSelector(
            self._transport, self._session, self._ffwd_button,
            self._rwd_button, self._loop_button)
        transport_view_modes.name = 'Transport_View_Modes'

    def _setup_device(self):
        self._device_for_encoders = BestBankDeviceComponent(
            device_selection_follows_track_selection=True)
        self._device_for_encoders.name = 'Device_Component_for_encoders'
        self._device_for_faders = BestBankDeviceComponent(
            device_selection_follows_track_selection=True)
        self._device_for_faders.name = 'Device_Component_for_faders'
        self.set_device_component(self._device_for_encoders)
        self.set_alt_device_component(self._device_for_faders)
        self._device_nav = DeviceNavComponent()
        self._device_nav.name = 'Device_Nav_Component'

    def _setup_modes(self):
        self._fader_button_modes = FaderButtonModeSelector(
            self._mixer_for_faders, tuple(self._fader_buttons))
        self._fader_button_modes.name = 'Fader_Button_Modes'
        self._fader_button_modes.set_mode_toggle(self._fader_group_mode_button)
        self._fader_modes = FaderModeSelector(self._mixer_for_faders,
                                              self._device_for_faders,
                                              tuple(self._faders),
                                              self._fader_button_modes,
                                              self._master_fader_button)
        self._fader_modes.name = 'Fader_Modes'
        self._fader_modes.set_mode_buttons(
            (self._fader_group_mix_button, self._fader_group_fx_button))
        self._encoder_modes = EncoderModeSelector(self._mixer_for_encoders,
                                                  self._device_for_encoders,
                                                  tuple(self._encoders))
        self._encoder_modes.name = 'Encoder_Modes'
        self._encoder_modes.set_mode_buttons(
            (self._encoder_group_mix_button, self._encoder_group_fx_button))
        main_modes = MainModeSelector(self._device_for_encoders,
                                      self._device_for_faders, self._session,
                                      self._mixer_for_faders, self._device_nav,
                                      self._up_button, self._down_button,
                                      self._left_button, self._right_button,
                                      self._select_button)
        main_modes.name = 'Main_Modes'
        main_modes.set_mode_buttons(
            (self._main_group_track_button, self._main_group_fx_button))

    def _setup_master_fader(self):
        if self._has_faders:
            self._mixer_for_encoders.master_strip().set_volume_control(
                self._master_fader)
        else:
            self._mixer_for_encoders.selected_strip().set_volume_control(
                self._master_fader)

    def _setup_single_fader_button_modes(self):
        self._single_fader_button_modes = SingleFaderButtonModeSelector(
            self._mixer_for_encoders, self._fader_group_midi_button)
        self._single_fader_button_modes.name = 'Single_Fader_Button_Modes'
        self._single_fader_button_modes.set_mode_toggle(
            self._fader_group_mode_button)

    def _complete_setup(self):
        self._setup_drum_pads()
        self._set_drum_pads_to_hc()
        self._setup_master_fader()
        if not self._has_faders:
            self._setup_single_fader_button_modes()
        for control in self.controls:
            if isinstance(control, InputControlElement):
                control.clear_send_cache()

        for component in self.components:
            component.set_enabled(True)

        self._fader_group_midi_button.send_value(LED_OFF, True)
        self._encoder_group_midi_button.send_value(LED_OFF, True)
        self._main_group_hyper_button.send_value(AMB_FULL, True)
        self.request_rebuild_midi_map()
        self._on_selected_track_changed()
        self.schedule_message(1, self._show_startup_message)

    def _show_startup_message(self):
        self._send_midi(SYSEX_START + CLEAR_ALL)
        self._name_display.display_message('Ableton Live')
        self._display_reset_delay = INITIAL_DISPLAY_DELAY

    def _select_button_value(self, value):
        self._display_reset_delay = STANDARD_DISPLAY_DELAY

    def _identify_value(self, value):
        for encoder in self._encoders:
            encoder.set_identify_mode(value > 0)

        for fader in self._faders:
            fader.set_identify_mode(value > 0)

        self._master_fader.set_identify_mode(value > 0)
        self._display_reset_delay = 0
        self._identify_button.turn_on(
        ) if value > 0 else self._identify_button.turn_off()

    def _midi_button_value(self, value, sender):
        if value > 0:
            if sender is self._drum_group_midi_button:
                hc_byte = self._hc_byte ^ PADS
                if hc_byte != self._hc_byte:
                    self._hc_byte = hc_byte
                    self._drum_group_hyper_button.send_value(LED_OFF, True)
                    self.schedule_message(
                        1, self._send_midi,
                        SYSEX_START + (32, self._hc_byte, 247))
            elif sender is self._encoder_group_midi_button:
                hc_byte = self._hc_byte ^ ENCODERS
                if hc_byte != self._hc_byte:
                    self._hc_byte = hc_byte
                    self._encoder_group_mix_button.send_value(LED_OFF, True)
                    self._encoder_group_fx_button.send_value(LED_OFF, True)
                    if self._encoder_modes.mode_index < 3:
                        self._encoder_modes.set_enabled(False)
                    self.schedule_message(
                        1, self._send_midi,
                        SYSEX_START + (32, self._hc_byte, 247))
            elif sender is self._fader_group_midi_button:
                if self._has_faders:
                    hc_byte = self._hc_byte ^ FADERS
                    if hc_byte != self._hc_byte:
                        self._hc_byte = hc_byte
                        self._fader_group_mix_button.send_value(LED_OFF, True)
                        self._fader_group_fx_button.send_value(LED_OFF, True)
                        self._fader_group_mode_button.send_value(LED_OFF, True)
                        if self._fader_modes.mode_index < 2:
                            self._fader_modes.set_enabled(False)
                            self._fader_button_modes.set_enabled(False)
                        self.schedule_message(
                            1, self._send_midi,
                            SYSEX_START + (32, self._hc_byte, 247))
                else:
                    self._display_reset_delay = STANDARD_DISPLAY_DELAY

    def _hyper_button_value(self, value, sender):
        if value > 0:
            if sender is self._drum_group_hyper_button:
                if self._hc_byte | PADS != self._hc_byte:
                    self._hc_byte = self._hc_byte | PADS
                    self._send_midi(SYSEX_START + (32, self._hc_byte, 247))
                    self.schedule_message(1, self._set_drum_pads_to_hc)
            elif sender is self._encoder_group_fx_button or sender is self._encoder_group_mix_button:
                if self._hc_byte | ENCODERS != self._hc_byte:
                    self._hc_byte = self._hc_byte | ENCODERS
                    self._send_midi(SYSEX_START + (32, self._hc_byte, 247))
                    self._encoder_group_midi_button.turn_off()
                    if sender is self._encoder_group_fx_button:
                        self._encoder_modes.set_enabled(True)
                        self._display_reset_delay = 0
                        return
                    self.schedule_message(1, self._encoder_modes.set_enabled,
                                          True)
                    self.schedule_message(1, self._encoder_modes.update)
                    self._display_reset_delay = 2
                    return
            else:
                if sender is self._fader_group_fx_button or (
                        sender is self._fader_group_mix_button):
                    if self._hc_byte | FADERS != self._hc_byte:
                        self._hc_byte = self._hc_byte | FADERS
                        self._send_midi(SYSEX_START + (32, self._hc_byte, 247))
                        self._fader_group_midi_button.turn_off()
                        self._fader_button_modes.set_enabled(True)
                        if sender is self._fader_group_fx_button:
                            self._fader_modes.set_enabled(True)
                            self._fader_button_modes.set_enabled(True)
                            self._display_reset_delay = 0
                            return
                        self.schedule_message(1, self._fader_modes.set_enabled,
                                              True)
                        self.schedule_message(1, self._fader_modes.update)
                        self.schedule_message(
                            1, self._fader_button_modes.set_enabled, True)
                        self.schedule_message(1,
                                              self._fader_button_modes.update)
                        self._display_reset_delay = 2
                        return
            self._display_reset_delay = 0

    def _set_drum_pads_to_hc(self):
        self._drum_group_midi_button.send_value(LED_OFF, True)
        self._drum_group_hyper_button.send_value(RED_FULL, True)
        for index in range(len(self._drum_pads)):
            self._drum_pads[index].send_value(RED_LOW, True)

    def _fader_button_value(self, value, sender):
        self._display_reset_delay = STANDARD_DISPLAY_DELAY

    def _fader_value(self, value, sender):
        param = sender.mapped_parameter()
        if param != None:
            param_range = param.max - param.min
            if param.name == 'Track Volume':
                if sender == self._master_fader:
                    if self._has_faders:
                        name_string = 'Master  Vol'
                    else:
                        name_string = self._mixer_for_faders.selected_strip(
                        ).track_name_data_source().display_string() + '   Vol'
                else:
                    name_string = self._mixer_for_faders.channel_strip(
                        self._faders.index(sender)).track_name_data_source(
                        ).display_string() + '   Vol'
            else:
                name_string = param.name
                value = int(
                    old_div(param.value - param.min, param_range) * 127)
            value_string = str(value)
        else:
            name_string = '<unmapped>'
            value_string = None
            self.schedule_message(1, self._set_value_string)
        self._set_name_string(name_string)
        self._set_value_string(value_string)

    def _encoder_value(self, value, sender):
        param = sender.mapped_parameter()
        if param != None:
            param_range = param.max - param.min
            if param.name == 'Track Volume':
                name_string = self._mixer_for_encoders.channel_strip(
                    self._encoders.index(sender)).track_name_data_source(
                    ).display_string() + '   Vol'
                value = int(
                    old_div(param.value - param.min, param_range) * 127)
            elif param.name == 'Track Panning':
                name_string = self._mixer_for_encoders.channel_strip(
                    self._encoders.index(sender)).track_name_data_source(
                    ).display_string() + '   Pan'
                value = int(old_div(param.value, param_range) * 127)
                if value < 0:
                    name_string += '  L'
                elif value > 0:
                    name_string += '  R'
                else:
                    name_string += '  C'
            else:
                name_string = param.name
                value = int(
                    old_div(param.value - param.min, param_range) * 127)
            value_string = str(value)
        else:
            name_string = '<unmapped>'
            value_string = None
            self.schedule_message(1, self._set_value_string)
        self._set_name_string(name_string)
        self._set_value_string(value_string)

    def _set_displays_to_default(self):
        self._name_display.segment(0).set_data_source(
            self._mixer_for_encoders.selected_strip().track_name_data_source())
        self._name_display.update()
        self._update_bank_value()
        self._set_value_string(None)
        self._send_midi(SYSEX_START + LCD_HC_DEFAULT)

    def _set_name_string(self, name_string):
        self._name_display.segment(0).set_data_source(
            self._name_display_data_source)
        self._name_display_data_source.set_display_string(name_string)
        self._display_reset_delay = STANDARD_DISPLAY_DELAY

    def _set_value_string(self, value_string=None):
        if value_string != None:
            self._value_display_data_source.set_display_string(value_string)
        else:
            self._value_display.reset()

    def _set_bank_string(self, bank_string=None):
        if bank_string != None:
            self._bank_display_data_source.set_display_string(bank_string)
        else:
            self._bank_display.reset()

    def _update_bank_value(self):
        bank = old_div(self._session.track_offset() + 1,
                       self._session.width()) + 1
        self._set_bank_string(str(bank))

    def _install_mapping(self, midi_map_handle, control, parameter,
                         feedback_delay, feedback_map):
        success = False
        feedback_rule = None
        if control.message_type() is MIDI_NOTE_TYPE:
            feedback_rule = Live.MidiMap.NoteFeedbackRule()
            feedback_rule.note_no = 0
            feedback_rule.vel_map = (0, )
        elif control.message_type() is MIDI_CC_TYPE:
            feedback_rule = Live.MidiMap.CCFeedbackRule()
            feedback_rule.cc_no = 0
            feedback_rule.cc_value_map = (0, )
        elif control.message_type() is MIDI_PB_TYPE:
            feedback_rule = Live.MidiMap.PitchBendFeedbackRule()
            feedback_rule.value_pair_map = feedback_map
        feedback_rule.channel = control.message_channel()
        feedback_rule.delay_in_ms = feedback_delay
        if control.message_type() is MIDI_NOTE_TYPE:
            success = Live.MidiMap.map_midi_note_with_feedback_map(
                midi_map_handle, parameter, control.message_channel(),
                control.message_identifier(), feedback_rule)
        elif control.message_type() is MIDI_CC_TYPE:
            success = Live.MidiMap.map_midi_cc_with_feedback_map(
                midi_map_handle, parameter, control.message_channel(),
                control.message_identifier(), control.message_map_mode(),
                feedback_rule, not control.needs_takeover())
        elif control.message_type() is MIDI_PB_TYPE:
            success = Live.MidiMap.map_midi_pitchbend_with_feedback_map(
                midi_map_handle, parameter, control.message_channel(),
                feedback_rule, not control.needs_takeover())
        return success