Ejemplo n.º 1
0
class Alias8(ControlSurface):
  num_tracks = 8
  knobs_top = [1, 2, 3, 4, 5, 6, 7, 8]
  knobs_bottom = [9, 10, 11, 12, 13, 14, 15, 16]
  faders = [17, 18, 19, 20, 21, 22, 23, 24]
  master_fader = 25
  encoder = 42
  buttons_top = [0, 1, 2, 3, 4, 5, 6, 7]
  buttons_bottom = [8, 9, 10, 11, 12, 13, 14, 15]
  
  def __init__(self, instance):
    super(Alias8, self).__init__(instance, False)
    with self.component_guard():
      self._set_suppress_rebuild_requests(True)
      self.init_session()
      self.init_mixer() 
      
      # Connect mixer to session.
      self.session.set_mixer(self.mixer)
      self.session.update()
      # New in Live 9: must explicitly activate session component.
      self.set_highlighting_session_component(self.session)
      self._set_suppress_rebuild_requests(False)

  def init_session(self):
    self.session = SessionComponent(self.num_tracks, 1)
    self.session.name = 'Alias 8 Session'
    self.session.update()

    # Connect the encoder to track scroller.
    def scroll_cb(value):
      if value == 1:
        self.session._horizontal_banking.scroll_down()
      elif value == 127:
        self.session._horizontal_banking.scroll_up()
    self.track_encoder = EncoderElement(MIDI_CC_TYPE, 0, self.encoder,
        Live.MidiMap.MapMode.absolute)
    self.track_encoder.add_value_listener(scroll_cb)

  def init_mixer(self):
    self.mixer = MixerComponent(self.num_tracks, 0)
    self.mixer.id = 'Mixer' 
    self.song().view.selected_track = self.mixer.channel_strip(0)._track
    for i in range(self.num_tracks):
      self.mixer.channel_strip(i).set_volume_control(
          fader(self.faders[i]))
      self.mixer.channel_strip(i).set_solo_button(
          button(self.buttons_top[i], color=CYAN))
      self.mixer.channel_strip(i).set_arm_button(
          button(self.buttons_bottom[i], color=RED))
      self.mixer.channel_strip(i).set_pan_control(
          knob(self.knobs_bottom[i]))
    self.mixer.master_strip().set_volume_control(fader(self.master_fader))
    self.mixer.update()
Ejemplo n.º 2
0
    def __init__(self, control_surface):
        self.control_surface = control_surface
        self.song = control_surface.song
        self.view = control_surface.application().view  # Live.Application.get_application().view

        self.is_paused = False

        for cc in (70, 86, 102):
            encoder = EncoderElement(MIDI_CC_TYPE, settings.CHANNEL, cc, Live.MidiMap.MapMode.relative_two_compliment)
            encoder.add_value_listener(self._undo_redo)

        for cc in (71, 87, 103):
            encoder = EncoderElement(MIDI_CC_TYPE, settings.CHANNEL, cc, Live.MidiMap.MapMode.relative_two_compliment)
            encoder.add_value_listener(self._play_pause_stop)
Ejemplo n.º 3
0
class HyperionChan(CompoundComponent):
    def __init__(self, hyperion, mod_num, *a, **kw):
        super(HyperionChan, self).__init__(*a, **kw)

        self.hyperion = hyperion
        self.mod_num = mod_num

        self._track_selector_encoder = EncoderElement(MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 0x15, Live.MidiMap.MapMode.relative_smooth_binary_offset) # Right encoder on Hyperion
        self._track_selector_encoder.add_value_listener(self._on_track_selector_encoder)

        self._fader = SliderElement(MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 0x25)
        self._pots = [
            EncoderElement(MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 0x1E + num, Live.MidiMap.MapMode.absolute, name='Pot{}'.format(num))
            for num in range(8)
        ]
        self._btns = [
            ButtonElement(True, MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 1 + num, name='Btn{}'.format(num))
            for num in range(8)
        ]
        self._enc_right_btn = ButtonElement(True, MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 10, name='EncRightBtn')
        self._enc_right_btn.add_value_listener(self._on_enc_right_btn)

        #self._cs = ChannelStripComponent()
        #self._cs.set_volume_control(self._fader)
        self._vu_slider = SliderElement(MIDI_CC_TYPE, MIDI_MASTER_CH + 1 + mod_num, 60)

        self._vu = VUMeter(self)

        self._track = None

        tracks = self._get_all_tracks(self.hyperion.song().tracks)
        if len(tracks) > self.mod_num:
            self._bind_to_track(tracks[self.mod_num])
        else:
            self._bind_to_track(None)

    def log(self, msg, *args):
        self.hyperion.log_message(('HyperionChan({}): ' + msg).format(self.mod_num, *args))

    def disconnect(self):
        super(HyperionChan, self).disconnect()

        self._enc_right_btn.remove_value_listener(self._on_enc_right_btn)

    def _get_parent_by_type(self, obj, parent_type):
        if not obj.canonical_parent:
            return
        if isinstance(obj.canonical_parent, parent_type):
            return obj.canonical_parent
        return self._get_parent_by_type(obj.canonical_parent, parent_type)

    def _on_enc_right_btn(self, value):
        if value and self._track:
            self.log('type {}',type(self._track))

            song = self.hyperion.song()
            if isinstance(self._track, Live.Track.Track):
                song.view.selected_track = self._track
            elif isinstance(self._track, Live.DrumChain.DrumChain):
                parent_track = self._get_parent_by_type(self._track, Live.Track.Track)

                song.view.selected_track = parent_track
                try:
                    song.view.selected_chain = self._track
                except:
                    try:
                        song.view.selected_track = parent_track
                        song.view.selected_chain = self._track.canonical_parent.canonical_parent
                        self._track.canonical_parent.view.selected_chain = self._track
                    except:
                        pass

    def _get_track_mapper_device(self, track):
        for device in track.devices:
            if device.name == 'MultiMapper16 V2.0':
                return device

    def _get_all_tracks(self, all_tracks):
        got_tracks = []

        for cur_track in all_tracks:
            if isinstance(cur_track, (Live.Track.Track, Live.DrumChain.DrumChain)):
                got_tracks.append(cur_track)

            devices = list(cur_track.devices)
            if len(devices) and isinstance(devices[0], Live.RackDevice.RackDevice):
                got_tracks.extend(self._get_all_tracks(devices[0].chains))

        return [track for track in got_tracks if self._get_track_mapper_device(track)]

    def _on_track_selector_encoder(self, value):
        direction = 1 if value > 64 else -1

        tracks = self._get_all_tracks(self.hyperion.song().tracks)

        # for t in tracks:
        #     self.log('AAAAA {}', t.name)

        try:
            cur_track_idx = tracks.index(self._track)
        except ValueError:
            self.log('track disappeared :(')
            self._bind_to_track(tracks[0])
        else:
            cur_track_idx += direction
            if cur_track_idx == len(tracks):
                cur_track_idx = 0
            if cur_track_idx == -1:
                cur_track_idx = len(tracks) - 1
            self._bind_to_track(tracks[cur_track_idx])

    def _bind_to_track(self, track):
        if self._track:
            #self._cs.set_track(None)
            self._fader.release_parameter()
            [pot.release_parameter() for pot in self._pots]
            [btn.release_parameter() for btn in self._btns]

            self._track.remove_name_listener(self._on_name_changed)

            self._track = None

        if not track:
            return

        self.log('binding to {}', track.name)
        self._track = track

        self._fader.connect_to(track.mixer_device.volume)

        mapper_dev = self._get_track_mapper_device(track)
        for num in range(8):
            self._pots[num].connect_to(mapper_dev.parameters[3 + num]) # MacroA0 MacroA1 etc
            self._btns[num].connect_to(mapper_dev.parameters[11 + num]) # MacroB0 MacroB1 etc

        # self._cs.set_track(track)

        if getattr(self._track, 'has_audio_output', False) and hasattr(self._track, 'add_output_meter_left_listener'):
            self._vu.set_vu_meter(track, self._vu_slider)
        else:
            self._vu.set_vu_meter(None, None)

        self._track.add_name_listener(self._on_name_changed)
        self._on_name_changed()

    def _on_name_changed(self):
        self.hyperion.sysex.set_title(self.mod_num, self._track.name if self._track else '-')
Ejemplo n.º 4
0
class nanoKONTROL(ControlSurface):
    __doc__ = " Initial script for Korg nanoKONTROL controller script "
    __module__ = __name__

    """------------------------------------------------------------------------------------------"""
    def __init__(self, c_instance):
        ControlSurface.__init__(self, c_instance)
        with self.component_guard():
            # Turn off rebuild MIDI map until after we're done setting up
            self._set_suppress_rebuild_requests(True)


            """ Call our setup functions """
            # Setup the transport part
            self._setup_transport_control()

            # Setup the mixer part
            self._setup_mixer_control()

            # Setup the session part
            self._setup_session_control()

            # Track navigation
            #self._setup_track_navigation_control()

            #link the session model to the Live object-> shows your ring
            self.set_highlighting_session_component(session)

            # Device component control (effect rack)
            self._setup_device_control()


            """ Here is some Live API stuff just for fun """
            app = Live.Application.get_application()  # get a handle to the App
            maj = app.get_major_version()  # get the major version from the App
            min = app.get_minor_version()  # get the minor version from the App
            bug = app.get_bugfix_version()  # get the bugfix version from the App
            self.show_message("Loading nanoKONTROL for Live " + str(maj) + "." + str(min) + "." + str(
                bug))  # put them together and use the ControlSurface show_message method to output version info to console on the status bar of Live

            """ Listener for midi input """
            self.encoder_test_input=EncoderElement(MIDI_CC_TYPE, midi_channel, 94, Live.MidiMap.MapMode.relative_smooth_two_compliment) # identifies P&d1 as a ButtonElement. IS_MOMENTARY is true if the button send a message on being released
            self.encoder_test_input.add_value_listener(self._encoder_test_input, identify_sender= False) #adda value listener that will lauch the method self.helloworld when Pad1 is pressed

            # Turn rebuild back on, now that we're done setting up
            self._set_suppress_rebuild_requests(False)



    """ Device control allow to control the audio effect rack for the selected track """
    """------------------------------------------------------------------------------------------"""
    def _setup_device_control(self):
        is_momentary = True

        global device   # We want to instantiate the global mixer as a MixerComponent object (it was a global "None" type up until now...)
        device = DeviceComponent()
        device.name = 'Audio Effect Rack'

        # Button on/off
        button_on_off = ButtonElement(is_momentary, MIDI_CC_TYPE, midi_channel, midi_device_on_off)
        device.set_on_off_button(button_on_off)

        # Audio Effect Rack - 8 modulation controls
        device_parameter_controls = []
        for index in range(8):
            device_parameter_controls.append(EncoderElement(MIDI_CC_TYPE, midi_channel, midi_device_controls[index], Live.MidiMap.MapMode.absolute))
        device.set_parameter_controls(device_parameter_controls)

        self.set_device_component(device)
        self._device_selection_follows_track_selection = True   # select automatically the device of the selected track



    """ Called on configured encoder input """
    """------------------------------------------------------------------------------------------"""
    def _encoder_test_input(self, value):
        self.show_message("Yeah ==> " + str(value) + " ! " + str(mixer.song().view.selected_track))


    """ Initialisation of transport part """
    """------------------------------------------------------------------------------------------"""
    def _setup_transport_control(self):
        is_momentary = True

        """ Instantiate a Transport Component """
        transport = TransportComponent()

        """ Buttons declaration """
        button_play = ButtonElement(is_momentary, MIDI_CC_TYPE, midi_channel, midi_transport_play_button)
        button_stop = ButtonElement(is_momentary, MIDI_CC_TYPE, midi_channel, midi_transport_stop_button)
        button_record = ButtonElement(is_momentary, MIDI_CC_TYPE, midi_channel, midi_transport_record_button)
        button_loop = ButtonElement(is_momentary, MIDI_CC_TYPE, midi_channel, midi_transport_loop_button)
        button_seek_ffwd = ButtonElement(is_momentary, MIDI_CC_TYPE, midi_channel, midi_transport_seek_ffwd)
        button_seek_rwd = ButtonElement(is_momentary, MIDI_CC_TYPE, midi_channel, midi_transport_seek_rwd)

        """ Buttons association """
        transport.set_play_button(button_play)
        transport.set_stop_button(button_stop)
        transport.set_record_button(button_record)
        transport.set_loop_button(button_loop)
        transport.set_seek_buttons(button_seek_ffwd, button_seek_rwd)


    """ Initialisation of mixer part """
    """------------------------------------------------------------------------------------------"""
    def _setup_mixer_control(self):
        is_momentary = True

        """ Instantiate a Mixer Component """
        global mixer  # We want to instantiate the global mixer as a MixerComponent object (it was a global "None" type up until now...)
        mixer = MixerComponent(mixer_num_tracks, 2, with_eqs=True, with_filters=True)  # (num_tracks, num_returns, with_eqs, with_filters)
        mixer.set_track_offset(0)  # Sets start point for mixer strip (offset from left)
        self.song().view.selected_track = mixer.channel_strip(0)._track  # set the selected strip to the first track, so that we don't, for example, try to assign a button to arm the master track, which would cause an assertion error

        """ Buttons and Sliders association """
        # Master channel
        mixer.master_strip().set_volume_control(SliderElement(MIDI_CC_TYPE, midi_channel, midi_mixer_volume_master))  # sets the continuous controller for volume

        # Other channels, same size as mixer_num_tracks
        # Set volume control, solo and mute buttons
        for index in range(mixer_num_tracks):  # launch_button assignment must match number of scenes
            mixer.channel_strip(index).set_volume_control(SliderElement(MIDI_CC_TYPE, midi_channel, midi_mixer_volume_channels[index]))  # sets the continuous controller for volume
            mixer.channel_strip(index).set_solo_button(ButtonElement(is_momentary, MIDI_CC_TYPE, midi_channel, midi_mixer_solo_channels[index]))  # sets the solo button
            mixer.channel_strip(index).set_mute_button(ButtonElement(is_momentary, MIDI_CC_TYPE, midi_channel, midi_mixer_mute_channels[index]))  # sets the mute ("activate") button

        """ Buttons to select track """
        button_next_track = ButtonElement(is_momentary, MIDI_CC_TYPE, midi_channel, midi_track_select_next)
        button_previous_track = ButtonElement(is_momentary, MIDI_CC_TYPE, midi_channel, midi_track_select_previous)
        mixer.set_select_buttons(button_next_track, button_previous_track)

        """ Listeners """
        # When selected track if changed
        self.song().view.add_selected_track_listener(self.on_track_selected)


    """ Called when a track is changed """
    """------------------------------------------------------------------------------------------"""
    def on_track_selected(self):
        self.show_message("Track changed!")

        """ Encoder for Sends controls on selected track """
        '''encoder_test = EncoderElement(MIDI_CC_TYPE, midi_channel, 57, Live.MidiMap.MapMode.absolute)
        encoder_test2 = EncoderElement(MIDI_CC_TYPE, midi_channel, 58, Live.MidiMap.MapMode.absolute)
        encoder_send_buttons = []
        encoder_send_buttons.append(encoder_test)
        encoder_send_buttons.append(encoder_test2)
        mixer.selected_strip().set_send_controls(encoder_send_buttons)'''



    """ Initialisation of session part """
    """------------------------------------------------------------------------------------------"""
    def _setup_session_control(self):
        is_momentary = True

        # Size of session box
        num_tracks = 8 # column
        num_scenes = 1 # row


        """ Buttons declaration """
        # Navigation
        button_navig_up = ButtonElement(is_momentary, MIDI_CC_TYPE, midi_channel, midi_session_up)
        button_navig_down = ButtonElement(is_momentary, MIDI_CC_TYPE, midi_channel, midi_session_down)


        """ Declare our session box """
        global session #We want to instantiate the global session as a SessionComponent object (it was a global "None" type up until now...)
        session = SessionComponent(num_tracks, num_scenes) #(num_tracks, num_scenes) A session highlight ("red box") will appear with any two non-zero values
        session.set_offsets(0, 0) #(track_offset, scene_offset) Sets the initial offset of the "red box" from top left
        session._do_show_highlight()   #to ensure that this session will be highlighted


        """ Buttons association """
        # Navigation up, down, left, right
        session.set_scene_bank_buttons(button_navig_down, button_navig_up)
        # Launch selected clip
        for index in range(num_tracks):
            session.scene(0).clip_slot(index).set_launch_button(ButtonElement(is_momentary, MIDI_CC_TYPE, midi_channel, midi_session_launch_clip[index]))
        # Stop selected track
        stop_track_buttons = []
        for index in range(num_tracks):
            stop_track_buttons.append(ButtonElement(is_momentary, MIDI_CC_TYPE, midi_channel, midi_session_stop_track[index]))
        session.set_stop_track_clip_buttons(tuple(stop_track_buttons)) #array size needs to match num_tracks



    """------------------------------------------------------------------------------------------"""
    def disconnect(self):
        """clean things up on disconnect"""
        self.log_message(time.strftime("%d.%m.%Y %H:%M:%S", time.localtime()) + "--------------= nanoKONTROL log closed =--------------")  # Create entry in log file
        ControlSurface.disconnect(self)
        return None
class OP1(ControlSurface):
    def __init__(self, c_instance):
        ControlSurface.__init__(self, c_instance)
        with self.component_guard():
            self.c_instance = c_instance

            self.retries_count = 0
            self.device_connected = False

            self.clip_color_callbacks = {}
            self.slot_callbacks = {}

            self.text_start_sequence = (0xf0, 0x0, 0x20, 0x76, 0x00, 0x03)
            self.text_end_sequence = (0xf7, )
            self.enable_sequence = (0xf0, 0x00, 0x20, 0x76, 0x00, 0x01, 0x02,
                                    0xf7)
            self.disable_sequence = (0xf0, 0x00, 0x20, 0x76, 0x00, 0x01, 0x00,
                                     0xf7)

            self.id_sequence = (0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7)

            self.text_color_start_sequence = (0xf0, 0x0, 0x20, 0x76, 0x00,
                                              0x04)

            #			self.log('INITIALIZING')

            self.app = Live.Application.get_application()

            #maj = self.app.get_major_version()
            #min = self.app.get_minor_version()
            #bug = self.app.get_bugfix_version()

            #self.show_message(str(maj) + "." + str(min) + "." + str(bug))

            self.show_message("Version: " + VERSION)

            # reseting text
            self.write_text(' ')

            # reset display clips
            self.reset_display_clips()

            # getting browser visible state
            self.session_browser_visible = self.app.view.is_view_visible(
                "Browser")

            # getting browser visible state
            self.arrange_browser_visible = self.app.view.is_view_visible(
                "Browser")

            # getting session view visible state
            self.session_visible = self.app.view.is_view_visible("Session")

            # getting arrange view visible state
            self.arrange_visible = self.app.view.is_view_visible("Arranger")

            # getting detail view visible state
            self.detail_visible = self.app.view.is_view_visible("Detail")

            # getting back to arranger state
            self.back_to_arranger_state = self.song().back_to_arranger

            # initializing channel strip to null
            self._channel_strip = None

            # initializing mixer component
            self._mixer = MixerComponent(NUM_TRACKS, 2)

            # initializing session component
            self._session = SessionComponent(NUM_TRACKS, NUM_ROWS)
            self._session.add_offset_listener(self.session_offset_changed)
            self.set_highlighting_session_component(self._session)
            self._suppress_session_highlight = False

            # initializing transport component
            self._transport = TransportComponent()

            # configuring operation mode selector buttons
            self._operation_mode_buttons = ButtonElement(
                True, MIDI_CC_TYPE, CHANNEL, OP1_MODE_1_BUTTON), ButtonElement(
                    True, MIDI_CC_TYPE,
                    CHANNEL, OP1_MODE_2_BUTTON), ButtonElement(
                        True, MIDI_CC_TYPE, CHANNEL,
                        OP1_MODE_3_BUTTON), ButtonElement(
                            True, MIDI_CC_TYPE, CHANNEL, OP1_MODE_4_BUTTON),

            # initializing operation mode selector
            self._operation_mode_selector = OP1ModeSelectorComponent(
                self, self._transport, self._mixer, self._session)

            # setting operation mode selector buttons
            self._operation_mode_selector.set_mode_buttons(
                self._operation_mode_buttons)

            # adding value listener for operation mode index
            self._operation_mode_selector.add_mode_index_listener(
                self.mode_index_changed)

            # setting global transport assignments
            self._transport.set_record_button(
                ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_REC_BUTTON))
            self._transport.set_stop_button(
                ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_STOP_BUTTON))
            self._transport.set_metronome_button(
                ButtonElement(True, MIDI_CC_TYPE, CHANNEL,
                              OP1_METRONOME_BUTTON))
            self._transport.set_tap_tempo_button(
                ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_HELP_BUTTON))
            #			self._transport.set_punch_buttons(ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS1_BUTTON), ButtonElement(True,MIDI_CC_TYPE, CHANNEL, OP1_SS2_BUTTON))
            self._transport.set_loop_button(
                ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS3_BUTTON))
            self._transport.set_overdub_button(
                ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS4_BUTTON))

            self._play_button = ButtonElement(True, MIDI_CC_TYPE, CHANNEL,
                                              OP1_PLAY_BUTTON)
            #                        self._transport.set_play_button(self._play_button)
            self.shift_pressed = False
            self._play_button.add_value_listener(self.play_button_callback)

            # setting global session assignments
            self._session.set_scene_bank_buttons(
                ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_COM),
                ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_MICRO))

            # encoder for transport control - leave it empty for now
            self._encoder_1 = EncoderElement(
                MIDI_CC_TYPE, CHANNEL, OP1_ENCODER_1,
                Live.MidiMap.MapMode.relative_two_compliment)
            self._encoder_2 = EncoderElement(
                MIDI_CC_TYPE, CHANNEL, OP1_ENCODER_2,
                Live.MidiMap.MapMode.relative_two_compliment)
            self._encoder_3 = EncoderElement(
                MIDI_CC_TYPE, CHANNEL, OP1_ENCODER_3,
                Live.MidiMap.MapMode.relative_two_compliment)
            self._encoder_4 = EncoderElement(
                MIDI_CC_TYPE, CHANNEL, OP1_ENCODER_4,
                Live.MidiMap.MapMode.relative_two_compliment)
            # setting misc listeners

            self._encoder_1_push = ButtonElement(True, MIDI_CC_TYPE, CHANNEL,
                                                 OP1_ENCODER_1_PUSH)
            self._encoder_2_push = ButtonElement(True, MIDI_CC_TYPE, CHANNEL,
                                                 OP1_ENCODER_2_PUSH)
            self._encoder_3_push = ButtonElement(True, MIDI_CC_TYPE, CHANNEL,
                                                 OP1_ENCODER_3_PUSH)
            self._encoder_4_push = ButtonElement(True, MIDI_CC_TYPE, CHANNEL,
                                                 OP1_ENCODER_4_PUSH)

            self._encoder_3_push.add_value_listener(self.e3_push_callback)
            self._e3_pressed = False

            self.mainview_toggle_button = ButtonElement(
                False, MIDI_CC_TYPE, CHANNEL, OP1_ARROW_DOWN_BUTTON)
            self.mainview_toggle_button.add_value_listener(
                self.mainview_toggle_button_callback)

            self.detailview_toggle_button = ButtonElement(
                False, MIDI_CC_TYPE, CHANNEL, OP1_SCISSOR_BUTTON)
            self.detailview_toggle_button.add_value_listener(
                self.detailview_toggle_button_callback)

            self.clear_track_button = ButtonElement(True, MIDI_CC_TYPE,
                                                    CHANNEL, OP1_SS8_BUTTON)
            self.clear_track_button.add_value_listener(
                self.clear_track_button_callback)

            self.back_to_arranger_button = ButtonElement(
                True, MIDI_CC_TYPE, CHANNEL, OP1_SEQ_BUTTON)
            self.back_to_arranger_button.add_value_listener(
                self.back_to_arranger_button_callback)

            # adding value listener for selected track change
            self.song().view.add_selected_track_listener(
                self.selected_track_changed)

            # adding value listener for selected scene change
            self.song().view.add_selected_scene_listener(
                self.selected_scene_changed)

            # setting assignments for currently selected track
            self.selected_track_changed()

            # setting assignments for currently selected scene
            self.selected_scene_changed()

    def handle_sysex(self, midi_bytes):
        if ((midi_bytes[6] == 32) and (midi_bytes[7] == 118)):
            self.device_connected = True
            self.log("OP-1 CONNECTED. SENDING ABLETON LIVE MODE INIT SEQUENCE")
            self._send_midi(self.enable_sequence)

    def add_clip_slot_listeners(self):
        #self.log('ADDING CLIP SLOT LISTENERS')

        # creating an empty list for all clip slots
        clip_slots = []

        # getting a reference to all tracks
        tracks = self.song().tracks

        # appending all tracks clip slots to clip_slots
        for track in tracks:
            clip_slots.append(track.clip_slots)

        # iterating over all clip slots
        for t in range(len(clip_slots)):
            for c in range(len(clip_slots[t])):
                clip_slot = clip_slots[t][c]

                # adding has clip listener to clip slot
                self.add_slot_listener(clip_slot)

                # if clip slot has clip
                if clip_slot.has_clip:
                    # adding clip listeners
                    self.add_clip_listener(clip_slot.clip)

    def rem_clip_slot_listeners(self):
        #self.log('REMOVING CLIP SLOT LISTENERS')

        # iterate over all clip color change callbacks
        for c in self.clip_color_callbacks:
            # if clip still exists
            if c != None:
                # and it has a has clip listener
                if c.color_has_listener(self.clip_color_callbacks[c]) == 1:
                    # remove it
                    c.remove_color_listener(self.clip_color_callbacks[c])

        # iterate over all clip slot callbacks
        for cs in self.slot_callbacks:
            # if clip slot still exists
            if cs != None:
                # and it has a has clip listener
                if cs.has_clip_has_listener(self.slot_callbacks[cs]) == 1:
                    # remove it
                    cs.remove_has_clip_listener(self.slot_callbacks[cs])

    def add_slot_listener(self, cs):
        # setting has clip listener
        callback = lambda: self.has_clip_listener(cs)

        # if we don't have a clip slot has clip listener for this clip slot yet
        if not (self.slot_callbacks.has_key(cs)):
            # adding has clip callback to clip slot
            cs.add_has_clip_listener(callback)

            # saving callback for future release
            self.slot_callbacks[cs] = callback

    def add_clip_listener(self, clip):
        # setting callback for clip color change
        color_callback = lambda: self.update_display_clips()

        # if we don't have a clip color change callback for this clip yet
        if not (self.clip_color_callbacks.has_key(clip)):
            # adding clip color change callback
            clip.add_color_listener(color_callback)

            # saving callback for future release
            self.clip_color_callbacks[clip] = color_callback

    def has_clip_listener(self, cs):
        # clip slot has clip listener callback
        if cs.has_clip:
            # add clip listener
            self.add_clip_listener(cs.clip)
        else:
            # update display if clip slot was removed
            self.update_display_clips()

    def session_offset_changed(self):
        # if session component offset changes, update display
        self.update_display_clips()

    def selected_scene_changed(self):
        # if on clip mode update display
        if (self._operation_mode_selector.mode_index == OP1_MODE_CLIP):
            self.update_display_clip_mode()

    def e3_push_callback(self, value):
        self._e3_pressed = True if value == 127 else False

    def e1_transport_scrub(self, value):
        if value == 4:
            self.song().scrub_by(1)
        else:
            self.song().scrub_by(-1)

    def e2_transport_scrub(self, value):
        if value == 4:
            x = 1
        else:
            x = -1
        idx = get_q_idx(self.song().clip_trigger_quantization)
        self.song().clip_trigger_quantization = get_q_enum(idx + x)

    def e_transport_scroll(self, value, b):
        if value == 4:
            x = Live.Application.Application.View.NavDirection.right
        else:
            x = Live.Application.Application.View.NavDirection.left
        self.app.view.scroll_view(x, "Arranger", b)

    def e3_transport_scroll(self, value):
        self.e_transport_scroll(value, self._e3_pressed)

    def play_button_callback(self, value):
        if self.shift_pressed == True:
            self.song().play_selection()
        else:
            self.song().start_playing()

    def mode_index_changed(self):
        # update display to current mode info
        self._encoder_1.remove_value_listener(self.e1_transport_scrub)
        self._encoder_2.remove_value_listener(self.e2_transport_scrub)
        self._encoder_3.remove_value_listener(self.e3_transport_scroll)
        if (self._operation_mode_selector.mode_index == OP1_MODE_PERFORM):
            self.update_display_perform_mode()
        elif (self._operation_mode_selector.mode_index == OP1_MODE_CLIP):
            self.update_display_clip_mode()
        elif (self._operation_mode_selector.mode_index == OP1_MODE_TRANSPORT):
            self.update_display_transport_mode()
            self.clear_tracks_assigments()
            self._encoder_1.add_value_listener(self.e1_transport_scrub)
            self._encoder_2.add_value_listener(self.e2_transport_scrub)
            self._encoder_3.add_value_listener(self.e3_transport_scroll)
        elif (self._operation_mode_selector.mode_index == OP1_MODE_MIXER):
            self.update_display_mixer_mode()

    def clear_track_button_callback(self, value):
        # if clear track button was called, reset track
        if (value == 127):
            for i in range(len(self.song().tracks)):
                self.song().tracks[i].arm = 0
                self.song().tracks[i].solo = 0
                self.song().tracks[i].mute = 0

            for i in range(len(self.song().return_tracks)):
                self.song().tracks[i].solo = 0
                self.song().tracks[i].mute = 0

    def clear_return_track_assignment(self, strip):
        # clear return track assingments
        strip.set_volume_control(None)
        strip.set_pan_control(None)
        strip.set_mute_button(None)
        strip.set_solo_button(None)

    def clear_track_assignment(self, strip):
        # clear track assignments
        strip.set_volume_control(None)
        strip.set_pan_control(None)
        strip.set_mute_button(None)
        strip.set_solo_button(None)
        strip.set_arm_button(None)

    def clear_tracks_assigments(self):
        # for all normal tracks, clear assignments
        self.clear_track_assignment(self._mixer.selected_strip())
        self.clear_return_track_assignment(self._mixer.selected_strip())
        self._mixer.selected_strip().set_send_controls(tuple((None, None)))
        for i in range(NUM_TRACKS):
            strip = self._mixer.channel_strip(i)
            if (strip != None):
                self.clear_track_assignment(strip)

    # for all return tracks, clear assignments
        for i in range(2):
            return_strip = self._mixer.return_strip(i)
            if (return_strip != None):
                self.clear_return_track_assignment(return_strip)

    def selected_track_changed(self):
        # if on mixer mode update display
        if (self._operation_mode_selector.mode_index == OP1_MODE_MIXER):
            self.update_display_mixer_mode()

        # clear track assignments
        self.clear_tracks_assigments()

        # if transport mode, we don't want to control stuff
        if (self._operation_mode_selector.mode_index == OP1_MODE_TRANSPORT):
            return

        # getting selected strip
        self._channel_strip = self._mixer.selected_strip()

        # perform track assignments
        self._channel_strip.set_volume_control(self._encoder_1)
        self._channel_strip.set_pan_control(self._encoder_2)

        # setting a tuple of encoders to control sends
        send_controls = self._encoder_3, self._encoder_4,

        # setting send encoders
        self._channel_strip.set_send_controls(tuple(send_controls))

        # setting solo button
        self._channel_strip.set_solo_button(
            ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS6_BUTTON))

        # if track can be armed, set arm button
        if (self._channel_strip._track.can_be_armed):
            self._channel_strip.set_arm_button(
                ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS7_BUTTON))

        # if track is no master, set mute button
        if (self._channel_strip._track != self.song().master_track):
            self._channel_strip.set_mute_button(
                ButtonElement(True, MIDI_CC_TYPE, CHANNEL, OP1_SS5_BUTTON))

    def back_to_arranger_button_callback(self, value):
        if (value == 127):
            self.song().back_to_arranger = False

    def mainview_toggle_button_callback(self, value):
        if (value == 127):
            if (self.session_visible == True):
                self.session_visible = False
                self.arrange_visible = True
                self.app.view.show_view("Arranger")
                self.arrange_browser_visible = self.app.view.is_view_visible(
                    "Browser")
            else:
                self.session_visible = True
                self.arrange_visible = False
                self.app.view.show_view("Session")
                self.session_browser_visible = self.app.view.is_view_visible(
                    "Browser")

    def detailview_toggle_button_callback(self, value):
        if (value == 127):
            if (self.detail_visible == True):
                self.detail_visible = False
                self.app.view.hide_view("Detail")
            else:
                self.detail_visible = True
                self.app.view.show_view("Detail")

    def write_text(self, msg):
        text_list = []
        sequence = ()

        text_list.append(len(msg.strip()))

        for i in msg.strip():
            text_list.append(ord(i))

        sequence = self.text_start_sequence + tuple(
            text_list) + self.text_end_sequence

        self._send_midi(sequence)

    def suggest_input_port(self):
        return "OP-1 Midi Device"

    def suggest_output_port(self):
        return "OP-1 Midi Device"

    def update_display_perform_mode(self):
        self.write_text("perform\rmode")

    def reset_display_clips(self):
        count = 0
        colors = []
        length = []
        sequence = ()

        for i in range(NUM_TRACKS):
            count += 1

            colors.append(0x00)
            colors.append(0x00)
            colors.append(0x00)

        length.append(count)
        sequence = self.text_color_start_sequence + tuple(length) + tuple(
            colors) + self.text_end_sequence
        self._send_midi(sequence)

    def update_display_clips(self):
        #self.log("UPDATING DISPLAY CLIPS")
        count = 0
        colors = []
        length = []
        sequence = ()

        tracks_len = len(self.song().tracks) - self._session._track_offset

        if (tracks_len > NUM_TRACKS):
            tracks_len = NUM_TRACKS

        for i in range(tracks_len):
            count += 1

            clip_slot = self._session.scene(0).clip_slot(i)

            if (clip_slot != None):
                if (clip_slot.has_clip() != False):
                    clip_color = clip_slot._clip_slot.clip.color
                    colors.append(((clip_color >> 16) & 0x000000ff) >> 1)
                    colors.append(((clip_color >> 8) & 0x000000ff) >> 1)
                    colors.append((clip_color & 0x000000ff) >> 1)
                else:
                    colors.append(0x00)
                    colors.append(0x00)
                    colors.append(0x00)
            else:
                colors.append(0x00)
                colors.append(0x00)
                colors.append(0x00)

        length.append(count)
        sequence = self.text_color_start_sequence + tuple(length) + tuple(
            colors) + self.text_end_sequence
        self._send_midi(sequence)

    def update_display_clip_mode(self):
        self.write_text(
            "sel. scene\r" +
            str(self.song().view.selected_scene.name.lower().strip()))

    def get_quant_str(self, q):
        if q == Live.Song.Quantization.q_2_bars: return "2b"
        if q == Live.Song.Quantization.q_4_bars: return "4b"
        if q == Live.Song.Quantization.q_8_bars: return "8b"
        if q == Live.Song.Quantization.q_bar: return "1b"
        if q == Live.Song.Quantization.q_half: return "/2"
        if q == Live.Song.Quantization.q_half_triplet: return "/2t"
        if q == Live.Song.Quantization.q_quarter: return "/4"
        if q == Live.Song.Quantization.q_quarter_triplet: return "/4t"
        if q == Live.Song.Quantization.q_eight: return "/8"
        if q == Live.Song.Quantization.q_eight_triplet: return "/8t"
        if q == Live.Song.Quantization.q_sixtenth: return "/16"
        if q == Live.Song.Quantization.q_sixtenth_triplet: return "/16t"
        if q == Live.Song.Quantization.q_thirtytwoth: return "/32"
        if q == Live.Song.Quantization.q_no_q: return "non"
        return ":("

    def update_display_transport_mode(self):
        song_time = str(self.song().get_current_beats_song_time())
        playing = '>' if self.song().is_playing else ''
        record = '*' if self.song().record_mode else ''
        self.write_text(
            playing + record + " " +
            self.get_quant_str(self.song().clip_trigger_quantization) + " " +
            str("%.2f" % round(self.song().tempo, 2)) + "\r" +
            song_time[:len(song_time) - 4])

    def update_display_mixer_mode(self):
        self.write_text("sel. track\r" +
                        str(self.song().view.selected_track.name.lower()))

    def update_display(self):
        if not (self.device_connected):
            if (self.retries_count < 5):
                self.log("TRYING OP-1 CONNECTION")
                self.retries_count += 1
                self._send_midi(self.id_sequence)
                time.sleep(1)

        # if in transport mode, update display with song position
        if (self._operation_mode_selector.mode_index == OP1_MODE_TRANSPORT):
            self.update_display_transport_mode()

        # checking if app current view is session
        if (self.app.view.is_view_visible("Session")):
            # checking if session browser state is diferent from the internal
            if (self.session_browser_visible !=
                    self.app.view.is_view_visible("Browser")):
                self.session_browser_visible = self.app.view.is_view_visible(
                    "Browser")

        # checking if app current view is arrange
        if (self.app.view.is_view_visible("Arranger")):
            # checking if arrange browser state is diferent from the internal
            if (self.arrange_browser_visible !=
                    self.app.view.is_view_visible("Browser")):
                self.arrange_browser_visible = self.app.view.is_view_visible(
                    "Browser")

        # checking if app current view is detail
        if (self.app.view.is_view_visible("Detail")):
            # checking if detail state is diferent from the internal
            if (self.detail_visible !=
                    self.app.view.is_view_visible("Detail")):
                self.detail_visible = self.app.view.is_view_visible("Detail")

    def refresh_state(self):
        self.log("REFRESH STATE")
        self.retries_count = 0
        self.device_connected = False

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

        # remove clip listeners
        self.rem_clip_slot_listeners()

        # add clip listeners
        self.add_clip_slot_listeners()

        # update display
        self.update_display_clips()

    def log(self, msg):
        self.c_instance.log_message("[TE OP-1] " + msg)

    def disconnect(self):
        # removing clip slots listeners
        self.rem_clip_slot_listeners()

        # removing value listener for track changed
        self.song().view.remove_selected_track_listener(
            self.selected_track_changed)

        # removing value listener for scene changed
        self.song().view.remove_selected_scene_listener(
            self.selected_scene_changed)

        # removing value listener for operation mode index
        self._operation_mode_selector.remove_mode_index_listener(
            self.mode_index_changed)

        # removing global transport assignments
        self._transport.set_punch_buttons(None, None)
        self._transport.set_loop_button(None)
        self._transport.set_overdub_button(None)
        self._transport.set_record_button(None)
        self._transport.set_play_button(None)
        self._transport.set_stop_button(None)
        self._transport.set_metronome_button(None)
        self._transport.set_tap_tempo_button(None)

        # removing global session assignments
        self._session.set_scene_bank_buttons(None, None)

        # removing misc listeners
        self.browser_toggle_button.remove_value_listener(
            self.browser_toggle_button_callback)
        self.mainview_toggle_button.remove_value_listener(
            self.mainview_toggle_button_callback)
        self.detailview_toggle_button.remove_value_listener(
            self.detailview_toggle_button_callback)
        self.clear_track_button.remove_value_listener(
            self.clear_track_button_callback)
        self.back_to_arranger_button.remove_value_listener(
            self.back_to_arranger_button_callback)

        # sending special ableton mode disable sequence
        self._send_midi(self.disable_sequence)

        # disconnecting control surface
        ControlSurface.disconnect(self)

        self.log("DISCONNECTED")
Ejemplo n.º 6
0
class joyMIDI(ControlSurface):
    def __init__(self, c_instance):
        ControlSurface.__init__(self, c_instance)
        with self.component_guard():
            self.setup_transport()
            self.setup_mixer()
            self.setup_session()

    def setup_transport(self):
        # elements
        self.play_button = ButtonElement(True, MIDI_NOTE_TYPE, 0, 93)
        self.stop_button = ButtonElement(True, MIDI_NOTE_TYPE, 0, 94)
        self.record_button = ButtonElement(True, MIDI_NOTE_TYPE, 0, 95)
        # transport
        self.transport = TransportComponent()
        self.transport.set_play_button(self.play_button)
        self.transport.set_stop_button(self.stop_button)
        self.transport.set_record_button(self.record_button)

    def setup_mixer(self):
        # elements
        self.mute_button = ButtonElement(True, MIDI_NOTE_TYPE, 0,
                                         92)  # tracks, return_tracks
        self.solo_button = ButtonElement(True, MIDI_NOTE_TYPE, 0,
                                         84)  # tracks, return_tracks
        self.arm_button = ButtonElement(True, MIDI_NOTE_TYPE, 0, 85)  # tracks
        self.senda_up_button = ButtonElement(True, MIDI_NOTE_TYPE, 0,
                                             96)  # tracks, return_tracks
        self.senda_down_button = ButtonElement(True, MIDI_NOTE_TYPE, 0,
                                               88)  # tracks, return_tracks
        self.sendb_up_button = ButtonElement(True, MIDI_NOTE_TYPE, 0,
                                             97)  # tracks, return_tracks
        self.sendb_down_button = ButtonElement(True, MIDI_NOTE_TYPE, 0,
                                               89)  # tracks, return_tracks
        self.pan_up_button = ButtonElement(
            True, MIDI_NOTE_TYPE, 0, 98)  # tracks, return_tracks, master_track
        self.pan_down_button = ButtonElement(
            True, MIDI_NOTE_TYPE, 0, 90)  # tracks, return_tracks, master_track
        self.volume_up_button = ButtonElement(
            True, MIDI_NOTE_TYPE, 0, 99)  # tracks, return_tracks, master_track
        self.volume_down_button = ButtonElement(
            True, MIDI_NOTE_TYPE, 0, 91)  # tracks, return_tracks, master_track
        self.track_nav_encoder = EncoderElement(
            MIDI_CC_TYPE, 0, 14, Live.MidiMap.MapMode.relative_binary_offset)
        # mixer
        self.mixer = MixerComponent(7, 2)
        self.mixer.selected_strip().set_mute_button(self.mute_button)
        self.mixer.selected_strip().set_solo_button(self.solo_button)
        self.mixer.selected_strip().set_arm_button(self.arm_button)
        # send A/B, pan, volume
        self.senda_up_button.add_value_listener(self.on_senda_up_changed)
        self.senda_down_button.add_value_listener(self.on_senda_down_changed)
        self.sendb_up_button.add_value_listener(self.on_sendb_up_changed)
        self.sendb_down_button.add_value_listener(self.on_sendb_down_changed)
        self.pan_up_button.add_value_listener(self.on_pan_up_changed)
        self.pan_down_button.add_value_listener(self.on_pan_down_changed)
        self.volume_up_button.add_value_listener(self.on_volume_up_changed)
        self.volume_down_button.add_value_listener(self.on_volume_down_changed)
        # nav
        self.track_nav_encoder.add_value_listener(self.on_mixer_track_nav)

    def on_senda_up_changed(self, value):
        if value > 0:
            param = self.song().view.selected_track.mixer_device.sends[0]
            param.value = max(
                param.min,
                min(param.max,
                    param.value + ((param.max - param.min) / SEND_STEPS)))

    def on_senda_down_changed(self, value):
        if value > 0:
            param = self.song().view.selected_track.mixer_device.sends[0]
            param.value = max(
                param.min,
                min(param.max,
                    param.value - ((param.max - param.min) / SEND_STEPS)))

    def on_sendb_up_changed(self, value):
        if value > 0:
            param = self.song().view.selected_track.mixer_device.sends[1]
            param.value = max(
                param.min,
                min(param.max,
                    param.value + ((param.max - param.min) / SEND_STEPS)))

    def on_sendb_down_changed(self, value):
        if value > 0:
            param = self.song().view.selected_track.mixer_device.sends[1]
            param.value = max(
                param.min,
                min(param.max,
                    param.value - ((param.max - param.min) / SEND_STEPS)))

    def on_pan_up_changed(self, value):
        if value > 0:
            param = self.song().view.selected_track.mixer_device.panning
            param.value = max(
                param.min,
                min(param.max,
                    param.value + ((param.max - param.min) / PAN_STEPS)))

    def on_pan_down_changed(self, value):
        if value > 0:
            param = self.song().view.selected_track.mixer_device.panning
            param.value = max(
                param.min,
                min(param.max,
                    param.value - ((param.max - param.min) / PAN_STEPS)))

    def on_volume_up_changed(self, value):
        if value > 0:
            param = self.song().view.selected_track.mixer_device.volume
            param.value = max(
                param.min,
                min(param.max,
                    param.value + ((param.max - param.min) / VOLUME_STEPS)))

    def on_volume_down_changed(self, value):
        if value > 0:
            param = self.song().view.selected_track.mixer_device.volume
            param.value = max(
                param.min,
                min(param.max,
                    param.value - ((param.max - param.min) / VOLUME_STEPS)))

    def on_mixer_track_nav(self, value):
        move = -1 if value > 64 else +1
        # get tracks-info
        tracks = self.song().tracks
        return_tracks = self.song().return_tracks
        master_track = self.song().master_track
        all_tracks = list(chain(tracks, return_tracks))
        all_tracks.append(master_track)
        num_tracks = len(tracks)
        num_return_tracks = len(return_tracks)
        num_master_track = 1
        num_all_tracks = num_tracks + num_return_tracks + num_master_track
        # update selected-track
        index = index_if(lambda t: t == self.song().view.selected_track,
                         all_tracks)
        index += move
        index = min(max(index, 0), num_all_tracks - 1)
        self.song().view.selected_track = all_tracks[index]

    def setup_session(self):
        num_tracks = 7
        num_scenes = 1
        track_offset = 0
        scene_offset = 0
        # elements
        self.clip_launch_buttons = ButtonMatrixElement(rows=[[
            ButtonElement(True, MIDI_NOTE_TYPE, 0, 76 + i)
            for i in range(num_tracks)
        ]])
        self.clip_stop_buttons = [
            ButtonElement(True, MIDI_NOTE_TYPE, 0, 68 + i)
            for i in range(num_tracks)
        ]
        self.scene_launch_buttons = ButtonMatrixElement(rows=[[
            ButtonElement(True, MIDI_NOTE_TYPE, 0, 83 + i)
            for i in range(num_scenes)
        ]])
        self.scene_stop_button = ButtonElement(True, MIDI_NOTE_TYPE, 0, 75)
        self.session_scene_nav_encoder = EncoderElement(
            MIDI_CC_TYPE, 0, 15, Live.MidiMap.MapMode.relative_binary_offset)
        # session
        self.session = SessionComponent(num_tracks, num_scenes)
        self.session.set_offsets(track_offset, scene_offset)
        self.session.add_offset_listener(self.on_session_offset_changed)
        self.set_highlighting_session_component(self.session)
        # clips
        self.session.set_clip_launch_buttons(self.clip_launch_buttons)
        self.session.set_stop_track_clip_buttons(self.clip_stop_buttons)
        self.session.set_scene_launch_buttons(self.scene_launch_buttons)
        self.session.set_stop_all_clips_button(self.scene_stop_button)
        # nav
        self.session_scene_nav_encoder.add_value_listener(
            self.on_session_scene_nav)

    def on_session_offset_changed(self):
        pass

    def on_session_scene_nav(self, value):
        value -= 64
        value = -value
        track_offset = self.session.track_offset()
        scene_offset = max(0, self.session.scene_offset() + value)
        self.session.set_offsets(track_offset, scene_offset)

    def disconnect(self):
        u"""Live -> Script
        Called right before we get disconnected from Live.
        """
        self.log_message('disconnect')
        self.show_message('disconnect')