]> git.donarmstrong.com Git - lilypond.git/blobdiff - scripts/abc2ly.py
release: 1.3.107
[lilypond.git] / scripts / abc2ly.py
index 68073f20bae6a34d4d4f03b51bae744230d5ed3c..b31a56bc3c914048f6192478ea8d44a33e3bc846 100644 (file)
@@ -6,7 +6,7 @@
 #
 # (not finished.)
 # ABC standard v1.6:  http://www.gre.ac.uk/~c.walshaw/abc2mtex/abc.txt
-# 
+#
 # Enhancements  (Roy R. Rankin)
 #
 # Header section moved to top of lilypond file
 # Chord strings([-^]"string") can contain a '#'
 # Header fields enclosed by [] in notes string processed
 # W: words output after tune as abc2ps does it (they failed before)
+
+# Enhancements (Laura Conrad)
 #
+# Barring now preserved between ABC and lilypond
+# the default placement for text in abc is above the staff.
+# %%LY now supported.                                          
+                       
 # Limitations
 #
 # Multiple tunes in single file not supported
 # Blank T: header lines should write score and open a new score
 # Not all header fields supported
-# Beaming not preserved between ABC and lilypond
 # ABC line breaks are ignored
 # Block comments generate error and are ignored
 # Postscript commands are ignored
@@ -71,6 +76,8 @@ current_lyric_idx = -1
 lyric_idx = -1
 part_names = 0
 default_len = 8
+length_specified = 0
+nobarlines = 0
 global_key = [0] * 7                   # UGH
 names = ["One", "Two", "Three"]
 DIGITS='0123456789'
@@ -89,6 +96,10 @@ def check_clef(s):
               else:
                       state.base_octave = 0
                       voices_append("\\clef treble;\n")
+      elif re.match('^-8va', s):
+             s = s[4:]
+             state.base_octave = -1
+             voices_append("\\clef \"G_8\";\n")
       elif re.match('^alto', s):
               s = s[4:]
               state.base_octave = -1
@@ -147,6 +158,10 @@ def dump_lyrics (outf):
                        outf.write ("\n")
                outf.write("    >\n    \\paper{}\n}\n")
 
+def dump_default_bar (outf):
+       outf.write ("\n\\property Score.defaultBarType=\"empty\"\n")
+
+
 def dump_slyrics (outf):
        ks = voice_idx_dict.keys()
        ks.sort ()
@@ -161,6 +176,7 @@ def dump_voices (outf):
        ks.sort ()
        for k in ks:
                outf.write ("\nvoice%s = \\notes {" % k)
+               dump_default_bar(outf)
                outf.write ("\n" + voices [voice_idx_dict[k]])
                outf.write ("\n}")
        
@@ -198,9 +214,11 @@ def dump_score (outf):
 
 
 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
 
 def set_default_len_from_time_sig (s):
        m =  re.search ('([0-9]+)/([0-9]+)', s)
@@ -223,7 +241,7 @@ def gulp_file(f):
                return ''
        s = i.read (n)
        if len (s) <= 0:
-               sys.stderr.write ("gulped emty file: %s\n" % f)
+               sys.stderr.write ("gulped empty file: %s\n" % f)
        i.close ()
        return s
 
@@ -310,7 +328,7 @@ def lily_key (k):
                key = key + 'es'
                k = k[1:]
        if not k:
-               return(key)
+               return '%s \\major' % key
 
        type = k[0:3]
        if key_lookup.has_key(type):
@@ -408,7 +426,7 @@ def  try_parse_group_end (str, state):
        if str and str[0] in HSPACE:
                str = str[1:]
        return str
-
+       
 def header_append (key, a):
        s = ''
        if header.has_key (key):
@@ -479,6 +497,7 @@ def slyrics_append(a):
 
 
 def try_parse_header_line (ln, state):
+       global length_specified
        m = re.match ('^([A-Za-z]): *(.*)$', ln)
 
        if m:
@@ -495,15 +514,17 @@ def try_parse_header_line (ln, state):
                        if a == 'C':
                                if not state.common_time:
                                        state.common_time = 1
-                                       voices_append ("\\property Staff.timeSignatureStyle=\"C\"\n")
+                                       voices_append ("\\property Staff.TimeSignature \push #\'style = #\"C\"\n")
                                a = '4/4'
                        if a == 'C|':
                                if not state.common_time:
                                        state.common_time = 1
-                                       voices_append ("\\property Staff.timeSignatureStyle=\"C\"\n")
+                                       voices_append ("\\property Staff.TimeSignature \push #\'style = #\"C\"\n")
                                a = '2/2'
-#                      global_voice_stuff.append ('\\time %s;' % a)
-                       set_default_len_from_time_sig (a)
+                       if not length_specified:
+                               set_default_len_from_time_sig (a)
+                       else:
+                               length_specified = 0
                        voices_append ('\\time %s;' % a)
                        state.next_bar = ''
                if g == 'K': # KEY
@@ -511,11 +532,19 @@ def try_parse_header_line (ln, state):
                        if a:
                                m = re.match ('^([^ \t]*) *(.*)$', a) # seperate clef info
                                if m:
-                                       __main__.global_key  =compute_key (m.group(1))# ugh.
-                                       voices_append ('\\key %s \\major;' % lily_key(m.group(1)))
-                                       check_clef(m.group(2))
+                                       # 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)# ugh.
+                                       voices_append ('\\key %s;' % lily_key(key_info))
+                                       check_clef(clef_info)
                                else:
-                                       __main__.global_key  =compute_key (a)# ugh.
+                                       __main__.global_key  = compute_key (a)# ugh.
                                        voices_append ('\\key %s \\major;' % lily_key(a))
                if g == 'O': # Origin
                        header ['origin'] = a
@@ -554,10 +583,11 @@ def try_parse_header_line (ln, state):
 
 # we use in this order specified accidental, active accidental for bar,
 # active accidental for key
-def pitch_to_mudela_name (name, acc, bar_acc, key):
+def pitch_to_lilypond_name (name, acc, bar_acc, key):
        s = ''
        if acc == UNDEF:
-               acc = bar_acc
+               if not nobarlines:
+                       acc = bar_acc
        if acc == UNDEF:
                acc = key
        if acc == -1:
@@ -570,7 +600,7 @@ def pitch_to_mudela_name (name, acc, bar_acc, key):
        return(chr (name  + ord('c')) + s)
 
 
-def octave_to_mudela_quotes (o):
+def octave_to_lilypond_quotes (o):
        o = o + 2
        s =''
        if o < 0:
@@ -593,14 +623,11 @@ def parse_num (str):
        return (str,n)
 
 
-def duration_to_mudela_duration  (multiply_tup, defaultlen, dots):
+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
-
-
        return '%d%s' % ( base, '.'* dots)
 
 class Parser_state:
@@ -684,7 +711,7 @@ def try_parse_rest (str, parser_state):
        str = str[1:]
 
        (str, num,den,d) = parse_duration (str, parser_state)
-       voices_append ('%s%s' % (rest, duration_to_mudela_duration ((num,den), default_len, d)))
+       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 = ''
@@ -814,15 +841,15 @@ def try_parse_note (str, parser_state):
                voices_append ('%s' % ')' *slur_end )
 
        bar_acc = get_bar_acc(notename, octave, parser_state)
-       pit = pitch_to_mudela_name(notename, acc, bar_acc, global_key[notename])
-       oct = octave_to_mudela_quotes (octave)
+       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_mudela_duration ((num,den), default_len, current_dots)))
+                duration_to_lilypond_duration ((num,den), default_len, current_dots)))
        
        set_bar_acc(notename, octave, acc, parser_state)
        if parser_state.next_articulation:
@@ -859,7 +886,7 @@ def try_parse_guitar_chord (str, state):
                if str:
                        str = str[1:]
                gc = re.sub('#', '\\#', gc)     # escape '#'s
-               state.next_articulation = ("-\"%s\"" % gc) + state.next_articulation
+               state.next_articulation = ("^\"%s\"" % gc) + state.next_articulation
        return str
 
 def try_parse_escape (str):
@@ -886,10 +913,11 @@ bar_dict = {
 '[|' : '||',
 ':|' : ':|',
 '|:' : '|:',
-'::' : '::',
+'::' : ':|:',
 '|1' : '|',
 '|2' : '|',
-':|2' : ':|'
+':|2' : ':|',
+'|' :  '|'
 }
 
 
@@ -899,7 +927,7 @@ def try_parse_bar (str,state):
        bs = None
 
        # first try the longer one
-       for trylen in [3,2]:
+       for trylen in [3,2,1]:
                if str[:trylen] and bar_dict.has_key (str[:trylen]):
                        s = str[:trylen]
                        bs = "\\bar \"%s\";" % bar_dict[s]
@@ -989,6 +1017,33 @@ def try_parse_grace_delims (str, state):
 
        return str
 
+def try_parse_comment (str):
+       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
+#pitch that will be played.
+#
+#In standard 19th century musical notation, the algorithm for translating
+#between printed note and pitch involves using the barlines to determine
+#the scope of the accidentals.
+#
+#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):
+                               #debugging
+                               nobarlines = 1
+               elif str[0:3] == '%LY':
+                       p = string.find(str, 'voices')
+                       if (p > -1):
+                               voices_append(str[p+7:])
+                               voices_append("\n")
+#write other kinds of appending  if we ever need them.                 
+       return str
 
 happy_count = 100
 def parse_file (fn):
@@ -1010,6 +1065,7 @@ def parse_file (fn):
                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)
 
@@ -1047,14 +1103,17 @@ def identify():
 
 def help ():
        print r"""
-Convert ABC to Mudela.
+Convert ABC to Lilypond.
 
-Usage: abc2ly [OPTION]... ABC-FILE
+Usage: abc2ly [OPTIONS]... ABC-FILE
 
 Options:
   -h, --help          this help
   -o, --output=FILE   set output filename to FILE
   -v, --version       version information
+
+This program converts ABC music files (see
+http://www.gre.ac.uk/~c.walshaw/abc2mtex/abc.txt) To LilyPond input.
 """
 
 def print_version ():