X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=scripts%2Fabc2ly.py;h=e6e0d12407e4ed53006ee529da85f55f7ac7dbab;hb=HEAD;hp=8029dbea8f222ce93ab42137e46cc72a8939cef1;hpb=3fdd94a56964583aa4493ea9ed71b3077c005d76;p=lilypond.git diff --git a/scripts/abc2ly.py b/scripts/abc2ly.py index 8029dbea8f..e6e0d12407 100644 --- a/scripts/abc2ly.py +++ b/scripts/abc2ly.py @@ -1,11 +1,28 @@ -#!@PYTHON@ +#!@TARGET_PYTHON@ # -*- coding: utf-8 -*- + # once upon a rainy monday afternoon. + +# 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 . + # # ... # # (not finished.) -# ABC standard v1.6: http://www.gre.ac.uk/~c.walshaw/abc2mtex/abc.txt +# ABC standard v1.6: http://abcnotation.com/ # # Enhancements (Roy R. Rankin) # @@ -33,6 +50,7 @@ # %%LY now supported. # \breve and \longa supported. # M:none doesn't crash lily. +# lilypond '--' supported. # Enhancements (Guy Gascoigne-Piggford) # @@ -57,33 +75,39 @@ #TODO: # +# * coding style # * lilylib # * GNU style messages: warning:FILE:LINE: # * l10n -# +# # Convert to new chord styles. # # UNDEF -> None # - + import __main__ import getopt import sys import re -import string import os program_name = sys.argv[0] + +""" +@relocate-preamble@ +""" + +import lilylib as ly +global _;_=ly._ + version = '@TOPLEVEL_VERSION@' if version == '@' + 'TOPLEVEL_VERSION' + '@': - version = '(unknown version)' # uGUHGUHGHGUGH + version = '(unknown version)' # uGUHGUHGHGUGH UNDEF = 255 state = UNDEF -strict = 0 -preserve_beams = 0 voice_idx_dict = {} header = {} header['footnotes'] = '' @@ -99,7 +123,7 @@ part_names = 0 default_len = 8 length_specified = 0 nobarlines = 0 -global_key = [0] * 7 # UGH +global_key = [0] * 7 # UGH names = ["One", "Two", "Three"] DIGITS='0123456789' HSPACE=' \t' @@ -107,959 +131,989 @@ midi_specs = '' def error (msg): - sys.stderr.write (msg) - if strict: - sys.exit (1) - + sys.stderr.write (msg) + if global_options.strict: + sys.exit (1) + def alphabet (i): - return chr (i + ord('A')) - + return chr (i + ord('A')) + def check_clef(s): - if not s: - return '' - if re.match('-8va', s) or re.match('treble8', s): - # treble8 is used by abctab2ps; -8va is used by barfly, - # and by my patch to abc2ps. If there's ever a standard - # about this we'll support that. - s = s[4:] - state.base_octave = -1 - voices_append("\\clef \"G_8\"\n") - elif re.match('^treble', s): - s = s[6:] - if re.match ('^-8', s): - s = s[2:] - state.base_octave = -2 - voices_append("\\clef \"G_8\"\n") - else: - state.base_octave = 0 - voices_append("\\clef treble\n") - elif re.match('^alto', s): - s = s[4:] - state.base_octave = -1 - voices_append ("\\clef alto\n" ) - elif re.match('^bass',s ): - s = s[4:] - state.base_octave = -2 - voices_append ("\\clef bass\n" ) - return s + # the number gives the base_octave + clefs = [("treble", "treble", 0), + ("treble1", "french", 0), + ("bass3", "varbaritone", 0), + ("bass", "bass", 0), + ("alto4", "tenor", 0), + ("alto2", "mezzosoprano", 0), + ("alto1", "soprano", 0), + ("alto", "alto", 0), + ("perc", "percussion", 0)] + modifier = [("-8va", "_8", -1), + ("-8", "_8", -1), + ("\+8", "^8", +1), + ("8", "_8", -1)] + + if not s: + return '' + clef = None; + octave = 0; + for c in clefs: + m = re.match('^'+c[0], s) + if m: + (clef, octave) = (c[1], c[2]) + s = s[m.end():] + break; + if not clef: + return s + + mod = ""; + for md in modifier: + m = re.match('^'+md[0], s) + if m: + mod = md[1]; + octave += md[2]; + s = s[m.end():] + break; + + state.base_octave = octave + voices_append ("\\clef \""+clef+mod+"\"\n") + return s def select_voice (name, rol): - if not voice_idx_dict.has_key (name): - state_list.append(Parser_state()) - voices.append ('') - slyrics.append ([]) - voice_idx_dict[name] = len (voices) -1 - __main__.current_voice_idx = voice_idx_dict[name] - __main__.state = state_list[current_voice_idx] - while rol != '': - m = re.match ('^([^ \t=]*)=(.*)$', rol) # find keywork - if m: - keyword = m.group(1) - rol = m.group (2) - a = re.match ('^("[^"]*"|[^ \t]*) *(.*)$', rol) - if a: - value = a.group (1) - rol = a.group ( 2) - if keyword == 'clef': - check_clef(value) - elif keyword == "name": - value = re.sub ('\\\\','\\\\\\\\', value) - ## < 2.2 - voices_append ("\\set Staff.instrument = %s\n" % value ) - - __main__.part_names = 1 - elif keyword == "sname" or keyword == "snm": - voices_append ("\\set Staff.instr = %s\n" % value ) - else: - break + if not voice_idx_dict.has_key (name): + state_list.append(Parser_state()) + voices.append ('') + slyrics.append ([]) + voice_idx_dict[name] = len (voices) -1 + __main__.current_voice_idx = voice_idx_dict[name] + __main__.state = state_list[current_voice_idx] + while rol != '': + m = re.match ('^([^ \t=]*)=(.*)$', rol) # find keywork + if m: + keyword = m.group(1) + rol = m.group (2) + a = re.match ('^("[^"]*"|[^ \t]*) *(.*)$', rol) + if a: + value = a.group (1) + rol = a.group ( 2) + if keyword == 'clef': + check_clef(value) + elif keyword == "name": + value = re.sub ('\\\\','\\\\\\\\', value) + ## < 2.2 + voices_append ("\\set Staff.instrument = %s\n" % value ) + + __main__.part_names = 1 + elif keyword == "sname" or keyword == "snm": + voices_append ("\\set Staff.instr = %s\n" % value ) + else: + break def dump_header (outf,hdr): - outf.write ('\\header {\n') - ks = hdr.keys () - ks.sort () - for k in ks: - hdr[k] = re.sub('"', '\\"', hdr[k]) - outf.write ('\t%s = "%s"\n'% (k,hdr[k])) - outf.write ('}') + outf.write ('\\header {\n') + ks = hdr.keys () + ks.sort () + for k in ks: + hdr[k] = re.sub('"', '\\"', hdr[k]) + outf.write ('\t%s = "%s"\n'% (k,hdr[k])) + outf.write ('}') def dump_lyrics (outf): - if (len(lyrics)): - outf.write("\n\\score\n{\n \\lyrics\n <<\n") - for i in range (len (lyrics)): - outf.write ( lyrics [i]) - outf.write ("\n") - outf.write(" >>\n \\layout{}\n}\n") + if (len(lyrics)): + outf.write("\n\\markup \\column {\n") + for i in range (len (lyrics)): + outf.write (lyrics [i]) + outf.write ("\n") + outf.write("}\n") def dump_default_bar (outf): - """ - Nowadays abc2ly outputs explicits barlines (?) - """ - ## < 2.2 - outf.write ("\n\\set Score.defaultBarType = \"empty\"\n") + """ + Nowadays abc2ly outputs explicits barlines (?) + """ + ## < 2.2 + outf.write ("\n\\set Score.defaultBarType = \"\"\n") def dump_slyrics (outf): - ks = voice_idx_dict.keys() - ks.sort () - for k in ks: - if re.match('[1-9]', k): - m = alphabet(string.atoi(k)) - else: - m = k - for i in range (len(slyrics[voice_idx_dict[k]])): - l= alphabet(i) - outf.write ("\nwords%sV%s = \lyricmode {" % (m, l)) - outf.write ("\n" + slyrics [voice_idx_dict[k]][i]) - outf.write ("\n}") + ks = voice_idx_dict.keys() + ks.sort () + for k in ks: + if re.match('[1-9]', k): + m = alphabet (int (k)) + else: + m = k + for i in range (len(slyrics[voice_idx_dict[k]])): + l= alphabet(i) + outf.write ("\nwords%sV%s = \\lyricmode {" % (m, l)) + outf.write ("\n" + slyrics [voice_idx_dict[k]][i]) + outf.write ("\n}") def dump_voices (outf): - global doing_alternative, in_repeat - ks = voice_idx_dict.keys() - ks.sort () - for k in ks: - if re.match ('[1-9]', k): - m = alphabet(string.atoi(k)) - else: - m = k - outf.write ("\nvoice%s = {" % m) - dump_default_bar(outf) - if repeat_state[voice_idx_dict[k]]: - outf.write("\n\\repeat volta 2 {") - outf.write ("\n" + voices [voice_idx_dict[k]]) - if not using_old: - if doing_alternative[voice_idx_dict[k]]: - outf.write("}") - if in_repeat[voice_idx_dict[k]]: - outf.write("}") - outf.write ("\n}") + global doing_alternative, in_repeat + ks = voice_idx_dict.keys() + ks.sort () + for k in ks: + if re.match ('[1-9]', k): + m = alphabet (int (k)) + else: + m = k + outf.write ("\nvoice%s = {" % m) + dump_default_bar(outf) + if repeat_state[voice_idx_dict[k]]: + outf.write("\n\\repeat volta 2 {") + outf.write ("\n" + voices [voice_idx_dict[k]]) + if not using_old: + if doing_alternative[voice_idx_dict[k]]: + outf.write("}") + if in_repeat[voice_idx_dict[k]]: + outf.write("}") + outf.write ("\n}") def try_parse_q(a): - global midi_specs - #assume that Q takes the form "Q:1/4=120" - #There are other possibilities, but they are deprecated - if string.count(a, '/') == 1: - array=string.split(a,'/') - numerator=array[0] - if numerator != 1: - sys.stderr.write("abc2ly: Warning, unable to translate a Q specification with a numerator of %s: %s\n" % (numerator, a)) - array2=string.split(array[1],'=') - denominator=array2[0] - perminute=array2[1] - duration=str(string.atoi(denominator)/string.atoi(numerator)) - midi_specs=string.join(["\\tempo", duration, "=", perminute]) - else: - sys.stderr.write("abc2ly: Warning, unable to parse Q specification: %s\n" % a) - + #assume that Q takes the form "Q:'opt. description' 1/4=120" + #There are other possibilities, but they are deprecated + r = re.compile ('^(.*) *([0-9]+) */ *([0-9]+) *=* *([0-9]+)\s*') + m = r.match (a) + if m: + descr = m.group(1) # possibly empty + numerator = int(m.group (2)) + denominator = int(m.group (3)) + tempo = m.group (4) + dur = duration_to_lilypond_duration ((numerator,denominator), 1, 0) + voices_append ("\\tempo " + descr + " " + dur + "=" + tempo + "\n") + else: + # Parsing of numeric tempi, as these are fairly + # common. The spec says the number is a "beat" so using + # a quarter note as the standard time + numericQ = re.compile ('[0-9]+') + m = numericQ.match (a) + if m: + voices_append ("\\tempo 4=" + m.group(0)) + else: + sys.stderr.write("abc2ly: Warning, unable to parse Q specification: %s\n" % a) + def dump_score (outf): - outf.write (r""" + outf.write (r""" \score{ - << + << """) - ks = voice_idx_dict.keys (); - ks.sort () - for k in ks: - if re.match('[1-9]', k): - m = alphabet (string.atoi(k)) - else: - m = k - if k == 'default' and len (voice_idx_dict) > 1: - break - outf.write ("\n\t\\context Staff=\"%s\"\n\t{\n" %k ) - if k != 'default': - outf.write ("\t \\voicedefault\n") - outf.write ("\t \\voice%s " % m) - outf.write ("\n\t}\n") - - l = ord( 'A' ) - for lyrics in slyrics [voice_idx_dict[k]]: - outf.write ("\n\t\\addlyrics { \n") - if re.match('[1-9]',k): - m = alphabet (string.atoi(k)) - else: - m = k - - outf.write ( " \\words%sV%s } " % ( m, chr (l)) ) - l += 1 - - outf.write ("\n >>") - outf.write ("\n\t\\layout {\n") - outf.write ("\t}\n\t\\midi {%s}\n}\n" % midi_specs) + ks = voice_idx_dict.keys (); + ks.sort () + for k in ks: + if re.match('[1-9]', k): + m = alphabet (int (k)) + else: + m = k + if k == 'default' and len (voice_idx_dict) > 1: + break + outf.write ("\n\t\\context Staff=\"%s\"\n\t{\n" %k ) + if k != 'default': + outf.write ("\t \\voicedefault\n") + outf.write ("\t \\voice%s " % m) + outf.write ("\n\t}\n") + + l = ord( 'A' ) + for lyrics in slyrics [voice_idx_dict[k]]: + outf.write ("\n\t\\addlyrics {\n") + if re.match('[1-9]',k): + m = alphabet (int (k)) + else: + m = k + + outf.write ( " \\words%sV%s } " % ( m, chr (l)) ) + l += 1 + + outf.write ("\n >>") + outf.write ("\n\t\\layout {\n") + outf.write ("\t}\n\t\\midi {%s}\n}\n" % midi_specs) def set_default_length (s): - global length_specified - m = re.search ('1/([0-9]+)', s) - if m: - __main__.default_len = string.atoi ( m.group (1)) - length_specified = 1 + global length_specified + m = re.search ('1/([0-9]+)', s) + if m: + __main__.default_len = int ( m.group (1)) + length_specified = 1 def set_default_len_from_time_sig (s): - m = re.search ('([0-9]+)/([0-9]+)', s) - if m: - n = string.atoi (m.group (1)) - d = string.atoi (m.group (2)) - if (n * 1.0 )/(d * 1.0) < 0.75: - __main__.default_len = 16 - else: - __main__.default_len = 8 + m = re.search ('([0-9]+)/([0-9]+)', s) + if m: + n = int (m.group (1)) + d = int (m.group (2)) + if (n * 1.0 )/(d * 1.0) < 0.75: + __main__.default_len = 16 + else: + __main__.default_len = 8 def gulp_file(f): - try: - i = open(f) - i.seek (0, 2) - n = i.tell () - i.seek (0,0) - except: - sys.stderr.write ("can't open file: `%s'\n" % f) - return '' - s = i.read (n) - if len (s) <= 0: - sys.stderr.write ("gulped empty file: `%s'\n" % f) - i.close () - return s + try: + i = open(f) + i.seek (0, 2) + n = i.tell () + i.seek (0,0) + except: + sys.stderr.write ("cannot open file: `%s'\n" % f) + return '' + s = i.read (n) + if len (s) <= 0: + sys.stderr.write ("gulped empty file: `%s'\n" % f) + i.close () + return s # pitch manipulation. Tuples are (name, alteration). # 0 is (central) C. Alteration -1 is a flat, Alteration +1 is a sharp -# pitch in semitones. +# pitch in semitones. def semitone_pitch (tup): - p =0 - - t = tup[0] - p = p + 12 * (t / 7) - t = t % 7 - - if t > 2: - p = p- 1 - - p = p + t* 2 + tup[1] - return p + p =0 + + t = tup[0] + p = p + 12 * (t / 7) + t = t % 7 + + if t > 2: + p = p- 1 + + p = p + t* 2 + tup[1] + return p def fifth_above_pitch (tup): - (n, a) = (tup[0] + 4, tup[1]) + (n, a) = (tup[0] + 4, tup[1]) - difference = 7 - (semitone_pitch ((n,a)) - semitone_pitch (tup)) - a = a + difference - - return (n,a) + difference = 7 - (semitone_pitch ((n,a)) - semitone_pitch (tup)) + a = a + difference + + return (n,a) def sharp_keys (): - p = (0,0) - l = [] - k = 0 - while 1: - l.append (p) - (t,a) = fifth_above_pitch (p) - if semitone_pitch((t,a)) % 12 == 0: - break - - p = (t % 7, a) - return l + p = (0,0) + l = [] + k = 0 + while 1: + l.append (p) + (t,a) = fifth_above_pitch (p) + if semitone_pitch((t,a)) % 12 == 0: + break + + p = (t % 7, a) + return l def flat_keys (): - p = (0,0) - l = [] - k = 0 - while 1: - l.append (p) - (t,a) = quart_above_pitch (p) - if semitone_pitch((t,a)) % 12 == 0: - break - - p = (t % 7, a) - return l + p = (0,0) + l = [] + k = 0 + while 1: + l.append (p) + (t,a) = quart_above_pitch (p) + if semitone_pitch((t,a)) % 12 == 0: + break + + p = (t % 7, a) + return l def quart_above_pitch (tup): - (n, a) = (tup[0] + 3, tup[1]) - - difference = 5 - (semitone_pitch ((n,a)) - semitone_pitch (tup)) - a = a + difference - - return (n,a) - -key_lookup = { # abc to lilypond key mode names - 'm' : 'minor', - 'min' : 'minor', - 'maj' : 'major', - 'major' : 'major', - 'phr' : 'phrygian', - 'ion' : 'ionian', - 'loc' : 'locrian', - 'aeo' : 'aeolian', - 'mix' : 'mixolydian', - 'mixolydian' : 'mixolydian', - 'lyd' : 'lydian', - 'dor' : 'dorian', - 'dorian' : 'dorian' + (n, a) = (tup[0] + 3, tup[1]) + + difference = 5 - (semitone_pitch ((n,a)) - semitone_pitch (tup)) + a = a + difference + + return (n,a) + +key_lookup = { # abc to lilypond key mode names + 'm' : 'minor', + 'min' : 'minor', + 'maj' : 'major', + 'major' : 'major', + 'phr' : 'phrygian', + 'ion' : 'ionian', + 'loc' : 'locrian', + 'aeo' : 'aeolian', + 'mix' : 'mixolydian', + 'mixolydian' : 'mixolydian', + 'lyd' : 'lydian', + 'dor' : 'dorian', + 'dorian' : 'dorian' } def lily_key (k): - orig = "" + k - # UGR - k = string.lower (k) - key = k[0] - #UGH - k = k[1:] - if k and k[0] == '#': - key = key + 'is' - k = k[1:] - elif k and k[0] == 'b': - key = key + 'es' - k = k[1:] - if not k: - return '%s \\major' % key - - type = k[0:3] - if not key_lookup.has_key (type): - #ugh, use lilylib, say WARNING:FILE:LINE: - sys.stderr.write ("abc2ly:warning:") - sys.stderr.write ("ignoring unknown key: `%s'" % orig) - sys.stderr.write ('\n') - return 0 - return ("%s \\%s" % ( key, key_lookup[type])) + if k == 'none': + return + orig = "" + k + # UGR + k = k.lower () + key = k[0] + #UGH + k = k[1:] + if k and k[0] == '#': + key = key + 'is' + k = k[1:] + elif k and k[0] == 'b': + key = key + 'es' + k = k[1:] + if not k: + return '%s \\major' % key + + type = k[0:3] + if not key_lookup.has_key (type): + #ugh, use lilylib, say WARNING:FILE:LINE: + sys.stderr.write ("abc2ly:warning:") + sys.stderr.write ("ignoring unknown key: `%s'" % orig) + sys.stderr.write ('\n') + return 0 + return ("%s \\%s" % ( key, key_lookup[type])) def shift_key (note, acc, shift): - s = semitone_pitch((note, acc)) - s = (s + shift + 12) % 12 - if s <= 4: - n = s / 2 - a = s % 2 - else: - n = (s + 1) / 2 - a = (s + 1) % 2 - if a: - n = n + 1 - a = -1 - return (n,a) + s = semitone_pitch((note, acc)) + s = (s + shift + 12) % 12 + if s <= 4: + n = s / 2 + a = s % 2 + else: + n = (s + 1) / 2 + a = (s + 1) % 2 + if a: + n = n + 1 + a = -1 + return (n,a) key_shift = { # semitone shifts for key mode names - 'm' : 3, - 'min' : 3, - 'minor' : 3, - 'maj' : 0, - 'major' : 0, - 'phr' : -4, - 'phrygian' : -4, - 'ion' : 0, - 'ionian' : 0, - 'loc' : 1, - 'locrian' : 1, - 'aeo' : 3, - 'aeolian' : 3, - 'mix' : 5, - 'mixolydian' : 5, - 'lyd' : -5, - 'lydian' : -5, - 'dor' : -2, - 'dorian' : -2 + 'm' : 3, + 'min' : 3, + 'minor' : 3, + 'maj' : 0, + 'major' : 0, + 'phr' : -4, + 'phrygian' : -4, + 'ion' : 0, + 'ionian' : 0, + 'loc' : 1, + 'locrian' : 1, + 'aeo' : 3, + 'aeolian' : 3, + 'mix' : 5, + 'mixolydian' : 5, + 'lyd' : -5, + 'lydian' : -5, + 'dor' : -2, + 'dorian' : -2 } def compute_key (k): - k = string.lower (k) - intkey = (ord (k[0]) - ord('a') + 5) % 7 - intkeyacc =0 - k = k[1:] - - if k and k[0] == 'b': - intkeyacc = -1 - k = k[1:] - elif k and k[0] == '#': - intkeyacc = 1 - k = k[1:] - k = k[0:3] - if k and key_shift.has_key(k): - (intkey, intkeyacc) = shift_key(intkey, intkeyacc, key_shift[k]) - keytup = (intkey, intkeyacc) - - sharp_key_seq = sharp_keys () - flat_key_seq = flat_keys () - - accseq = None - accsign = 0 - if keytup in sharp_key_seq: - accsign = 1 - key_count = sharp_key_seq.index (keytup) - accseq = map (lambda x: (4*x -1 ) % 7, range (1, key_count + 1)) - - elif keytup in flat_key_seq: - accsign = -1 - key_count = flat_key_seq.index (keytup) - accseq = map (lambda x: (3*x + 3 ) % 7, range (1, key_count + 1)) - else: - error ("Huh?") - raise "Huh" - - key_table = [0] * 7 - for a in accseq: - key_table[a] = key_table[a] + accsign - - return key_table + k = k.lower () + intkey = (ord (k[0]) - ord('a') + 5) % 7 + intkeyacc =0 + k = k[1:] + + if k and k[0] == 'b': + intkeyacc = -1 + k = k[1:] + elif k and k[0] == '#': + intkeyacc = 1 + k = k[1:] + k = k[0:3] + if k and key_shift.has_key(k): + (intkey, intkeyacc) = shift_key(intkey, intkeyacc, key_shift[k]) + keytup = (intkey, intkeyacc) + + sharp_key_seq = sharp_keys () + flat_key_seq = flat_keys () + + accseq = None + accsign = 0 + if keytup in sharp_key_seq: + accsign = 1 + key_count = sharp_key_seq.index (keytup) + accseq = map (lambda x: (4*x -1 ) % 7, range (1, key_count + 1)) + + elif keytup in flat_key_seq: + accsign = -1 + key_count = flat_key_seq.index (keytup) + accseq = map (lambda x: (3*x + 3 ) % 7, range (1, key_count + 1)) + else: + error ("Huh?") + raise Exception ("Huh") + + key_table = [0] * 7 + for a in accseq: + key_table[a] = key_table[a] + accsign + + return key_table tup_lookup = { - '2' : '3/2', - '3' : '2/3', - '4' : '4/3', - '5' : '4/5', - '6' : '4/6', - '7' : '6/7', - '9' : '8/9', - } + '2' : '3/2', + '3' : '2/3', + '4' : '4/3', + '5' : '4/5', + '6' : '4/6', + '7' : '6/7', + '9' : '8/9', + } def try_parse_tuplet_begin (str, state): - if re.match ('\([2-9]', str): - dig = str[1] - str = str[2:] - prev_tuplet_state = state.parsing_tuplet - state.parsing_tuplet = string.atoi (dig[0]) - if prev_tuplet_state: - voices_append ("}") - voices_append ("\\times %s {" % tup_lookup[dig]) - return str + if re.match ('\([2-9]', str): + dig = str[1] + str = str[2:] + prev_tuplet_state = state.parsing_tuplet + state.parsing_tuplet = int (dig[0]) + if prev_tuplet_state: + voices_append ("}") + voices_append ("\\times %s {" % tup_lookup[dig]) + return str def try_parse_group_end (str, state): - if str and str[0] in HSPACE: - str = str[1:] - close_beam_state(state) - return str - + if str and str[0] in HSPACE: + str = str[1:] + close_beam_state(state) + return str + def header_append (key, a): - s = '' - if header.has_key (key): - s = header[key] + "\n" - header [key] = s + a + s = '' + if header.has_key (key): + s = header[key] + "\n" + header [key] = s + a def wordwrap(a, v): - linelen = len (v) - string.rfind(v, '\n') - if linelen + len (a) > 80: - v = v + '\n' - return v + a + ' ' + linelen = len (v) - v.rfind ('\n') + if linelen + len (a) > 80: + v = v + '\n' + return v + a + ' ' def stuff_append (stuff, idx, a): - if not stuff: - stuff.append (a) - else: - stuff [idx] = wordwrap(a, stuff[idx]) + if not stuff: + stuff.append (a) + else: + stuff [idx] = wordwrap(a, stuff[idx]) # ignore wordwrap since we are adding to the previous word def stuff_append_back(stuff, idx, a): - if not stuff: - stuff.append (a) - else: - point = len(stuff[idx])-1 - while stuff[idx][point] is ' ': - point = point - 1 - point = point +1 - stuff[idx] = stuff[idx][:point] + a + stuff[idx][point:] + if not stuff: + stuff.append (a) + else: + point = len(stuff[idx])-1 + while stuff[idx][point] is ' ': + point = point - 1 + point = point +1 + stuff[idx] = stuff[idx][:point] + a + stuff[idx][point:] def voices_append(a): - if current_voice_idx < 0: - select_voice ('default', '') - stuff_append (voices, current_voice_idx, a) + if current_voice_idx < 0: + select_voice ('default', '') + stuff_append (voices, current_voice_idx, a) # word wrap really makes it hard to bind beams to the end of notes since it # pushes out whitespace on every call. The _back functions do an append # prior to the last space, effectively tagging whatever they are given # onto the last note def voices_append_back(a): - if current_voice_idx < 0: - select_voice ('default', '') - stuff_append_back(voices, current_voice_idx, a) + if current_voice_idx < 0: + select_voice ('default', '') + stuff_append_back(voices, current_voice_idx, a) def repeat_prepend(): - global repeat_state - if current_voice_idx < 0: - select_voice ('default', '') - if not using_old: - repeat_state[current_voice_idx] = 't' + global repeat_state + if current_voice_idx < 0: + select_voice ('default', '') + if not using_old: + repeat_state[current_voice_idx] = 't' + - def lyrics_append(a): - a = re.sub ('#', '\\#', a) # latex does not like naked #'s - a = re.sub ('"', '\\"', a) # latex does not like naked "'s - a = '\t{ "' + a + '" }\n' - stuff_append (lyrics, current_lyric_idx, a) + a = re.sub ('#', '\\#', a) # latex does not like naked #'s + a = re.sub ('"', '\\"', a) # latex does not like naked "'s + a = ' \\line { "' + a + '" }\n' + stuff_append (lyrics, current_lyric_idx, a) # break lyrics to words and put "'s around words containing numbers and '"'s def fix_lyric(str): - ret = '' - while str != '': - m = re.match('[ \t]*([^ \t]*)[ \t]*(.*$)', str) - if m: - word = m.group(1) - str = m.group(2) - word = re.sub('"', '\\"', word) # escape " - if re.match('.*[0-9"\(]', word): - word = re.sub('_', ' ', word) # _ causes probs inside "" - ret = ret + '\"' + word + '\" ' - else: - ret = ret + word + ' ' - else: - return (ret) - return (ret) + ret = '' + while str != '': + m = re.match('[ \t]*([^ \t]*)[ \t]*(.*$)', str) + if m: + word = m.group(1) + str = m.group(2) + word = re.sub('"', '\\"', word) # escape " + if re.match('.*[0-9"\(]', word): + word = re.sub('_', ' ', word) # _ causes probs inside "" + ret = ret + '\"' + word + '\" ' + else: + ret = ret + word + ' ' + else: + return (ret) + return (ret) def slyrics_append(a): - a = re.sub ( '_', ' _ ', a) # _ to ' _ ' - a = re.sub ( '-', '- ', a) # split words with - - a = re.sub ( '\\\\- ', '-', a) # unless \- - a = re.sub ( '~', '_', a) # ~ to space('_') - a = re.sub ( '\*', '_ ', a) # * to to space - a = re.sub ( '#', '\\#', a) # latex does not like naked #'s - if re.match('.*[0-9"\(]', a): # put numbers and " and ( into quoted string - a = fix_lyric(a) - a = re.sub ( '$', ' ', a) # insure space between lines - __main__.lyric_idx = lyric_idx + 1 - if len(slyrics[current_voice_idx]) <= lyric_idx: - slyrics[current_voice_idx].append(a) - else: - v = slyrics[current_voice_idx][lyric_idx] - slyrics[current_voice_idx][lyric_idx] = wordwrap(a, slyrics[current_voice_idx][lyric_idx]) + a = re.sub ( '_', ' _ ', a) # _ to ' _ ' + a = re.sub ( '([^-])-([^-])', '\\1- \\2', a) # split words with "-" unless was originally "--" + a = re.sub ( '\\\\- ', '-', a) # unless \- + a = re.sub ( '~', '_', a) # ~ to space('_') + a = re.sub ( '\*', '_ ', a) # * to to space + a = re.sub ( '#', '\\#', a) # latex does not like naked #'s + if re.match('.*[0-9"\(]', a): # put numbers and " and ( into quoted string + a = fix_lyric(a) + a = re.sub ( '$', ' ', a) # insure space between lines + __main__.lyric_idx = lyric_idx + 1 + if len(slyrics[current_voice_idx]) <= lyric_idx: + slyrics[current_voice_idx].append(a) + else: + v = slyrics[current_voice_idx][lyric_idx] + slyrics[current_voice_idx][lyric_idx] = wordwrap(a, slyrics[current_voice_idx][lyric_idx]) def try_parse_header_line (ln, state): - global length_specified - m = re.match ('^([A-Za-z]): *(.*)$', ln) - - if m: - g =m.group (1) - a = m.group (2) - if g == 'T': #title - a = re.sub('[ \t]*$','', a) #strip trailing blanks - if header.has_key('title'): - if a: - if len(header['title']): - # the non-ascii character - # in the string below is a - # punctuation dash. (TeX ---) - header['title'] = header['title'] + ' — ' + a - else: - header['subtitle'] = a - else: - header['title'] = a - if g == 'M': # Meter - if a == 'C': - if not state.common_time: - state.common_time = 1 - voices_append (" \\override Staff.TimeSignature #\'style = #'C\n") - a = '4/4' - if a == 'C|': - if not state.common_time: - state.common_time = 1 - voices_append ("\\override Staff.TimeSignature #\'style = #'C\n") - a = '2/2' - if not length_specified: - set_default_len_from_time_sig (a) - else: - length_specified = 0 - if not a == 'none': - voices_append ('\\time %s' % a) - state.next_bar = '' - if g == 'K': # KEY - a = check_clef(a) - if a: - m = re.match ('^([^ \t]*) *(.*)$', a) # seperate clef info - if m: - # there may or may not be a space - # between the key letter and the mode - if key_lookup.has_key(m.group(2)[0:3]): - key_info = m.group(1) + m.group(2)[0:3] - clef_info = m.group(2)[4:] - else: - key_info = m.group(1) - clef_info = m.group(2) - __main__.global_key = compute_key (key_info) - k = lily_key (key_info) - if k: - voices_append ('\\key %s' % k) - check_clef(clef_info) - else: - __main__.global_key = compute_key (a) - k = lily_key (a) - if k: - voices_append ('\\key %s \\major' % k) - if g == 'N': # Notes - header ['footnotes'] = header['footnotes'] + '\\\\\\\\' + a - if g == 'O': # Origin - header ['origin'] = a - if g == 'X': # Reference Number - header ['crossRefNumber'] = a - if g == 'A': # Area - header ['area'] = a - if g == 'H': # History - header_append ('history', a) - if g == 'B': # Book - header ['book'] = a - if g == 'C': # Composer - if header.has_key('composer'): - if a: - header['composer'] = header['composer'] + '\\\\\\\\' + a - else: - header['composer'] = a - if g == 'S': - header ['subtitle'] = a - if g == 'L': # Default note length - set_default_length (ln) - if g == 'V': # Voice - voice = re.sub (' .*$', '', a) - rest = re.sub ('^[^ \t]* *', '', a) - if state.next_bar: - voices_append(state.next_bar) - state.next_bar = '' - select_voice (voice, rest) - if g == 'W': # Words - lyrics_append(a) - if g == 'w': # vocals - slyrics_append (a) - if g == 'Q': #tempo - try_parse_q (a) - return '' - return ln + global length_specified + m = re.match ('^([A-Za-z]): *(.*)$', ln) + + if m: + g =m.group (1) + a = m.group (2) + if g == 'T': #title + a = re.sub('[ \t]*$','', a) #strip trailing blanks + if header.has_key('title'): + if a: + if len(header['title']): + # the non-ascii character + # in the string below is a + # punctuation dash. (TeX ---) + header['title'] = header['title'] + ' — ' + a + else: + header['subtitle'] = a + else: + header['title'] = a + if g == 'M': # Meter + if a == 'C': + if not state.common_time: + state.common_time = 1 + voices_append (" \\override Staff.TimeSignature #'style = #'C\n") + a = '4/4' + if a == 'C|': + if not state.common_time: + state.common_time = 1 + voices_append ("\\override Staff.TimeSignature #'style = #'C\n") + a = '2/2' + if not length_specified: + set_default_len_from_time_sig (a) + else: + length_specified = 0 + if not a == 'none': + voices_append ('\\time %s' % a) + state.next_bar = '' + if g == 'K': # KEY + a = check_clef(a) + if a: + m = re.match ('^([^ \t]*) *([^ ]*)( *)(.*)$', a) # separate clef info + if m: + # there may or may not be a space + # between the key letter and the mode + # convert the mode to lower-case before comparing + mode = m.group(2)[0:3].lower(); + if key_lookup.has_key(mode): + # use the full mode, not only the first three letters + key_info = m.group(1) + m.group(2).lower() + clef_info = a[m.start(4):] + else: + key_info = m.group(1) + clef_info = a[m.start(2):] + __main__.global_key = compute_key (key_info) + k = lily_key (key_info) + if k: + voices_append ('\\key %s' % k) + check_clef(clef_info) + else: + __main__.global_key = compute_key (a) + k = lily_key (a) + if k: + voices_append ('\\key %s \\major' % k) + if g == 'N': # Notes + header ['footnotes'] = header['footnotes'] + '\\\\\\\\' + a + if g == 'O': # Origin + header ['origin'] = a + if g == 'X': # Reference Number + header ['crossRefNumber'] = a + if g == 'A': # Area + header ['area'] = a + if g == 'H': # History + header_append ('history', a) + if g == 'B': # Book + header ['book'] = a + if g == 'C': # Composer + if header.has_key('composer'): + if a: + header['composer'] = header['composer'] + '\\\\\\\\' + a + else: + header['composer'] = a + if g == 'S': + header ['subtitle'] = a + if g == 'L': # Default note length + set_default_length (ln) + if g == 'V': # Voice + voice = re.sub (' .*$', '', a) + rest = re.sub ('^[^ \t]* *', '', a) + if state.next_bar: + voices_append(state.next_bar) + state.next_bar = '' + select_voice (voice, rest) + if g == 'W': # Words + lyrics_append(a) + if g == 'w': # vocals + slyrics_append (a) + if g == 'Q': # tempo + try_parse_q (a) + if g == 'R': # Rhythm (e.g. jig, reel, hornpipe) + header['meter'] = a + if g == 'Z': # Transcription (e.g. Steve Mansfield 1/2/2000) + header['transcription'] = a + return '' + return ln # we use in this order specified accidental, active accidental for bar, # active accidental for key def pitch_to_lilypond_name (name, acc, bar_acc, key): - s = '' - if acc == UNDEF: - if not nobarlines: - acc = bar_acc - if acc == UNDEF: - acc = key - if acc == -1: - s = 'es' - elif acc == 1: - s = 'is' - - if name > 4: - name = name -7 - return(chr (name + ord('c')) + s) + s = '' + if acc == UNDEF: + if not nobarlines: + acc = bar_acc + if acc == UNDEF: + acc = key + if acc == -1: + s = 'es' + elif acc == 1: + s = 'is' + + if name > 4: + name = name -7 + return(chr (name + ord('c')) + s) def octave_to_lilypond_quotes (o): - o = o + 2 - s ='' - if o < 0: - o = -o - s=',' - else: - s ='\'' + o = o + 2 + s ='' + if o < 0: + o = -o + s=',' + else: + s ='\'' - return s * o + return s * o def parse_num (str): - durstr = '' - while str and str[0] in DIGITS: - durstr = durstr + str[0] - str = str[1:] + durstr = '' + while str and str[0] in DIGITS: + durstr = durstr + str[0] + str = str[1:] - n = None - if durstr: - n =string.atoi (durstr) - return (str,n) + n = None + if durstr: + n = int (durstr) + return (str,n) def duration_to_lilypond_duration (multiply_tup, defaultlen, dots): - base = 1 - # (num / den) / defaultlen < 1/base - while base * multiply_tup[0] < multiply_tup[1]: - base = base * 2 - if base == 1: - if (multiply_tup[0] / multiply_tup[1]) == 2: - base = '\\breve' - if (multiply_tup[0] / multiply_tup[1]) == 3: - base = '\\breve' - dots = 1 - if (multiply_tup[0] / multiply_tup[1]) == 4: - base = '\longa' - return '%s%s' % ( base, '.'* dots) + base = 1 + # (num / den) / defaultlen < 1/base + while base * multiply_tup[0] < multiply_tup[1]: + base = base * 2 + if base == 1: + if (multiply_tup[0] / multiply_tup[1]) == 2: + base = '\\breve' + if (multiply_tup[0] / multiply_tup[1]) == 3: + base = '\\breve' + dots = 1 + if (multiply_tup[0] / multiply_tup[1]) == 4: + base = '\\longa' + return '%s%s' % ( base, '.'* dots) class Parser_state: - def __init__ (self): - self.in_acc = {} - self.next_articulation = '' - self.next_bar = '' - self.next_dots = 0 - self.next_den = 1 - self.parsing_tuplet = 0 - self.plus_chord = 0 - self.base_octave = 0 - self.common_time = 0 - self.parsing_beam = 0 + def __init__ (self): + self.in_acc = {} + self.next_articulation = '' + self.next_bar = '' + self.next_dots = 0 + self.next_den = 1 + self.parsing_tuplet = 0 + self.plus_chord = 0 + self.base_octave = 0 + self.common_time = 0 + self.parsing_beam = 0 -# return (str, num,den,dots) +# return (str, num,den,dots) def parse_duration (str, parser_state): - num = 0 - den = parser_state.next_den - parser_state.next_den = 1 - - (str, num) = parse_num (str) - if not num: - num = 1 - if len(str): - if str[0] == '/': - if len(str[0]): - while str[:1] == '/': - str= str[1:] - d = 2 - if str[0] in DIGITS: - (str, d) =parse_num (str) - - den = den * d - - den = den * default_len - - current_dots = parser_state.next_dots - parser_state.next_dots = 0 - if re.match ('[ \t]*[<>]', str): - while str[0] in HSPACE: - str = str[1:] - while str[0] == '>': - str = str [1:] - current_dots = current_dots + 1 - parser_state.next_den = parser_state.next_den * 2 - - while str[0] == '<': - str = str [1:] - den = den * 2 - parser_state.next_dots = parser_state.next_dots + 1 - - - - try_dots = [3, 2, 1] - for d in try_dots: - f = 1 << d - multiplier = (2*f-1) - if num % multiplier == 0 and den % f == 0: - num = num / multiplier - den = den / f - current_dots = current_dots + d - - return (str, num,den,current_dots) + num = 0 + den = parser_state.next_den + parser_state.next_den = 1 + + (str, num) = parse_num (str) + if not num: + num = 1 + if len(str): + if str[0] == '/': + if len(str[0]): + while str[:1] == '/': + str= str[1:] + d = 2 + if str[0] in DIGITS: + (str, d) =parse_num (str) + + den = den * d + + den = den * default_len + + current_dots = parser_state.next_dots + parser_state.next_dots = 0 + if re.match ('[ \t]*[<>]', str): + while str[0] in HSPACE: + str = str[1:] + while str[0] == '>': + str = str [1:] + current_dots = current_dots + 1 + parser_state.next_den = parser_state.next_den * 2 + + while str[0] == '<': + str = str [1:] + den = den * 2 + parser_state.next_dots = parser_state.next_dots + 1 + + + + try_dots = [3, 2, 1] + for d in try_dots: + f = 1 << d + multiplier = (2*f-1) + if num % multiplier == 0 and den % f == 0: + num = num / multiplier + den = den / f + current_dots = current_dots + d + + return (str, num,den,current_dots) def try_parse_rest (str, parser_state): - if not str or str[0] <> 'z' and str[0] <> 'x': - return str + if not str or str[0] <> 'z' and str[0] <> 'x': + return str - __main__.lyric_idx = -1 + __main__.lyric_idx = -1 - if parser_state.next_bar: - voices_append(parser_state.next_bar) - parser_state.next_bar = '' + if parser_state.next_bar: + voices_append(parser_state.next_bar) + parser_state.next_bar = '' - if str[0] == 'z': - rest = 'r' - else: - rest = 's' - str = str[1:] + if str[0] == 'z': + rest = 'r' + else: + rest = 's' + str = str[1:] - (str, num,den,d) = parse_duration (str, parser_state) - voices_append ('%s%s' % (rest, duration_to_lilypond_duration ((num,den), default_len, d))) - if parser_state.next_articulation: - voices_append (parser_state.next_articulation) - parser_state.next_articulation = '' + (str, num,den,d) = parse_duration (str, parser_state) + voices_append ('%s%s' % (rest, duration_to_lilypond_duration ((num,den), default_len, d))) + if parser_state.next_articulation: + voices_append (parser_state.next_articulation) + parser_state.next_articulation = '' - return str + return str artic_tbl = { - '.' : '-.', - 'T' : '^\\trill', - 'H' : '^\\fermata', - 'u' : '^\\upbow', - 'K' : '^\\ltoe', - 'k' : '^\\accent', - 'M' : '^\\tenuto', - '~' : '^"~" ', - 'J' : '', # ignore slide - 'R' : '', # ignore roll - 'v' : '^\\downbow' + '.' : '-.', + 'T' : '^\\trill', + 'H' : '^\\fermata', + 'u' : '^\\upbow', + 'K' : '^\\ltoe', + 'k' : '^\\accent', + 'M' : '^\\tenuto', + '~' : '^"~" ', + 'J' : '', # ignore slide + 'R' : '', # ignore roll + 'S' : '^\\segno', + 'O' : '^\\coda', + 'v' : '^\\downbow' } - + def try_parse_articulation (str, state): - while str and artic_tbl.has_key(str[:1]): - state.next_articulation = state.next_articulation + artic_tbl[str[:1]] - if not artic_tbl[str[:1]]: - sys.stderr.write("Warning: ignoring `%s'\n" % str[:1] ) - - str = str[1:] - - - - # s7m2 input doesnt care about spaces - if re.match('[ \t]*\(', str): - str = string.lstrip (str) - - slur_begin =0 - while str[:1] =='(' and str[1] not in DIGITS: - slur_begin = slur_begin + 1 - state.next_articulation = state.next_articulation + '(' - str = str[1:] - - return str - + while str and artic_tbl.has_key(str[:1]): + state.next_articulation = state.next_articulation + artic_tbl[str[:1]] + if not artic_tbl[str[:1]]: + sys.stderr.write("Warning: ignoring `%s'\n" % str[:1] ) + + str = str[1:] + + + + # s7m2 input doesn't care about spaces + if re.match('[ \t]*\(', str): + str = str.lstrip () + + slur_begin =0 + while str[:1] =='(' and str[1] not in DIGITS: + slur_begin = slur_begin + 1 + state.next_articulation = state.next_articulation + '(' + str = str[1:] + + return str + # # remember accidental for rest of bar # def set_bar_acc(note, octave, acc, state): - if acc == UNDEF: - return - n_oct = note + octave * 7 - state.in_acc[n_oct] = acc + if acc == UNDEF: + return + n_oct = note + octave * 7 + state.in_acc[n_oct] = acc # get accidental set in this bar or UNDEF if not set def get_bar_acc(note, octave, state): - n_oct = note + octave * 7 - if state.in_acc.has_key(n_oct): - return(state.in_acc[n_oct]) - else: - return(UNDEF) + n_oct = note + octave * 7 + if state.in_acc.has_key(n_oct): + return(state.in_acc[n_oct]) + else: + return(UNDEF) def clear_bar_acc(state): - for k in state.in_acc.keys(): - del state.in_acc[k] - + state.in_acc = {} + # if we are parsing a beam, close it off def close_beam_state(state): - if state.parsing_beam and preserve_beams: - state.parsing_beam = 0 - voices_append_back( ']' ) + if state.parsing_beam and global_options.beams: + state.parsing_beam = 0 + voices_append_back( ']' ) + - # WAT IS ABC EEN ONTZETTENDE PROGRAMMEERPOEP ! def try_parse_note (str, parser_state): - mud = '' - - slur_begin =0 - if not str: - return str - - articulation ='' - acc = UNDEF - if str[0] in '^=_': - c = str[0] - str = str[1:] - if c == '^': - acc = 1 - if c == '=': - acc = 0 - if c == '_': - acc = -1 - - octave = parser_state.base_octave - if str[0] in "ABCDEFG": - str = string.lower (str[0]) + str[1:] - octave = octave - 1 - - - notename = 0 - if str[0] in "abcdefg": - notename = (ord(str[0]) - ord('a') + 5)%7 - str = str[1:] - else: - return str # failed; not a note! - - - __main__.lyric_idx = -1 - - if parser_state.next_bar: - voices_append(parser_state.next_bar) - parser_state.next_bar = '' - - while str[0] == ',': - octave = octave - 1 - str = str[1:] - while str[0] == '\'': - octave = octave + 1 - str = str[1:] - - (str, num,den,current_dots) = parse_duration (str, parser_state) - - if re.match('[ \t]*\)', str): - str = string.lstrip (str) - - slur_end =0 - while str[:1] ==')': - slur_end = slur_end + 1 - str = str[1:] - - - bar_acc = get_bar_acc(notename, octave, parser_state) - pit = pitch_to_lilypond_name(notename, acc, bar_acc, global_key[notename]) - oct = octave_to_lilypond_quotes (octave) - if acc != UNDEF and (acc == global_key[notename] or acc == bar_acc): - mod='!' - else: - mod = '' - voices_append ("%s%s%s%s" % - (pit, oct, mod, - duration_to_lilypond_duration ((num,den), default_len, current_dots))) - - set_bar_acc(notename, octave, acc, parser_state) - if parser_state.next_articulation: - articulation = articulation + parser_state.next_articulation - parser_state.next_articulation = '' - - voices_append (articulation) - - if parser_state.parsing_tuplet: - parser_state.parsing_tuplet = parser_state.parsing_tuplet - 1 - if not parser_state.parsing_tuplet: - voices_append ("}") - if slur_begin: - voices_append ('-(' * slur_begin ) - if slur_end: - voices_append ('-)' *slur_end ) - - if preserve_beams and \ - str[0] in '^=_ABCDEFGabcdefg' and \ - not parser_state.parsing_beam and \ - not parser_state.parsing_tuplet: - parser_state.parsing_beam = 1 - voices_append_back( '[' ) - - return str + mud = '' + + slur_begin =0 + if not str: + return str + + articulation ='' + acc = UNDEF + if str[0] in '^=_': + c = str[0] + str = str[1:] + if c == '^': + acc = 1 + if c == '=': + acc = 0 + if c == '_': + acc = -1 + + octave = parser_state.base_octave + if str[0] in "ABCDEFG": + str = str[0].lower () + str[1:] + octave = octave - 1 + + + notename = 0 + if str[0] in "abcdefg": + notename = (ord(str[0]) - ord('a') + 5)%7 + str = str[1:] + else: + return str # failed; not a note! + + + __main__.lyric_idx = -1 + + if parser_state.next_bar: + voices_append(parser_state.next_bar) + parser_state.next_bar = '' + + while str[0] == ',': + octave = octave - 1 + str = str[1:] + while str[0] == '\'': + octave = octave + 1 + str = str[1:] + + (str, num,den,current_dots) = parse_duration (str, parser_state) + + if re.match('[ \t]*\)', str): + str = str.lstrip () + + slur_end =0 + while str[:1] ==')': + slur_end = slur_end + 1 + str = str[1:] + + + bar_acc = get_bar_acc(notename, octave, parser_state) + pit = pitch_to_lilypond_name(notename, acc, bar_acc, global_key[notename]) + oct = octave_to_lilypond_quotes (octave) + if acc != UNDEF and (acc == global_key[notename] or acc == bar_acc): + mod='!' + else: + mod = '' + voices_append ("%s%s%s%s" % + (pit, oct, mod, + duration_to_lilypond_duration ((num,den), default_len, current_dots))) + + set_bar_acc(notename, octave, acc, parser_state) + if parser_state.next_articulation: + articulation = articulation + parser_state.next_articulation + parser_state.next_articulation = '' + + voices_append (articulation) + + if slur_begin: + voices_append ('-(' * slur_begin ) + if slur_end: + voices_append ('-)' *slur_end ) + + if parser_state.parsing_tuplet: + parser_state.parsing_tuplet = parser_state.parsing_tuplet - 1 + if not parser_state.parsing_tuplet: + voices_append ("}") + + if global_options.beams and \ + str[0] in '^=_ABCDEFGabcdefg' and \ + not parser_state.parsing_beam and \ + not parser_state.parsing_tuplet: + parser_state.parsing_beam = 1 + voices_append_back( '[' ) + + return str def junk_space (str,state): - while str and str[0] in '\t\n\r ': - str = str[1:] - close_beam_state(state) + while str and str[0] in '\t\n\r ': + str = str[1:] + close_beam_state(state) - return str + return str def try_parse_guitar_chord (str, state): - if str[:1] =='"': - str = str[1:] - gc = '' - if str[0] == '_' or (str[0] == '^'): - position = str[0] - str = str[1:] - else: - position = '^' - while str and str[0] != '"': - gc = gc + str[0] - str = str[1:] - - if str: - str = str[1:] - gc = re.sub('#', '\\#', gc) # escape '#'s - state.next_articulation = ("%c\"%s\"" % (position, gc)) \ - + state.next_articulation - return str + if str[:1] =='"': + str = str[1:] + gc = '' + if str[0] == '_' or (str[0] == '^'): + position = str[0] + str = str[1:] + else: + position = '^' + while str and str[0] != '"': + gc = gc + str[0] + str = str[1:] + + if str: + str = str[1:] + gc = re.sub('#', '\\#', gc) # escape '#'s + state.next_articulation = ("%c\"%s\"" % (position, gc)) \ + + state.next_articulation + return str def try_parse_escape (str): - if not str or str [0] != '\\': - return str - - str = str[1:] - if str[:1] =='K': - key_table = compute_key () - return str + if not str or str [0] != '\\': + return str + + str = str[1:] + if str[:1] =='K': + key_table = compute_key () + return str # # |] thin-thick double bar line @@ -1074,26 +1128,26 @@ old_bar_dict = { '|]' : '|.', '||' : '||', '[|' : '||', -':|' : ':|', +':|' : ':|.', '|:' : '|:', -'::' : ':|:', +'::' : ':|.|:', '|1' : '|', '|2' : '|', -':|2' : ':|', +':|2' : ':|.', '|' : '|' } bar_dict = { - '|]' : '\\bar "|."', - '||' : '\\bar "||"', - '[|' : '\\bar "||"', - ':|' : '}', - '|:' : '\\repeat volta 2 {', - '::' : '} \\repeat volta 2 {', - '|1' : '} \\alternative{{', - '|2' : '} {', - ':|2' : '} {', - '|' : '\\bar "|"' - } +'|]' : '\\bar "|."', +'||' : '\\bar "||"', +'[|' : '\\bar "||"', +':|' : '}', +'|:' : '\\repeat volta 2 {', +'::' : '} \\repeat volta 2 {', +'|1' : '} \\alternative{{', +'|2' : '} {', +':|2' : '} {', +'|' : '\\bar "|"' + } warn_about = ['|:', '::', ':|', '|1', ':|2', '|2'] @@ -1105,137 +1159,140 @@ doing_alternative = [''] * 8 using_old = '' def try_parse_bar (str,state): - global in_repeat, doing_alternative, using_old - do_curly = '' - bs = None - if current_voice_idx < 0: - select_voice ('default', '') - # first try the longer one - for trylen in [3,2,1]: - if str[:trylen] and bar_dict.has_key (str[:trylen]): - s = str[:trylen] - if using_old: - bs = "\\bar \"%s\"" % old_bar_dict[s] - else: - bs = "%s" % bar_dict[s] - str = str[trylen:] - if s in alternative_opener: - if not in_repeat[current_voice_idx]: - using_old = 't' - bs = "\\bar \"%s\"" % old_bar_dict[s] - else: - doing_alternative[current_voice_idx] = 't' - - if s in repeat_ender: - if not in_repeat[current_voice_idx]: - sys.stderr.write("Warning: inserting repeat to beginning of notes.\n") - repeat_prepend() - in_repeat[current_voice_idx] = '' - else: - if doing_alternative[current_voice_idx]: - do_curly = 't' - if using_old: - bs = "\\bar \"%s\"" % old_bar_dict[s] - else: - bs = bar_dict[s] - doing_alternative[current_voice_idx] = '' - in_repeat[current_voice_idx] = '' - if s in repeat_opener: - in_repeat[current_voice_idx] = 't' - if using_old: - bs = "\\bar \"%s\"" % old_bar_dict[s] - else: - bs = bar_dict[s] - break - if str[:1] == '|': - state.next_bar = '|\n' - str = str[1:] - clear_bar_acc(state) - close_beam_state(state) - - if bs <> None or state.next_bar != '': - if state.parsing_tuplet: - state.parsing_tuplet =0 - voices_append ('} ') - - if bs <> None: - clear_bar_acc(state) - close_beam_state(state) - voices_append (bs) - if do_curly != '': - voices_append("} }") - do_curly = '' - return str + global in_repeat, doing_alternative, using_old + do_curly = '' + bs = None + if current_voice_idx < 0: + select_voice ('default', '') + # first try the longer one + for trylen in [3,2,1]: + if str[:trylen] and bar_dict.has_key (str[:trylen]): + s = str[:trylen] + if using_old: + bs = "\\bar \"%s\"" % old_bar_dict[s] + else: + bs = "%s" % bar_dict[s] + str = str[trylen:] + if s in alternative_opener: + if not in_repeat[current_voice_idx]: + using_old = 't' + bs = "\\bar \"%s\"" % old_bar_dict[s] + else: + doing_alternative[current_voice_idx] = 't' + + if s in repeat_ender: + if not in_repeat[current_voice_idx]: + sys.stderr.write("Warning: inserting repeat to beginning of notes.\n") + repeat_prepend() + in_repeat[current_voice_idx] = '' + else: + if doing_alternative[current_voice_idx]: + do_curly = 't' + if using_old: + bs = "\\bar \"%s\"" % old_bar_dict[s] + else: + bs = bar_dict[s] + doing_alternative[current_voice_idx] = '' + in_repeat[current_voice_idx] = '' + if s in repeat_opener: + in_repeat[current_voice_idx] = 't' + if using_old: + bs = "\\bar \"%s\"" % old_bar_dict[s] + else: + bs = bar_dict[s] + break + if str[:1] == '|': + state.next_bar = '|\n' + str = str[1:] + clear_bar_acc(state) + close_beam_state(state) + + if str[:1] == '}': + close_beam_state(state) + + if bs <> None or state.next_bar != '': + if state.parsing_tuplet: + state.parsing_tuplet =0 + voices_append ('} ') + + if bs <> None: + clear_bar_acc(state) + close_beam_state(state) + voices_append (bs) + if do_curly != '': + voices_append("} ") + do_curly = '' + return str def try_parse_tie (str): - if str[:1] =='-': - str = str[1:] - voices_append (' ~ ') - return str + if str[:1] =='-': + str = str[1:] + voices_append (' ~ ') + return str def bracket_escape (str, state): - m = re.match ( '^([^\]]*)] *(.*)$', str) - if m: - cmd = m.group (1) - str = m.group (2) - try_parse_header_line (cmd, state) - return str + m = re.match ( '^([^\]]*)] *(.*)$', str) + if m: + cmd = m.group (1) + str = m.group (2) + try_parse_header_line (cmd, state) + return str def try_parse_chord_delims (str, state): - if str[:1] =='[': - str = str[1:] - if re.match('[A-Z]:', str): # bracket escape - return bracket_escape(str, state) - if state.next_bar: - voices_append(state.next_bar) - state.next_bar = '' - voices_append ('<<') - - if str[:1] == '+': - str = str[1:] - if state.plus_chord: - voices_append ('>>') - state.plus_chord = 0 - else: - if state.next_bar: - voices_append(state.next_bar) - state.next_bar = '' - voices_append ('<<') - state.plus_chord = 1 - - ch = '' - if str[:1] ==']': - str = str[1:] - ch = '>>' - - end = 0 - while str[:1] ==')': - end = end + 1 - str = str[1:] - - - voices_append ("\\spanrequest \\stop \"slur\"" * end) - voices_append (ch) - return str + if str[:1] =='[': + str = str[1:] + if re.match('[A-Z]:', str): # bracket escape + return bracket_escape(str, state) + if state.next_bar: + voices_append(state.next_bar) + state.next_bar = '' + voices_append ('<<') + + if str[:1] == '+': + str = str[1:] + if state.plus_chord: + voices_append ('>>') + state.plus_chord = 0 + else: + if state.next_bar: + voices_append(state.next_bar) + state.next_bar = '' + voices_append ('<<') + state.plus_chord = 1 + + ch = '' + if str[:1] ==']': + str = str[1:] + ch = '>>' + + end = 0 + while str[:1] ==')': + end = end + 1 + str = str[1:] + + + voices_append ("\\spanrequest \\stop \"slur\"" * end) + voices_append (ch) + return str def try_parse_grace_delims (str, state): - if str[:1] =='{': - if state.next_bar: - voices_append(state.next_bar) - state.next_bar = '' - str = str[1:] - voices_append ('\\grace { ') + if str[:1] =='{': + if state.next_bar: + voices_append(state.next_bar) + state.next_bar = '' + str = str[1:] + voices_append ('\\grace { ') - if str[:1] =='}': - str = str[1:] - voices_append ('}') + if str[:1] =='}': + str = str[1:] + voices_append ('}') - return str + return str def try_parse_comment (str): - global nobarlines - if (str[0] == '%'): - if str[0:5] == '%MIDI': + global nobarlines + if (str[0] == '%'): + if str[0:5] == '%MIDI': #the nobarlines option is necessary for an abc to lilypond translator for #exactly the same reason abc2midi needs it: abc requires the user to enter #the note that will be printed, and MIDI and lilypond expect entry of the @@ -1248,153 +1305,154 @@ def try_parse_comment (str): #Since ABC is frequently used for music in styles that do not use this #convention, such as most music written before 1700, or ethnic music in #non-western scales, it is necessary to be able to tell a translator that -#the barlines should not affect its interpretation of the pitch. - if (string.find(str,'nobarlines') > 0): - nobarlines = 1 - elif str[0:3] == '%LY': - p = string.find(str, 'voices') - if (p > -1): - voices_append(str[p+7:]) - voices_append("\n") - p = string.find(str, 'slyrics') - if (p > -1): - slyrics_append(str[p+8:]) - -#write other kinds of appending if we ever need them. - return str +#the barlines should not affect its interpretation of the pitch. + if 'nobarlines' in str: + nobarlines = 1 + elif str[0:3] == '%LY': + p = str.find ('voices') + if (p > -1): + voices_append(str[p+7:]) + voices_append("\n") + p = str.find ('slyrics') + if (p > -1): + slyrics_append(str[p+8:]) + +#write other kinds of appending if we ever need them. + return str lineno = 0 happy_count = 100 def parse_file (fn): - f = open (fn) - ls = f.readlines () - ls = map (lambda x: re.sub ("\r$", '', x), ls) - - select_voice('default', '') - global lineno - lineno = 0 - sys.stderr.write ("Line ... ") - sys.stderr.flush () - __main__.state = state_list[current_voice_idx] - - for ln in ls: - lineno = lineno + 1 - - if not (lineno % happy_count): - sys.stderr.write ('[%d]'% lineno) - sys.stderr.flush () - m = re.match ('^([^%]*)%(.*)$',ln) # add comments to current voice - if m: - if m.group(2): - try_parse_comment(m.group(2)) - voices_append ('%% %s\n' % m.group(2)) - ln = m.group (1) - - orig_ln = ln - - ln = try_parse_header_line (ln, state) - - # Try nibbling characters off until the line doesn't change. - prev_ln = '' - while ln != prev_ln: - prev_ln = ln - ln = try_parse_chord_delims (ln, state) - ln = try_parse_rest (ln, state) - ln = try_parse_articulation (ln,state) - ln = try_parse_note (ln, state) - ln = try_parse_bar (ln, state) - ln = try_parse_tie (ln) - ln = try_parse_escape (ln) - ln = try_parse_guitar_chord (ln, state) - ln = try_parse_tuplet_begin (ln, state) - ln = try_parse_group_end (ln, state) - ln = try_parse_grace_delims (ln, state) - ln = junk_space (ln, state) - - if ln: - error ("%s: %d: Huh? Don't understand\n" % (fn, lineno)) - left = orig_ln[0:-len (ln)] - sys.stderr.write (left + '\n') - sys.stderr.write (' ' * len (left) + ln + '\n') + f = open (fn) + ls = f.readlines () + ls = map (lambda x: re.sub ("\r$", '', x), ls) + + select_voice('default', '') + global lineno + lineno = 0 + if not global_options.quiet: + sys.stderr.write ("Line ... ") + sys.stderr.flush () + __main__.state = state_list[current_voice_idx] + + for ln in ls: + lineno = lineno + 1 + + if not (lineno % happy_count): + sys.stderr.write ('[%d]'% lineno) + sys.stderr.flush () + m = re.match ('^([^%]*)%(.*)$',ln) # add comments to current voice + if m: + if m.group(2): + try_parse_comment(m.group(2)) + voices_append ('%% %s\n' % m.group(2)) + ln = m.group (1) + + orig_ln = ln + + ln = junk_space (ln, state) + ln = try_parse_header_line (ln, state) + + # Try nibbling characters off until the line doesn't change. + prev_ln = '' + while ln != prev_ln: + prev_ln = ln + ln = try_parse_chord_delims (ln, state) + ln = try_parse_rest (ln, state) + ln = try_parse_articulation (ln,state) + ln = try_parse_note (ln, state) + ln = try_parse_bar (ln, state) + ln = try_parse_tie (ln) + ln = try_parse_escape (ln) + ln = try_parse_guitar_chord (ln, state) + ln = try_parse_tuplet_begin (ln, state) + ln = try_parse_group_end (ln, state) + ln = try_parse_grace_delims (ln, state) + ln = junk_space (ln, state) + + if ln: + error ("%s: %d: Huh? Don't understand\n" % (fn, lineno)) + left = orig_ln[0:-len (ln)] + sys.stderr.write (left + '\n') + sys.stderr.write (' ' * len (left) + ln + '\n') def identify(): - sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version)) - -def help (): - print r""" -Convert ABC to lilypond. - -Usage: abc2ly [OPTIONS]... ABC-FILE - -Options: - -h, --help print this help - -o, --output=FILE set output filename to FILE - -v, --version show version information - -s, --strict be strict about succes - -b, --beams preserve ABC's notion of beams - -This program converts ABC music files (see -http://www.gre.ac.uk/~c.walshaw/abc2mtex/abc.txt) to LilyPond input. - - -Report bugs to bug-lilypond@gnu.org. + if not global_options.quiet: + sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version)) -Written by Han-Wen Nienhuys , Laura Conrad +authors = """ +Written by Han-Wen Nienhuys , Laura Conrad , Roy Rankin . """ def print_version (): - print r"""abc2ly (GNU lilypond) %s""" % version - - - -(options, files) = getopt.getopt (sys.argv[1:], 'vo:hsb', ['help','version', 'output=', 'strict', 'beams']) -out_filename = '' - -for opt in options: - o = opt[0] - a = opt[1] - if o== '--help' or o == '-h': - help () - sys.exit (0) - elif o == '--version' or o == '-v': - print_version () - sys.exit(0) - elif o == '--strict' or o == '-s': - strict = 1 - elif o == '--output' or o == '-o': - out_filename = a - elif o == '--beams' or o == '-b': - preserve_beams = 1 - else: - print o - raise getopt.error - -identify() + print r"""abc2ly (GNU lilypond) %s""" % version + +def get_option_parser (): + p = ly.get_option_parser (usage=_ ("%s [OPTION]... FILE") % 'abc2ly', + description=_ ('''abc2ly converts ABC music files (see +%s) to LilyPond input. +''') % 'http://abcnotation.com/abc2mtex/abc.txt', + add_help_option=False) + + p.version = "abc2ly (LilyPond) @TOPLEVEL_VERSION@" + p.add_option("--version", + action="version", + help=_ ("show version number and exit")) + p.add_option("-h", "--help", + action="help", + help=_ ("show this help and exit")) + p.add_option ("-o", "--output", metavar='FILE', + action="store", + help=_ ("write output to FILE")) + p.add_option ("-s", "--strict", + action="store_true", + help=_ ("be strict about success")) + p.add_option ('-b', '--beams', + action="store_true", + help=_ ("preserve ABC's notion of beams")) + p.add_option ('-q', '--quiet', + action="store_true", + help=_ ("suppress progress messages")) + p.add_option_group ('', + description=( + _ ('Report bugs via %s') + % 'http://post.gmane.org/post.php' + '?group=gmane.comp.gnu.lilypond.bugs') + '\n') + return p + + +option_parser = get_option_parser () +(global_options, files) = option_parser.parse_args () + + +identify () header['tagline'] = 'Lily was here %s -- automatically converted from ABC' % version for f in files: - if f == '-': - f = '' + if f == '-': + f = '' - sys.stderr.write ('Parsing `%s\'...\n' % f) - parse_file (f) + if not global_options.quiet: + sys.stderr.write ('Parsing `%s\'...\n' % f) + parse_file (f) - if not out_filename: - out_filename = os.path.basename (os.path.splitext (f)[0]) + ".ly" - sys.stderr.write ('lilypond output to: `%s\'...' % out_filename) - outf = open (out_filename, 'w') + if not global_options.output: + global_options.output = os.path.basename (os.path.splitext (f)[0]) + ".ly" + if not global_options.quiet: + sys.stderr.write ('lilypond output to: `%s\'...' % global_options.output) + outf = open (global_options.output, 'w') # don't substitute @VERSION@. We want this to reflect # the last version that was verified to work. - outf.write ('\\version "2.5.20"\n') - -# dump_global (outf) - dump_header (outf, header) - dump_slyrics (outf) - dump_voices (outf) - dump_score (outf) - dump_lyrics (outf) - sys.stderr.write ('\n') - + outf.write ('\\version "2.7.40"\n') + +# dump_global (outf) + dump_header (outf, header) + dump_slyrics (outf) + dump_voices (outf) + dump_score (outf) + dump_lyrics (outf) + if not global_options.quiet: + sys.stderr.write ('\n')