#!@TARGET_PYTHON@
-# Copyright (c) 2006--2009 Brailcom, o.p.s.
+# Copyright (C) 2006--2012 Brailcom, o.p.s.
#
# Author: Milan Zamazal <pdm@brailcom.org>
#
-# COPYRIGHT NOTICE
+# This file is part of LilyPond, the GNU music typesetter.
#
-# This program is free software; you can redistribute it and/or modify
+# LilyPond is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
+# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# for more details.
+# LilyPond is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-
+# along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
import optparse
import os
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:
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 (ord (val2[0])*65536 + ord (val2[1])*256 + ord (val2[2])) \
+ + " msec/quarter"
+
+class time_signature_formatter (formatter):
+ def format_vals (self, val1, val2 = ""):
+ from fractions import Fraction
+ # if there are more notated 32nd notes per midi quarter than 8,
+ # we display a fraction smaller than 1 as scale factor.
+ r = Fraction(8, ord (val2[3]))
+ if r == 1:
+ ratio =""
+ else:
+ ratio = " *" + str (r)
+ return str (ord (val2[0])) + "/" + str(1 << ord (val2[1])) + ratio \
+ + ", metronome " + str (Fraction (ord (val2[2]), 96))
+class key_signature_formatter (formatter):
+ def format_vals (self, val1, val2):
+ key_names = ['F', 'C', 'G', 'D', 'A', 'E', 'B']
+ key = (((ord(val2[0])+128)%256)-128) + ord(val2[1])*3 + 1;
+ return (key_names[key%7] + (key/7) * "is" + (-(key/7)) * "es"
+ + " " + ['major','minor'][ord(val2[1])])
+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)