Browse Source

Features and bugfixes all around

Grissess 9 years ago
parent
commit
90fe1672d8
3 changed files with 77 additions and 10 deletions
  1. 4 0
      client.py
  2. 18 2
      mkiv.py
  3. 55 8
      shiv.py

+ 4 - 0
client.py

@@ -141,6 +141,10 @@ def pygame_notes():
                     thread.interrupt_main()
                     thread.interrupt_main()
                     pygame.quit()
                     pygame.quit()
                     exit()
                     exit()
+            elif ev.type == pygame.QUIT:
+                thread.interrupt_main()
+                pygame.quit()
+                exit()
 
 
         clock.tick(60)
         clock.tick(60)
 
 

+ 18 - 2
mkiv.py

@@ -17,6 +17,7 @@ import os
 import optparse
 import optparse
 
 
 TRACKS = object()
 TRACKS = object()
+PROGRAMS = object()
 
 
 parser = optparse.OptionParser()
 parser = optparse.OptionParser()
 parser.add_option('-s', '--channel-split', dest='chansplit', action='store_true', help='Split MIDI channels into independent tracks (as far as -T is concerned)')
 parser.add_option('-s', '--channel-split', dest='chansplit', action='store_true', help='Split MIDI channels into independent tracks (as far as -T is concerned)')
@@ -25,6 +26,7 @@ parser.add_option('-c', '--preserve-channels', dest='chanskeep', action='store_t
 parser.add_option('-T', '--track-split', dest='tracks', action='append_const', const=TRACKS, help='Ensure all tracks are on non-mutual streams')
 parser.add_option('-T', '--track-split', dest='tracks', action='append_const', const=TRACKS, help='Ensure all tracks are on non-mutual streams')
 parser.add_option('-t', '--track', dest='tracks', action='append', help='Reserve an exclusive set of streams for certain conditions (try --help-conds)')
 parser.add_option('-t', '--track', dest='tracks', action='append', help='Reserve an exclusive set of streams for certain conditions (try --help-conds)')
 parser.add_option('--help-conds', dest='help_conds', action='store_true', help='Print help on filter conditions for streams')
 parser.add_option('--help-conds', dest='help_conds', action='store_true', help='Print help on filter conditions for streams')
+parser.add_option('-p', '--program-split', dest='tracks', action='append_const', const=PROGRAMS, help='Ensure all programs are on non-mutual streams (overrides -T presently)')
 parser.add_option('-P', '--percussion', dest='perc', help='Which percussion standard to use to automatically filter to "perc" (GM, GM2, or none)')
 parser.add_option('-P', '--percussion', dest='perc', help='Which percussion standard to use to automatically filter to "perc" (GM, GM2, or none)')
 parser.add_option('-f', '--fuckit', dest='fuckit', action='store_true', help='Use the Python Error Steamroller when importing MIDIs (useful for extended formats)')
 parser.add_option('-f', '--fuckit', dest='fuckit', action='store_true', help='Use the Python Error Steamroller when importing MIDIs (useful for extended formats)')
 parser.add_option('-n', '--target-num', dest='repeaterNumber', type='int', help='Target count of devices')
 parser.add_option('-n', '--target-num', dest='repeaterNumber', type='int', help='Target count of devices')
@@ -62,11 +64,19 @@ will cause these groups to be made:
 
 
 As can be seen, order of specification is important. Equally important is the location of -T, which should be at the end.
 As can be seen, order of specification is important. Equally important is the location of -T, which should be at the end.
 
 
-NoteOffEvents are always matched to the stream which has their corresponding NoteOnEvent (in track and pitch), and so are
+NoteOffEvents are always matched to the stream which has their corresponding NoteOnEvent (in track, pitch, and channel), and so are
 not affected or observed by filters.
 not affected or observed by filters.
 
 
 If the filters specified are not a complete cover, an anonymous group will be created with no filter to contain the rest. If
 If the filters specified are not a complete cover, an anonymous group will be created with no filter to contain the rest. If
-it is desired to force this group to have a name, use -t <group>=True.'''
+it is desired to force this group to have a name, use -t <group>=True. This should be placed at the end.
+
+-T behaves exactly as if:
+    -t trk0=ev.tidx==0 -t trk1=ev.tidx==1 -t trk2=ev.tidx==2 [...]
+had been specified in its place, though it is automatically sized to the number of tracks. Similarly, -P operates as if
+    -t prg31=ev.prog==31 -t prg81=ev.prog==81 [...]
+had been specified, again containing only the programs that were observed in the piece.
+
+Groups for which no streams are generated are not written to the resulting file.'''
     exit()
     exit()
 
 
 if not args:
 if not args:
@@ -178,6 +188,7 @@ for fname in args:
     chg_prog = [[0 for i in range(16)] for j in range(len(pat))]
     chg_prog = [[0 for i in range(16)] for j in range(len(pat))]
     ev_cnts = [[0 for i in range(16)] for j in range(len(pat))]
     ev_cnts = [[0 for i in range(16)] for j in range(len(pat))]
     tnames = [''] * len(pat)
     tnames = [''] * len(pat)
+    progs = set([0])
 
 
     for tidx, track in enumerate(pat):
     for tidx, track in enumerate(pat):
         abstime = 0
         abstime = 0
@@ -189,6 +200,7 @@ for fname in args:
             absticks += ev.tick
             absticks += ev.tick
             if isinstance(ev, midi.ProgramChangeEvent):
             if isinstance(ev, midi.ProgramChangeEvent):
                 cur_prog[tidx][ev.channel] = ev.value
                 cur_prog[tidx][ev.channel] = ev.value
+                progs.add(ev.value)
                 chg_prog[tidx][ev.channel] += 1
                 chg_prog[tidx][ev.channel] += 1
             elif isinstance(ev, midi.ControlChangeEvent):
             elif isinstance(ev, midi.ControlChangeEvent):
                 if ev.control == 0:
                 if ev.control == 0:
@@ -210,6 +222,7 @@ for fname in args:
         print 'Track name, event count, final banks, bank changes, final programs, program changes:'
         print 'Track name, event count, final banks, bank changes, final programs, program changes:'
         for tidx, tname in enumerate(tnames):
         for tidx, tname in enumerate(tnames):
             print tidx, ':', tname, ',', ','.join(map(str, ev_cnts[tidx])), ',', ','.join(map(str, cur_bank[tidx])), ',', ','.join(map(str, chg_bank[tidx])), ',', ','.join(map(str, cur_prog[tidx])), ',', ','.join(map(str, chg_prog[tidx]))
             print tidx, ':', tname, ',', ','.join(map(str, ev_cnts[tidx])), ',', ','.join(map(str, cur_bank[tidx])), ',', ','.join(map(str, chg_bank[tidx])), ',', ','.join(map(str, cur_prog[tidx])), ',', ','.join(map(str, chg_prog[tidx]))
+        print 'All programs observed:', progs
 
 
     print 'Sorting events...'
     print 'Sorting events...'
 
 
@@ -275,6 +288,9 @@ for fname in args:
         if spec is TRACKS:
         if spec is TRACKS:
             for tidx in xrange(len(pat)):
             for tidx in xrange(len(pat)):
                 notegroups.append(NSGroup(filter = lambda mev, tidx=tidx: mev.tidx == tidx, name = 'trk%d'%(tidx,)))
                 notegroups.append(NSGroup(filter = lambda mev, tidx=tidx: mev.tidx == tidx, name = 'trk%d'%(tidx,)))
+        elif spec is PROGRAMS:
+            for prog in progs:
+                notegroups.append(NSGroup(filter = lambda mev, prog=prog: mev.prog == prog, name = 'prg%d'%(prog,)))
         else:
         else:
             if '=' in spec:
             if '=' in spec:
                 name, _, spec = spec.partition('=')
                 name, _, spec = spec.partition('=')

+ 55 - 8
shiv.py

@@ -17,6 +17,8 @@ parser.add_option('--vel-hist-tracks', dest='vel_hist_tracks', action='store_tru
 parser.add_option('-d', '--duration', dest='duration', action='store_true', help='Show the duration of the piece')
 parser.add_option('-d', '--duration', dest='duration', action='store_true', help='Show the duration of the piece')
 parser.add_option('-D', '--duty-cycle', dest='duty_cycle', action='store_true', help='Show the duration of the notes within tracks, and as a percentage of the piece duration')
 parser.add_option('-D', '--duty-cycle', dest='duty_cycle', action='store_true', help='Show the duration of the notes within tracks, and as a percentage of the piece duration')
 parser.add_option('-H', '--height', dest='height', type='int', help='Height of histograms')
 parser.add_option('-H', '--height', dest='height', type='int', help='Height of histograms')
+parser.add_option('-C', '--no-color', dest='no_color', action='store_true', help='Don\'t use ANSI color escapes')
+parser.add_option('-x', '--aux', dest='aux', action='store_true', help='Show information about the auxiliary streams')
 
 
 parser.add_option('-a', '--almost-all', dest='almost_all', action='store_true', help='Show useful information')
 parser.add_option('-a', '--almost-all', dest='almost_all', action='store_true', help='Show useful information')
 parser.add_option('-A', '--all', dest='all', action='store_true', help='Show everything')
 parser.add_option('-A', '--all', dest='all', action='store_true', help='Show everything')
@@ -34,11 +36,31 @@ if options.almost_all or options.all:
     options.vel_hist = True
     options.vel_hist = True
     options.duration = True
     options.duration = True
     options.duty_cycle = True
     options.duty_cycle = True
-    options.meta = True
     if options.all:
     if options.all:
+        options.aux = True
+        options.meta = True
         options.histogram_tracks= True
         options.histogram_tracks= True
         options.vel_hist_tracks = True
         options.vel_hist_tracks = True
 
 
+if options.no_color:
+    class COL:
+        NONE=''
+        RED=''
+        GREEN=''
+        BLUE=''
+        YELLOW=''
+        MAGENTA=''
+        CYAN=''
+else:
+    class COL:
+        NONE='\x1b[0m'
+        RED='\x1b[31m'
+        GREEN='\x1b[32m'
+        BLUE='\x1b[34m'
+        YELLOW='\x1b[33m'
+        MAGENTA='\x1b[35m'
+        CYAN='\x1b[36m'
+
 def show_hist(values, height=None):
 def show_hist(values, height=None):
     if not values:
     if not values:
         print '{empty histogram}'
         print '{empty histogram}'
@@ -49,13 +71,18 @@ def show_hist(values, height=None):
     miny, maxy = min(ys), max(ys)
     miny, maxy = min(ys), max(ys)
     xv = range(minx, maxx + 1)
     xv = range(minx, maxx + 1)
     incs = max((maxy - miny) / height, 1)
     incs = max((maxy - miny) / height, 1)
-    print '\t --' + '-' * len(xv)
+    print COL.BLUE + '\t --' + '-' * len(xv) + COL.NONE
     for ub in range(maxy + incs, miny, -incs):
     for ub in range(maxy + incs, miny, -incs):
-        print '{}\t | {}'.format(ub, ''.join(['#' if values.get(x) > (ub - incs) else ' ' for x in xv]))
-    print '\t |-' + '-' * len(xv)
+        print '{}{}\t | {}{}{}'.format(COL.BLUE, ub, COL.YELLOW, ''.join(['#' if values.get(x) > (ub - incs) else ' ' for x in xv]), COL.NONE)
+    print COL.BLUE + '\t |-' + '-' * len(xv) + COL.NONE
     xvs = map(str, xv)
     xvs = map(str, xv)
     for i in range(max(map(len, xvs))):
     for i in range(max(map(len, xvs))):
-        print '\t   ' + ''.join([s[i] if len(s) > i else ' ' for s in xvs])
+        print COL.BLUE + '\t   ' + ''.join([s[i] if len(s) > i else ' ' for s in xvs]) + COL.NONE
+    print
+    xcs = map(str, [values.get(x, 0) for x in xv])
+    for i in range(max(map(len, xcs))):
+        print COL.YELLOW + '\t   ' + ''.join([s[i] if len(s) > i else ' ' for s in xcs]) + COL.NONE
+    print
 
 
 for fname in args:
 for fname in args:
     try:
     try:
@@ -81,17 +108,18 @@ for fname in args:
                 for elem in bpms.iterfind('./bpm'):
                 for elem in bpms.iterfind('./bpm'):
                     print '\t\tAt ticks {}, time {}: {} bpm'.format(elem.get('ticks'), elem.get('time'), elem.get('bpm'))
                     print '\t\tAt ticks {}, time {}: {} bpm'.format(elem.get('ticks'), elem.get('time'), elem.get('bpm'))
 
 
-    if not (options.number or options.groups or options.notes or options.histogram or options.histogram_tracks or options.duration or options.duty_cycle):
+    if not (options.number or options.groups or options.notes or options.histogram or options.histogram_tracks or options.vel_hist or options.vel_hist_tracks or options.duration or options.duty_cycle or options.aux):
         continue
         continue
 
 
     streams = iv.findall('./streams/stream')
     streams = iv.findall('./streams/stream')
     notestreams = [s for s in streams if s.get('type') == 'ns']
     notestreams = [s for s in streams if s.get('type') == 'ns']
+    auxstreams = [s for s in streams if s.get('type') == 'aux']
     if options.number:
     if options.number:
         print 'Stream count:'
         print 'Stream count:'
         print '\tNotestreams:', len(notestreams)
         print '\tNotestreams:', len(notestreams)
         print '\tTotal:', len(streams)
         print '\tTotal:', len(streams)
 
 
-    if not (options.groups or options.notes or options.histogram or options.histogram_tracks or options.duration or options.duty_cycle):
+    if not (options.groups or options.notes or options.histogram or options.histogram_tracks or options.vel_hist or options.vel_hist_tracks or options.duration or options.duty_cycle or options.aux):
         continue
         continue
 
 
     if options.groups:
     if options.groups:
@@ -103,7 +131,26 @@ for fname in args:
         for name, cnt in groups.iteritems():
         for name, cnt in groups.iteritems():
             print '\t{} ({} streams)'.format(name, cnt)
             print '\t{} ({} streams)'.format(name, cnt)
 
 
-    if not (options.notes or options.notes_stream or options.histogram or options.histogram_tracks or options.duration or options.duty_cycle):
+    if options.aux:
+        import midi
+        fr = midi.FileReader()
+        fr.RunningStatus = None  # XXX Hack
+        print 'Aux stream data:'
+        for aidx, astream in enumerate(auxstreams):
+            evs = astream.findall('ev')
+            failed = 0
+            print '\tFrom stream {}, {} events:'.format(aidx, len(evs))
+            for ev in evs:
+                try:
+                    data = eval(ev.get('data'))
+                    mev = fr.parse_midi_event(iter(data))
+                except AssertionError:
+                    failed += 1
+                else:
+                    print '\t\tAt time {}: {}'.format(ev.get('time'), mev)
+            print '\t\t(...and {} others which failed to parse)'.format(failed)
+
+    if not (options.notes or options.notes_stream or options.histogram or options.histogram_tracks or options.vel_hist or options.vel_hist_tracks or options.duration or options.duty_cycle):
         continue
         continue
 
 
     if options.notes:
     if options.notes: