-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
zblack
committed
Aug 6, 2012
0 parents
commit 447e0fe
Showing
2 changed files
with
162 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
* Polyphony the cheap (and a bit lengthy) way * | ||
|
||
This Python script converts a midi file containing a polyphonic track into a multitrack one (one voice per track). | ||
This can be useful if you need to render polyphony with an outboard mono synth. | ||
|
||
--------------------------------------------------------- | ||
Installation: | ||
|
||
- get python if not already present (try "python" at command line) | ||
http://www.python.org/download/ | ||
I used 2.6, chances are it may work with others, likely not 3.X though. | ||
|
||
- get this mighty library from pichenettes' git | ||
https://raw.github.com/pichenettes/avril-firmware_tools/master/midi/midifile.py | ||
(save as midifile.py) | ||
|
||
- get the converter script | ||
https://raw.github.com/zblack/p2m/master/p2m.py | ||
(save as p2m.py) | ||
|
||
put the two saved files in a proper folder where you plan to do conversions. | ||
|
||
--------------------------------------------------------- | ||
Usage: | ||
|
||
python p2m.py -i somefilename.mid | ||
|
||
it will create a new file called somefilename_multi.mid | ||
|
||
--------------------------------------------------------- | ||
Tipical workflow: | ||
|
||
- MIDI export the track you want to convert from your DAW software in the conversion folder | ||
- execute the script | ||
- reimport the new "multi" midi file into the DAW software, if asked to merge tracks say no because it will make this procedure perfectly useless. | ||
- create as many mono audio tracks as the midi tracks you just imported. | ||
- one track at a time render the midi using the outboard gear (solo and play one midi track while recording to a soloed audio track at a time). | ||
- optional: route the audio tracks generated to a submix fader using some panning to give spatiality. | ||
|
||
I made very little test, chances are not all cases covered, just let me know and I can eventually fix bugs. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
#!/usr/bin/python2.6 | ||
|
||
#Polyphonic to Multitrack midi file Converter | ||
#Splits all the voices of a midi file into several mono tracks and writes the result in the output midi file | ||
#It will produce as many tracks as the maximum polyphony used at any given time in the whole file | ||
#no limits on the voices, you can sprawl on the keyboard :) | ||
#usage: python p2m.py -i inputfile | ||
|
||
import os | ||
import sys | ||
import optparse | ||
from midifile import * | ||
|
||
class Voices(object): | ||
def __init__(self): | ||
#list of allocated voices/tracks, contains note numbers, 0 means a now free but previously played voice | ||
self.voices = list() | ||
|
||
def SlotOf(self, note): | ||
return self.voices.index(note) | ||
|
||
#looks for an empty slot, if not found allocates a new track | ||
#returns the slot number | ||
def NoteOn(self, d, note, w): | ||
try: | ||
i = self.SlotOf(0) | ||
except: | ||
self.voices.append(note) | ||
i = len(self.voices) - 1 | ||
if i > 0: | ||
w.AddTrack() | ||
|
||
self.voices[i] = note | ||
return i | ||
|
||
def NoteOff(self, note): | ||
try: | ||
i = self.SlotOf(note) | ||
self.voices[i] = 0 | ||
return i | ||
#it should never happen, but just in case it won't wreak havoc | ||
except: | ||
return -1 | ||
|
||
class Converter(object): | ||
def __init__(self): | ||
self.v = Voices() | ||
|
||
def Convert(self, ifname, ofname): | ||
r = Reader() | ||
w = Writer() | ||
|
||
#a first track always needed for a no note event occurring before a note event | ||
w.AddTrack() | ||
|
||
fin = file(ifname, 'rb') | ||
r.Read(fin) | ||
|
||
#Considering the limited scope of this script, tempo info tracks will be discarded | ||
for t in r.tracks: | ||
#identify a playable track | ||
playabletrack = False | ||
for _, e in t: | ||
if isinstance(e, NoteOnEvent): | ||
playabletrack = True | ||
break | ||
|
||
if playabletrack: | ||
for d, e in t: | ||
#voice specific events | ||
if isinstance(e, ChannelEvent): | ||
if isinstance(e, NoteOnEvent): | ||
i = self.v.NoteOn(d, e.note, w) | ||
#a bit unfair, but had not choice | ||
w._tracks[i].AddEvent(d, e) | ||
elif isinstance(e, NoteOffEvent): | ||
i = self.v.NoteOff(e.note) | ||
if i >= 0: | ||
w._tracks[i].AddEvent(d, e) | ||
elif isinstance(e, KeyAftertouchEvent): | ||
i = self.v.SlotOf(e.note) | ||
w._tracks[i].AddEvent(d, e) | ||
#any else channel event must be copied as is and propagated to the other tracks | ||
else: | ||
for i in 0..len(self.w._tracks) - 1: | ||
w._tracks[i].AddEvent(d, e) | ||
#everything else except track names will be copied to the first track | ||
else: | ||
if isinstance(e, TrackNameEvent): | ||
trackname = e.text | ||
else: | ||
w._tracks[0].AddEvent(d, e) | ||
|
||
for t in w._tracks: | ||
t.AddEvent(1, TrackNameEvent(trackname + '_' + str(w._tracks.index(t) + 1))) | ||
|
||
w._ppq = r.ppq | ||
fout = file(ofname, 'wb') | ||
w.Write(fout, format=1) | ||
fin.close() | ||
fout.close() | ||
|
||
return len(w._tracks) | ||
|
||
if __name__ == '__main__': | ||
parser = optparse.OptionParser() | ||
parser.add_option( | ||
'-i', | ||
'--input_file', | ||
dest='input_file', | ||
default='Untitled.mid', | ||
help='input file FILE', | ||
metavar='FILE') | ||
|
||
options, _ = parser.parse_args() | ||
|
||
c = Converter() | ||
ifname = options.input_file | ||
ofname = os.path.splitext(ifname)[0] + '_multi.mid' | ||
i = c.Convert(ifname, ofname) | ||
|
||
sys.stdout.write(str(i) + ' Mono Tracks Generated into ' + ofname) |