]> git.donarmstrong.com Git - lilypond.git/commitdiff
lilypond-1.1.56
authorfred <fred>
Wed, 7 Jul 1999 12:06:27 +0000 (12:06 +0000)
committerfred <fred>
Wed, 7 Jul 1999 12:06:27 +0000 (12:06 +0000)
scripts/abc2ly.py [new file with mode: 0644]

diff --git a/scripts/abc2ly.py b/scripts/abc2ly.py
new file mode 100644 (file)
index 0000000..dd4bcf2
--- /dev/null
@@ -0,0 +1,617 @@
+#!@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 ()
+       
+