PK!!superconf18_midibadge/__init__.pyPK!][ [ "superconf18_midibadge/midistuff.pyimport copy import midi def read_midifile(fname): return midi.read_midifile(str(fname)) def get_track_info_from_file(fname): mf = midi.read_midifile(fname) tracks = {} for idx, el in enumerate(mf): if not hasattr(el[0], 'text'): continue tracks[idx] = { "name": el[0].text, "tones": sum([1 for event in el[1:] if type(event) == midi.events.NoteOnEvent], 1) } return tracks def single_track_midi(mf, dest, track): if isinstance(mf, str): mf = midi.read_midifile(mf) pat = midi.Pattern() pat.append(mf[track]) midi.write_midifile(dest, pat) def three_track_midi(source, dest, tr1, tr2, tr3): if isinstance(mf, str): mf = midi.read_midifile(mf) pat = midi.Pattern() pat.append(mf[tr1]) pat.append(mf[tr2]) pat.append(mf[tr3]) midi.write_midifile(dest, pat) def get_three_tracks_from_file(mf, t1, t2, t3): if isinstance(mf, str): mf = midi.read_midifile(mf) return [mf[t1], mf[t2], mf[t3]] def get_timeline_from_track(track, tick_multiplier=2): timestamp = 0 notes = [] for ev in track: timestamp += ev.tick * tick_multiplier if type(ev) == midi.events.NoteOnEvent: if ev.data[1] == 0: # if the volume is set to zero we shouldn't play the tone, # setting the tone to 0 apparently achieves that notes.append((timestamp, 0)) else: notes.append((timestamp, ev.data[0])) # insert noop first entry if the first entry isn't at t=0 if notes[0][0] != 0: notes = [(0, 0), *notes] return notes def get_notes_from_timelines(timelines: list): timelines = copy.copy(timelines) last_timestamp = 0 notes = [] while sum(len(t) for t in timelines) > 0: next_timestamp = min(t[0][0] for t in timelines if len(t) > 0) if len(notes) == 0: next_note = [0, 0, 0, 0] else: # sub in duration for last note notes[-1][0] = next_timestamp - last_timestamp next_note = [0, *notes[-1][1:]] for i in range(len(timelines)): if len(timelines[i]) > 0 and timelines[i][0][0] == next_timestamp: next_track_note = timelines[i].pop(0) next_note[i+1] = next_track_note[1] notes.append(next_note) last_timestamp = next_timestamp return notes # MIDI notes are numbered from 0 to 127 assigned to C-1 to G9. This # corresponds to a range of 8.175798916Hz to 12543.85395Hz (assuming equal # temperament and 440Hz A4) and extends beyond the 88 note piano range from # A0 to C8. # return [ # f"tune {ev.data[0]},{ev.data[0]},{ev.data[0]},1000" # for ev in track # if type(ev) == midi.events.NoteOnEvent # ] PK!'superconf18_midibadge/tools/__init__.pyPK!VV)superconf18_midibadge/tools/midi2basic.py#!/usr/bin/env python3 import argparse from pathlib import Path from ..midistuff import get_track_info_from_file from ..midistuff import read_midifile from ..midistuff import get_three_tracks_from_file from ..midistuff import get_timeline_from_track from ..midistuff import get_notes_from_timelines from ..uistuff import print_trackinfo from ..uistuff import input_tracklist def main(): parser = argparse.ArgumentParser( description="Creates BASIC file form three tracks of a MIDI file.") parser.add_argument("inpath", help="Input MIDI file") parser.add_argument("outpath", help="Output BASIC file") args = parser.parse_args() infile = Path(args.inpath) outfile = Path(args.outpath) trackinfo = get_track_info_from_file(str(infile)) print(f"These tracks in MIDI file {infile} contain sound:") print("") print_trackinfo(trackinfo) print("") print("") print("Select *exactly* three tracks to export into BASIC file!") tracklist = input_tracklist(validate_length=3) mf = read_midifile(str(infile)) tracks_from_file = get_three_tracks_from_file(str(infile), *tracklist) t1 = get_timeline_from_track(tracks_from_file[0]) t2 = get_timeline_from_track(tracks_from_file[1]) t3 = get_timeline_from_track(tracks_from_file[2]) notes = get_notes_from_timelines([t1, t2, t3]) with open(outfile, 'w') as out: for line, note in enumerate(notes): out.write(f"{(line+1)} tune {note[1]},{note[2]},{note[3]},{note[0]}\n") print(f"Wrote {len(notes)} notes to file {outfile}") if __name__ == "__main__": main() PK!˒cc'superconf18_midibadge/tools/midiinfo.py#!/usr/bin/env python3 import argparse from pathlib import Path from ..midistuff import get_track_info_from_file from ..uistuff import print_trackinfo def main(): parser = argparse.ArgumentParser( description="Prints a table of tracks in the given MIDI file.") parser.add_argument("inpath", help="Input MIDI file") args = parser.parse_args() infile = Path(args.inpath) trackinfo = get_track_info_from_file(str(infile)) print(f"These tracks in MIDI file {infile} contain sound:") print("") print_trackinfo(trackinfo) if __name__ == "__main__": main() PK!G(superconf18_midibadge/tools/midisplit.py#!/usr/bin/env python3 import argparse import os from pathlib import Path from ..midistuff import get_track_info_from_file from ..midistuff import read_midifile from ..midistuff import single_track_midi from ..uistuff import print_trackinfo from ..uistuff import input_tracklist def main(): parser = argparse.ArgumentParser( description="Split a single MIDI file into multiple MIDI files, one for each track.") parser.add_argument( "inpath", help="Input MIDI file") parser.add_argument( "--out", dest="outpath", default=".", required=False, help="Output folder for single-track MIDI files (defaults to current directory)") args = parser.parse_args() infile = Path(args.inpath) inname = Path(args.inpath).stem # stem = file name without extension outfolder = Path(args.outpath) trackinfo = get_track_info_from_file(str(infile)) print(f"These tracks in MIDI file {infile} contain sound:") print("") print_trackinfo(trackinfo) print("") print("") tracklist = input_tracklist(default=list(trackinfo.keys())) os.makedirs(str(outfolder), exist_ok=True) mf = read_midifile(str(infile)) for track in tracklist: destfile = f"{outfolder}/{inname}-{track}-{trackinfo[track]['name'][:10]}.mid" single_track_midi(mf, destfile, track) print(f"Wrote file {destfile}") if __name__ == "__main__": main() PK!J,<:: superconf18_midibadge/uistuff.pydef print_trackinfo(trackinfo): print(" # Tones Track Name") print("--- ------ ------------------------------") for idx, track in trackinfo.items(): print(f"{str(idx).rjust(3)} {str(track['tones']).rjust(6)} {track['name'][:30]}") def input_tracklist(validate_length=None, default=None): default_info = " [press enter for all]" if default else "" tracklist = input( f"Select tracks as comma separated list, e.g. 2,6,7{default_info} ") tracklist = tracklist.strip().strip(',') if default and tracklist == "": tracklist = default else: tracklist = [int(el.strip()) for el in tracklist.split(',')] if validate_length: assert len(tracklist) == validate_length, f"Expected {validate_length} tracks, got {len(tracklist)}" return tracklist PK!H[.5V6superconf18_midibadge-0.1.0.dist-info/entry_points.txtM @὇jA'PGi n;-%m\F3ڃ v7JXmSg4ɛpK>X;baP>PK!H .superconf18_midibadge-0.1.0.dist-info/METADATAUmSF~bgXe;hB`:[;锳;^N2ę6b}Zp˃P2A7dxWLIPDxFd>[zGE8F!Ul0l` ~8!SVp!9*0(rȬ-M뭅ܪw$?s4x1_qtb"HeL~Sn6hyX֧UrB 6dJp<vޏ'Ӝ#RI.s8)K1)3+֚k8r}T _k=|xW06/1|8w:Ibbl/\cJo:XҒ؂ fkHuwzw?0~N;8NwrR Kz󈱛VDҟ++@[ p&jU~B üd+ ?ȳ6c4!-W9J)tUZܣQtfRXm"MD\ζ;}.Ai0z FahQغG^l!]]Om+Nr2n,I\r^VP4%y_#P>р7M"Byd<,,Mh]Ri&h\#S]N^)- 9ƣ&NXr/q.u 3j>0 La4KQ\Fe.l[t!=@B7!q/GbՒƽp"m]~Y^,g;!oY u(wo˭(۸ _iqV/,JmhV<>ٺ۪$rP|bBBK9zVSՎpzOe3  V~9~?栵j'UsgJEځ-?mzikoPK!H^Q-U,superconf18_midibadge-0.1.0.dist-info/RECORDIsPF-`x.z   cuYeUb%ݻ9C7|MTekMGXW$9Y١WE;#g}pFaY5+7#^o<AOEݝ]>d]Aa՛W%=A~$9˘.0kB*# ̰`% ezVT# K4E>W5e1oVSH4䮘dNE#=ms9Hi"u8WsB1(*-vf$='`A,n_5y,[S,~RPTu^N,^`{2f-)[[K> K81Zsz;z,yV ˨ɸ O1@pOSeswϔI;&Ɓxg`5x_Q6Do'ɝ1PA.it6vt;B<]|uEtȂLIQXnPK!!superconf18_midibadge/__init__.pyPK!][ [ "?superconf18_midibadge/midistuff.pyPK!' superconf18_midibadge/tools/__init__.pyPK!VV) superconf18_midibadge/tools/midi2basic.pyPK!˒cc'superconf18_midibadge/tools/midiinfo.pyPK!G(dsuperconf18_midibadge/tools/midisplit.pyPK!J,<:: >superconf18_midibadge/uistuff.pyPK!H[.5V6superconf18_midibadge-0.1.0.dist-info/entry_points.txtPK!&%Y..-`superconf18_midibadge-0.1.0.dist-info/LICENSEPK!H\TT+#superconf18_midibadge-0.1.0.dist-info/WHEELPK!H .v$superconf18_midibadge-0.1.0.dist-info/METADATAPK!H^Q-U,v(superconf18_midibadge-0.1.0.dist-info/RECORDPK *