Ableton Live and Akai’s Endless Encoders

Have you bought an Akai MPK225, or some­thing sim­i­lar, and plugged it into Able­ton Live only to be stumped because its end­less” rotary encoders are mapped to a device as absolute encoders by default? No? Well, sor­ry — you’ve picked a bad time to casu­al­ly browse through my web­site, because I’m about to get tech­ni­cal.

Device Advice

Because you’re still read­ing, I’ll assume you know all about the ben­e­fits of using rel­a­tive encoders with Live devices. Con­grats! You’ve earned a short­cut to the good stuff. If you did­n’t earn a short­cut, here’s a quick primer.

If a con­troller is avail­able and select­ed as a con­trol sur­face in Live’s pref­er­ences, its encoders will be auto­mat­i­cal­ly assigned to var­i­ous Live func­tions. Most con­trollers — the MPK, for instance — have a group of rotary encoders assigned to Live’s devices. When you select a device in Live, the con­troller is auto­mat­i­cal­ly mapped to it, so you can use the same set of encoders to con­trol dif­fer­ent device para­me­ters as they are select­ed. Tidy, right?

Most peo­ple pre­fer to use rel­a­tive encoders for device con­trol. A rel­a­tive encoder sends instruc­tions to increase or decrease a control’s val­ue rel­a­tive to the control’s cur­rent set­ting. The MPK’s rotary encoders, on the oth­er hand, are end­less” knobs which — if you select its Live Lite” pre­set — are pro­grammed as absolute encoders.

The prob­lem with an absolute encoder in this set­ting is that it sends val­ue reflect­ing the encoder’s cur­rent posi­tion. If an encoder is set to, say, 10, using it to change a con­trol set to 125 will cause a huge jump in val­ue. You can smooth these jumps by adjust­ing Live’s takeover mode, but the behav­ior can be incon­sis­tent, espe­cial­ly if you’re only mak­ing small adjustments.

End­less encoders are typ­i­cal­ly used as rel­a­tive encoders, so it’s easy to assume the MPK’s will be set up this way in Live. But that’s not the case, as you already know, and so here we are.

It’s all Relative

The first step to make things right in the uni­verse — at least as far as Akai and Able­ton are con­cerned — is to set up a new pre­set for Live in the MPK. You can over­write the cur­rent Live pre­set, or make a copy:

  1. Select the Pre­set” but­ton and open the Live Lite preset.
  2. Page over to the Store Pro­gram” menu.
  3. Select an open slot, and give it a title.
  4. Push the data knob to save your new preset.

By default, the MPK’s Live Lite” pre­set pro­grams its knobs as absolute encoders, but this is easy to change:

  1. Select the Edit” but­ton on the MPK.
  2. Move K1 (the first knob) to select it.
  3. The Type” will be set to MIDI CC. Use the data knob to select INC/DEC2. This is one of two rel­a­tive modes, and the only one Live will recognize.
  4. Leave all oth­er set­tings the same.
  5. Repeat for K2 – K8.

When you’re fin­ished, select the Pre­set” but­ton and page over to the Store Pro­gram” menu to save your changes.

Now that we have rel­a­tive encoders, it’s time to cel­e­brate with com­mon sense device con­trol, right? Not so fast…it turns out Live doesn’t care what you just did, and will still inter­pret these encoders as absolute by default. The rea­son it does this is because the MPK asked it to, and it asked this by using a MIDI Remote Script.

A MIDI Remote Wha?

Look­ing through Live’s pref­er­ences, you’ll see a pret­ty large list of con­trol sur­faces Live sup­ports by default:

Live's control surface selection menu

Select­ing one of those con­trollers sets up the map­ping between Live and the con­troller; this map­ping is defined by the controller’s MIDI Remote Script. The MIDI Remote Script is a small Python file in the Live instal­la­tion which, among oth­er things, assigns MIDI mes­sages from the con­troller to var­i­ous Live functions.

Python scripts are com­piled” — that is, they are trans­formed from a human-read­able pro­gram­ming lan­guage to a com­put­er-read­able byte­code lan­guage — and there­fore, so are Live’s MIDI Remote Scripts. Lucky for us, Live will com­pile these scripts every time it’s launched, which means we can mod­i­fy an uncom­piled source” script, and Live will con­vert it to some­thing it can use.

Able­ton doesn’t pub­licly doc­u­ment MIDI Remote Scripts, so any attempts to mod­i­fy them — or know­ing they exist — would be close to impos­si­ble for me if it wasn’t for some great ref­er­ences on the web:

To find the MIDI Remote Script for the MPK225, right-click on the Live appli­ca­tion icon and select Show Pack­age Con­tents.” From there, nav­i­gate to Con­tents > App-Resources > MIDI Remote Scripts > MPK225:

Mac Finder window open to Live's MIDI remote scripts

The two .pyc images inside are the com­pressed Python scripts used by the MPK. Make a back­up of the MPK225 fold­er and put it some­where safe in case all goes to hell. (It might.) Now, delete those two images — we’ll replace them with new ones.

Next, let’s find our new source scripts. Peo­ple bet­ter than us have uncom­piled all of Live’s MIDI Remote Scripts and uploaded them to a repos­i­to­ry. You’ll find the MPK225 images here. Down­load the two scripts and move them into the MPK225 folder.

A Quick Python Disclaimer

Before con­tin­u­ing, please take a moment to read the fol­low­ing disclaimer:

I HAVE NO IDEA WHATAM DOING IN PYTHON. EVERY­THINGSAY ISBAD IDEA.

Seri­ous­ly, the fix you are about to read is the equiv­a­lent of fix­ing a leaky kitchen faucet by bury­ing your kitchen in con­crete. On the oth­er hand — it works, and at the moment no pro­gram­mers at Akai or Able­ton are pop­ping into sup­port forums to pro­vide some ele­gant code for us, so…let’s fol­low our heart and not our minds for a moment.

Ok Then

Open MPK225.py in a text edi­tor (TextE­d­it is fine) and take a look at lines 22 – 29:

self.add_matrix('Encoders', make_encoder, 0, [[22,
   23,
   24,
   25,
   26,
   27,
   28,
   29]], MIDI_CC_TYPE)

This code intro­duces Live to the MPK’s encoders. The num­bers 22 – 29 are the MIDI CC num­bers of the eight knobs, and make_encoder is a call to the func­tion that does the heavy lift­ing. It turns out that make_encoder is defined in the _Framework/MidiMap.py file…

def make_encoder(name, channel, number, midi_message_type): return EncoderElement(midi_message_type, channel, number, Live.MidiMap.MapMode.absolute, name=name)

…and there you’ll see the source of all this has­sle, the Live.MidiMap.MapMode.absolute line which says to define each encoder as absolute.

We’ll fix that by going back to MPK225.py and adding a mod­i­fied function…

def make_encoder(name, channel, number, midi_message_type): return EncoderElement(midi_message_type, channel, number, Live.MidiMap.MapMode.relative_two_compliment, name=name)

…where Live.MidiMap.MapMode.relative_two_compliment defines each encoder as rel­a­tive. Add that code between lines 10 (the end of the from” calls) and 12 (the begin­ning of the class MidiMap definition).

Launch­ing Live at this point would result in a Python error, because our mod­i­fied make_encoder needs a func­tion, EncoderEle­ment,” which isn’t defined any­where in our script. That func­tion is locat­ed in the _Framework/EncoderElement.py file, and we bring it into our script by adding from _Framework.EncoderElement import EncoderElement just before line 8 (the calls to _Framework.MidiMap”).

Final­ly, this EncoderEle­ment func­tion would throw an error as well, as it needs code writ­ten in the basic Live frame­work. Adding import Live just after line 2 (the first real” line of code) fix­es this last issue.

Our final script looks like this:

#edited for relative encoder functionality
from __future__ import with_statement
import Live
from _Framework.ControlSurface import ControlSurface
from _Framework.Layer import Layer
from _Framework.DrumRackComponent import DrumRackComponent
from _Framework.TransportComponent import TransportComponent
from _Framework.DeviceComponent import DeviceComponent
from _Framework.EncoderElement import EncoderElement
from _Framework.MidiMap import MidiMap as MidiMapBase
from _Framework.MidiMap import make_button, make_encoder
from _Framework.InputControlElement import MIDI_NOTE_TYPE, MIDI_CC_TYPE
def make_encoder(name, channel, number, midi_message_type):
   return EncoderElement(midi_message_type, channel, number, Live.MidiMap.MapMode.relative_two_compliment, name=name)
class MidiMap(MidiMapBase):
   def __init__(self, *a, **k):
       super(MidiMap, self).__init__(*a, **k)
       self.add_button('Play', 0, 118, MIDI_CC_TYPE)
       self.add_button('Record', 0, 119, MIDI_CC_TYPE)
       self.add_button('Stop', 0, 117, MIDI_CC_TYPE)
       self.add_button('Loop', 0, 114, MIDI_CC_TYPE)
       self.add_button('Forward', 0, 116, MIDI_CC_TYPE)
       self.add_button('Backward', 0, 115, MIDI_CC_TYPE)
       self.add_matrix('Encoders', make_encoder, 0, [[22,
         23,
         24,
         25,
         26,
         27,
         28,
         29]], MIDI_CC_TYPE)
       self.add_matrix('Drum_Pads', make_button, 1, [[67,
         69,
         71,
         72], [60,
         62,
         64,
         65]], MIDI_NOTE_TYPE)

class MPK225(ControlSurface):
   def __init__(self, *a, **k):
       super(MPK225, self).__init__(*a, **k)
       with self.component_guard():
           midimap = MidiMap()
           drum_rack = DrumRackComponent(name='Drum_Rack', is_enabled=False, layer=Layer(pads=midimap['Drum_Pads']))
           drum_rack.set_enabled(True)
           transport = TransportComponent(name='Transport', is_enabled=False, layer=Layer(play_button=midimap['Play'], record_button=midimap['Record'], stop_button=midimap['Stop'], seek_forward_button=midimap['Forward'], seek_backward_button=midimap['Backward'], loop_button=midimap['Loop']))
           transport.set_enabled(True)
           device = DeviceComponent(name='Device', is_enabled=False, layer=Layer(parameter_controls=midimap['Encoders']))
           device.set_enabled(True)
           self.set_device_component(device)
           self._device_selection_follows_track_selection = True
           

Down­load: MPK225.py

Save this file and relaunch Live. The .py images will be com­piled into new .pyc images, and our new MIDI Remote Script will be ready to load. Select the MPK225 con­trol sur­face in Live’s pref­er­ences, load a few devices in your Live ses­sion, and, assum­ing all has gone well, bask in the joy of using the rel­a­tive encoders you thought you had already.

One Last Thing

Be sure to save a copy of your mod­i­fied .py file; because MIDI Remote Scripts are part of Live’s appli­ca­tion images, your mod­i­fi­ca­tions will be over­writ­ten when Live is updated.

I hope this info is use­ful for you. If you get in a bind — or bet­ter yet, have a bet­ter solu­tion! — feel free to get in touch.