Ableton Python API? Kinda, but not really.

Share your favorite Ableton Live tips, tricks, and techniques.
Post Reply
halcyon days
Posts: 23
Joined: Tue Jan 30, 2007 11:51 pm

Ableton Python API? Kinda, but not really.

Post by halcyon days » Wed Feb 14, 2007 8:08 am

Hey everyone. I've completed a simple toolkit that allows you to poke around in Ableton's Python virtual machine (PVM). It's not very useful, as you can't actually touch the PVM while Ableton is processing music. But its a start. None of this work was performed with disassemblers or poking memory. This is a pure Python solution just to see how far I could get.

Let me start out by saying that you should by no means run any of the code posted here. I (Nathan Ramella) and remix.net accept no liability for the results of your experimentation on your own computer. If you do something that causes any damage or loss to your or someone else's computer, you accept full responsibility. You should not use these scripts under any circumstances. Python is a full programming language and has all sorts of commands that could render your computer useless if you make a mistake. If you understand and accept that risk read on, if not go find a different webpage to read.

I would also like to say that this effort has nothing to do with 'cracking' Ableton in any form, this is purely an attempt to gain access to the Python framework that Ableton utilizes so that native solutions could be found for problems that packages like Gustavo Bravetti's excellent pushandpull
solve. I'm a registered user and hope if you use Ableton you are as well.

I make no claims to the validity of any of this information, I am not a Python or computer expert. If you see any errors please let me know and I will be happy to fix them -- feedback is always appreciated.

- nar(ATSIGN)remix.net

Ableton Live is written in Python and C++. C++ we're all pretty familiar with at least by name but Python might be new to some people. I don't know how much Python is used, or where its used. So my initial goal was to get the lay of the land.

Python is a GPL licensed object oriented scripting language, if I had to draw parallels I'd say its somewhere between Java and Perl, easier to program and comes with a robust standard library including threading and communication libraries.

You can get compiled Python installers for almost every operating system, OSX ships with it. I used the <a href="http://www.cygwin.net">Cygwin</a> port of Python on Windows. The programs I wrote for this effort should be portable to both Windows and OSX. My work was performed on Windows XP sp2 using version 6.0.1 of Ableton. I wrote and ran each one of these scripts on my own machine to verify that they worked as expected before trying to take them into Ableton.

I know Ableton has some Python in it, but not what amount. There's also the question of how to gain access to it. I'm not even sure how useful it will be, given that an application who's users demand single digit milisecond latency may not respond well to outside tampering. But, you never know until you try. It would appear that all of the Python content is compiled into the Ableton binary with none available on disk. Well.. Almost none.

C:\Program Files\Ableton\Resources\MIDI Remote Devices, or MRD directory as I'll refer to it later in this document has directories with compiled Python bytecode. These files are recognizable by the extension <b>.pyc</b>. Similar to Java, Python can generate compiled bytecode that allows for faster execution by not recompiling every time you run it, it'll just pick up the bytecode and run it in the Python Virtual Machine or PVM.

The MRD/_Generic directory appears to have the skeleton of Python modules that all remote MIDI devices may have, while the named directories such as MRD/Axiom and MRD/ BCF2000 appeared to have a subset of these .pyc files. I guess that these stock .pyc files only cover the differences from _Generic and that each instrument is considered generic until its loaded and its unique characteristics override _Generic behavior. I don't know enough about this mechanism to say for sure.

This might give us an injection point for our code. Python will examine source code during import that isn't byte compiled, it'll compile it for you on the fly and put the .pyc file in the same directory. Given the dynamic nature of this process if we remove a .pyc file and substitute a .py file with the same basename it might compile and run it.

I started my work in the MRD/Axiom directory. I backed it up so I wouldn't lose anything and then removed Axiom.pyc and put my replacement Axiom.py in the directory.
This script will create a file named "DEADBEEF.txt" in Ableton's current working directory at the time of code execution and write the line 'remix.net 1 2 3' followed by a newline.

Code: Select all


# CODE START
# Replacement Axiom.py - File write test

import sys

myFile = open('DEADBEEF.txt', 'a')
myFile.write('remix.net test 1 2 3\n')
myFile.close

# CODE END

When I launched Ableton, it went through the initialization and after a few seconds I saw that 'C:\Documents and Settings\nar\DEADBEEF.txt' had been created. Success! In the Axiom directory, a new Axiom.pyc file had been created which contained my byte compiled code. Ableton will allow user provided source code to be executed during startup.

Ableton was even usable after startup, although when I went to the MIDI Mapping menu and selected 'Axiom', it crashed with a C++ error. My substitute Axiom.py isn't trying very hard to look like the objects that previously made up the Axiom MIDI mapping, so this isn't suprising. It is interesting though, as it means that code that is loaded through the MIDI mapping initialization may be accessable once Ableton is up and running. It also could just mean that there was an error when Ableton tried to access the MIDI mapping.

We've managed to get our code executed and into the embedded PVM but there are still many unknowns. The PVM could just be an initialization trick to allow for dynamic custom MIDI mapping support and might be destroyed when Ableton hits running mode. To get more information on Python's behavior in Ableton I modified the first script to be a blocking test. It will never die it. It will just do a loop forever, writing to our file and sleeping a second between every loop.

Code: Select all


# CODE START

import time

while 1: 
    myFile = open('DEADBEEF.txt', 'w')
    myFile.write('remix.net test 1 2 3\n')
    myFile.close
    time.sleep(1)

# CODE END

This time when I started Ableton, it got to the splash screen and when 'Initializing MIDI Devices' got painted the application halted. When I examined the DEADBEEF.txt file, every second it was growing by one line. We now know exactly when our code is being executed and that for our code to exist within Ableton it will need to be threaded. I had to kill Ableton through the task manager as my script would never complete.

Threads in the context of programming are a way for multiple tasks to be undertaken by a program simultaniously. You can't really run multiple threads simultaniously but a program will switch execution context between threads so quickly it appears simultanious to the user. The best we can hope for in this case is to have a thread that steals a little CPU time when necessary to interact with the user and deliver those messages to the Ableton engine. I'd chalk this up to one of the main reasons why Ableton haven't allowed users this API access, intellectual property concerns aside if people started creating add-ons that talk directly to the API it would be a tech support nightmare.

Imagine being a mechanic and having to diagnose car trouble over the phone, if the car was stock from your factory you'd be able to get some pretty good guesses of whats going wrong by someone just describing them to you. However if they had replaced the engine with a bunch of spare parts they scavenged from a junkyard, all of your previous experience diagnosing problems would be almost useless as all variables would be changed. But, I digress.

I wrote one more script to give me an idea of what was going on inside the PVM before trying out the background thread idea. Once our code is loaded into the PVM we might be able to get access to other modules in memory as well as examine PVM environment.

This code will give us the version of Python thats running and dump out the list of builtin modules and dynamically loaded modules. Builtin modules contain the basic things that Python can do but sys.modules contains everything thats been loaded into the runtime engine. There may be more interesting things to get out of the PVM but I'm new to Python.

Code: Select all


# Python Environment Dumper

import sys

myFile = open('ableton_dump.txt', 'a')

myFile.write('Python version:' + sys.version + '\n')

myFile.write('sys.modules.builtin:\n**************'**\n')

for builtinModule in sys.modules.builtin:
    myFile.write('\t' + sys.modules.builtin[builtinModule] + '\n')

myFile.write('sys.modules:\n**************'**\n')

for loadedModule in sys.modules:
     myFile.write('\t' + sys.modules[loadedModule] + '\n')

myFile.close()

The results are interesting, when I probed deeper into the objects like 'sys.modules['Live'].Clip.Clip' they had objects and properties, but most appeared to be Boost binding references which go to compiled C++ code. Not so useful. But this hints to me that Python is an integral part of the framework and may be accessible when the application is playing music.

Other useful information from this is that Ableton's PVM doesn't have the newer 'threading' module. It only has the older 'thread' module. That means we have to use older Python thread programming techniques and don't get the benefits of the high level interface.

My next script is a little more complex and has two components, a client and server pair. The server will be created in a background thread which in a pure Python environment would run along side the main application. It will accept client requests and return results, requests will be Python code snippets. This is possible because of try/except in Python, if we try to execute some code and it fails we can catch the exception and ignore it.

This makes it simple to experiment as we won't have to restart Ableton every time we want to try a new snippet of code and we won't crash it if we try something that would cause a runtime error. By not including the blocking module Ableton will spawn our thread and then initialize to full running state. If we're lucky we'll be able to connect and issue commands. We start out without using the blocking module to see if it works at all.

For all intents and purposes this client/server pair is a backdoor similar to a rootkit and should be treated as such. I hard-coded the the scripts to only work with 'localhost' but that doesn't mitigate the danger of running code of this nature. But, I'm a <i>fearless risk taker</i>.

Long story short is, it works BUT not while Ableton is capable of playing music. You're stuck in initialization mode. If you run it background threaded the thread never gets execution focus until Ableton quits then only for one or two instructions and it dies too.

Someone on the #python channel suggested the thread freezing behavior may be caused by Python's GIL. The GIL is the Global Interface Lock for threads. As it turns out, Python isn't thread safe. So the GIL is used to manage thread context. If the GIL doesn't give your thread execution time, you can't take it. As well, if C/C++ has control of the GIL then you're at its mercy for getting thread focus. Normally the GIL will release and reaquire the lock every 100 bytecode instructions. You can modify this value by using sys.setcheckinterval(), but this didn't have any effect on the remix-server.py. This is a difficult roadblock. Once we start venturing into compiled C++ things get more difficult and less elegant than a pure Python solution would be.

I tried playing round with overwriting some of the objects in memory with little luck. At this point I think I'm at the limit that I could go without cracking out ollydbg and that would go against the spirit of this effort.

Here's the ta+gz file of the code I used for the client/server. Instructions are provided inside. If you find anything interesting I'd be happy to hear about it. And as always, feedback is appreciated!

http://www.remix.net/RemixShell.tar.gz
-nar

hoffman2k
Posts: 14718
Joined: Tue Jun 15, 2004 6:40 pm
Location: Belgium
Contact:

Post by hoffman2k » Wed Feb 14, 2007 8:34 am

AFAIK, python first appeared with the first MIDI remote scripts in Live 5.
And is only used for the MIDI remote scripts.

Live does scan these scripts upon load. But thats hardly magic, since that is a feature (though unofficial).
http://www.ableton.com/forum/viewtopic.php?t=47157

halcyon days
Posts: 23
Joined: Tue Jan 30, 2007 11:51 pm

Post by halcyon days » Wed Feb 14, 2007 8:40 am

hoffman2k wrote:AFAIK, python first appeared with the first MIDI remote scripts in Live 5.
And is only used for the MIDI remote scripts.

Live does scan these scripts upon load. But thats hardly magic, since that is a feature (though unofficial).
http://www.ableton.com/forum/viewtopic.php?t=47157
Python objects exist for Clip, ClipSlot, Device, DeviceParameter, MidiMap, MixerDevice, Scene, Song, and Track. Not just MIDI remote scripts.

And I never said it was magic. I posted my findings and my code so that I can save people who might be thinking along the same lines as me some time.

If I'm going to not catch a fish, I'm going to not catch a big fish.

hoffman2k
Posts: 14718
Joined: Tue Jun 15, 2004 6:40 pm
Location: Belgium
Contact:

Post by hoffman2k » Wed Feb 14, 2007 8:58 am

halcyon days wrote:
hoffman2k wrote:AFAIK, python first appeared with the first MIDI remote scripts in Live 5.
And is only used for the MIDI remote scripts.

Live does scan these scripts upon load. But thats hardly magic, since that is a feature (though unofficial).
http://www.ableton.com/forum/viewtopic.php?t=47157
Python objects exist for Clip, ClipSlot, Device, DeviceParameter, MidiMap, MixerDevice, Scene, Song, and Track. Not just MIDI remote scripts.

And I never said it was magic. I posted my findings and my code so that I can save people who might be thinking along the same lines as me some time.

If I'm going to not catch a fish, I'm going to not catch a big fish.
Cool, I missed that bit. Nice find :D

There are many undocumented things under live's hood.
During the Live 6 beta, we got a glimpse of the event recorder.

I can understand that there isn't an API for adding 3rd party devices.
For the reasons you described and one other main reason.

The way Ableton is going on with their own devices, shows me that Live can become modular enough to allow the creation of your own devices as racks.
The dudes at Ableton are max heads, I hope they share the same desire like me to make Live as modular as possible without compromising stability.

I'm not sure what your goal is, but it's always nice to know that some people like to "go deep" inside of Live. You never know what you stumble across.
You should have seen the madness when somebody found "operator.jpg" in Live 4. Before Live 4.1 was announced.

creepjoint
Posts: 112
Joined: Fri Jul 09, 2004 8:01 am

Post by creepjoint » Wed Feb 14, 2007 9:17 am

when I first came across those python files in Live I imagined someone hacking an internal midi routing workaround so we could use midi plugins without having to resort to third party midi loop back drivers, or maybe allowing us to use the rack macro interface to send midi to external hardware.

halcyon days
Posts: 23
Joined: Tue Jan 30, 2007 11:51 pm

Post by halcyon days » Wed Feb 14, 2007 9:33 am

creepjoint wrote:when I first came across those python files in Live I imagined someone hacking an internal midi routing workaround so we could use midi plugins without having to resort to third party midi loop back drivers, or maybe allowing us to use the rack macro interface to send midi to external hardware.
Thats something I haven't explored much yet. My first effort was really pointed at getting a background thread spawned so I could communicate with Live 'in action'.

When I looked at the MIDI devices I had considered this as another entry vector for code and is likely to be the next place I poke around.

When I tried to overwrite live_io.stdout/stderr I didn't have much success, so I still can't really say if the MIDI Remote Scripts are in a sandbox or not. I'm sure someone from Ableton could explain it all. :)

My original goal was just to get OSC control integrated in which is why RemixShell uses UDP OSC as its transport. I figured if it worked it'd just be a couple extra lines to add OSC addresses for things like '/mixer/mainmixer/volume' and bind them to the internal objects. I don't know Python so I substitute wild guesses where experience fails me.

nicogrubert
Posts: 155
Joined: Sat Jan 20, 2007 7:02 pm
Location: Zurich, Switzerland
Contact:

Post by nicogrubert » Wed Feb 14, 2007 1:26 pm

I am pretty new to Ableton Live but I develop applications in Python for more than 4 years. :)

Exciting, Ableton Live uses Python...

Post Reply