]> git.donarmstrong.com Git - lilypond.git/commitdiff
lilymidi: Add --pretty switch to lilymidi.py to display midi data in human-readable...
authorReinhold Kainhofer <reinhold@kainhofer.com>
Mon, 16 Nov 2009 17:58:11 +0000 (18:58 +0100)
committerReinhold Kainhofer <reinhold@kainhofer.com>
Sun, 3 Jul 2011 19:55:57 +0000 (21:55 +0200)
scripts/lilymidi.py

index 73809a4d4d9f227591ded8fbfb0a63664f9e8226..357988818d13fe45d377e693a0707082e92fb3e0 100644 (file)
@@ -35,6 +35,8 @@ def process_options (args):
                        help="prefix filtered track numbers with PREFIX")
     parser.add_option ('', '--dump', action='store_true', dest='dump',
                        help="just dump parsed contents of the MIDI file")
+    parser.add_option ('', '--pretty', action='store_true', dest='pretty',
+                       help="dump parsed contents of the MIDI file in human-readable form (implies --dump)")
     parser.usage = parser.usage + " FILE"
     options, args = parser.parse_args (args)
     if len (args) != 1:
@@ -62,13 +64,142 @@ def track_info (data):
         track_info.append ((i, track_name (tracks[i])))
     return track_info
 
+
+class formatter:
+   def __init__ (self, txt = ""):
+     self.text = txt
+   def format_vals (self, val1, val2 = ""):
+     return str (val1) + str(val2)
+   def format (self, val1, val2 = ""):
+     return self.text + self.format_vals (val1, val2)
+class none_formatter (formatter):
+   def format_vals (self, val1, val2 = ""):
+     return ''
+class meta_formatter (formatter):
+   def format_vals (self, val1, val2):
+     return str (val2);
+class tempo_formatter (formatter):
+   def format_vals (self, val1, val2):
+     return str (val2) + " msec/quarter"
+
+class time_signature_formatter (formatter):
+   def format_vals (self, val1, val2 = ""):
+     return str (val2)   # TODO
+class key_signature_formatter (formatter):
+   def format_vals (self, val1, val2):
+     return str (val2)   # TODO
+class channel_formatter (formatter):
+   def __init__ (self, txt, ch):
+     formatter.__init__ (self, txt)
+     self.channel = ch
+   def format (self, val1, val2 = ""):
+     return self.text + "Channel " + str (self.channel) + ", " + \
+            self.format_vals (val1, val2)
+class control_mode_formatter (formatter):
+   def __init__ (self, txt, ch):
+     formatter.__init__ (self, txt)
+     self.mode = ch
+   def format (self, val1, val2 = ""):
+     return self.text + str (self.mode) + ", " + \
+            self.format_vals (val1, val2)
+class note_formatter (channel_formatter):
+   def pitch (self, val):
+     pitch_names = ['C', 'Cis', 'D', 'Dis', 'E', 'F', 'Fis', 'G', 'Gis', 'A', 'Ais', 'B'];
+     p = val % 12;
+     oct = val / 12 -1;
+     return pitch_names[p] + str(oct) + "(" + str(val) + ")"
+   def velocity (self, val):
+     #01   #10   #20   #30   #40   #50   #60   #70   #7F
+     pass;
+   def format_vals (self, val1, val2):
+     return self.pitch (val1)
+
+
+meta_dict = {0x00: meta_formatter ("Seq.Nr.:    "),
+             0x01: meta_formatter ("Text:       "),
+             0x02: meta_formatter ("Copyright:  "),
+             0x03: meta_formatter ("Track name: "),
+             0x04: meta_formatter ("Instrument: "),
+             0x05: meta_formatter ("Lyric:      "),
+             0x06: meta_formatter ("Marker:     "),
+             0x07: meta_formatter ("Cue point:  "),
+             0x2F: none_formatter ("End of Track"),
+             0x51: tempo_formatter ("Tempo:      "),
+             0x54: meta_formatter ("SMPTE Offs.:"),
+             0x58: time_signature_formatter ("Time signature: "),
+             0x59: key_signature_formatter ("Key signature: ")
+}
+
+def dump_event (ev, time, padding):
+    ch = ev[0] & 0x0F;
+    func = ev[0] & 0xF0;
+    f = None
+    if (ev[0] == 0xFF):
+        f = meta_dict.get (ev[1], formatter ())
+    if (func == 0x80):
+        f = note_formatter ("Note off: ", ch)
+    elif (func == 0x90):
+        if (ev[2] == 0):
+          desc = "Note off: "
+        else:
+          desc = "Note on: "
+        f = note_formatter (desc, ch)
+    elif (func == 0xA0):
+        f = note_formatter ("Polyphonic aftertouch: ", ch, "Aftertouch pressure: ")
+    elif (func == 0xB0):
+        f = control_mode_formatter ("Control mode change: ", ch)
+    elif (func == 0xC0):
+        f = channel_formatter ("Program Change: ", ch)
+    elif (func == 0xD0):
+        f = channel_formatter ("Channel aftertouch: ", ch)
+    elif (ev[0] in [0xF0, 0xF7]):
+        f = meta_formatter ("System-exclusive event: ")
+
+    if f:
+      if len (ev) > 2:
+        print padding + f.format (ev[1], ev[2])
+      elif len (ev) > 1:
+        print padding + f.format (ev[1])
+      else:
+        print padding + f.format ()
+    else:
+      print padding + "Unrecognized MIDI event: " + str (ev);
+
+def dump_midi (data, midi_file, options):
+    if not options.pretty:
+        print data
+        return
+    # First, dump general info, #tracks, etc.
+    print "Filename:     " + midi_file;
+    i = data[0];
+    m_formats = {0: 'single multi-channel track',
+                 1: "one or more simultaneous tracks",
+                 2: "one or more sequentially independent single-track patterns"}
+    print "MIDI format:  " + str (i[0]) + " (" + m_formats.get (i[0], "") + ")";
+    print "Divisions:    " + str (i[1]) + " per whole note";
+    print "#Tracks:      " + str ( len (data[1]))
+    n = 0;
+    for tr in data[1]:
+      time = 0;
+      n += 1;
+      print
+      print "Track " + str(n) + ":"
+      print "    Time 0:"
+      for ev in tr:
+        if ev[0]>time:
+           time = ev[0]
+           print "    Time " + str(time) + ": "
+        dump_event (ev[1], time, "        ");
+
+
+
 def go ():
     options, args = process_options (sys.argv[1:])
     midi_file = args[0]
     midi_data = read_midi (midi_file)
     info = track_info (midi_data)
-    if options.dump:
-        print midi_data
+    if (options.dump or options.pretty):
+        dump_midi (midi_data, midi_file, options);
     elif options.regexp:
         import re
         regexp = re.compile (options.regexp)