]> git.donarmstrong.com Git - lilypond.git/blobdiff - scripts/abc2ly.py
Add '-dcrop' option to ps and svg backends
[lilypond.git] / scripts / abc2ly.py
index 570846c4ac40bbda4ebee33a5874be19eb82e974..e6e0d12407e4ed53006ee529da85f55f7ac7dbab 100644 (file)
@@ -1,11 +1,28 @@
 #!@TARGET_PYTHON@
 # -*- coding: utf-8 -*-
+
 # once upon a rainy monday afternoon.
+
+#    This file is part of LilyPond, the GNU music typesetter.
+#
+#    LilyPond is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    LilyPond is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
+
 #
 #   ...
 #
 # (not finished.)
-# ABC standard v1.6:  http://www.gre.ac.uk/~c.walshaw/abc2mtex/abc.txt
+# ABC standard v1.6:  http://abcnotation.com/
 #
 # Enhancements  (Roy R. Rankin)
 #
 
 #TODO:
 #
+# * coding style
 # * lilylib
 # * GNU style messages:  warning:FILE:LINE:
 # * l10n
-# 
+#
 # Convert to new chord styles.
 #
 # UNDEF -> None
 #
+
 
 import __main__
 import getopt
 import sys
 import re
-import string
 import os
 
 program_name = sys.argv[0]
@@ -87,7 +104,7 @@ global _;_=ly._
 
 version = '@TOPLEVEL_VERSION@'
 if version == '@' + 'TOPLEVEL_VERSION' + '@':
-    version = '(unknown version)'                # uGUHGUHGHGUGH  
+    version = '(unknown version)'                # uGUHGUHGHGUGH
 
 UNDEF = 255
 state = UNDEF
@@ -117,38 +134,51 @@ def error (msg):
     sys.stderr.write (msg)
     if global_options.strict:
         sys.exit (1)
-    
+
 
 def alphabet (i):
     return chr (i + ord('A'))
-    
+
 def check_clef(s):
+    # the number gives the base_octave
+    clefs = [("treble", "treble", 0),
+             ("treble1", "french", 0),
+             ("bass3", "varbaritone", 0),
+             ("bass", "bass", 0),
+             ("alto4", "tenor", 0),
+             ("alto2", "mezzosoprano", 0),
+             ("alto1", "soprano", 0),
+             ("alto", "alto", 0),
+             ("perc", "percussion", 0)]
+    modifier = [("-8va", "_8", -1),
+                ("-8", "_8", -1),
+                ("\+8", "^8", +1),
+                ("8", "_8", -1)]
+
     if not s:
         return ''
-    if re.match('-8va', s) or re.match('treble8', s):
-        # treble8 is used by abctab2ps; -8va is used by barfly,
-        # and by my patch to abc2ps. If there's ever a standard
-        # about this we'll support that.
-        s = s[4:]
-        state.base_octave = -1
-        voices_append("\\clef \"G_8\"\n")
-    elif re.match('^treble', s):
-        s = s[6:]
-        if re.match ('^-8', s):
-            s = s[2:]
-            state.base_octave = -2
-            voices_append("\\clef \"G_8\"\n")
-        else:
-            state.base_octave = 0
-            voices_append("\\clef treble\n")
-    elif re.match('^alto', s):
-        s = s[4:]
-        state.base_octave = -1
-        voices_append ("\\clef alto\n" )
-    elif re.match('^bass',s ):
-        s = s[4:]
-        state.base_octave = -2
-        voices_append ("\\clef bass\n" )
+    clef = None;
+    octave = 0;
+    for c in clefs:
+      m = re.match('^'+c[0], s)
+      if m:
+        (clef, octave) = (c[1], c[2])
+        s = s[m.end():]
+        break;
+    if not clef:
+      return s
+
+    mod = "";
+    for md in modifier:
+      m = re.match('^'+md[0], s)
+      if m:
+        mod = md[1];
+        octave += md[2];
+        s = s[m.end():]
+        break;
+
+    state.base_octave = octave
+    voices_append ("\\clef \""+clef+mod+"\"\n")
     return s
 
 def select_voice (name, rol):
@@ -174,7 +204,7 @@ def select_voice (name, rol):
                     value = re.sub ('\\\\','\\\\\\\\', value)
                     ## < 2.2
                     voices_append ("\\set Staff.instrument = %s\n" % value )
-                    
+
                     __main__.part_names = 1
                 elif keyword == "sname" or keyword == "snm":
                     voices_append ("\\set Staff.instr = %s\n" % value )
@@ -186,24 +216,24 @@ def dump_header (outf,hdr):
     ks = hdr.keys ()
     ks.sort ()
     for k in ks:
-        hdr[k] = re.sub('"', '\\"', hdr[k])                
+        hdr[k] = re.sub('"', '\\"', hdr[k])
         outf.write ('\t%s = "%s"\n'% (k,hdr[k]))
     outf.write ('}')
 
 def dump_lyrics (outf):
     if (len(lyrics)):
-        outf.write("\n\\score\n{\n \\lyrics\n <<\n")
+        outf.write("\n\\markup \\column {\n")
         for i in range (len (lyrics)):
-            outf.write ( lyrics [i])
+            outf.write (lyrics [i])
             outf.write ("\n")
-        outf.write("    >>\n    \\layout{}\n}\n")
+        outf.write("}\n")
 
 def dump_default_bar (outf):
     """
     Nowadays abc2ly outputs explicits barlines (?)
     """
     ## < 2.2
-    outf.write ("\n\\set Score.defaultBarType = \"empty\"\n")
+    outf.write ("\n\\set Score.defaultBarType = \"\"\n")
 
 
 def dump_slyrics (outf):
@@ -211,12 +241,12 @@ def dump_slyrics (outf):
     ks.sort ()
     for k in ks:
         if re.match('[1-9]', k):
-            m = alphabet(string.atoi(k))
+            m = alphabet (int (k))
         else:
             m = k
         for i in range (len(slyrics[voice_idx_dict[k]])):
             l= alphabet(i)
-            outf.write ("\nwords%sV%s = \lyricmode {" % (m, l))
+            outf.write ("\nwords%sV%s = \\lyricmode {" % (m, l))
             outf.write ("\n" + slyrics [voice_idx_dict[k]][i])
             outf.write ("\n}")
 
@@ -226,7 +256,7 @@ def dump_voices (outf):
     ks.sort ()
     for k in ks:
         if re.match ('[1-9]', k):
-            m = alphabet(string.atoi(k))
+            m = alphabet (int (k))
         else:
             m = k
         outf.write ("\nvoice%s =  {" % m)
@@ -242,22 +272,28 @@ def dump_voices (outf):
         outf.write ("\n}")
 
 def try_parse_q(a):
-    global midi_specs
-    #assume that Q takes the form "Q:1/4=120"
+    #assume that Q takes the form "Q:'opt. description' 1/4=120"
     #There are other possibilities, but they are deprecated
-    if string.count(a, '/') == 1:
-        array=string.split(a,'/')
-        numerator=array[0]
-        if int(numerator) != 1:
-            sys.stderr.write("abc2ly: Warning, unable to translate a Q specification with a numerator of %s: %s\n" % (numerator, a))
-        array2=string.split(array[1],'=')
-        denominator=array2[0]
-        perminute=array2[1]
-        duration=str(string.atoi(denominator)/string.atoi(numerator))
-        midi_specs=string.join(["    \n\t\t\context {\n\t\t \Score tempoWholesPerMinute = #(ly:make-moment ", perminute, " ", duration, ")\n\t\t }\n"])
+    r = re.compile ('^(.*) *([0-9]+) */ *([0-9]+) *=* *([0-9]+)\s*')
+    m = r.match (a)
+    if m:
+        descr = m.group(1) # possibly empty
+        numerator = int(m.group (2))
+        denominator = int(m.group (3))
+        tempo = m.group (4)
+        dur = duration_to_lilypond_duration ((numerator,denominator), 1, 0)
+        voices_append ("\\tempo " + descr + " " + dur + "=" + tempo + "\n")
     else:
-        sys.stderr.write("abc2ly: Warning, unable to parse Q specification: %s\n" % a)
-    
+        # Parsing of numeric tempi, as these are fairly
+        # common.  The spec says the number is a "beat" so using
+        # a quarter note as the standard time
+        numericQ = re.compile ('[0-9]+')
+        m = numericQ.match (a)
+        if m:
+            voices_append ("\\tempo 4=" + m.group(0))
+        else:
+            sys.stderr.write("abc2ly: Warning, unable to parse Q specification: %s\n" % a)
+
 def dump_score (outf):
     outf.write (r"""
 
@@ -269,12 +305,12 @@ def dump_score (outf):
     ks.sort ()
     for k in  ks:
         if re.match('[1-9]', k):
-            m = alphabet (string.atoi(k))
+            m = alphabet (int (k))
         else:
             m = k
         if k == 'default' and len (voice_idx_dict) > 1:
             break
-        outf.write ("\n\t\\context Staff=\"%s\"\n\t{\n" %k ) 
+        outf.write ("\n\t\\context Staff=\"%s\"\n\t{\n" %k )
         if k != 'default':
             outf.write ("\t    \\voicedefault\n")
         outf.write ("\t    \\voice%s " % m)
@@ -282,9 +318,9 @@ def dump_score (outf):
 
         l = ord( 'A' )
         for lyrics in slyrics [voice_idx_dict[k]]:
-            outf.write ("\n\t\\addlyrics { \n")
+            outf.write ("\n\t\\addlyrics {\n")
             if re.match('[1-9]',k):
-                m = alphabet (string.atoi(k))
+                m = alphabet (int (k))
             else:
                 m = k
 
@@ -301,14 +337,14 @@ 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))
+        __main__.default_len = int ( m.group (1))
         length_specified = 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))
+        n = int (m.group (1))
+        d = int (m.group (2))
         if (n * 1.0 )/(d * 1.0) <  0.75:
             __main__.default_len =  16
         else:
@@ -332,17 +368,17 @@ def gulp_file(f):
 
 # pitch manipulation. Tuples are (name, alteration).
 # 0 is (central) C. Alteration -1 is a flat, Alteration +1 is a sharp
-# pitch in semitones. 
+# 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
 
@@ -351,7 +387,7 @@ def fifth_above_pitch (tup):
 
     difference = 7 - (semitone_pitch ((n,a)) - semitone_pitch (tup))
     a = a + difference
-    
+
     return (n,a)
 
 def sharp_keys ():
@@ -385,29 +421,31 @@ def quart_above_pitch (tup):
 
     difference = 5 - (semitone_pitch ((n,a)) - semitone_pitch (tup))
     a = a + difference
-    
+
     return (n,a)
 
 key_lookup = {         # abc to lilypond key mode names
     'm'   : 'minor',
     'min' : 'minor',
     'maj' : 'major',
-    'major' : 'major',        
+    'major' : 'major',
     'phr' : 'phrygian',
     'ion' : 'ionian',
     'loc' : 'locrian',
     'aeo' : 'aeolian',
     'mix' : 'mixolydian',
-    'mixolydian' : 'mixolydian',        
+    'mixolydian' : 'mixolydian',
     'lyd' : 'lydian',
     'dor' : 'dorian',
-    'dorian' : 'dorian'        
+    'dorian' : 'dorian'
 }
 
 def lily_key (k):
+    if k == 'none':
+        return
     orig = "" + k
     # UGR
-    k = string.lower (k)
+    k = k.lower ()
     key = k[0]
     #UGH
     k = k[1:]
@@ -448,28 +486,28 @@ key_shift = { # semitone shifts for key mode names
     'min' : 3,
     'minor' : 3,
     'maj' : 0,
-    'major' : 0,        
+    'major' : 0,
     'phr' : -4,
     'phrygian' : -4,
     'ion' : 0,
     'ionian' : 0,
     'loc' : 1,
-    'locrian' : 1,        
+    'locrian' : 1,
     'aeo' : 3,
     'aeolian' : 3,
     'mix' : 5,
-    'mixolydian' : 5,        
+    'mixolydian' : 5,
     'lyd' : -5,
-    'lydian' : -5,        
+    'lydian' : -5,
     'dor' :        -2,
-    'dorian' :        -2        
+    'dorian' :        -2
 }
 def compute_key (k):
-    k = string.lower (k)
+    k = k.lower ()
     intkey = (ord (k[0]) - ord('a') + 5) % 7
     intkeyacc =0
     k = k[1:]
-    
+
     if k and k[0] == 'b':
         intkeyacc = -1
         k = k[1:]
@@ -480,7 +518,7 @@ def compute_key (k):
     if k and key_shift.has_key(k):
         (intkey, intkeyacc) = shift_key(intkey, intkeyacc, key_shift[k])
     keytup = (intkey, intkeyacc)
-    
+
     sharp_key_seq = sharp_keys ()
     flat_key_seq = flat_keys ()
 
@@ -497,8 +535,8 @@ def compute_key (k):
         accseq = map (lambda x: (3*x + 3 ) % 7, range (1, key_count + 1))
     else:
         error ("Huh?")
-        raise "Huh"
-    
+        raise Exception ("Huh")
+
     key_table = [0] * 7
     for a in accseq:
         key_table[a] = key_table[a] + accsign
@@ -521,9 +559,9 @@ def try_parse_tuplet_begin (str, state):
         dig = str[1]
         str = str[2:]
         prev_tuplet_state = state.parsing_tuplet
-        state.parsing_tuplet = string.atoi (dig[0])
+        state.parsing_tuplet = int (dig[0])
         if prev_tuplet_state:
-            voices_append ("}")                
+            voices_append ("}")
         voices_append ("\\times %s {" % tup_lookup[dig])
     return str
 
@@ -532,7 +570,7 @@ def  try_parse_group_end (str, state):
         str = str[1:]
         close_beam_state(state)
     return str
-    
+
 def header_append (key, a):
     s = ''
     if header.has_key (key):
@@ -540,7 +578,7 @@ def header_append (key, a):
         header [key] = s + a
 
 def wordwrap(a, v):
-    linelen = len (v) - string.rfind(v, '\n')
+    linelen = len (v) - v.rfind ('\n')
     if linelen + len (a) > 80:
         v = v + '\n'
     return v + a + ' '
@@ -583,11 +621,11 @@ def repeat_prepend():
     if not using_old:
         repeat_state[current_voice_idx] = 't'
 
-    
+
 def lyrics_append(a):
     a = re.sub ('#', '\\#', a)        # latex does not like naked #'s
     a = re.sub ('"', '\\"', a)        # latex does not like naked "'s
-    a = '\t{  "' + a + '" }\n'
+    a = '  \\line { "' + a + '" }\n'
     stuff_append (lyrics, current_lyric_idx, a)
 
 # break lyrics to words and put "'s around words containing numbers and '"'s
@@ -610,7 +648,7 @@ def fix_lyric(str):
 
 def slyrics_append(a):
     a = re.sub ( '_', ' _ ', a)        # _ to ' _ '
-    a = re.sub ( '([^-])-([^-])', '\\1- \\2', a)        # split words with "-" unless was originally "--" 
+    a = re.sub ( '([^-])-([^-])', '\\1- \\2', a)        # split words with "-" unless was originally "--"
     a = re.sub ( '\\\\- ', '-', a)         # unless \-
     a = re.sub ( '~', '_', a)        # ~ to space('_')
     a = re.sub ( '\*', '_ ', a)        # * to to space
@@ -650,12 +688,12 @@ def try_parse_header_line (ln, state):
             if a == 'C':
                 if not state.common_time:
                     state.common_time = 1
-                    voices_append (" \\override Staff.TimeSignature #\'style = #'C\n")
+                    voices_append (" \\override Staff.TimeSignature #'style = #'C\n")
                 a = '4/4'
             if a == 'C|':
                 if not state.common_time:
                     state.common_time = 1
-                    voices_append ("\\override Staff.TimeSignature #\'style = #'C\n")
+                    voices_append ("\\override Staff.TimeSignature #'style = #'C\n")
                 a = '2/2'
             if not length_specified:
                 set_default_len_from_time_sig (a)
@@ -667,16 +705,19 @@ def try_parse_header_line (ln, state):
         if g == 'K': # KEY
             a = check_clef(a)
             if a:
-                m = re.match ('^([^ \t]*) *(.*)$', a) # seperate clef info
+                m = re.match ('^([^ \t]*) *([^ ]*)( *)(.*)$', a) # separate clef info
                 if m:
                     # 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:]
+                    # convert the mode to lower-case before comparing
+                    mode = m.group(2)[0:3].lower();
+                    if key_lookup.has_key(mode):
+                        # use the full mode, not only the first three letters
+                        key_info = m.group(1) + m.group(2).lower()
+                        clef_info = a[m.start(4):]
                     else:
                         key_info = m.group(1)
-                        clef_info = m.group(2)
+                        clef_info = a[m.start(2):]
                     __main__.global_key  = compute_key (key_info)
                     k = lily_key (key_info)
                     if k:
@@ -709,7 +750,7 @@ def try_parse_header_line (ln, state):
             header ['subtitle'] = a
         if g == 'L':        # Default note length
             set_default_length (ln)
-        if g == 'V':        # Voice 
+        if g == 'V':        # Voice
             voice = re.sub (' .*$', '', a)
             rest = re.sub ('^[^ \t]*  *', '', a)
             if state.next_bar:
@@ -720,8 +761,12 @@ def try_parse_header_line (ln, state):
             lyrics_append(a)
         if g == 'w':        # vocals
             slyrics_append (a)
-        if g == 'Q':    #tempo
+        if g == 'Q':        # tempo
             try_parse_q (a)
+        if g == 'R':        # Rhythm (e.g. jig, reel, hornpipe)
+            header['meter'] = a
+        if g == 'Z':        # Transcription (e.g. Steve Mansfield 1/2/2000)
+            header['transcription'] = a
         return ''
     return ln
 
@@ -738,7 +783,7 @@ def pitch_to_lilypond_name (name, acc, bar_acc, key):
         s = 'es'
     elif acc == 1:
         s =  'is'
-    
+
     if name > 4:
         name = name -7
     return(chr (name  + ord('c')) + s)
@@ -763,7 +808,7 @@ def parse_num (str):
 
     n = None
     if durstr:
-        n  =string.atoi (durstr) 
+        n = int (durstr)
     return (str,n)
 
 
@@ -779,7 +824,7 @@ def duration_to_lilypond_duration  (multiply_tup, defaultlen, dots):
             base = '\\breve'
             dots = 1
         if (multiply_tup[0] / multiply_tup[1]) == 4:
-            base = '\longa'
+            base = '\\longa'
     return '%s%s' % ( base, '.'* dots)
 
 class Parser_state:
@@ -797,7 +842,7 @@ class Parser_state:
 
 
 
-# return (str, num,den,dots) 
+# return (str, num,den,dots)
 def parse_duration (str, parser_state):
     num = 0
     den = parser_state.next_den
@@ -818,7 +863,7 @@ def parse_duration (str, parser_state):
                     den = den * d
 
     den = den * default_len
-    
+
     current_dots = parser_state.next_dots
     parser_state.next_dots = 0
     if re.match ('[ \t]*[<>]', str):
@@ -828,7 +873,7 @@ def parse_duration (str, parser_state):
             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
@@ -844,7 +889,7 @@ def parse_duration (str, parser_state):
             num = num / multiplier
             den = den / f
             current_dots = current_dots + d
-        
+
     return (str, num,den,current_dots)
 
 
@@ -887,7 +932,7 @@ artic_tbl = {
     'O' : '^\\coda',
     'v' : '^\\downbow'
 }
-    
+
 def try_parse_articulation (str, state):
     while str and  artic_tbl.has_key(str[:1]):
         state.next_articulation = state.next_articulation + artic_tbl[str[:1]]
@@ -896,11 +941,11 @@ def try_parse_articulation (str, state):
 
         str = str[1:]
 
-    
-        
-    # s7m2 input doesnt care about spaces
+
+
+    # s7m2 input doesn't care about spaces
     if re.match('[ \t]*\(', str):
-        str = string.lstrip (str)
+        str = str.lstrip ()
 
     slur_begin =0
     while str[:1] =='(' and str[1] not in DIGITS:
@@ -909,7 +954,7 @@ def try_parse_articulation (str, state):
         str = str[1:]
 
     return str
-        
+
 #
 # remember accidental for rest of bar
 #
@@ -928,9 +973,8 @@ def get_bar_acc(note, octave, state):
         return(UNDEF)
 
 def clear_bar_acc(state):
-    for k in state.in_acc.keys():
-        del state.in_acc[k]
-    
+    state.in_acc = {}
+
 
 # if we are parsing a beam, close it off
 def close_beam_state(state):
@@ -938,7 +982,7 @@ def close_beam_state(state):
         state.parsing_beam = 0
         voices_append_back( ']' )
 
-        
+
 # WAT IS ABC EEN ONTZETTENDE PROGRAMMEERPOEP  !
 def try_parse_note (str, parser_state):
     mud = ''
@@ -961,7 +1005,7 @@ def try_parse_note (str, parser_state):
 
     octave = parser_state.base_octave
     if str[0] in "ABCDEFG":
-        str = string.lower (str[0]) + str[1:]
+        str = str[0].lower () + str[1:]
         octave = octave - 1
 
 
@@ -972,7 +1016,7 @@ def try_parse_note (str, parser_state):
     else:
         return str                # failed; not a note!
 
-    
+
     __main__.lyric_idx = -1
 
     if parser_state.next_bar:
@@ -989,14 +1033,14 @@ def try_parse_note (str, parser_state):
     (str, num,den,current_dots) = parse_duration (str, parser_state)
 
     if re.match('[ \t]*\)', str):
-        str = string.lstrip (str)
-    
+        str = str.lstrip ()
+
     slur_end =0
     while str[:1] ==')':
         slur_end = slur_end + 1
         str = str[1:]
 
-    
+
     bar_acc = get_bar_acc(notename, octave, parser_state)
     pit = pitch_to_lilypond_name(notename, acc, bar_acc, global_key[notename])
     oct = octave_to_lilypond_quotes (octave)
@@ -1007,7 +1051,7 @@ def try_parse_note (str, parser_state):
     voices_append ("%s%s%s%s" %
         (pit, oct, mod,
          duration_to_lilypond_duration ((num,den), default_len, current_dots)))
-    
+
     set_bar_acc(notename, octave, acc, parser_state)
     if parser_state.next_articulation:
         articulation = articulation + parser_state.next_articulation
@@ -1015,22 +1059,23 @@ def try_parse_note (str, parser_state):
 
     voices_append (articulation)
 
-    if parser_state.parsing_tuplet:
-        parser_state.parsing_tuplet = parser_state.parsing_tuplet - 1
-        if not parser_state.parsing_tuplet:
-            voices_append ("}")
     if slur_begin:
         voices_append ('-(' * slur_begin )
     if slur_end:
         voices_append ('-)' *slur_end )
 
+    if parser_state.parsing_tuplet:
+        parser_state.parsing_tuplet = parser_state.parsing_tuplet - 1
+        if not parser_state.parsing_tuplet:
+            voices_append ("}")
+
     if global_options.beams and \
      str[0] in '^=_ABCDEFGabcdefg' and \
      not parser_state.parsing_beam and \
      not parser_state.parsing_tuplet:
         parser_state.parsing_beam = 1
         voices_append_back( '[' )
-        
+
     return str
 
 def junk_space (str,state):
@@ -1053,7 +1098,7 @@ def try_parse_guitar_chord (str, state):
         while str and str[0] != '"':
             gc = gc + str[0]
             str = str[1:]
-            
+
         if str:
             str = str[1:]
         gc = re.sub('#', '\\#', gc)        # escape '#'s
@@ -1064,7 +1109,7 @@ def try_parse_guitar_chord (str, state):
 def try_parse_escape (str):
     if not str or str [0] != '\\':
         return str
-    
+
     str = str[1:]
     if str[:1] =='K':
         key_table = compute_key ()
@@ -1083,12 +1128,12 @@ old_bar_dict = {
 '|]' : '|.',
 '||' : '||',
 '[|' : '||',
-':|' : ':|',
+':|' : ':|.',
 '|:' : '|:',
-'::' : ':|:',
+'::' : ':|.|:',
 '|1' : '|',
 '|2' : '|',
-':|2' : ':|',
+':|2' : ':|.',
 '|' :  '|'
 }
 bar_dict = {
@@ -1161,12 +1206,15 @@ def try_parse_bar (str,state):
         str = str[1:]
         clear_bar_acc(state)
         close_beam_state(state)
-    
+
+    if str[:1] == '}':
+        close_beam_state(state)
+
     if bs <> None or state.next_bar != '':
         if state.parsing_tuplet:
             state.parsing_tuplet =0
             voices_append ('} ')
-        
+
     if bs <> None:
         clear_bar_acc(state)
         close_beam_state(state)
@@ -1222,7 +1270,7 @@ def try_parse_chord_delims (str, state):
         end = end + 1
         str = str[1:]
 
-    
+
     voices_append ("\\spanrequest \\stop \"slur\"" * end)
     voices_append (ch)
     return str
@@ -1257,19 +1305,19 @@ def try_parse_comment (str):
 #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.  
+#the barlines should not affect its interpretation of the pitch.
             if 'nobarlines' in str:
                 nobarlines = 1
         elif str[0:3] == '%LY':
-            p = string.find(str, 'voices')
+            p = str.find ('voices')
             if (p > -1):
                 voices_append(str[p+7:])
                 voices_append("\n")
-            p = string.find(str, 'slyrics')
+            p = str.find ('slyrics')
             if (p > -1):
                 slyrics_append(str[p+8:])
-            
-#write other kinds of appending  if we ever need them.                        
+
+#write other kinds of appending  if we ever need them.
     return str
 
 lineno = 0
@@ -1282,10 +1330,11 @@ def parse_file (fn):
     select_voice('default', '')
     global lineno
     lineno = 0
-    sys.stderr.write ("Line ... ")
-    sys.stderr.flush ()
+    if not global_options.quiet:
+        sys.stderr.write ("Line ... ")
+        sys.stderr.flush ()
     __main__.state = state_list[current_voice_idx]
-    
+
     for ln in ls:
         lineno = lineno + 1
 
@@ -1300,7 +1349,8 @@ def parse_file (fn):
             ln = m.group (1)
 
         orig_ln = ln
-        
+
+        ln = junk_space (ln, state)
         ln = try_parse_header_line (ln, state)
 
         # Try nibbling characters off until the line doesn't change.
@@ -1324,11 +1374,12 @@ def parse_file (fn):
             error ("%s: %d: Huh?  Don't understand\n" % (fn, lineno))
             left = orig_ln[0:-len (ln)]
             sys.stderr.write (left + '\n')
-            sys.stderr.write (' ' *  len (left) + ln + '\n')        
+            sys.stderr.write (' ' *  len (left) + ln + '\n')
 
 
 def identify():
-    sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
+    if not global_options.quiet:
+        sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
 
 authors = """
 Written by Han-Wen Nienhuys <hanwen@xs4all.nl>, Laura Conrad
@@ -1340,20 +1391,35 @@ def print_version ():
 
 def get_option_parser ():
     p = ly.get_option_parser (usage=_ ("%s [OPTION]... FILE") % 'abc2ly',
-                 version="abc2ly (LilyPond) @TOPLEVEL_VERSION@",
                  description=_ ('''abc2ly converts ABC music files (see
-%s) to LilyPond input.''') % 'http://www.gre.ac.uk/~c.walshaw/abc2mtex/abc.txt')
-
-    p.add_option ('-o', '--output', metavar='FILE',
-                  help=_ ("write output to FILE"),
-                  action='store')
-    p.add_option ('-s', '--strict', help=_ ("be strict about succes"),
-                  action='store_true')
-    p.add_option ('-b', '--beams', help=_ ("preserve ABC's notion of beams"))
-    p.add_option_group ('bugs',
-                        description=(_ ('Report bugs via')
-                                     + ''' http://post.gmane.org/post.php'''
-                                     '''?group=gmane.comp.gnu.lilypond.bugs\n'''))
+%s) to LilyPond input.
+''') % 'http://abcnotation.com/abc2mtex/abc.txt',
+                 add_help_option=False)
+
+    p.version = "abc2ly (LilyPond) @TOPLEVEL_VERSION@"
+    p.add_option("--version",
+                 action="version",
+                 help=_ ("show version number and exit"))
+    p.add_option("-h", "--help",
+                 action="help",
+                 help=_ ("show this help and exit"))
+    p.add_option ("-o", "--output", metavar='FILE',
+                  action="store",
+                  help=_ ("write output to FILE"))
+    p.add_option ("-s", "--strict",
+                  action="store_true",
+                  help=_ ("be strict about success"))
+    p.add_option ('-b', '--beams',
+                  action="store_true",
+                  help=_ ("preserve ABC's notion of beams"))
+    p.add_option ('-q', '--quiet',
+                  action="store_true",
+                  help=_ ("suppress progress messages"))
+    p.add_option_group ('',
+                        description=(
+            _ ('Report bugs via %s')
+            % 'http://post.gmane.org/post.php'
+            '?group=gmane.comp.gnu.lilypond.bugs') + '\n')
     return p
 
 
@@ -1368,12 +1434,14 @@ for f in files:
     if f == '-':
         f = ''
 
-    sys.stderr.write ('Parsing `%s\'...\n' % f)
+    if not global_options.quiet:
+        sys.stderr.write ('Parsing `%s\'...\n' % f)
     parse_file (f)
 
     if not global_options.output:
         global_options.output = os.path.basename (os.path.splitext (f)[0]) + ".ly"
-    sys.stderr.write ('lilypond output to: `%s\'...' % global_options.output)
+    if not global_options.quiet:
+        sys.stderr.write ('lilypond output to: `%s\'...' % global_options.output)
     outf = open (global_options.output, 'w')
 
 # don't substitute @VERSION@. We want this to reflect
@@ -1386,5 +1454,5 @@ for f in files:
     dump_voices (outf)
     dump_score (outf)
     dump_lyrics (outf)
-    sys.stderr.write ('\n')
-    
+    if not global_options.quiet:
+        sys.stderr.write ('\n')