]> git.donarmstrong.com Git - lilypond.git/blobdiff - scripts/abc2ly.py
release: 1.2.6
[lilypond.git] / scripts / abc2ly.py
index d06608da23c4caff890d3cc88d3cfcf6e40a0254..7b075e727e637b8fd2ca0ee8418dde05377a8dbb 100644 (file)
 
 program_name = 'abc2ly'
 version = '@TOPLEVEL_VERSION@'
+if version == '@' + 'TOPLEVEL_VERSION' + '@':
+       version = '1.2.6'               # uGUHGUHGHGUGH
+
 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)
-
+import os
 
-voice_idx_dict = {}
 
 
+voice_idx_dict = {}
 header = {}
 lyrics = []
 voices = []
@@ -41,7 +39,6 @@ def select_voice (name):
 #      assert 0
 # current_voice_idx >= 0
 
-global_voice_stuff = []
 default_len = 8
 global_key = [0] * 7                   # UGH
 names = ["One", "Two", "Three"]
@@ -70,12 +67,6 @@ class Rational:
                pass
        
 
-def dump_global (outf):
-       outf.write ("\nglobal = \\notes{")
-       for i in global_voice_stuff:
-               outf.write (i);
-       outf.write ("\n}")
-
 
 def dump_header (outf,hdr):
        outf.write ('\\header {')
@@ -101,8 +92,8 @@ def dump_voices (outf):
        
 def dump_score (outf):
        outf.write (r"""\score{
-        \notes<
-           \global""")
+        \notes <
+""")
 
        ks  = voice_idx_dict.keys ();
        ks.sort ()
@@ -126,6 +117,16 @@ def set_default_length (s):
        if m:
                __main__.default_len = string.atoi ( m.group (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:
+                       default_len =  16
+               else:
+                       default_len = 8
+
 def gulp_file(f):
        try:
                i = open(f)
@@ -247,7 +248,7 @@ tup_lookup = {
        '5' : '4/5',
        '6' : '4/6',
        '7' : '6/7',
-       '9': '8/9',
+       '9' : '8/9',
        }
 
 
@@ -311,11 +312,13 @@ def try_parse_header_line (ln):
                if g == 'M':
                        if a == 'C':
                                a = '4/4'
-                       global_voice_stuff.append ('\\time %s;' % a)
+#                      global_voice_stuff.append ('\\time %s;' % a)
+                       set_default_len_from_time_sig (a)
+                       voices_append ('\\time %s;' % a)
                if g == 'K':
                        __main__.global_key  =compute_key (a)# ugh.
+                       voices_append ('\\key %s;' % a)
 
-                       global_voice_stuff.append ('\\key %s;' % a)
                if g == 'O': 
                        header ['origin'] = a
                if g == 'X': 
@@ -326,6 +329,8 @@ def try_parse_header_line (ln):
                        header_append ('history', a)
                if g == 'B':
                        header ['book'] = a
+               if g == 'C':
+                       header ['composer'] = a
                if g == 'S':
                        header ['subtitle'] = a
                if g == 'L':
@@ -381,7 +386,7 @@ 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]:
+       while base * multiply_tup[0] < multiply_tup[1]:
                base = base * 2
 
 
@@ -394,7 +399,9 @@ class Parser_state:
                self.next_den = 1
                self.parsing_tuplet = 0
 
-# return (num,den,dots) 
+
+
+# return (str, num,den,dots) 
 def parse_duration (str, parser_state):
        num = 0
        den = parser_state.next_den
@@ -405,7 +412,7 @@ def parse_duration (str, parser_state):
                num = 1
        
        if str[0] == '/':
-               while str[0] == '/':
+               while str[:1] == '/':
                        str= str[1:]
                        d = 2
                        if str[0] in DIGITS:
@@ -413,18 +420,34 @@ def parse_duration (str, parser_state):
 
                        den = den * d
 
+       den = den * default_len
+       
        current_dots = parser_state.next_dots
        parser_state.next_dots = 0
+
+       while str[0] == ' ':
+           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)
 
@@ -442,16 +465,24 @@ def try_parse_rest (str, parser_state):
 
 def try_parse_articulation (str, state):
        
-       if str and str[0] == '.':
+       if str[:1] =='.':
                state.next_articulation = state.next_articulation + '-.'
                str = str[1:]
+
+       if str[:1] =='~':
+               state.next_articulation = state.next_articulation + '-\\trill'
+               str = str[1:]
                
+       if str[:1] =='H':
+               state.next_articulation = state.next_articulation + '-\\fermata'
+               str = str[1:]
+
        # s7m2 input doesnt care about spaces
        if re.match('[ \t]*\(', str):
                str = string.lstrip (str)
 
        slur_begin =0
-       while str and   str[0] == '(' and str[1] not in DIGITS:
+       while str[:1] =='(' and str[1] not in DIGITS:
                slur_begin = slur_begin + 1
                state.next_articulation = state.next_articulation + '('
                str = str[1:]
@@ -505,7 +536,7 @@ def try_parse_note (str, parser_state):
                str = string.lstrip (str)
        
        slur_end =0
-       while str and str[0] == ')':
+       while str[:1] ==')':
                slur_end = slur_end + 1
                str = str[1:]
 
@@ -535,7 +566,7 @@ def junk_space (str):
 
 
 def try_parse_guitar_chord (str, state):
-       if str and str[0] == '"':
+       if str[:1] =='"':
                str = str[1:]
                gc = ''
                while str and str[0] != '"':
@@ -553,7 +584,7 @@ def try_parse_escape (str):
                return str
        
        str = str[1:]
-       if str and str[0] == 'K':
+       if str[:1] =='K':
                key_table = compute_key ()
 
        return str
@@ -565,75 +596,67 @@ def try_parse_escape (str):
 # :| left repeat
 # |: right repeat
 # :: left-right repeat
-#
+# |1 volta 1
+# |2 volta 2
+bar_dict = {
+'|]' : '|.',
+'||' : '||',
+'[|' : '||',
+':|' : ':|',
+'|:' : '|:',
+'::' : '::',
+'|1' : '|',
+'|2' : '|',
+':|2' : ':|'
+}
+
+
+warn_about = ['|:', '::', ':|', '|1', ':|2', '|2']
 
 def try_parse_bar (str,state):
-       if str and str[0] == '|':
+       bs = None
+
+       # first try the longer one
+       for trylen in [3,2]:
+               if str[:trylen] and bar_dict.has_key (str[:trylen]):
+                       s = str[:trylen]
+                       bs = "\\bar \"%s\";" % bar_dict[s]
+                       if s in warn_about:
+                               sys.stderr.write('Warning kludging for barline `%s\'\n' % s)
+                       str = str[trylen:]
+                       break
 
-               if state.parsing_tuplet:
-                       state.parsing_tuplet =0
-                       voices_append ('} ')
-               
-               bs = ''
+       if str[:1] == '|':
+               bs = '|\n'
                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] == '[|':
-               if state.parsing_tuplet:
-                       state.parsing_tuplet =0
-                       voices_append ('} ')
-               sys.stderr.write ("warning: thick-thin bar kludge\n")
-               voices_append ('\\bar "||";')
-               str = str[2:]
-
-       if str and str[:2] == ':|':
+       
+       if bs <> None:
                if state.parsing_tuplet:
                        state.parsing_tuplet =0
                        voices_append ('} ')
                
-               sys.stderr.write ("warning: repeat kludge\n")
-               voices_append ('\\bar ":|:";')
-               str = str[2:]
-
-       if str and str[:2] == '::':
-               if state.parsing_tuplet:
-                       state.parsing_tuplet =0
-                       voices_append ('} ')
-                       
-               sys.stderr.write ("warning: repeat kludge\n")
-               voices_append ('\\bar ":|:";')
-               str = str[2:]
+               voices_append (bs)
 
        return str
-       
+
 def try_parse_tie (str):
-       if str and str[0] == '-':
+       if str[:1] =='-':
                str = str[1:]
                voices_append (' ~ ')
        return str
 
 def try_parse_chord_delims (str):
-       if str and str[0] == '[':
+       if str[:1] =='[':
                str = str[1:]
                voices_append ('<')
 
        ch = ''
-       if str and str[0] == ']':
+       if str[:1] ==']':
                str = str[1:]
                ch = '>'
 
        end = 0
-       while str and str[0] == ')':
+       while str[:1] ==')':
                end = end + 1
                str = str[1:]
 
@@ -643,11 +666,11 @@ def try_parse_chord_delims (str):
        return str
 
 def try_parse_grace_delims (str):
-       if str and str[0] == '{':
+       if str[:1] =='{':
                str = str[1:]
                voices_append ('\\grace { ')
 
-       if str and str[0] == '}':
+       if str[:1] =='}':
                str = str[1:]
                voices_append ('}')
 
@@ -661,7 +684,7 @@ def parse_file (fn):
 
        state = Parser_state ()
        lineno = 0
-       sys.stderr.write ("Parsing line ... ")
+       sys.stderr.write ("Line ... ")
        sys.stderr.flush ()
        
        for ln in ls:
@@ -711,18 +734,22 @@ def identify():
 
 def help ():
        print r"""
-This is an ABC to mudela convertor.
+Convert ABC to Mudela.
 
-Usage: abc2ly INPUTFILE
+Usage: abc2ly [OPTION]... ABC-FILE
 
--h, --help   this help.
--o, --output set output filename
+Options:
+  -h, --help          this help
+  -o, --output=FILE   set output filename to FILE
+  -v, --version       version information
 """
 
+def print_version ():
+       print r"""abc2ly (GNU lilypond) %s""" % version
 
 
-identify()
-(options, files) = getopt.getopt (sys.argv[1:], 'o:h', ['help', 'output='])
+
+(options, files) = getopt.getopt (sys.argv[1:], 'vo:h', ['help','version', 'output='])
 out_filename = ''
 
 for opt in options:
@@ -730,29 +757,35 @@ for opt in options:
        a = opt[1]
        if o== '--help' or o == '-h':
                help ()
+               sys.exit (0)
+       if o == '--version' or o == '-v':
+               print_version ()
+               sys.exit(0)
+               
        if o == '--output' or o == '-o':
                out_filename = a
        else:
                print o
                raise getopt.error
 
+identify()
 
+header['tagline'] = 'Lily was here %s -- automatically converted from ABC' % version
 for f in files:
        if f == '-':
                f = ''
 
+       sys.stderr.write ('Parsing... [%s]\n' % f)
        parse_file (f)
 
-       outf = None
-       if out_filename:
-               outf = open (out_filename, 'w')
-       else:
-               outf = sys.stdout
-
+       if not out_filename:
+               out_filename = os.path.basename (os.path.splitext (f)[0]) + ".ly"
+       sys.stderr.write ('Ly output to: %s...' % out_filename)
+       outf = open (out_filename, 'w')
 
-       dump_global (outf)
+#      dump_global (outf)
        dump_lyrics (outf)
        dump_voices (outf)
        dump_score (outf)
-       
+       sys.stderr.write ('\n')