Generic Script for Red Box and more

Share your favorite Ableton Live tips, tricks, and techniques.
Post Reply
Flypado
Posts: 2
Joined: Sat Oct 31, 2020 4:18 am

Generic Script for Red Box and more

Post by Flypado » Sat Nov 07, 2020 9:17 pm

Hey guys

I wrote a script to have a Red Box, or Session Box, and to control it up, down, left and right.

I'm using M Audio Oxygen 49 so I also managed to assign the 8 pads to launch the scenes inside the red box.

I have absolutely no knowledge or skills about coding, python, and I've only recently began using Ableton. This was only possible thanks to few pieces of information shared by users of this forum, and very few other sites, so I think it's only fair for me to share this.

I know anyone who uses an M Audio Oxygen may find this useful, but this should work with any other midi device.

This is it:

1. __init__

Code: Select all

from sim_ses import sim_ses

def create_instance(c_instance):
    return sim_ses(c_instance)

2. sim_ses

Code: Select all

from __future__ import with_statement
import Live
from _Framework.ControlSurface import ControlSurface
from _Framework.ControlSurfaceComponent import ControlSurfaceComponent
from _Framework.InputControlElement import *
from _Framework.ButtonElement import ButtonElement
from _Framework.ButtonMatrixElement import ButtonMatrixElement
from _Framework.SessionComponent import SessionComponent
from _Framework.TransportComponent import TransportComponent
from _Framework.EncoderElement import EncoderElement
from SpecialTransportComponent import SpecialTransportComponent


numTRA = 4      #number of tracks

numSCE = 2      #number of scenes

sesBUT = [27, 22, 28, 26, 13, 15, 19, 23]      #1st top/left button of your matrix

upBUT = 53

dowBUT = 54

lefBUT = 55

rigBUT = 52

CHANNEL = 0      #main channel (0 - 15)

BUTTYP = MIDI_CC_TYPE

BUTTNP = MIDI_NOTE_TYPE

is_momentary = True

undoBUT = 113

CHANNELPADS = 9

class sim_ses(ControlSurface):

   def __init__(self, c_instance):
      super(sim_ses, self).__init__(c_instance)
      with self.component_guard():
         self._setup_controls()
         self._setup_session()

   def _setup_controls(self):
      self._pads = [ButtonElement(is_momentary, BUTTNP, CHANNELPADS, sesBUT[index]) for index in range(numTRA*numSCE)]
      self._grid = ButtonMatrixElement(rows=[self._pads[(index*numTRA):(index*numTRA)+numTRA] for index in range(numSCE)])
      self._up_but = ButtonElement(is_momentary, BUTTYP, CHANNEL, upBUT)
      self._dow_but = ButtonElement(is_momentary, BUTTYP, CHANNEL, dowBUT)
      self._lef_but = ButtonElement(is_momentary, BUTTYP, CHANNEL, lefBUT)
      self._rig_but = ButtonElement(is_momentary, BUTTYP, CHANNEL, rigBUT)


   def _setup_session(self):
      self._session = SessionComponent(numTRA, numSCE, enable_skinning = True)
      self._session.set_clip_launch_buttons(self._grid)
      self.set_highlighting_session_component(self._session)
      self._session.set_scene_bank_up_button(self._up_but)
      self._session.set_scene_bank_down_button(self._dow_but)
      self._session.set_track_bank_left_button(self._lef_but)
      self._session.set_track_bank_right_button(self._rig_but)
      self._session._link()

   def _setup_device_and_transport_control(self):
      is_momentary = True
      device_param_controls = []
      for index in range(8):
          device_param_controls.append(EncoderElement(MIDI_CC_TYPE, 15, 62 - index, Live.MidiMap.MapMode.absolute))

      device = SpecialDeviceComponent()
      device.set_bank_nav_buttons(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 107), ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 106))
      device.set_parameter_controls(tuple(device_param_controls))
      self.set_device_component(device)
      transport = SpecialTransportComponent()
      transport.set_undo_button(ButtonElement(is_momentary, MIDI_CC_TYPE, 0, undoBUT))

   def disconnect(self):
      super(sim_ses, self).disconnect()
3. SpecialTransportComponent

Code: Select all

import Live
from _Framework.TransportComponent import TransportComponent
from _Framework.InputControlElement import *
from _Framework.ButtonElement import ButtonElement
from _Framework.EncoderElement import EncoderElement

class SpecialTransportComponent(TransportComponent):
    """ Transport component that takes buttons for Undo and Redo """

    def __init__(self):
        TransportComponent.__init__(self)
        self._undo_button = None

    def disconnect(self):
        TransportComponent.disconnect(self)
        if self._undo_button != None:
            self._undo_button.remove_value_listener(self._undo_value)
            self._undo_button = None

    def set_undo_button(self, undo_button):
        if not isinstance(undo_button, (ButtonElement, type(None))):
            raise AssertionError
            if undo_button != self._undo_button:
                if self._undo_button != None:
                    self._undo_button.remove_value_listener(self._undo_value)
                self._undo_button = undo_button
                self._undo_button != None and self._undo_button.add_value_listener(self._undo_value)
            self.update()

    def _undo_value(self, value):
        if not self._undo_button != None:
            raise AssertionError
            if not value in range(128):
                raise AssertionError
                if self.is_enabled():
                    (value != 0 or not self._undo_button.is_momentary()) and self.song().can_undo and self.song().undo()
The undo button still doesn't work, I haven't figured it out.
I'm also trying to have the up, down, left, right assigned to only 2 knobs, instead of 4, but this one is making my head spin.

Would appreciate any hints!

bobavenger
Posts: 29
Joined: Sat Oct 11, 2014 10:44 am

Re: Generic Script for Red Box and more

Post by bobavenger » Wed Dec 09, 2020 2:17 pm

Hi Flypado,
I've tried to correct your script
first you have to call the device component (I'm not sure the SpecialDeviceComponent you use is in the framework, so maybe the classic one is enough
add thoses lines at the beginning

Code: Select all

from _Framework.DeviceComponent import DeviceComponent
and for the undo button I need the subjectcomponent

Code: Select all

from _Framework.SubjectSlot import subject_slot,Subject
and then call your def device here

Code: Select all

	def __init__(self, c_instance):
		super(sim_ses, self).__init__(c_instance)
		with self.component_guard():
			self._setup_controls()
			self._setup_session()
			self._setup_device_and_transport_control()
and then :

Code: Select all

	def _setup_device_and_transport_control(self):
		is_momentary=True
		self.device_param_controls=[]
		for index in range(8):
			self.device_param_controls.append(EncoderElement(MIDI_CC_TYPE, 15, 62 - index, Live.MidiMap.MapMode.absolute))

		self.device = DeviceComponent()
		self.device.set_bank_nav_buttons(ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 107), ButtonElement(is_momentary, MIDI_CC_TYPE, 15, 106))
		self.device.set_parameter_controls(self.device_param_controls)
		self.set_device_component(self.device)
		self.on_undo_value.subject=(ButtonElement(is_momentary, MIDI_CC_TYPE, 0, undoBUT))

	@subject_slot('value')
	def on_undo_value(self,v):
		if v and self.song().can_undo:
			self.song().undo()
			
					
hope it will work
do you read the log file for finding bugs?

Flypado
Posts: 2
Joined: Sat Oct 31, 2020 4:18 am

Re: Generic Script for Red Box and more

Post by Flypado » Wed Dec 23, 2020 7:32 pm

Hello Bobavenger !!

I'm also sorry for the late response.

First of all thank you so much for your help. It's insane how much easier it is to work now.

The script is working !! I erased some of the lines that were there from my last attempt at writing the Undo button, added your lines, and voilá.

This is what it looks like now:

Code: Select all

from __future__ import with_statement
import Live
from _Framework.ControlSurface import ControlSurface
from _Framework.ControlSurfaceComponent import ControlSurfaceComponent
from _Framework.InputControlElement import *
from _Framework.ButtonElement import ButtonElement
from _Framework.ButtonMatrixElement import ButtonMatrixElement
from _Framework.SessionComponent import SessionComponent
from _Framework.TransportComponent import TransportComponent
from _Framework.EncoderElement import EncoderElement
from _Framework.DeviceComponent import DeviceComponent
from _Framework.SubjectSlot import subject_slot,Subject



numTRA = 4      #number of tracks

numSCE = 2      #number of scenes

sesBUT = [27, 22, 28, 26, 13, 15, 19, 23]      #1st top/left button of your matrix

upBUT = 108

dowBUT = 109

lefBUT = 107

rigBUT = 110

CHANNEL = 0      #main channel (0 - 15)

BUTTYP = MIDI_CC_TYPE

BUTTNP = MIDI_NOTE_TYPE

is_momentary = True

undoBUT = 55

CHANNELPADS = 9

class sim_ses(ControlSurface):

   def __init__(self, c_instance):
      super(sim_ses, self).__init__(c_instance)
      with self.component_guard():
         self._setup_controls()
         self._setup_session()
         self._setup_device_and_transport_control()

   def _setup_controls(self):
      self._pads = [ButtonElement(is_momentary, BUTTNP, CHANNELPADS, sesBUT[index]) for index in range(numTRA*numSCE)]
      self._grid = ButtonMatrixElement(rows=[self._pads[(index*numTRA):(index*numTRA)+numTRA] for index in range(numSCE)])
      self._up_but = ButtonElement(is_momentary, BUTTYP, CHANNEL, upBUT)
      self._dow_but = ButtonElement(is_momentary, BUTTYP, CHANNEL, dowBUT)
      self._lef_but = ButtonElement(is_momentary, BUTTYP, CHANNEL, lefBUT)
      self._rig_but = ButtonElement(is_momentary, BUTTYP, CHANNEL, rigBUT)


   def _setup_session(self):
      self._session = SessionComponent(numTRA, numSCE, enable_skinning = True)
      self._session.set_clip_launch_buttons(self._grid)
      self.set_highlighting_session_component(self._session)
      self._session.set_scene_bank_up_button(self._up_but)
      self._session.set_scene_bank_down_button(self._dow_but)
      self._session.set_track_bank_left_button(self._lef_but)
      self._session.set_track_bank_right_button(self._rig_but)
      self._session._link()


   def _setup_device_and_transport_control(self):
      is_momentary=True
      self.device_param_controls=[]
      self.device = DeviceComponent()
      self.device.set_parameter_controls(self.device_param_controls)
      self.set_device_component(self.device)
      self.on_undo_value.subject=(ButtonElement(is_momentary, MIDI_CC_TYPE, 0, undoBUT))

   @subject_slot('value')
   def on_undo_value(self,v):
      if v and self.song().can_undo:
         self.song().undo()


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

bobavenger
Posts: 29
Joined: Sat Oct 11, 2014 10:44 am

Re: Generic Script for Red Box and more

Post by bobavenger » Mon Dec 28, 2020 10:31 am

HI FLypado, you're welcome, I'm glad to help on scripting!
1. Instead of 4 buttons to control up, down, left, right, do you think it's possible to use 2 knobs for the same effect ? One knob for left and right, another for up and down.
you can do that with a loop: just one right button that go back to the first track when it reach the last tracks

it can be done by overiding the session component:
at the end of your script create this class

Code: Select all

class ovrdSessionComponent(SessionComponent):

	def _can_bank_down(self):
		return len(self.song().scenes)>self.height()

	def can_loop_down(self):
		return len(self.song().scenes)-self.height()>self._get_minimal_scene_offset()

	def _bank_down(self):
		return self.set_offsets(self.track_offset(),self.scene_offset()+1 if self.can_loop_down()else 0)

	def _can_bank_right(self):
		return len(self.tracks_to_use())>self.width()

	def can_loop_right(self):
		return len(self.tracks_to_use())-self.width()>self._get_minimal_track_offset()

	def _bank_right(self):
		return self.set_offsets(self.track_offset()+1 if self.can_loop_right()else 0,self.scene_offset())
and in the setup_session call the new session component:

Code: Select all

      self._session = ovrdSessionComponent(numTRA, numSCE, enable_skinning = True)
I haven't test it but it should work
but maybe that's not what you're looking for?
2. PROGRAM CHANGE messages. Also looked everywhere for this. This controller (M Audio Oxygen 49) has a left and a right button which send program change messages. Could I use this for example to move between the tracks ?
for this I'm not sure but I think you have to get the PC message as a sysex in your script
I 'll maybe later try it if it's possible

Post Reply