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:

  • Download the python script into Pythonista
  • 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() 
  • pythonista_als_to_midifile_converter.1575844235.txt.gz
  • Last modified: 2019/12/09 09:30
  • by _ki