This is an old revision of the document!


Pythonista - Ableton Live Set (ALS) to MIDIfile converter script

# 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.1575843219.txt.gz
  • Last modified: 2019/12/09 09:13
  • by _ki