-#!@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 ()
-
-