diff options
-rw-r--r-- | downsamp.py | 117 | ||||
-rw-r--r-- | mkiv.py | 3 | ||||
-rw-r--r-- | mktune.py | 63 | ||||
-rw-r--r-- | shiv.py | 8 |
4 files changed, 187 insertions, 4 deletions
diff --git a/downsamp.py b/downsamp.py new file mode 100644 index 0000000..f7a0255 --- /dev/null +++ b/downsamp.py @@ -0,0 +1,117 @@ +from xml.etree import ElementTree as ET +import optparse +import os + +parser = optparse.OptionParser() +parser.add_option('-f', '--frequency', dest='frequency', type='float', help='How often to switch between active streams') +parser.set_defaults(frequency=0.016) +options, args = parser.parse_args() + +class Note(object): + def __init__(self, time, dur, pitch, ampl): + self.time = time + self.dur = dur + self.pitch = pitch + self.ampl = ampl + +for fname in args: + try: + iv = ET.parse(fname).getroot() + except IOError: + import traceback + traceback.print_exc() + print fname, ': Bad file' + continue + + print '----', fname, '----' + + notestreams = iv.findall("./streams/stream[@type='ns']") + print len(notestreams), 'notestreams' + + print 'Loading all events...' + + evs = [] + + dur = 0.0 + + for ns in notestreams: + for note in ns.findall('note'): + n = Note( + float(note.get('time')), + float(note.get('dur')), + float(note.get('pitch')), + float(note.get('ampl', float(note.get('vel', 127.0)) / 127.0)), + ) + evs.append(n) + if n.time + n.dur > dur: + dur = n.time + n.dur + + print len(evs), 'events' + print dur, 'duration' + + print 'Scheduling events...' + + sched = {} + + t = 0.0 + i = 0 + while t <= dur: + nextt = t + options.frequency + #print '-t', t, 'nextt', nextt + + evs_now = [n for n in evs if n.time <= t and t < n.time + n.dur] + if evs_now: + holding = False + count = 0 + while count < len(evs_now): + selidx = (count + i) % len(evs_now) + sel = evs_now[selidx] + sched[t] = (sel.pitch, sel.ampl) + if sel.time + sel.dur >= nextt: + holding = True + break + t = sel.time + sel.dur + count += 1 + if not holding: + sched[t] = (0, 0) + else: + sched[t] = (0, 0) + + t = nextt + i += 1 + + print len(sched), 'events scheduled' + + print 'Writing out schedule...' + + newiv = ET.Element('iv') + newiv.append(iv.find('meta')) + newivstreams = ET.SubElement(newiv, 'streams') + newivstream = ET.SubElement(newivstreams, 'stream', type='ns') + + prevt = None + prevev = None + for t, ev in sorted(sched.items(), key=lambda pair: pair[0]): + if prevt is not None: + if prevev[0] != 0: + ET.SubElement(newivstream, 'note', + pitch = str(prevev[0]), + ampl = str(prevev[1]), + time = str(prevt), + dur = str(t - prevt), + ) + prevev = ev + prevt = t + + t = dur + if prevev[0] != 0: + ET.SubElement(newivstream, 'note', + pitch = str(prevev[0]), + ampl = str(prevev[1]), + time = str(prevt), + dur = str(t - prevt), + ) + + print 'Done.' + txt = ET.tostring(newiv, 'UTF-8') + open(os.path.splitext(os.path.basename(fname))[0]+'.downsampled.iv', 'wb').write(txt) @@ -705,6 +705,9 @@ for fname in args: ivev.set('time', str(mev.abstime)) ivev.set('data', repr(fw.encode_midi_event(mev.ev))) + ivargs = ET.SubElement(ivmeta, 'args') + ivargs.text = ' '.join('%r' % (i,) for i in sys.argv[1:]) + print 'Done.' txt = ET.tostring(iv, 'UTF-8') open(os.path.splitext(os.path.basename(fname))[0]+'.iv', 'wb').write(txt) diff --git a/mktune.py b/mktune.py new file mode 100644 index 0000000..57715b9 --- /dev/null +++ b/mktune.py @@ -0,0 +1,63 @@ +from xml.etree import ElementTree as ET +import optparse + +parser = optparse.OptionParser() +parser.add_option('-t', '--tempo', dest='tempo', type='float', help='Tempo (in BPM)') +parser.add_option('-r', '--resolution', dest='resolution', type='float', help='Approximate resolution in seconds (overrides tempo)') +parser.add_option('-f', '--float', dest='float', action='store_true', help='Allow floating point representations on output') +parser.add_option('-T', '--transpose', dest='transpose', type='float', help='Transpose by this many semitones') +parser.set_defaults(tempo=60000, resolution=None, transpose=0) +options, args = parser.parse_args() + +maybe_int = int +if options.float: + maybe_int = float + +class Note(object): + def __init__(self, time, dur, pitch, ampl): + self.time = time + self.dur = dur + self.pitch = pitch + self.ampl = ampl + +if options.resolution is not None: + options.tempo = 60.0 / options.resolution + +options.tempo = maybe_int(options.tempo) + +def to_beats(tm): + return options.tempo * tm / 60.0 + +for fname in args: + try: + iv = ET.parse(fname).getroot() + except IOError: + import traceback + traceback.print_exc() + print fname, ': Bad file' + continue + + print options.tempo, + + ns = iv.find('./streams/stream[@type="ns"]') + prevn = None + for note in ns.findall('note'): + n = Note( + float(note.get('time')), + float(note.get('dur')), + float(note.get('pitch')) + options.transpose, + float(note.get('ampl', float(note.get('vel', 127.0)) / 127.0)), + ) + if prevn is not None: + rtime = to_beats(n.time - (prevn.time + prevn.dur)) + if rtime >= 1: + print 0, maybe_int(rtime), + ntime = to_beats(prevn.dur) + if ntime < 1 and not options.float: + ntime = 1 + print maybe_int(440.0 * 2**((prevn.pitch-69)/12.0)), maybe_int(ntime), + prevn = n + ntime = to_beats(n.dur) + if ntime < 1 and not options.float: + ntime = 1 + print int(440.0 * 2**((n.pitch-69)/12.0)), int(ntime), @@ -202,7 +202,7 @@ for fname in args: notes = stream.findall('note') for note in notes: pitch = float(note.get('pitch')) - vel = int(note.get('vel')) + ampl = float(note.get('ampl', float(note.get('vel', 127.0)) / 127.0)) time = float(note.get('time')) dur = float(note.get('dur')) if options.notes: @@ -218,11 +218,11 @@ for fname in args: if options.histogram_tracks: pitch_tracks[sidx][pitch] = pitch_tracks[sidx].get(pitch, 0) + 1 if options.vel_hist: - velocities[vel] = velocities.get(vel, 0) + 1 + velocities[ampl] = velocities.get(ampl, 0) + 1 if options.total: - tot_velocities[vel] = tot_velocities.get(vel, 0) + 1 + tot_velocities[ampl] = tot_velocities.get(ampl, 0) + 1 if options.vel_hist_tracks: - velocities_tracks[sidx][vel] = velocities_tracks[sidx].get(vel, 0) + 1 + velocities_tracks[sidx][ampl] = velocities_tracks[sidx].get(ampl, 0) + 1 if (options.duration or options.duty_cycle) and time + dur > max_dur: max_dur = time + dur if options.duty_cycle: |