Writing Custom Control Surfaces for Ableton
Nov 7, 2019Recently I’ve been really interested in what goes on in Ableton Live under the hood, since it’s a widely-used piece of proprietary software for making bangers. It turns out you can get pretty far knowing just a small amount of Python scripting!
For instance, I found that .als files (and .adv’s, and .adg’s, and probably more) are just gzipped XML and so therefore you can do some fun processing on them using any XML library, such as Python’s. So, remembering the week that it took me to convert all my Ableton DJ sets into Rekordbox, I wrote a script to automatically convert Ableton cues to Rekordbox and vice-versa. This could have definitely saved me about 10 hours of boring work time that could have been spent making bangers instead!
This week, I started looking into Ableton’s Python interface for control surfaces and you’ll never guess what happened next (a bunch of boring stuff).
What’s a control surface?
According to Ableton, “Control Surfaces are specially written scripts which allow controllers to interface with Live”.
Most people who use Ableton interact with it through a tactile controller, such as a MIDI keyboard or an Ableton Push or the APC40 controller. Control surfaces are scripts which act as a bridge between Ableton and the controller, telling Ableton what each button/knob/fader/etc. on the controller should do.
It turns out that these control surfaces are simply Python scripts.
Where does Ableton store its control surface scripts?
Ableton comes with a bunch of control surface scripts pre-installed for common controllers such as the Launchkey, Push, and APCs. For these controllers, you can just plug in the controller, open Ableton Preferences, and select the control surface for it via a drop-down list in the Link/MIDI tab.
These scripts are stored in /Applications/Ableton Live $VERSION.app/Contents/App Resources/MIDI Remote Scripts/
on Mac (where
$VERSION
is 10 Suite
for instance if you have Live 10 Suite installed) and
\ProgramData\Ableton\Live x.x\Resources\MIDI Remote Scripts\
on Windows.
For the rest of this post we will refer to this as the MIDI Remote Scripts directory.
Loading a custom control surface script
If you want to load your own custom control surface script for an existing MIDI controller, the steps are fairly straightforward:
- Find the existing control surface for the MIDI controller in the MIDI Remote
Scripts directory. For instance, for the APC40, this would be in
APC40/
as a bunch of .pyc files. You can either decompile the .pyc yourself or find the decompiled version in https://github.com/gluon/AbletonLive10.1_MIDIRemoteScripts. - Copy these decompiled Python files to a new directory in the MIDI Remote Scripts directory.
- Customize them (more on this in the next section).
- Now open Ableton and go to the Link/MIDI tab of Preferences. Ableton should automatically compile the .py files to .pyc. If everything loaded without errors, you will see your new MIDI Remote Scripts directory show up as an option in the Control Surface dropdown.
Customizing control surfaces
In the MIDI Remote Scripts directory, there’s various directories that start
with _
. These are libraries which other control surfaces can make use of. One
particularly useful one is _Framework
, which includes definitions for useful
classes like ButtonElement
, which represents a button on the controller.
The best intro I’ve found to _Framework
is this one by Hanz Petrov:
https://livecontrol.q3f.org/ableton-liveapi/articles/introduction-to-the-framework-classes/.
Let’s say we want to take the Metronome button on an APC40MKII and instead map
that to turn on looping for the currently active clip. Assume we’ve already
created a ControlSurface
subclass as a starting point.
- As a shortcut to calling
ButtonElement
directly, note that there is an_APC
library already which provides amake_button
utility method for APC controllers. Github link here. make_button
takes an identifier as input. To figure out what we need to pass here for the metronome button, we can look at the APC control protocol documentation which conveniently lists identifiers for all the buttons on the APC40MKII. We see that 0x5a, or 90, is the identifier for the metronome button.- Now we have to add a listener to the button using the
add_value_listener
method ofButtonElement
that triggers when the button is pressed. Let’s call this handlerstart_loop
. - In
start_loop
, we can call thesong()
method of the ControlSurface superclass in order to get a reference to the Live.Song.Song object that we are currently controlling. For documentation on this and other Live objects that are available in Python, see here. song().view.highlighted_clip_slot.clip
now gives us the active clip. This is a Live.Clip.Clip object according to the docs above. So we can simply setclip.looping
to a truthy value like 1 to make the clip loop!
In reality, I’ve found the best way to figure these things out is to simply copy/paste code from other control surface scripts as needed. In particular, Will Marshall’s unsupported Ableton 8 control surface scripts are really helpful. Also the unofficial Live Python API documentation is crucial.
Debugging tips
- You can log to Ableton’s main log file using the ControlSurface
log_message
method defined here. On Mac, the log file is at/Users/[username]/Library/Preferences/Ableton/Live x.x.x/Log.txt
and on Windows it’s\Users\[username]\AppData\Roaming\Ableton\Live x.x.x\Preferences\Log.txt
. This file also contains errors emitted by Python. - You can also reload the MIDI control surface without restarting Ableton: simply re-open the currently open session from
File > Open Recent Set
. - If you find that Ableton doesn’t auto-compile Python, you can compile using
python -m compileall .
- As mentioned previously, if a script throws an error immediately, it won’t show up in Ableton’s list of available control surfaces.
Example
As an example, see this Youtube video and Github repo for adding CDJ-style looping buttons to the APC40MK2. The work for this was done originally for Ableton 8 by Will Marshall and I only made some minor edits to make it more CDJ-like and loadable in Live 10.
Note that you can accomplish much of this already without control surface scripts simply by using Ableton’s MIDI mapping mode. One exception is the ability to halve and double loop lengths.
I hope this helps to show that control surface scripts are a quite powerful and flexible way to get your Ableton controller to behave exactly the way you want. Happy hacking!