k-l-lambda's picture
added node-addon-lilypond
f65fe85
#!@TARGET_PYTHON@
# Copyright (C) 2006--2020 Brailcom, o.p.s.
#
# Author: Milan Zamazal <pdm@brailcom.org>
#
# This file is part of LilyPond, the GNU music typesetter.
#
# 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 3 of the License, or
# (at your option) any later version.
#
# 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 LilyPond. If not, see <http://www.gnu.org/licenses/>.
import optparse
import os
import sys
"""
@relocate-preamble@
"""
def process_options(args):
parser = optparse.OptionParser(version="@TOPLEVEL_VERSION@")
parser.add_option('', '--filter-tracks', metavar='REGEXP', action='store', type='string', dest='regexp',
help="display only tracks numbers, of those track names matching REGEXP")
parser.add_option('', '--prefix-tracks', metavar='PREFIX', action='store', type='string', dest='prefix',
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:
parser.print_help()
sys.exit(2)
return options, args
def read_midi(file):
import midi
return midi.parse(open(file, 'rb').read())
def track_info(data):
tracks = data[1]
def track_name(track):
name = ''
for time, event in track:
if time > 0:
break
if event[0] == 255 and event[1] == 3:
name = event[2]
break
return name
track_info = []
for i in range(len(tracks)):
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 or options.pretty):
dump_midi(midi_data, midi_file, options)
elif options.regexp:
import re
regexp = re.compile(options.regexp)
numbers = [str(n+1) for n, name in info if regexp.search(name)]
if numbers:
if options.prefix:
sys.stdout.write('%s ' % (options.prefix,))
sys.stdout.write(','.join(numbers))
sys.stdout.write('\n')
else:
for n, name in info:
sys.stdout.write('%d %s\n' % (n+1, name,))
if __name__ == '__main__':
go()