]> git.donarmstrong.com Git - lilypond.git/blobdiff - scripts/abc2ly.py
(option_parser): update to 2.7.40
[lilypond.git] / scripts / abc2ly.py
index de7decda9666e62a5215bfee5cc925d261bac0be..1257182f718cf0ae268b436a357c099d9b1c2086 100644 (file)
@@ -1,5 +1,5 @@
 #!@PYTHON@
-
+# -*- coding: utf-8 -*-
 # once upon a rainy monday afternoon.
 #
 #   ...
 # \breve and \longa supported.
 # M:none doesn't crash lily.
 
+# Enhancements (Guy Gascoigne-Piggford)
+#
+# Add support for maintaining ABC's notion of beaming, this is selectable
+# from the command line with a -b or --beam option.
+# Fixd a problem where on cygwin empty lines weren't being correctly identifed
+# and so were complaining, but still generating the correct output.
+
 # Limitations
 #
 # Multiple tunes in single file not supported
 
 #TODO:
 #
+# * lilylib
+# * GNU style messages:  warning:FILE:LINE:
+# * l10n
+# 
 # Convert to new chord styles.
 #
 # UNDEF -> None
 #
   
-  
-program_name = 'abc2ly'
-version = '@TOPLEVEL_VERSION@'
-if version == '@' + 'TOPLEVEL_VERSION' + '@':
-       version = '(unknown version)'           # uGUHGUHGHGUGH
-  
+
 import __main__
 import getopt
 import sys
@@ -68,10 +74,38 @@ import re
 import string
 import os
 
+program_name = sys.argv[0]
+
+
+datadir = '@local_lilypond_datadir@'
+if not os.path.isdir (datadir):
+       datadir = '@lilypond_datadir@'
+
+sys.path.insert (0, os.path.join (datadir, 'python'))
+
+if os.environ.has_key ('LILYPONDPREFIX'):
+       datadir = os.environ['LILYPONDPREFIX']
+       while datadir[-1] == os.sep:
+               datadir= datadir[:-1]
+               
+       datadir = os.path.join (datadir, "share/lilypond/current/")
+sys.path.insert (0, os.path.join (datadir, 'python'))
+
+# dynamic relocation, for GUB binaries.
+bindir = os.path.split (sys.argv[0])[0]
+for p in ['share', 'lib']:
+       datadir = os.path.abspath (bindir + '/../%s/lilypond/current/python/' % p)
+       sys.path.insert (0, os.path.join (datadir))
+
+import lilylib as ly
+global _;_=ly._
+
+version = '@TOPLEVEL_VERSION@'
+if version == '@' + 'TOPLEVEL_VERSION' + '@':
+       version = '(unknown version)'           # uGUHGUHGHGUGH  
 
 UNDEF = 255
 state = UNDEF
-strict = 0
 voice_idx_dict = {}
 header = {}
 header['footnotes'] = ''
@@ -90,77 +124,77 @@ nobarlines = 0
 global_key = [0] * 7                   # UGH
 names = ["One", "Two", "Three"]
 DIGITS='0123456789'
-alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ"  
 HSPACE=' \t'
 midi_specs = ''
 
 
 def error (msg):
        sys.stderr.write (msg)
-       if strict:
+       if global_options.strict:
                sys.exit (1)
        
+
+def alphabet (i):
+       return chr (i + ord('A'))
        
 def check_clef(s):
-      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" )
-      return s
+       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" )
+       return s
 
 def select_voice (name, rol):
        if not voice_idx_dict.has_key (name):
-             state_list.append(Parser_state())
-             voices.append ('')
-             slyrics.append ([])
-             voice_idx_dict[name] = len (voices) -1
+               state_list.append(Parser_state())
+               voices.append ('')
+               slyrics.append ([])
+               voice_idx_dict[name] = len (voices) -1
        __main__.current_voice_idx =  voice_idx_dict[name]
        __main__.state = state_list[current_voice_idx]
        while rol != '':
-             m = re.match ('^([^ \t=]*)=(.*)$', rol) # find keywork
-             if m:
-                     keyword = m.group(1)
-                     rol = m.group (2)
-                     a = re.match ('^("[^"]*"|[^ \t]*) *(.*)$', rol)
-                     if a:
-                             value = a.group (1)
-                             rol = a.group ( 2)
-                             if keyword == 'clef':
-                                     check_clef(value)
-                             elif keyword == "name":
-                                     value = re.sub ('\\\\','\\\\\\\\', value)
-                                     voices_append ("\\property Staff.instrument = %s\n" % value )
-                                     __main__.part_names = 1
-                             elif keyword == "sname" or keyword == "snm":
-                                     voices_append ("\\property Staff.instr = %s\n" % value )
-
-             else:
-                     break
-
-       return
-
+               m = re.match ('^([^ \t=]*)=(.*)$', rol) # find keywork
+               if m:
+                       keyword = m.group(1)
+                       rol = m.group (2)
+                       a = re.match ('^("[^"]*"|[^ \t]*) *(.*)$', rol)
+                       if a:
+                               value = a.group (1)
+                               rol = a.group ( 2)
+                               if keyword == 'clef':
+                                       check_clef(value)
+                               elif keyword == "name":
+                                       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 )
+               else:
+                       break
 
 def dump_header (outf,hdr):
        outf.write ('\\header {\n')
@@ -173,17 +207,18 @@ def dump_header (outf,hdr):
 
 def dump_lyrics (outf):
        if (len(lyrics)):
-               outf.write("\n\\score\n{\n    \\context Lyrics\n    <\n")
+               outf.write("\n\\score\n{\n \\lyrics\n <<\n")
                for i in range (len (lyrics)):
                        outf.write ( lyrics [i])
                        outf.write ("\n")
-               outf.write("    >\n    \\paper{}\n}\n")
+               outf.write("    >>\n    \\layout{}\n}\n")
 
 def dump_default_bar (outf):
        """
        Nowadays abc2ly outputs explicits barlines (?)
        """
-       outf.write ("\n\\property Score.defaultBarType=\"empty\"\n")
+       ## < 2.2
+       outf.write ("\n\\set Score.defaultBarType = \"empty\"\n")
 
 
 def dump_slyrics (outf):
@@ -191,12 +226,12 @@ def dump_slyrics (outf):
        ks.sort ()
        for k in ks:
                if re.match('[1-9]', k):
-                       m = alphabet[string.atoi(k)]
+                       m = alphabet(string.atoi(k))
                else:
                        m = k
                for i in range (len(slyrics[voice_idx_dict[k]])):
-                       l=alphabet[i]
-                       outf.write ("\nwords%sV%s = \\lyrics  {" % (m, l))
+                       l= alphabet(i)
+                       outf.write ("\nwords%sV%s = \lyricmode {" % (m, l))
                        outf.write ("\n" + slyrics [voice_idx_dict[k]][i])
                        outf.write ("\n}")
 
@@ -206,10 +241,10 @@ def dump_voices (outf):
        ks.sort ()
        for k in ks:
                if re.match ('[1-9]', k):
-                       m = alphabet[string.atoi(k)]
+                       m = alphabet(string.atoi(k))
                else:
                        m = k
-               outf.write ("\nvoice%s = \\notes {" % m)
+               outf.write ("\nvoice%s =  {" % m)
                dump_default_bar(outf)
                if repeat_state[voice_idx_dict[k]]:
                        outf.write("\n\\repeat volta 2 {")
@@ -239,43 +274,40 @@ def try_parse_q(a):
                sys.stderr.write("abc2ly: Warning, unable to parse Q specification: %s\n" % a)
         
 def dump_score (outf):
-       outf.write (r"""\score{
-        \notes <
+       outf.write (r"""
+
+\score{
+         <<
 """)
 
-       ks  = voice_idx_dict.keys ();
+       ks = voice_idx_dict.keys ();
        ks.sort ()
        for k in  ks:
                if re.match('[1-9]', k):
-                       m = alphabet[string.atoi(k)]
+                       m = alphabet (string.atoi(k))
                else:
                        m = k
                if k == 'default' and len (voice_idx_dict) > 1:
                        break
-               if len ( slyrics [voice_idx_dict[k]] ):
-                       outf.write ("\n        \\addlyrics")
                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)
                outf.write ("\n\t}\n")
-               if len ( slyrics [voice_idx_dict[k]] ):
-                       outf.write ("\n\t\\context Lyrics=\"%s\" \n\t<\t" % k)
+
+               l = ord( 'A' )
+               for lyrics in slyrics [voice_idx_dict[k]]:
+                       outf.write ("\n\t\\addlyrics { \n")
                        if re.match('[1-9]',k):
-                               m = alphabet[string.atoi(k)]
+                               m = alphabet (string.atoi(k))
                        else:
                                m = k
-                       for i in range (len(slyrics[voice_idx_dict[k]])):
-                               l=alphabet[i]
-                               outf.write("\n\t  { \\words%sV%s }" % ( m, l) )
-                       outf.write ( "\n\t>\n" )
-       outf.write ("\n    >")
-       outf.write ("\n\t\\paper {\n")
-       if part_names:
-               outf.write ("\t    \\translator \n\t    {\n")
-               outf.write ("\t\t\\StaffContext\n")
-#              outf.write ("\t\t\\consists Staff_margin_engraver\n")
-               outf.write ("\t    }\n")
+
+                       outf.write ( " \\words%sV%s } " % ( m, chr (l)) )
+                       l += 1
+
+       outf.write ("\n    >>")
+       outf.write ("\n\t\\layout {\n")
        outf.write ("\t}\n\t\\midi {%s}\n}\n" % midi_specs)
 
 
@@ -388,8 +420,11 @@ key_lookup = {     # abc to lilypond key mode names
 }
 
 def lily_key (k):
+       orig = "" + k
+       # UGR
        k = string.lower (k)
        key = k[0]
+       #UGH
        k = k[1:]
        if k and k[0] == '#':
                key = key + 'is'
@@ -401,24 +436,27 @@ def lily_key (k):
                return '%s \\major' % key
 
        type = k[0:3]
-       if key_lookup.has_key(type):
-               return("%s \\%s" % ( key, key_lookup[type]))
-       sys.stderr.write("Unknown key type `%s' ignored\n" % type)
-       return key
-
-def shift_key (note, acc , shift):
-        s = semitone_pitch((note, acc))
-        s = (s + shift + 12) % 12
-        if s <= 4:
-                n = s / 2
-                a = s % 2
-        else:
-                n = (s + 1) / 2
-                a = (s + 1) % 2
-        if a:
-                n = n + 1
-                a = -1
-        return (n,a)
+       if not key_lookup.has_key (type):
+               #ugh, use lilylib, say WARNING:FILE:LINE:
+               sys.stderr.write ("abc2ly:warning:")
+               sys.stderr.write ("ignoring unknown key: `%s'" % orig)
+               sys.stderr.write ('\n')
+               return 0
+       return ("%s \\%s" % ( key, key_lookup[type]))
+
+def shift_key (note, acc, shift):
+       s = semitone_pitch((note, acc))
+       s = (s + shift + 12) % 12
+       if s <= 4:
+               n = s / 2
+               a = s % 2
+       else:
+               n = (s + 1) / 2
+               a = (s + 1) % 2
+       if a:
+               n = n + 1
+               a = -1
+       return (n,a)
 
 key_shift = { # semitone shifts for key mode names
        'm'   : 3,
@@ -507,6 +545,7 @@ def try_parse_tuplet_begin (str, state):
 def  try_parse_group_end (str, state):
        if str and str[0] in HSPACE:
                str = str[1:]
+               close_beam_state(state)
        return str
        
 def header_append (key, a):
@@ -527,13 +566,31 @@ def stuff_append (stuff, idx, a):
        else:
                stuff [idx] = wordwrap(a, stuff[idx])
 
-
+# ignore wordwrap since we are adding to the previous word
+def stuff_append_back(stuff, idx, a):
+       if not stuff:
+               stuff.append (a)
+       else:
+               point = len(stuff[idx])-1
+               while stuff[idx][point] is ' ':
+                       point = point - 1
+               point = point +1
+               stuff[idx] = stuff[idx][:point] + a + stuff[idx][point:]
 
 def voices_append(a):
        if current_voice_idx < 0:
                select_voice ('default', '')
        stuff_append (voices, current_voice_idx, a)
 
+# word wrap really makes it hard to bind beams to the end of notes since it
+# pushes out whitespace on every call. The _back functions do an append
+# prior to the last space, effectively tagging whatever they are given
+# onto the last note
+def voices_append_back(a):
+       if current_voice_idx < 0:
+               select_voice ('default', '')
+       stuff_append_back(voices, current_voice_idx, a)
+
 def repeat_prepend():
        global repeat_state
        if current_voice_idx < 0:
@@ -543,9 +600,9 @@ def repeat_prepend():
 
        
 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{ \\lyrics "' + a + '" }\n'
+       a = re.sub ('#', '\\#', a)      # latex does not like naked #'s
+       a = re.sub ('"', '\\"', a)      # latex does not like naked "'s
+       a = '\t{  "' + a + '" }\n'
        stuff_append (lyrics, current_lyric_idx, a)
 
 # break lyrics to words and put "'s around words containing numbers and '"'s
@@ -596,7 +653,10 @@ def try_parse_header_line (ln, state):
                        if header.has_key('title'):
                                if a:
                                        if len(header['title']):
-                                               header['title'] = header['title'] + '\\\\\\\\' + a
+                                               # the non-ascii character
+                                               # in the string below is a
+                                               # punctuation dash. (TeX ---)
+                                               header['title'] = header['title'] + ' — ' + a
                                        else:
                                                header['subtitle'] = a
                        else:
@@ -605,12 +665,12 @@ def try_parse_header_line (ln, state):
                        if a == 'C':
                                if not state.common_time:
                                        state.common_time = 1
-                                       voices_append ("\\property Staff.TimeSignature \\override #\'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 ("\\property Staff.TimeSignature \\override #\'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)
@@ -632,12 +692,16 @@ def try_parse_header_line (ln, state):
                                        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))
+                                       __main__.global_key  = compute_key (key_info)
+                                       k = lily_key (key_info)
+                                       if k:
+                                               voices_append ('\\key %s' % k)
                                        check_clef(clef_info)
                                else:
-                                       __main__.global_key  = compute_key (a)# ugh.
-                                       voices_append ('\\key %s \\major' % lily_key(a))
+                                       __main__.global_key  = compute_key (a)
+                                       k = lily_key (a)
+                                       if k:
+                                               voices_append ('\\key %s \\major' % k)
                if g == 'N': # Notes
                        header ['footnotes'] = header['footnotes'] +  '\\\\\\\\' + a
                if g == 'O': # Origin
@@ -744,6 +808,7 @@ class Parser_state:
                self.plus_chord = 0
                self.base_octave = 0
                self.common_time = 0
+               self.parsing_beam = 0
 
 
 
@@ -880,6 +945,12 @@ def clear_bar_acc(state):
                del state.in_acc[k]
        
 
+# if we are parsing a beam, close it off
+def close_beam_state(state):
+       if state.parsing_beam and global_options.beams:
+               state.parsing_beam = 0
+               voices_append_back( ']' )
+
                
 # WAT IS ABC EEN ONTZETTENDE PROGRAMMEERPOEP  !
 def try_parse_note (str, parser_state):
@@ -901,7 +972,7 @@ def try_parse_note (str, parser_state):
                if c == '_':
                        acc = -1
 
-        octave = parser_state.base_octave
+       octave = parser_state.base_octave
        if str[0] in "ABCDEFG":
                str = string.lower (str[0]) + str[1:]
                octave = octave - 1
@@ -966,13 +1037,19 @@ def try_parse_note (str, parser_state):
        if slur_end:
                voices_append ('-)' *slur_end )
 
-
-
+       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):
-       while str and str[0] in '\t\n ':
+def junk_space (str,state):
+       while str and str[0] in '\t\n\r ':
                str = str[1:]
+               close_beam_state(state)
 
        return str
 
@@ -993,7 +1070,8 @@ def try_parse_guitar_chord (str, state):
                if str:
                        str = str[1:]
                gc = re.sub('#', '\\#', gc)     # escape '#'s
-               state.next_articulation = ("%c\"%s\"" % (position ,gc)) + state.next_articulation
+               state.next_articulation = ("%c\"%s\"" % (position, gc)) \
+                                         + state.next_articulation
        return str
 
 def try_parse_escape (str):
@@ -1095,6 +1173,7 @@ def try_parse_bar (str,state):
                state.next_bar = '|\n'
                str = str[1:]
                clear_bar_acc(state)
+               close_beam_state(state)
        
        if bs <> None or state.next_bar != '':
                if state.parsing_tuplet:
@@ -1103,6 +1182,7 @@ def try_parse_bar (str,state):
                
        if bs <> None:
                clear_bar_acc(state)
+               close_beam_state(state)
                voices_append (bs)
                if do_curly != '':
                        voices_append("} }")
@@ -1131,24 +1211,24 @@ def try_parse_chord_delims (str, state):
                if state.next_bar:
                        voices_append(state.next_bar)
                        state.next_bar = ''
-               voices_append ('<')
+               voices_append ('<<')
 
        if str[:1] == '+':
                str = str[1:]
                if state.plus_chord:
-                       voices_append ('>')
+                       voices_append ('>>')
                        state.plus_chord = 0
                else:
                        if state.next_bar:
                                voices_append(state.next_bar)
                                state.next_bar = ''
-                       voices_append ('<')
+                       voices_append ('<<')
                        state.plus_chord = 1
 
        ch = ''
        if str[:1] ==']':
                str = str[1:]
-               ch = '>'
+               ch = '>>'
 
        end = 0
        while str[:1] ==')':
@@ -1205,12 +1285,15 @@ def try_parse_comment (str):
 #write other kinds of appending  if we ever need them.                 
        return str
 
+lineno = 0
 happy_count = 100
 def parse_file (fn):
        f = open (fn)
        ls = f.readlines ()
+       ls = map (lambda x: re.sub ("\r$", '', x), ls)
 
        select_voice('default', '')
+       global lineno
        lineno = 0
        sys.stderr.write ("Line ... ")
        sys.stderr.flush ()
@@ -1248,7 +1331,7 @@ def parse_file (fn):
                        ln = try_parse_tuplet_begin (ln, state)
                        ln = try_parse_group_end (ln, state)
                        ln = try_parse_grace_delims (ln, state)
-                       ln = junk_space (ln)
+                       ln = junk_space (ln, state)
 
                if ln:
                        error ("%s: %d: Huh?  Don't understand\n" % (fn, lineno))
@@ -1260,52 +1343,35 @@ def parse_file (fn):
 def identify():
        sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
 
-def help ():
-       print r"""
-Convert ABC to lilypond.
-
-Usage: abc2ly [OPTIONS]... ABC-FILE
-
-Options:
-  -h, --help          print this help
-  -o, --output=FILE   set output filename to FILE
-  -v, --version       show version information
-  -s, --strict        be strict about succes
-  
-This program converts ABC music files (see
-http://www.gre.ac.uk/~c.walshaw/abc2mtex/abc.txt) To LilyPond input.
-
-
-Report bugs to bug-lilypond@gnu.org.
-
+authors = """
 Written by Han-Wen Nienhuys <hanwen@cs.uu.nl>, Laura Conrad
-<lconrad@laymusic.org>, Roy Rankin <Roy.Rankin@@alcatel.com.au>
+<lconrad@laymusic.org>, Roy Rankin <Roy.Rankin@@alcatel.com.au>.
 """
 
 def print_version ():
        print r"""abc2ly (GNU lilypond) %s""" % version
 
+def get_option_parser ():
+       p = ly.get_option_parser (usage='abc2ly [OPTIONS] FILE',
+                                 version="abc2ly (LilyPond) @TOPLEVEL_VERSION@",
+                                 description=_('''This program converts ABC music files (see
+http://www.gre.ac.uk/~c.walshaw/abc2mtex/abc.txt) to LilyPond input.'''))
+
+       p.add_option ('-o', '--output', metavar='FILE',help=_("set output filename 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''')
+       
+       return p
 
 
-(options, files) = getopt.getopt (sys.argv[1:], 'vo:hs', ['help','version', 'output=', 'strict'])
-out_filename = ''
+option_parser = get_option_parser()
+(global_options, files) = option_parser.parse_args()
 
-for opt in options:
-       o = opt[0]
-       a = opt[1]
-       if o== '--help' or o == '-h':
-               help ()
-               sys.exit (0)
-       elif o == '--version' or o == '-v':
-               print_version ()
-               sys.exit(0)
-       elif o == '--strict' or o == '-s':
-               strict = 1
-       elif o == '--output' or o == '-o':
-               out_filename = a
-       else:
-               print o
-               raise getopt.error
 
 identify()
 
@@ -1317,13 +1383,17 @@ for f in files:
        sys.stderr.write ('Parsing `%s\'...\n' % f)
        parse_file (f)
 
-       if not out_filename:
-               out_filename = os.path.basename (os.path.splitext (f)[0]) + ".ly"
-       sys.stderr.write ('lilypond output to: `%s\'...' % out_filename)
-       outf = open (out_filename, 'w')
+       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)
+       outf = open (global_options.output, 'w')
+
+# don't substitute @VERSION@. We want this to reflect
+# the last version that was verified to work.
+       outf.write ('\\version "2.7.40"\n')
 
 #      dump_global (outf)
-       dump_header (outf ,header)
+       dump_header (outfheader)
        dump_slyrics (outf)
        dump_voices (outf)
        dump_score (outf)