X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=scripts%2Fmidi2ly.py;h=1130a0a12d4ca5d9df719feaa83522b9517b4204;hb=3bb99011330dcf1f97005a6216e2e1b7ae1052cb;hp=3e29526addfb7b80c106561948e6c1bef88a9847;hpb=a42a7d6ef35ab56e871c1c7f1cff3571e82a1ec3;p=lilypond.git diff --git a/scripts/midi2ly.py b/scripts/midi2ly.py index 3e29526add..1130a0a12d 100644 --- a/scripts/midi2ly.py +++ b/scripts/midi2ly.py @@ -4,8 +4,8 @@ # # source file of the GNU LilyPond music typesetter # -# convert MIDI to LilyPond source -# +# (c) 1998--2004 Han-Wen Nienhuys +# Jan Nieuwenhuizen ''' @@ -23,54 +23,271 @@ TODO: ''' import getopt -import __main__ -import sys +import os import string +import sys + + +################################################################ +# Users of python modules should include this snippet. +# +# This soon to be removed for: import lilypond.lilylib as ly +libdir = '@local_lilypond_libdir@' +if not os.path.isdir (libdir): + libdir = '@lilypond_libdir@' +sys.path.insert (0, os.path.join (libdir, 'python')) + -sys.path.append ('@datadir@/python') -sys.path.append ('@datadir@/buildscripts/out') -sys.path.append ('@datadir@/modules/out') +################################################################ import midi + +################################################################ +################ CONSTANTS + + +output_name = '' +LINE_BELL = 60 +scale_steps = [0,2,4,5,7,9,11] + +clocks_per_1 = 1536 +clocks_per_4 = 0 +key = 0 +time = 0 +reference_note = 0 +start_quant = 0 +start_quant_clocks = 0 +duration_quant = 0 +duration_quant_clocks = 0 +allowed_tuplets = [] +allowed_tuplet_clocks = [] +absolute_p = 0 +explicit_durations_p = 0 +text_lyrics_p = 0 + + + +################################################################ + +localedir = '@localedir@' try: import gettext - gettext.bindtextdomain ('lilypond', '@localedir@') + gettext.bindtextdomain ('lilypond', localedir) gettext.textdomain ('lilypond') _ = gettext.gettext except: def _ (s): return s -# Attempt to fix problems with limited stack size set by Python! -# Sets unlimited stack size. Note that the resource module only -# is available on UNIX. -try: - import resource - resource.setrlimit (resource.RLIMIT_STACK, (-1, -1)) -except: - pass +program_name = 'midi2ly' +program_version = '@TOPLEVEL_VERSION@' + +errorport = sys.stderr +verbose_p = 0 + +# temp_dir = os.path.join (original_dir, '%s.dir' % program_name) +# original_dir = os.getcwd () +# keep_temp_dir_p = 0 -program_name = 'midi2ly [experimental]' -package_name = 'lilypond' -help_summary = _ ("Convert MIDI to LilyPond source") + +help_summary = _ ("Convert MIDI to LilyPond source.") option_definitions = [ ('', 'a', 'absolute-pitches', _ ("print absolute pitches")), (_ ("DUR"), 'd', 'duration-quant', _ ("quantise note durations on DUR")), ('', 'e', 'explicit-durations', _ ("print explicit durations")), - ('', 'h', 'help', _ ("this help")), + ('', 'h', 'help', _ ("print this help")), (_ ("ALT[:MINOR]"), 'k', 'key', _ ("set key: ALT=+sharps|-flats; MINOR=1")), - (_ ("FILE"), 'o', 'output', _ ("write ouput to FILE")), + (_ ("FILE"), 'o', 'output', _ ("write output to FILE")), (_ ("DUR"), 's', 'start-quant', _ ("quantise note starts on DUR")), (_ ("DUR*NUM/DEN"), 't', 'allow-tuplet', _ ("allow tuplet durations DUR*NUM/DEN")), - ('', 'V', 'verbose', _ ("verbose")), + ('', 'V', 'verbose', _ ("be verbose")), ('', 'v', 'version', _ ("print version number")), ('', 'w', 'warranty', _ ("show warranty and copyright")), ('', 'x', 'text-lyrics', _ ("treat every text as a lyric")), ] -from lilylib import * +################################################################ +# lilylib.py -- options and stuff +# +# source file of the GNU LilyPond music typesetter + +import os + +try: + import gettext + gettext.bindtextdomain ('lilypond', localedir) + gettext.textdomain ('lilypond') + _ = gettext.gettext +except: + def _ (s): + return s + +if program_version == '@' + 'TOPLEVEL_VERSION' + '@': + program_version = '1.5.17' + +def identify (): + sys.stdout.write ('%s (GNU LilyPond) %s\n' % (program_name, program_version)) + +def warranty (): + identify () + sys.stdout.write ('\n') + sys.stdout.write (_ ('Copyright (c) %s by' % ' 2001--2004')) + sys.stdout.write ('\n') + sys.stdout.write (' Han-Wen Nienhuys') + sys.stdout.write (' Jan Nieuwenhuizen') + sys.stdout.write ('\n') + sys.stdout.write (_ (r''' +Distributed under terms of the GNU General Public License. It comes with +NO WARRANTY.''')) + sys.stdout.write ('\n') + +def progress (s): + errorport.write (s + '\n') + +def warning (s): + progress (_ ("warning: ") + s) + +def error (s): + + + '''Report the error S. Exit by raising an exception. Please + do not abuse by trying to catch this error. If you do not want + a stack trace, write to the output directly. + + RETURN VALUE + + None + + ''' + + progress (_ ("error: ") + s) + raise _ ("Exiting ... ") + +def getopt_args (opts): + '''Construct arguments (LONG, SHORT) for getopt from list of options.''' + short = '' + long = [] + for o in opts: + if o[1]: + short = short + o[1] + if o[0]: + short = short + ':' + if o[2]: + l = o[2] + if o[0]: + l = l + '=' + long.append (l) + return (short, long) + +def option_help_str (o): + '''Transform one option description (4-tuple ) into neatly formatted string''' + sh = ' ' + if o[1]: + sh = '-%s' % o[1] + + sep = ' ' + if o[1] and o[2]: + sep = ', ' + + long = '' + if o[2]: + long= '--%s' % o[2] + + arg = '' + if o[0]: + if o[2]: + arg = '=' + arg = arg + o[0] + return ' ' + sh + sep + long + arg + + +def options_help_str (opts): + '''Convert a list of options into a neatly formatted string''' + w = 0 + strs =[] + helps = [] + + for o in opts: + s = option_help_str (o) + strs.append ((s, o[3])) + if len (s) > w: + w = len (s) + + str = '' + for s in strs: + str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1]) + return str + +def help (): + ls = [(_ ("Usage: %s [OPTIONS]... FILE") % program_name), + ('\n\n'), + (help_summary), + ('\n\n'), + (_ ("Options:")), + ('\n'), + (options_help_str (option_definitions)), + ('\n\n'), + (_ ("Report bugs to %s.") % 'bug-lilypond@gnu.org'), + ('\n')] + map (sys.stdout.write, ls) + +def setup_temp (): + """ + Create a temporary directory, and return its name. + """ + global temp_dir + if not keep_temp_dir_p: + temp_dir = tempfile.mktemp (program_name) + try: + os.mkdir (temp_dir, 0777) + except OSError: + pass + + return temp_dir + + +def system (cmd, ignore_error = 0): + """Run CMD. If IGNORE_ERROR is set, don't complain when CMD returns non zero. + + RETURN VALUE + + Exit status of CMD + """ + + if verbose_p: + progress (_ ("Invoking `%s\'") % cmd) + st = os.system (cmd) + if st: + name = re.match ('[ \t]*([^ \t]*)', cmd).group (1) + msg = name + ': ' + _ ("command exited with value %d") % st + if ignore_error: + warning (msg + ' ' + _ ("(ignored)") + ' ') + else: + error (msg) + + return st + + +def cleanup_temp (): + if not keep_temp_dir_p: + if verbose_p: + progress (_ ("Cleaning %s...") % temp_dir) + shutil.rmtree (temp_dir) + + +def strip_extension (f, ext): + (p, e) = os.path.splitext (f) + if e == ext: + e = '' + return p + e + +################################################################ +# END Library +################################################################ + + class Duration: @@ -208,7 +425,8 @@ class Note: return (o, n, a) - def dump (self): + def dump (self, dump_dur = 1): + global reference_note s = chr ((self.notename + 2) % 7 + ord ('a')) s = s + self.alteration_names[self.alteration + 2] if absolute_p: @@ -228,12 +446,14 @@ class Note: elif commas < 0: s = s + "," * -commas - if explicit_durations_p \ - or Duration.compare (self.duration, reference_note.duration): + ## FIXME: compile fix --jcn + if dump_dur and (explicit_durations_p \ + or Duration.compare (self.duration, + reference_note.duration)): s = s + self.duration.dump () - global reference_note reference_note = self + # TODO: move space return s + ' ' @@ -352,25 +572,6 @@ class Text: return s -output_name = '' -LINE_BELL = 60 -scale_steps = [0,2,4,5,7,9,11] - -clocks_per_1 = 1536 -clocks_per_4 = 0 -key = 0 -time = 0 -reference_note = 0 -start_quant = 0 -start_quant_clocks = 0 -duration_quant = 0 -duration_quant_clocks = 0 -allowed_tuplets = [] -allowed_tuplet_clocks = [] -absolute_p = 0 -explicit_durations_p = 0 -text_lyrics_p = 0 - def split_track (track): chs = {} for i in range(16): @@ -585,11 +786,13 @@ def dump_chord (ch): elif len (notes) > 1: global reference_note s = s + '<' - s = s + notes[0].dump () + s = s + notes[0].dump (dump_dur = 0) r = reference_note for i in notes[1:]: - s = s + i.dump () + s = s + i.dump (dump_dur = 0 ) s = s + '>' + + s = s + notes[0].duration.dump() + ' ' reference_note = r return s @@ -703,7 +906,7 @@ def dump_track (channels, n): s = s + ' ' + dump_channel (channels[i][0], skip) s = s + '}\n\n' - s = s + '%s = <\n' % track + s = s + '%s = <<\n' % track if clef.type != 2: s = s + clef.dump () + '\n' @@ -717,7 +920,7 @@ def dump_track (channels, n): else: s = s + ' \\context Voice = %s \\%s\n' % (channel, track + channel) - s = s + '>\n\n' + s = s + '>>\n\n' return s def thread_first_item (thread): @@ -786,7 +989,7 @@ def convert_midi (f, o): for i in range (len (tracks)): s = s + dump_track (tracks[i], i) - s = s + '\n\\score {\n <\n' + s = s + '\n\\score {\n <<\n' for i in range (len (tracks)): track = track_name (i) item = track_first_item (tracks[i]) @@ -794,7 +997,7 @@ def convert_midi (f, o): s = s + ' \\context Staff=%s \\%s\n' % (track, track) elif item and item.__class__ == Text: s = s + ' \\context Lyrics=%s \\%s\n' % (track, track) - s = s + ' >\n}\n' + s = s + ' >>\n}\n' progress (_ ("%s output to `%s'...") % ('LY', o)) @@ -807,7 +1010,7 @@ def convert_midi (f, o): h.close () -(sh, long) = getopt_args (__main__.option_definitions) +(sh, long) = getopt_args (option_definitions) try: (options, files) = getopt.getopt(sys.argv[1:], sh, long) except getopt.error, s: @@ -848,13 +1051,10 @@ for opt in options: elif o == '--absolute-pitches' or o == '-a': - global absolute_p absolute_p = 1 elif o == '--duration-quant' or o == '-d': - global duration_quant duration_quant = string.atoi (a) elif o == '--explicit-durations' or o == '-e': - global explicit_durations_p explicit_durations_p = 1 elif o == '--key' or o == '-k': (alterations, minor) = map (string.atoi, string.split (a + ':0', ':'))[0:2] @@ -864,13 +1064,10 @@ for opt in options: sharps = alterations else: flats = - alterations - global key key = Key (sharps, flats, minor) elif o == '--start-quant' or o == '-s': - global start_quant, start_quant_clocks start_quant = string.atoi (a) elif o == '--allow-tuplet' or o == '-t': - global allowed_tuplets a = string.replace (a, '/', '*') tuplet = map (string.atoi, string.split (a, '*')) allowed_tuplets.append (tuplet) @@ -907,8 +1104,11 @@ for f in files: o = output_name (outdir, outbase) = os.path.split (o) - if outdir != '.': - mkdir_p (outdir, 0777) + if outdir != '.' and outdir != '': + try: + os.mkdir (outdir, 0777) + except OSError: + pass convert_midi (f, o)