Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
pythonista_als_to_midifile_converter [2019/12/10 08:09] – Updated code _ki | pythonista_als_to_midifile_converter [2020/04/22 18:40] (current) – MrBlaschke | ||
---|---|---|---|
Line 2: | Line 2: | ||
This Pythonista script installs a share extension to convert [[Ableton|Ableton Live Set export files]] into MIDI files containing the notes of the exported tracks. | This Pythonista script installs a share extension to convert [[Ableton|Ableton Live Set export files]] into MIDI files containing the notes of the exported tracks. | ||
+ | |||
+ | {{youtube> | ||
\\ | \\ | ||
How to install: | How to install: | ||
- | * Either | + | * First you need to install a newer version of the midiutil |
- | * Download the python script, long press the file in the files app, select share, choose 'Run Pythonista Script' | + | * Goto the [[https:// |
+ | * ' | ||
+ | * **Another approach** is to use the Readle Documents browser that allows to download the file to and then open that file to get a ' | ||
+ | * Open Pythonista and create a new file using the + button, choose 'Emtpy script' | ||
+ | * In the following dialog enter the name **midiutil_v1_2_1.py** exactly, select **site-package-3** as output folder and press ' | ||
+ | * Paste the clipboard, the content should be 1836 lines long. | ||
+ | |||
+ | * After installing the above file, either | ||
+ | * Download the python script | ||
* or | * or | ||
* Copy the script code block, open Pythonista, create a new file named ALS_to_MIDI.py and paste the clipboard | * Copy the script code block, open Pythonista, create a new file named ALS_to_MIDI.py and paste the clipboard | ||
Line 27: | Line 37: | ||
# Original script by MrBlaschke | # Original script by MrBlaschke | ||
# Usability enhancements by rs2000 | # Usability enhancements by rs2000 | ||
- | # Dec 8, 2019 | + | # Dec 11, 2019, V.04 |
+ | # | ||
+ | # greatly enhanced version that handles multiple scenes and clip offsets | ||
+ | # resulted in new parser engine | ||
+ | # request by @SpookyZoo | ||
# | # | ||
# Original request and idea by Svetlovska | # Original request and idea by Svetlovska | ||
Line 37: | Line 51: | ||
import xml.etree as XTree | import xml.etree as XTree | ||
from xml.etree.ElementTree import fromstring, ElementTree | from xml.etree.ElementTree import fromstring, ElementTree | ||
- | #thisis the old (default) library which does not support pitch-bend-data | ||
- | #from midiutil import MIDIFile | ||
import console | import console | ||
import io | import io | ||
Line 48: | Line 60: | ||
import binascii | import binascii | ||
from time import sleep | from time import sleep | ||
- | |||
#custom (newer) version - ahead of the Pythonista version | #custom (newer) version - ahead of the Pythonista version | ||
#get the code from: https:// | #get the code from: https:// | ||
Line 87: | Line 98: | ||
for elem in listOfiles: | for elem in listOfiles: | ||
if not elem.startswith(" | if not elem.startswith(" | ||
- | print(' | + | |
infile = ablezip.extract(elem) | infile = ablezip.extract(elem) | ||
elif inputFile.endswith(" | elif inputFile.endswith(" | ||
Line 111: | Line 122: | ||
tempo = 60 # In BPM | tempo = 60 # In BPM | ||
volume | volume | ||
+ | |||
+ | toffset | ||
+ | timeoff | ||
#Parse the data/file because parsing strings will not clean up bad characters in XML | #Parse the data/file because parsing strings will not clean up bad characters in XML | ||
Line 124: | Line 138: | ||
for child in master.iter(' | for child in master.iter(' | ||
tempo = int(float(child.get(' | tempo = int(float(child.get(' | ||
- | # | ||
#get amount of tracks to be allocated | #get amount of tracks to be allocated | ||
for tracks in root.iter(' | for tracks in root.iter(' | ||
- | numTracks = len(tracks.getchildren()) | + | numTracks = len(list(tracks.findall(' |
- | print(' | + | print(' |
#Preparing the target MIDI-file | #Preparing the target MIDI-file | ||
- | MyMIDI = MIDIFile(numTracks, | + | MyMIDI = MIDIFile(numTracks, |
MyMIDI.addTempo(track, | MyMIDI.addTempo(track, | ||
- | #Process every MIDI track found | + | #Give me aaaallll you've got |
- | for tracks | + | for miditrack |
- | for miditracks in tracks.iter('MidiTrack' | + | # |
- | | + | toffset = 0 |
+ | timeoff = 0 | ||
- | | + | |
- | for child in miditracks.iter(' | + | for uname in miditrack.findall('.//UserName' |
- | | + | |
- | #print(uName) | + | print(' |
- | MyMIDI.addTrackName(track, | + | MyMIDI.addTrackName(track, |
- | #getting the key(s) per miditrack | + | |
- | | + | # |
- | for child in keytracks.iter('MidiKey'): | + | |
- | | + | # |
- | print(' | + | toffset |
- | | + | |
- | | + | for loopinfo |
- | | + | |
- | | + | #store the next time offset |
- | dur = midiData.get(' | + | |
- | vel = midiData.get(' | + | |
- | #print(tim, dur, vel) | + | |
- | | + | |
- | # | + | |
- | MyMIDI.addNote(track, | + | |
- | mycount = mycount + 1 | + | |
- | print('processed', | + | |
- | #handling CC stuff | + | |
- | | + | |
- | for clipenvs in envs.iter(' | + | |
- | | + | |
- | for child in envtarget: | + | |
- | #this might be the CC-ID target based on 16200 | + | |
- | #it is possibly not that easy because i found values of 16111 which makes up for a CC of -88 | + | |
- | #damnit | + | |
- | #print(child.tag, child.attrib) | + | |
- | if int(child.get(' | + | for keytracks in noteinfo: |
- | | + | for key in keytracks.findall(' |
- | | + | keyt = int(key.attrib.get(' |
- | | + | print(' |
- | | + | |
- | targetCC = 74 | + | for notes in keytracks.findall(' |
- | | + | |
- | targetCC = -1 | + | |
+ | | ||
+ | | ||
- | | + | #getting automation data |
- | for events | + | |
- | for autoevent in events.iter('FloatEvent'): | + | for clipenv |
+ | #get the automation internal id | ||
+ | autoid = int(clipenv.find('.// | ||
+ | if autoid == 16200: # | ||
+ | | ||
+ | print(' | ||
+ | elif autoid == 16203: | ||
+ | targetCC = 1 | ||
+ | print('\tFound CC-data for: Modulation') | ||
+ | elif autoid == 16111: # | ||
+ | targetCC = 74 | ||
+ | print(' | ||
+ | else: | ||
+ | targetCC = -1 | ||
+ | print(' | ||
- | ccVal = int(autoevent.get('Value')) | + | #get the automation values for each envelope |
- | | + | for automs in clipenv.findall(' |
+ | for aevents in automs: | ||
+ | eventvals | ||
+ | ccTim = float(eventvals.get('Time')) | ||
+ | | ||
if ccTim < 0: | if ccTim < 0: | ||
- | ccTim = 0 | + | ccTim = 0 |
#writing pitchbend informations | #writing pitchbend informations | ||
if targetCC == 0: | if targetCC == 0: | ||
- | # | ||
MyMIDI.addPitchWheelEvent(track, | MyMIDI.addPitchWheelEvent(track, | ||
Line 201: | Line 220: | ||
if targetCC != -1 and targetCC != 0: | if targetCC != -1 and targetCC != 0: | ||
MyMIDI.addControllerEvent(track, | MyMIDI.addControllerEvent(track, | ||
- | + | | |
- | | + | |
with tempfile.NamedTemporaryFile(suffix=' | with tempfile.NamedTemporaryFile(suffix=' | ||
Line 218: | Line 236: | ||
</ | </ | ||
- | {{tag> | + | {{tag> |