--- /dev/null
+#!@PYTHON@
+
+# once upon a rainy monday afternoon.
+#
+# ...
+#
+# (not finished.)
+# ABC standard v1.6: http://www.gre.ac.uk/~c.walshaw/abc2mtex/abc.txt
+#
+
+program_name = 'abc-to-ly'
+version = '@TOPLEVEL_VERSION@'
+import __main__
+import getopt
+import sys
+import re
+import string
+try:
+ import mpz
+except:
+ sys.stderr.write ("This script needs Python 1.5.1\n")
+ sys.exit (1)
+
+
+header = {}
+lyrics = []
+voices = []
+global_voice_stuff = []
+default_len = 4
+global_key = [0] * 7 # UGH
+names = ["One", "Two", "Three"]
+DIGITS='0123456789'
+HSPACE=' \t'
+
+def gcd (a, b):
+ while a % b:
+ a,b = b, a % b
+ return b
+
+class Rational:
+ def __init__ (self, n, d = 1):
+ self.num = n
+ self.den = d
+
+ def simplify (self):
+ g = gcd (self.num, self.den)
+ self.num = self.num / g
+ self.den = self.den /g
+ if self.den < 0:
+ self.den = - self.den
+ self.num = - self.num
+
+ def __sub__ (self, other):
+ pass
+
+
+def dump_global ():
+ print ("global = \\notes{")
+ for i in global_voice_stuff:
+ print (i);
+ print ("}")
+
+
+def dump_header (hdr):
+ print '\\header {'
+ for k in hdr.keys ():
+ print '%s = "%s";\n'% (k,hdr[k])
+ print '}'
+
+def dump_lyrics ():
+ for i in range (len (lyrics)):
+ print ("verse%s = \\lyrics {" % names [i])
+ print (lyrics [i])
+ print ("}")
+
+def dump_voices ():
+ for i in range (len (voices)):
+ print ("voice%s = \\notes {" % names [i])
+ print (voices [i])
+ print ("}")
+
+def dump_score ():
+ print ("\\score{")
+ print (" \\notes<")
+ print (" \\global")
+ for i in range (len (voices)):
+ print (" \\context Staff=%s \\voice%s" %
+ (names [i], names [i]))
+ for i in range (len (lyrics)):
+ j = i
+ if j >= len (voices):
+ j = len (voices) - 1
+ print (" \\context Lyrics=%s \\rhythm \\voice%s \\verse%s" %
+ (names [i], names [j], names [i]))
+ print (" >")
+ dump_header (header)
+ #print "%%%s" % global_voice_stuff, 1
+ print ("}")
+
+def set_default_length (s):
+ m = re.search ('1/([0-9]+)', s)
+ if m:
+ __main__.default_len = string.atoi ( m.group (1))
+
+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 emty 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.
+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
+
+def fifth_above_pitch (tup):
+ (n, a) = (tup[0] + 4, tup[1])
+
+ 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
+
+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
+
+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)
+
+
+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:]
+
+ 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:
+ raise "Huh"
+
+ key_table = [0] * 7
+ for a in accseq:
+ key_table[a] = key_table[a] + accsign
+
+
+ return key_table
+
+tup_lookup = {
+ '3' : '2/3',
+ '4' : '4/3',
+ '5' : '4/5',
+ '6' : '4/6',
+ }
+
+
+def try_parse_tuplet_begin (str, state):
+ if str and str[0] in DIGITS:
+ dig = str[0]
+ str = str[1:]
+ state.parsing_tuplet = 1
+
+ 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:]
+ if state.parsing_tuplet:
+ state.parsing_tuplet = 0
+ voices_append ("}")
+ return str
+
+def header_append (key, a):
+ s = ''
+ if header.has_key (key):
+ s = header[key] + "\n"
+ header [key] = s + a
+
+def lyrics_append (a):
+ i = len (lyrics) - 1
+ if i < 0:
+ i = 0
+ if len (lyrics) <= i:
+ lyrics.append ('')
+ lyrics [i] = lyrics [i] + a + "\n"
+
+def voices_append (a):
+ i = len (voices) - 1
+ if i < 0:
+ i = 0
+ if len (voices) <= i:
+ voices.append ('')
+ voices [i] = voices [i] + a + "\n"
+
+def try_parse_header_line (ln):
+ m = re.match ('^(.): *(.*)$', ln)
+
+ if m:
+ g =m.group (1)
+ a = m.group (2)
+ a = re.sub ('"', '\\"', a)
+ if g == 'T':
+ header['title'] = a
+ if g == 'M':
+ if a == 'C':
+ a = '4/4'
+ global_voice_stuff.append ('\\time %s;' % a)
+ if g == 'K':
+ __main__.global_key =compute_key (a)# ugh.
+
+ global_voice_stuff.append ('\\key %s;' % a)
+ if g == 'O':
+ header ['origin'] = a
+ if g == 'X':
+ header ['crossRefNumber'] = a
+ if g == 'A':
+ header ['area'] = a
+ if g == 'H':
+ header_append ('history', a)
+ if g == 'B':
+ header ['book'] = a
+ if g == 'S':
+ header ['subtitle'] = a
+ if g == 'L':
+ set_default_length (ln)
+ if g == 'W':
+ if not len (a):
+ lyrics.append ('')
+ else:
+ lyrics_append (a);
+ return m
+
+def pitch_to_mudela_name (name, acc):
+ s = ''
+ if acc < 0:
+ s = 'es'
+ acc = -acc
+ elif acc > 0:
+ s = 'is'
+
+ if name > 4:
+ name = name -7
+ return chr (name + ord('c')) + s * acc
+
+def octave_to_mudela_quotes (o):
+ o = o + 2
+ s =''
+ if o < 0:
+ o = -o
+ s=','
+ else:
+ s ='\''
+
+ return s * o
+
+def parse_num (str):
+ 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)
+
+
+def duration_to_mudela_duration (multiply_tup, defaultlen, dots):
+ base = 1
+
+ # (num / den) / defaultlen < 1/base
+ while base * multiply_tup[0] < defaultlen * multiply_tup[1]:
+ base = base * 2
+
+
+ return '%d%s' % ( base, '.'* dots)
+
+class Parser_state:
+ def __init__ (self):
+ self.next_dots = 0
+ self.next_den = 1
+ self.parsing_tuplet = 0
+
+
+# WAT IS ABC EEN ONTZETTENDE PROGRAMMEERPOEP !
+def try_parse_note (str, parser_state):
+ mud = ''
+
+ slur_begin =0
+ if not str:
+ return str
+
+ if str[0] == '(':
+ slur_begin = 1
+ str = str[1:]
+
+ acc = 0
+ if str[0] in '^=_':
+ c = str[0]
+ str = str[1:]
+ if c == '^':
+ acc = 1
+ if c == '=':
+ acc = 0
+ if c == '_':
+ acc = -1
+
+ octave = 0;
+ if str[0] in "ABCDEFG":
+ str = string.lower (str[0]) + str[1:]
+ 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!
+
+ while str[0] == ',':
+ octave = octave - 1
+ str = str[1:]
+ while str[0] == '\'':
+ octave = octave + 1
+ str = str[1:]
+
+ num = 0
+ den = parser_state.next_den
+ parser_state.next_den = 1
+
+ (str, num) = parse_num (str)
+ if not num:
+ num = 1
+
+ if str[0] == '/':
+ while str[0] == '/':
+ str= str[1:]
+ d = 2
+ if str[0] in DIGITS:
+ (str, d) =parse_num (str)
+
+ den = den * d
+
+ current_dots = parser_state.next_dots
+ parser_state.next_dots = 0
+ 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
+
+
+
+ voices_append ("%s%s%s" %
+ (pitch_to_mudela_name (notename, acc + global_key[notename]),
+ octave_to_mudela_quotes (octave),
+ duration_to_mudela_duration ((num,den), default_len, current_dots)))
+ slur_end =0
+ if str[0] == ')':
+ slur_begin = 1
+ str = str[1:]
+
+
+ return str
+
+def junk_space (str):
+ while str and str[0] in '\t\n ':
+ str = str[1:]
+
+ return str
+
+
+def try_parse_guitar_chord (str):
+ if str and str[0] == '"':
+ str = str[1:]
+ gc = ''
+ while str and str[0] != '"':
+ gc = gc + str[0]
+ str = str[1:]
+
+ if str:
+ str = str[1:]
+
+ sys.stderr.write ("warning: ignoring guitar chord: %s\n" % gc)
+
+ return str
+
+def try_parse_escape (str):
+ if not str or str [0] != '\\':
+ return str
+
+ str = str[1:]
+ if str and str[0] == 'K':
+ key_table = compute_key ()
+
+ return str
+
+#
+# |] thin-thick double bar line
+# || thin-thin double bar line
+# [| thick-thin double bar line
+# :| left repeat
+# |: right repeat
+# :: left-right repeat
+#
+
+def try_parse_bar (str):
+ if str and str[0] == '|':
+ bs = ''
+ str = str[1:]
+ if str:
+ if str[0] == ']':
+ bs = '|.'
+ if str[0] == '|':
+ bs = '||'
+ if str[0] == '|:':
+ sys.stderr.write ("warning: repeat kludge\n")
+ bs = '|:'
+ if bs:
+ voices_append ('\\bar "%s";' % bs)
+ str = str[1:]
+
+ if str and str[:2] == '[|':
+ sys.stderr.write ("warning: thick-thin bar kludge\n")
+ voices_append ('\\bar "||";')
+ str = str[2:]
+
+ if str and str[:2] == ':|':
+ sys.stderr.write ("warning: repeat kludge\n")
+ voices_append ('\\bar ":|:";')
+ str = str[2:]
+
+ if str and str[:2] == '::':
+ sys.stderr.write ("warning: repeat kludge\n")
+ voices_append ('\\bar ":|:";')
+ str = str[2:]
+
+ return str
+
+
+def try_parse_chord_delims (str):
+ if str and str[0] == '[':
+ str = str[1:]
+ voices_append ('<')
+
+ if str and str[0] == ']':
+ str = str[1:]
+ voices_append ('>')
+
+ return str
+
+# urg, hairy to compute grace note hack using \times{}
+def try_parse_grace_delims (str):
+ if str and str[0] == '{':
+ str = str[1:]
+ voices_append ('\\grace { ')
+
+ if str and str[0] == '}':
+ str = str[1:]
+ voices_append ('}')
+
+ return str
+
+# Try nibbling characters off until the line doesn't change.
+def try_parse_body_line (ln, state):
+ prev_ln = ''
+ while ln != prev_ln:
+ prev_ln = ln
+ ln = try_parse_chord_delims (ln)
+ ln = try_parse_note (ln, state)
+ ln = try_parse_bar (ln)
+ ln = try_parse_escape (ln)
+ ln = try_parse_guitar_chord (ln)
+ ln = try_parse_tuplet_begin (ln, state)
+ ln = try_parse_group_end (ln, state)
+ ln = try_parse_grace_delims (ln)
+ ln = junk_space (ln)
+
+ if ln:
+ sys.stderr.write ("Huh? Don't understand `%s'\n" % ln)
+
+
+
+def parse_file (fn):
+ f = open (fn)
+ ls = f.readlines ()
+
+ head = 1
+ state = Parser_state ()
+ for l in ls:
+ if re.match ('^[\t ]*(%.*)?$', l):
+ continue
+
+ if head:
+ m = try_parse_header_line (l)
+ if not m:
+ head = 0
+
+ if not head:
+ m = try_parse_body_line (l,state)
+
+
+def identify():
+ sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
+
+def help ():
+ print r"""
+This is a disfunctional ABC to mudela convertor. It only gulps input, and
+says huh when confused. Go ahead and fix me.
+
+Usage: abc-2-ly INPUTFILE
+
+-h, --help this help.
+"""
+
+
+
+identify()
+(options, files) = getopt.getopt (sys.argv[1:], 'h', ['help'])
+
+for opt in options:
+ o = opt[0]
+ a = opt[1]
+ if o== '--help' or o == '-h':
+ help ()
+ else:
+ print o
+ raise getopt.error
+
+
+for f in files:
+ if f == '-':
+ f = ''
+ parse_file (f)
+
+ dump_global ()
+ dump_lyrics ()
+ dump_voices ()
+ dump_score ()
+
+