This is an old revision of the document!
Pythonista: Ableton Live Set (ALS) to MIDI converter script
This Pythonista script installs a share extension to convert Ableton Live Set export files into MIDI files containing the notes of the exported tracks.
How to install:
- Either
- Download the python script, long press the file in the files app, select share, choose 'Run Pythonista Script' and then 'Import File'
- or
- Copy the script code block, open Pythonista, create a new file named ALS_to_MIDI.py and paste the clipboard
- In Pythonista settings/App Extensions select 'Share Extension Shortcuts'
- Use the + sign to add a extension
- Select the downloaded python script
- Set the custom title: ALS to MIDI
- Select 'Primaries_Expand' as icon
- Select a pleasing icon color
How to use:
- In the files app, long press the exported ALS file you want to convert
- In the options popup, select share
- In the share popup, select 'Run Pythonista Script' and then select the 'ALS to MIDI' extension
- ALS_to_MIDI.py
# Ableton MIDI clip zip export to MIDI file converter # Original script by MrBlaschke # Usability enhancements by rs2000 # Dec 8, 2019 # # Original request and idea by Svetlovska import sys import os import tempfile import xml.etree.ElementTree as ET import xml.etree as XTree from midiutil import MIDIFile import console import io import appex import ui from zipfile import ZipFile from zipfile import BadZipfile from time import sleep def main(): if not appex.is_running_extension(): print('This script is intended to be run from the sharing extension.') return # Catch zip file from external "Open in..." dialog inputFile = appex.get_file_path() outfile = os.path.splitext(os.path.basename(inputFile))[0] + ".mid" #check if we have an ALS which is not renamed #so basically a zip-archive with ALS extension - is that of relevance? haveZIP = False try: with ZipFile(inputFile) as zf: print("Info: we have a real ZIP archive") haveZIP = True except BadZipfile: print("Info: It is a pure ALS file") if inputFile.endswith(".zip") or haveZIP == True: print("Importing ZIP archive...") with ZipFile(inputFile, 'r') as ablezip: # Iterate over the list of file names in given archive # filter out possible hidden files in "__MACOSX" directories for manually created ZIPs, etc listOfiles = ablezip.namelist() for elem in listOfiles: if not elem.startswith("__") and elem.endswith(".als"): print('Found:', elem, end=' ') infile = ablezip.extract(elem) elif inputFile.endswith(".als"): print("Input is direct Ableton ALS file") infile = inputFile else: print("filetype not supported...") sys.exit() track = 0 channel = 0 time = 0 # In beats duration = 1 # In beats tempo = 60 # In BPM volume = 100 # 0-127, as per the MIDI standard # Rather parse the file because parsing strings will not clean up bad characters in XML tree = ET.parse(str(infile)) root = tree.getroot() #getting the tempo/bpm (rounded) from the Ableton file for master in root.iter('Tempo'): for child in master.iter('FloatEvent'): tempo = int(float(child.get('Value'))) #print('tempo: ', tempo) #get amount of tracks to be allocated for tracks in root.iter('Tracks'): numTracks = len(tracks.getchildren()) print('Found',str(numTracks),'tracks') #Opening the target MIDI-file MyMIDI = MIDIFile(numTracks, adjust_origin=True) # One track, defaults to format 1 (tempo track is created automatically) MyMIDI.addTempo(track, time, tempo) # Process every MIDI track found for tracks in root.iter('Tracks'): for miditracks in tracks.iter('MidiTrack'): print('\nMIDITRACK ', track) #getting the track-name for child in miditracks.iter('UserName'): uName = child.get('Value') #print(uName) MyMIDI.addTrackName(track, 0, uName) #getting the key(s) per miditrack for keytracks in miditracks.iter('KeyTrack'): for child in keytracks.iter('MidiKey'): keyt = int(child.get('Value')) print('key:', str(keyt) + ',', end=' ') #getting the notes mycount = 0 for midiData in keytracks.iter('MidiNoteEvent'): tim = midiData.get('Time') dur = midiData.get('Duration') vel = midiData.get('Velocity') #print(tim, dur, vel) #writing the actual note information to file #MIDIFile.addNote(track, channel, pitch, time, duration, volume, annotation=None MyMIDI.addNote(track, channel, keyt, float(tim), float(dur), int(vel)) mycount = mycount + 1 print('processed',int(mycount),'note events') track = track + 1 with tempfile.NamedTemporaryFile(suffix='.mid') as fp: MyMIDI.writeFile(fp) fp.seek(0) fp.read() # Open the MIDI file in your app of choice :) console.open_in(str(fp.name)) #closing and deleting the temporary file fp.close() print ('done.') if __name__ == '__main__': main()