]> git.donarmstrong.com Git - lilypond.git/commitdiff
release: 1.3.84 release/1.3.84
authorHan-Wen Nienhuys <hanwen@xs4all.nl>
Sat, 2 Sep 2000 20:46:50 +0000 (22:46 +0200)
committerHan-Wen Nienhuys <hanwen@xs4all.nl>
Sat, 2 Sep 2000 20:46:50 +0000 (22:46 +0200)
======

* pmx2ly, PMX to LilyPond conversion. Succesfully converts barsant.pmx.

* musedata2ly, a Musedata to LilyPond convertor. Succesfully converts
wtk1-fugue2

* bugfixes for dynamics: make sure that dynamic alignments encompass
the dynamics they support, and that any columns they attach to aren't
prematurely killed.

* bugfix: make sure \time gets noticed early enough.

1.3

24 files changed:
CHANGES
Documentation/regression-test.tely
NEWS
VERSION
buildscripts/musedata2ly.py [deleted file]
buildscripts/pmx2ly.py [deleted file]
input/bugs/script-collide.ly [new file with mode: 0644]
input/test/lyrics-multi-stanza.ly [new file with mode: 0644]
input/test/multistanza.ly [deleted file]
lily/axis-group-engraver.cc
lily/directional-element-interface.cc
lily/dynamic-engraver.cc
lily/include/spanner.hh
lily/line-group-group-engraver.cc
lily/main.cc
lily/paper-column.cc
lily/score-engraver.cc
lily/spanner.cc
lily/timing-translator.cc
make/out/lilypond.lsm
make/out/lilypond.spec
scripts/etf2ly.py
scripts/musedata2ly.py [new file with mode: 0644]
scripts/pmx2ly.py [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index 1e2dd72443c7d1bdc2d928127d247b0986b17ed1..ef5141e5be04ef465052e7dcea9a163414682fee 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,18 @@
 
+1.3.84
+======
+
+* pmx2ly, PMX to LilyPond conversion. Succesfully converts barsant.pmx.
+
+* musedata2ly, a Musedata to LilyPond convertor. Succesfully converts
+wtk1-fugue2
+
+* bugfixes for dynamics: make sure that dynamic alignments encompass
+the dynamics they support, and that any columns they attach to aren't
+prematurely killed.
+
+* bugfix: make sure \time gets noticed early enough.
+
 1.3.83
 ======
 
@@ -33,7 +47,6 @@
 * Smobified Translator and Translator_group, junked
 Translator_group_identifier.
 
-
 * \pushproperty and \popproperty withing \translator, similar to
 predefining \property, ie.
 
@@ -330,9 +343,6 @@ have self_scm_)
 * Glen Prideaux lyric phrasing engraver. See
 input/test/lyric-phrasing.ly
 
-
-
-
 1.3.73
 ======
 * Removed \interscoreline after the last line, prevents some
index c157ffa346d21e35ae86f9e3b7713d79ac78c7d9..127f991038074b4488aeb30f5a65949deeb91f64 100644 (file)
@@ -262,6 +262,10 @@ to work.
 
 @mudelafile{lyric-combine.ly}
 
+Multiple stanzas
+
+@mudelafile{lyrics-multi-stanza.ly}
+
 @section Multiple notes
 
 Rests should not collide with beams, stems and noteheads.  Rests may
diff --git a/NEWS b/NEWS
index 90f069c6f7a41164940a0b869b4e3fa911606abb..f2da758e4516e273c10cb6c9adb8c80380da20a0 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-* Finale (.etf) import program. 
+* Finale (.etf), PMX (.pmx) and musedata import tools.
 
 * Point and click functionality using emacs and Xdvi.
 
@@ -18,10 +18,12 @@ internals: LilyPond is smaller, cleaner, more flexible, etc.
 
 * Typography: More elegant slurs, aligned dynamics, text crescendos,
 
-* Nice lyrics placement: Automagical phrasing and melisma alignment.
+* Better lyrics placement: Automagical phrasing, melisma alignment,
+  stanza numbering.
 
-* Part combining for orchestral scores and hymns: Automagical combining
-  and separating of two voices, with Solo/`a2 indications as appropriate.
+* Part combining for orchestral scores and hymns: two voices are
+  combined automatic into a staff automatically, including Solo/`a2
+  indications as appropriate.
 
 * Chordnames are now configurable in every respect
 
@@ -32,3 +34,5 @@ internals: LilyPond is smaller, cleaner, more flexible, etc.
 * Finished ouverture Coriolan as full orchestral score example.
 
 * AsciiScript [check if broken, decide wether to keep]
+
+* Translations into Japanese and Russian
diff --git a/VERSION b/VERSION
index 745ecaa54b98dd35ca73d1188ad3e5db6830866b..c692feef2144f27eecac44c70e079731396880ca 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1,7 +1,7 @@
 PACKAGE_NAME=LilyPond
 MAJOR_VERSION=1
 MINOR_VERSION=3
-PATCH_LEVEL=83
+PATCH_LEVEL=84
 MY_PATCH_LEVEL=
 
 # use the above to send patches: MY_PATCH_LEVEL is always empty for a
diff --git a/buildscripts/musedata2ly.py b/buildscripts/musedata2ly.py
deleted file mode 100644 (file)
index 1bbd165..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-#!@PYTHON@
-
-# musedata = musedata.stanford.edu
-# musedata = COBOL for musicians.
-# todo: rewrite this.
-
-import re
-import sys
-import string
-
-f = open (sys.argv[1])
-lines =f.readlines()
-
-def chomp (x):
-       return re.sub ('[\r\n \t]+$','', x)
-
-lines = map (chomp, lines)
-
-default_header_dict = {
-       'tagline' :'automatically converted from Musedata',
-       'copyright' : 'all rights reserved -- free for noncommercial use'
-       }
-
-# Jezus, wat een ranzig formaat. (2am)
-def parse_header (lines):
-       d = default_header_dict
-       enter = string.split (lines[3], ' ')
-       d['enteredby']  = string.join (enter[1:])
-       d['enteredon'] = enter[0]
-       d['opus'] = lines[4]
-       d['source'] = lines[5]
-       d['title'] = lines[6]
-       d['subtitle'] = lines[7]
-       d['instrument']= lines[8]
-       d['musedatamisc'] =lines[9]
-       d['musedatagroups'] =lines[10]
-       d['musedatagroupnumber']=lines[11]
-
-       return d
-
-clef_dict = {
-04: 'treble',
-13 : 'alto',
-22: 'bass',
-
-}
-
-def get_clef(s):
-       return '\\clef "%s";\n' % clef_dict [string.atoi (s)]
-
-def get_mudela_notename (p, ac):
-       if p > 5:
-               p = p - 7
-       s = chr (p + ord ('c'))
-       infix = 'i'
-       if ac < 0:
-               infix = 'e'
-               ac = -ac
-
-       while ac:
-               s = s + infix + 's'
-               ac = ac - 1
-       return s
-
-def get_key (s):
-       i = string.atoi (s)
-       return ''
-
-def get_timesig (s):
-       return '\\time %s;\n' % s
-
-
-divisions = 4
-def get_divisions_per_quarter (s):
-       divisions = string.atoi (s) 
-       return ''
-
-def get_directive (s):
-       return '%% %s\n' % s
-
-def get_transposing (s):
-       return ''
-
-def get_num_instruments (s):
-       return ''
-
-attr_dict = {
-       'C' : get_clef,
-       'K' : get_key ,
-       'T' : get_timesig,
-       'Q' : get_divisions_per_quarter,
-       'D' : get_directive,
-       'X' : get_transposing,
-       'I': get_num_instruments,
-       }
-
-def parse_musical_attributes (l):
-       s = ''
-       l = l[1:]
-       atts = re.split('[ \t]+', l)
-       for a in atts:
-               if not a:
-                       continue
-               m = re.search ('(.):(.*)', a)
-               if m == None:
-                       print 'Huh, unknown attr `%s\'' % a
-                       continue
-
-               s = s + attr_dict[m.group(1)](m.group (2))
-       return s
-
-
-def get_mudela_pitch (n, a, o):
-       c = '\''
-       if o < 1:
-               c = ','
-               o = 1 - o
-
-       return get_mudela_notename (n,a) +  '%s' % c * o
-
-def dump_header (h, out):
-       out.write ('\\header {\n')
-       for tup in h.items ():
-               out.write ('\t%s = \"%s\";\n' % tup)
-       out.write ('}\n')
-               
-header_dict = parse_header (lines[0:12])
-dump_header (header_dict, sys.stdout)
-
-
-lines  = lines [12:]
-
-
-def parse_line_comment (l):
-       return re.sub ('@' , '%' , l)
-
-def parse_note_line (l):
-       pitch = ((ord (l[0]) -ord('A')) + 5) % 7
-       acc = 0
-       l= l[1:]
-       while l[0] == 'f':
-               l= l[1:]
-               acc = acc - 1
-       while l[0] == '#':
-               l= l[1:]
-               acc = acc + 1
-       while l[0] in ' \t':
-               l= l[1:]
-               
-       oct = 0
-       if l[0] in '0123456789':
-               oct = string.atoi (l[0]) - 4
-               l= l[1:]
-
-       while l[0] in ' \t':
-               l= l[1:]
-
-       
-       print get_mudela_pitch (pitch,acc,oct), parse_duration(l[:2])
-       l = l[2:]
-       
-       
-
-       
-def parse_duration (l):
-       s = ''
-       while l[0] in '0123456789':
-               s = s + l[0]
-               l= l[1:]
-       print l
-       num = string.atoi (s)
-       den = 4 * divisions 
-
-       current_dots = 0
-       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
-
-       if num <> 1:
-               sys.stderr.write ('huh. Durations left')
-       return '%s%s' % (den, '.' * current_dots)
-       
-comment_switch = 0
-for l in lines:
-       if l[0] == '&':
-               comment_switch = not comment_switch
-               if comment_switch:
-                       l= l[1:]
-                       print '%{'
-               else:
-                       print '%}'
-                       
-       if comment_switch:
-               print l
-               continue
-
-       if 0:
-               pass
-       elif l[0] == '$':
-               print parse_musical_attributes (l)
-       elif l[0] == '@':
-               parse_line_comment (l)
-
-       elif l[0] in 'ABCDEFG':
-               parse_note_line (l)
diff --git a/buildscripts/pmx2ly.py b/buildscripts/pmx2ly.py
deleted file mode 100644 (file)
index 2f91550..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-#!@PYTHON@
-
-# (urg! wat een pokkeformaat (pokkenformaat?))  
-
-import string
-import sys
-import re
-
-fn = sys.argv[1]
-
-ls = open (fn).readlines ()
-def stripcomment (l):
-       return re.sub ('[ \t]*%.*$\n', '', l)
-       
-def stripwhite (l):
-       return re.sub ('[ \n\t]+', ' ', l)
-       
-def stripeols (l):
-       return re.sub ('^ ',  '', re.sub (' $', '', l))
-       
-ls = map (stripcomment, ls)
-ls = map (stripwhite, ls)
-ls = map (stripeols, ls)
-
-
-ls = filter (lambda x: x <> '', ls)
-
-opening = ls[0]
-ls = ls[1:]
-
-
-opening = map (string.atoi, re.split ('[\t ]+', opening))
-
-(no_staffs, no_instruments, timesig_num,timesig_den, ptimesig_num,
- ptimesig_den, pickup_beats,keysig_number) = tuple (opening)
-
-
-opening = ls[0]
-ls = ls[1:]
-
-# ignore this.
-# opening = map (string.atoi, re.split ('[\t ]+', opening))
-# (no_pages,no_systems, musicsize, fracindent) = tuple (opening)
-
-instruments = []
-while len (instruments) < no_instruments:
-       instruments.append (ls[0])
-       ls = ls[1:]
-
-class Staff:
-       def __init__ (self): 
-               self.voices = ([],[])
-               self.clef = None
-               self.instrument = 0 
-l = ls[0]
-ls = ls[1:]
-
-staffs = map (lambda x: Staff (), range(0, no_staffs))
-staff_idx = 0
-
-for s in staffs:
-       s.clef = l[0]
-       l = l[1:]
-
-# dump path 
-ls = ls[1:] 
-
-# dump more ?
-ls = ls[2:]
-
-actab = {-2: 'eses', -1: 'es', 0 : '', 1: 'is', 2:'isis'}
-
-def pitch_to_lily_string (tup):
-       (o,n,a) = tup
-
-       nm = chr((n + 2) % 7 + ord ('a'))
-       nm = nm + actab[a]
-       if o > 0:
-               nm = nm + "'" * o
-       elif o < 0:
-               nm = nm + "," * -o
-       return nm
-
-class Chord:
-       def __init__ (self):
-               self.pitches = []
-               self.dots = 0
-               self.basic_duration = 0
-               
-       def dump (self):
-               str = ''
-
-               for p in self.pitches:
-                       if str:
-                               str = str + ' ' 
-                       str = str + pitch_to_lily_string (p)
-
-               if len (self.pitches) > 1:
-                       str = '<%s>' % str
-               elif len (self.pitches) == 0:
-                       str = 'r'
-               
-               
-               sd = ''
-               if self.basic_duration == 0.5:
-                       sd = '\\breve'
-               else:
-                       sd = '%d' % self.basic_duration
-
-               str = str + sd + '.' * self.dots 
-               return str
-               
-
-input_left = string.join (ls, ' ')
-
-
-input_left = re.sub ('[ \t\n]+',   ' ', input_left)
-
-SPACE=' \t\n'
-DIGITS ='0123456789'
-basicdur_table = {
-       9: 0.5,
-       0: 0 ,
-       2: 2 ,
-       4: 4 ,
-       8: 8 ,
-       1: 16,
-       3: 32,
-       6: 64
-       }
-
-class Parser:
-       def __init__ (self):
-               self.chords = []
-               self.forced_duration = None
-               self.last_octave = 4
-               
-       def parse_note (self, str):
-               ch = Chord ()
-
-               name = None
-               if str[0] <> 'r':
-                       name = (ord (str[0]) - ord('a') + 5) % 7
-               str = str[1:]
-               
-               forced_duration  = 0
-               alteration = 0
-               dots = 0
-               oct = None
-               durdigit = None
-               multibar = 0
-               while str[0] in 'dsfmnul0123456789.,':
-                       c = str[0]
-                       str = str[1:]
-                       if c == 'f':
-                               alteration = alteration -1
-                       elif c == 'n':
-                               alteration = 0
-                       elif c == 'm':
-                               multibar = 1
-                       elif c == 's':
-                               alteration = alteration +1
-                       elif c == 'd':
-                               dots = dots + 1
-                       elif c in DIGITS and durdigit == None:
-                               durdigit = string.atoi (c)
-                       elif c in DIGITS:
-                               oct = string.atoi (c) - 4
-                       elif c == '.':
-                               dots = dots+ 1
-                               forced_duration = 2
-                       elif c == ',':
-                               forced_duration = 2
-                       
-
-               if durdigit:
-                       ch.basic_duration = basicdur_table[durdigit]
-                       self.last_basic_duration = ch.basic_duration
-               else:
-                       ch.basic_duration = self.last_basic_duration
-
-               if name:
-                       if oct:
-                               self.last_octave =oct
-                       else:
-                               oct = self.last_octave
-
-               if name:
-                       ch.pitches.append ((oct, name,  alteration))
-                       
-               ch.dots = dots
-
-               
-               if forced_duration:
-                       self.forced_duration = ch.basic_duration / forced_duration
-
-
-               self.chords.append (ch)
-               while str[0] in SPACE:
-                       str = str [1:]
-               return str
-
-
-parser =  Parser()
-while input_left:
-       while input_left[0] in 'abcdefgr':
-               input_left = parser.parse_note (input_left)
-       print input_left[0]
-       
-       sys.stderr.write ("\nHuh? Unknown directive %s" %input_left[0:1])
-       input_left = input_left[1:]
-
-
-
-for c in parser.chords:
-       print c.dump ()
-       
diff --git a/input/bugs/script-collide.ly b/input/bugs/script-collide.ly
new file mode 100644 (file)
index 0000000..3167a77
--- /dev/null
@@ -0,0 +1,2 @@
+
+\score { \notes { c4-1-2-3 }}
diff --git a/input/test/lyrics-multi-stanza.ly b/input/test/lyrics-multi-stanza.ly
new file mode 100644 (file)
index 0000000..71f0a50
--- /dev/null
@@ -0,0 +1,147 @@
+% Tests a number of features:
+% * Lyric_phrasing_engraver
+% * Stanza_number_engraver
+% * Automatic melismata on beamed notes
+
+\version "1.3.59";
+\include "english.ly"
+
+\header{
+    title = "Crowned with Honour";
+    composer = "Oliver Holden (1765-1844)";
+    poet = "Thomas Kelly (1769-1855)";
+}
+
+allup = \notes{
+       \property Voice.verticalDirection = \up
+       \property Voice.slurVerticalDirection = \up
+        \property Voice.tieVerticalDirection = \up
+       \property Voice.dynamicDirection = \up
+       \autoBeamOff
+}
+alldown = \notes{
+       \property Voice.verticalDirection = \down
+       \property Voice.slurVerticalDirection = \down
+        \property Voice.tieVerticalDirection = \down
+       \property Voice.dynamicDirection = \down
+       \autoBeamOff
+}
+
+Global =  \notes{
+        \key g \major;
+        \time 4/4;
+       \partial 4;
+}
+
+Soprano = \notes \relative c' {
+       \allup
+       d4 | g g b b a g a b a g b a g( a )g % modified to test melisma align right
+
+       [a8 b] | b4 % modified to test melisma align left
+        a g b [d16 d c8] [b a] b4 % modified to test beam melisma
+%      a4 | b a g b [d8 \melisma c] \melismaEnd [b \melisma a] \melismaEnd b4
+
+       d d2 d e d4( cs8 ~ )cs d2.
+
+       b4 | d b g b [a8  g]  [a b]  a4
+%      b4 | d b g b [a8 \melisma g] \melismaEnd [a \melisma b] \melismaEnd a4
+
+       g d'2 c b4.( c8 )a4 a g2.
+}
+Alto = \notes \relative c'{
+       \alldown
+       d4 | d d g g fs g fs g fs e g fs d2.
+       d4 | g d b g' [b8 a] [g fs] g4 fs g2 a g fs4( e8 )g fs2.
+       d4 | g g d g [fs8 e] [fs g] fs4 g f2 e d4.( d8 )d4 fs4 d2.
+}
+Tenor = \notes \relative c{
+       \allup
+       d4 | b' b d d c  b c  d c  b d c  b2.
+       a4 | b a g b  [d8 c] [b a] b4 a  b2 c b  a   a2.
+       g4 | b d b d [c8  b] [c  d]  c4 b g2 g g4.( a8 [fs )a] c4 b2.
+}
+Bass = \notes \relative c{
+       \alldown
+       d4 | g g g g d d d g d e d d g,2.
+       d'4 | g d b g' [b8 a] [g fs] g4 d g2 fs e a d,2.
+       g4 | g g g g d d d e b2 c d2. d4 g,2.
+}
+
+TheLyrics =  \lyrics <
+        {
+           \context LyricVoice = "Soprano-1"
+           \property LyricVoice .stanza = "1:"
+           \property LyricVoice .stz = "(1)"
+                The4 head    that once was crowned with thorns
+                Is   crowned with glo -- ry  now;
+                A roy -- al di -- a -- dem a -- dorns
+                The might -- y Vic -- tor's  brow.
+                A roy -- al di -- a -- dem a -- dorns
+                The might -- y Vic -- tor's  brow.
+        }
+        {
+           \context LyricVoice = "Soprano-2"
+           \property LyricVoice .stanza = "2:"
+           \property LyricVoice .stz = "(2)"
+                The4 high -- est place that heav'n af -- fords
+                Is His by sov -- 'reign  right;
+                The King of kings, the Lord of lords,
+                He reigns in glo -- ry  bright,
+                The King of kings, the Lord of lords, 
+                He reigns in glo -- ry  bright.
+        }
+        {
+           \context LyricVoice = "Soprano-3"
+           \property LyricVoice .stanza = "3:"
+           \property LyricVoice .stz = "(3)"
+                The joy of all who dwell a -- bove,
+                The joy of saints be --  low,
+                To4 whom He man -- i -- fests His love,
+                And grants His name to  know,
+                To4 whom He man -- i -- fests His love,
+                And grants His name to4  know.
+        }
+>
+
+
+\score{
+  \context ChoirStaff
+    \notes
+    <
+      \property Score.barNumberScriptPadding = #10.0
+      \context Staff = "treblestaff"{
+        <
+         \context Voice = "Soprano" { }
+         \context Voice = "Alto" { }
+        >
+      }
+      \context Lyrics = mainlyrics { }
+      \context Staff = "treblestaff"{
+       <
+         \Global
+         \addlyrics { \context Voice = "Soprano" \Soprano }
+           { \context Lyrics = mainlyrics \TheLyrics }
+         \context Voice = "Alto"  \Alto
+        >
+       \bar "|.";
+      }
+      \context Staff = "bassstaff"{
+        \clef "bass";
+       <
+         \context Voice = "Tenor" { \Tenor }
+         \context Voice = "Bass"  { \Bass }
+       >
+        \bar "|.";
+      }
+    >
+
+   \paper {
+      \translator{
+       \VoiceContext
+       automaticMelismata = ##t;
+       noAutoBeaming = ##t;
+        \remove   "Auto_beam_engraver";
+      }
+
+   }
+}
diff --git a/input/test/multistanza.ly b/input/test/multistanza.ly
deleted file mode 100644 (file)
index 71f0a50..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-% Tests a number of features:
-% * Lyric_phrasing_engraver
-% * Stanza_number_engraver
-% * Automatic melismata on beamed notes
-
-\version "1.3.59";
-\include "english.ly"
-
-\header{
-    title = "Crowned with Honour";
-    composer = "Oliver Holden (1765-1844)";
-    poet = "Thomas Kelly (1769-1855)";
-}
-
-allup = \notes{
-       \property Voice.verticalDirection = \up
-       \property Voice.slurVerticalDirection = \up
-        \property Voice.tieVerticalDirection = \up
-       \property Voice.dynamicDirection = \up
-       \autoBeamOff
-}
-alldown = \notes{
-       \property Voice.verticalDirection = \down
-       \property Voice.slurVerticalDirection = \down
-        \property Voice.tieVerticalDirection = \down
-       \property Voice.dynamicDirection = \down
-       \autoBeamOff
-}
-
-Global =  \notes{
-        \key g \major;
-        \time 4/4;
-       \partial 4;
-}
-
-Soprano = \notes \relative c' {
-       \allup
-       d4 | g g b b a g a b a g b a g( a )g % modified to test melisma align right
-
-       [a8 b] | b4 % modified to test melisma align left
-        a g b [d16 d c8] [b a] b4 % modified to test beam melisma
-%      a4 | b a g b [d8 \melisma c] \melismaEnd [b \melisma a] \melismaEnd b4
-
-       d d2 d e d4( cs8 ~ )cs d2.
-
-       b4 | d b g b [a8  g]  [a b]  a4
-%      b4 | d b g b [a8 \melisma g] \melismaEnd [a \melisma b] \melismaEnd a4
-
-       g d'2 c b4.( c8 )a4 a g2.
-}
-Alto = \notes \relative c'{
-       \alldown
-       d4 | d d g g fs g fs g fs e g fs d2.
-       d4 | g d b g' [b8 a] [g fs] g4 fs g2 a g fs4( e8 )g fs2.
-       d4 | g g d g [fs8 e] [fs g] fs4 g f2 e d4.( d8 )d4 fs4 d2.
-}
-Tenor = \notes \relative c{
-       \allup
-       d4 | b' b d d c  b c  d c  b d c  b2.
-       a4 | b a g b  [d8 c] [b a] b4 a  b2 c b  a   a2.
-       g4 | b d b d [c8  b] [c  d]  c4 b g2 g g4.( a8 [fs )a] c4 b2.
-}
-Bass = \notes \relative c{
-       \alldown
-       d4 | g g g g d d d g d e d d g,2.
-       d'4 | g d b g' [b8 a] [g fs] g4 d g2 fs e a d,2.
-       g4 | g g g g d d d e b2 c d2. d4 g,2.
-}
-
-TheLyrics =  \lyrics <
-        {
-           \context LyricVoice = "Soprano-1"
-           \property LyricVoice .stanza = "1:"
-           \property LyricVoice .stz = "(1)"
-                The4 head    that once was crowned with thorns
-                Is   crowned with glo -- ry  now;
-                A roy -- al di -- a -- dem a -- dorns
-                The might -- y Vic -- tor's  brow.
-                A roy -- al di -- a -- dem a -- dorns
-                The might -- y Vic -- tor's  brow.
-        }
-        {
-           \context LyricVoice = "Soprano-2"
-           \property LyricVoice .stanza = "2:"
-           \property LyricVoice .stz = "(2)"
-                The4 high -- est place that heav'n af -- fords
-                Is His by sov -- 'reign  right;
-                The King of kings, the Lord of lords,
-                He reigns in glo -- ry  bright,
-                The King of kings, the Lord of lords, 
-                He reigns in glo -- ry  bright.
-        }
-        {
-           \context LyricVoice = "Soprano-3"
-           \property LyricVoice .stanza = "3:"
-           \property LyricVoice .stz = "(3)"
-                The joy of all who dwell a -- bove,
-                The joy of saints be --  low,
-                To4 whom He man -- i -- fests His love,
-                And grants His name to  know,
-                To4 whom He man -- i -- fests His love,
-                And grants His name to4  know.
-        }
->
-
-
-\score{
-  \context ChoirStaff
-    \notes
-    <
-      \property Score.barNumberScriptPadding = #10.0
-      \context Staff = "treblestaff"{
-        <
-         \context Voice = "Soprano" { }
-         \context Voice = "Alto" { }
-        >
-      }
-      \context Lyrics = mainlyrics { }
-      \context Staff = "treblestaff"{
-       <
-         \Global
-         \addlyrics { \context Voice = "Soprano" \Soprano }
-           { \context Lyrics = mainlyrics \TheLyrics }
-         \context Voice = "Alto"  \Alto
-        >
-       \bar "|.";
-      }
-      \context Staff = "bassstaff"{
-        \clef "bass";
-       <
-         \context Voice = "Tenor" { \Tenor }
-         \context Voice = "Bass"  { \Bass }
-       >
-        \bar "|.";
-      }
-    >
-
-   \paper {
-      \translator{
-       \VoiceContext
-       automaticMelismata = ##t;
-       noAutoBeaming = ##t;
-        \remove   "Auto_beam_engraver";
-      }
-
-   }
-}
index 57d42f31139ac6b28104259f9499129aba885717..ef031b322953df93a43bfbf426f72c3efe04d5db 100644 (file)
@@ -47,7 +47,7 @@ Axis_group_engraver::do_creation_processing ()
   Axis_group_interface::set_axes (staffline_p_, Y_AXIS, Y_AXIS);
 
   Score_element *  it = unsmob_element (get_property ("currentCommandColumn"));
-  Pointer_group_interface (it, "bounded-by-me").add_element (staffline_p_);  
+
   staffline_p_->set_bound(LEFT,it);
 
   announce_element (staffline_p_, 0);
@@ -84,7 +84,7 @@ Axis_group_engraver::do_removal_processing ()
 
   Score_element *  it = unsmob_element (get_property ("currentCommandColumn"));
 
-  Pointer_group_interface (it, "bounded-by-me").add_element (staffline_p_);  
+
   staffline_p_->set_bound(RIGHT,it);
 
   typeset_element (staffline_p_);
index 9689529f434a3b4c44ed337067535ba9cbf768cc..0b08777be979e03fa1684fcd32b8608f48ce51f8 100644 (file)
@@ -10,7 +10,7 @@
 #include "directional-element-interface.hh"
 
 
-static SCM Directional_element_interface::direction_sym;
+SCM Directional_element_interface::direction_sym;
 
 static void
 init_functions ()
index 9093c2aa66d9ca5f3274e5bb37323e3d8bb95409..0593606bb6f852073fdf838253e1537b44bc14cd 100644 (file)
@@ -186,7 +186,10 @@ Dynamic_engraver::do_process_music ()
       else
        {
          assert (!finished_cresc_p_);
-         cresc_p_->set_bound (RIGHT, unsmob_element (get_property ("currentMusicalColumn")));
+         Score_element* cc = unsmob_element (get_property ("currentMusicalColumn"));
+         
+         cresc_p_->set_bound (RIGHT, cc);
+
          finished_cresc_p_ = cresc_p_;
          cresc_p_ = 0;
          current_cresc_req_ = 0;
@@ -233,7 +236,8 @@ Dynamic_engraver::do_process_music ()
                                            + "Spanner", SCM_UNDEFINED);
            }
 
-         cresc_p_->set_bound (LEFT, unsmob_element (get_property ("currentMusicalColumn")));
+         Score_element *cc = unsmob_element (get_property ("currentMusicalColumn"));
+         cresc_p_->set_bound (LEFT, cc);
 
 
          /* 
@@ -324,7 +328,7 @@ Dynamic_engraver::typeset_all ()
   if (finished_line_spanner_)
     {
       Side_position::add_staff_support (finished_line_spanner_);
-
+#if 0
       if (!finished_line_spanner_->get_bound (LEFT))
        {
          Score_element * cmc
@@ -335,7 +339,8 @@ Dynamic_engraver::typeset_all ()
        finished_line_spanner_->set_bound (RIGHT,
                                           finished_line_spanner_->get_bound (LEFT));
       
-      
+#endif
+      extend_spanner_over_elements (finished_line_spanner_);
       typeset_element (finished_line_spanner_);
       finished_line_spanner_ = 0;
     }
index 165ed99efe23b04b27c93ec9b2bc7965d416c7cb..8792301dfc0ec9c27bc15ff337142d3fa435c218 100644 (file)
@@ -61,6 +61,7 @@ protected:
 
 
 void add_bound_item (Spanner*, Item* n);
+void extend_spanner_over_elements (Score_element*  span);
 
 
 #endif
index 53c61f40c3d0c92284bffa464007463a9e18dd45..a03c8b6a064b99428010ce793cc8054a22947deb 100644 (file)
@@ -37,7 +37,7 @@ Line_group_engraver_group::do_removal_processing()
   Score_element *  it
     = unsmob_element (get_property (ly_symbol2scm ("currentCommandColumn")));
 
-  Pointer_group_interface (it, "bounded-by-me").add_element (staffline_p_);  
+
   staffline_p_->set_bound(RIGHT,it);
   Engraver_group_engraver::typeset_element (staffline_p_);
   staffline_p_ = 0;
@@ -50,7 +50,6 @@ Line_group_engraver_group::do_creation_processing()
   Score_element *  it
     = unsmob_element (get_property (ly_symbol2scm ("currentCommandColumn"))); 
   staffline_p_->set_bound(LEFT,it);
-  Pointer_group_interface (it, "bounded-by-me").add_element (staffline_p_);
   
   Engraver::announce_element (staffline_p_,0);
 }
index c1d6d6e6abf35264691649f3684979d1a4dde735..1808daefc5748dfa89412384507de80dd13c8ebd 100644 (file)
@@ -100,8 +100,10 @@ identify (ostream* os)
 void
 usage ()
 {
-  identify (&cout);
-  cout << "\n";
+  
+  /*
+    No version number or newline here. It confuses help2man
+   */
   cout << _f ("Usage: %s [OPTION]... [FILE]...", "lilypond");
   cout << "\n\n";
   cout << _ ("Typeset music and or play MIDI from FILE");
index fcdc933f9aa39c0eb43d1a8d245719c0cf207a4e..c94541577ac2d021252523c0dab33156fc600319 100644 (file)
@@ -43,7 +43,7 @@ Paper_column::Paper_column (SCM l)
   Axis_group_interface::set_interface (this);
   Axis_group_interface::set_axes (this, X_AXIS, X_AXIS);
   Spaceable_element::set_interface (this);
-  set_elt_property ("bounded-by-me", SCM_EOL);
+
   line_l_=0;
   rank_i_ = -1;
 }
index 2cbeafd1eb7b9c89a265d00f008bb2ccf65b9137..66b759e7d237bc7c9cd31c67ce45afd7fbdd1b35 100644 (file)
@@ -81,11 +81,9 @@ Score_engraver::do_removal_processing()
   Engraver_group_engraver::do_removal_processing();
   scoreline_l_->set_bound(RIGHT,command_column_l_);
   command_column_l_->set_elt_property ("breakable", SCM_BOOL_T);
-
   
   typeset_all ();
 
-
   set_columns (0,0);
 }
 
@@ -170,16 +168,16 @@ Score_engraver::typeset_all()
 void
 Score_engraver::do_pre_move_processing()
 {
+  // this generates all items.
+  Engraver_group_engraver::do_pre_move_processing();
+  
+  typeset_all();
   if (to_boolean (command_column_l_->get_elt_property ("breakable")))
     {
       breaks_i_ ++;
       if (! (breaks_i_%8))
        progress_indication ("[" + to_str ( breaks_i_) + "]");
     }
-  // this generates all items.
-  Engraver_group_engraver::do_pre_move_processing();
-  
-  typeset_all();
 }
 
 void
@@ -194,11 +192,16 @@ Score_engraver::set_columns (Paper_column *new_command_l,
       if (*current[i])
        {
          scoreline_l_->add_column ((*current[i]));
+#if 0
+         /*
+           TODO: delay this decision.
+          */
          if (!Paper_column::used_b (*current[i]))
            {
              (*current[i])->suicide ();
              *current[i]  =0;
            }
+#endif
        }
       if (news[i])
        *current[i] = news[i];
index 131f19e5afc1c4e7288fcd69c54b70f432c3d1c3..a19032f2ebfeaf4ce9fd0dd76e8d1acc02785293 100644 (file)
@@ -18,7 +18,7 @@
 #include "paper-column.hh"
 #include "line-of-score.hh"
 #include "break-align-item.hh"
-
+#include "group-interface.hh"
 
 void
 Spanner::do_break_processing ()
@@ -194,6 +194,19 @@ Spanner::set_bound(Direction d, Score_element*s)
     {
       set_parent (i, X_AXIS);
     }
+
+  /*
+    Signal that this column needs to be kept alive. They need to be
+    kept alive to have meaningful position and linebreaking.
+
+    [maybe we should try keeping all columns alive?, and perhaps
+    inherit position from their (non-)musical brother]
+    
+  */
+  if (dynamic_cast<Paper_column*> (i))
+    {
+      Pointer_group_interface (i, "bounded-by-me").add_element (this);  
+    }
 }
 
 
@@ -314,3 +327,70 @@ add_bound_item (Spanner* sp, Item*it)
   else
     sp->set_bound (RIGHT, it);
 }
+
+static void
+extend_spanner_over_item (Item *it, SCM extremal_pair)
+{
+  if (!it)
+    return;
+  Item * col = it->column_l ();
+  Item * i1 = dynamic_cast<Item*> (unsmob_element (gh_car (extremal_pair)));
+  Item * i2 = dynamic_cast<Item*> (unsmob_element (gh_cdr (extremal_pair)));
+  int r = Paper_column::rank_i (col);
+  if (!i1 || r < Paper_column::rank_i (i1->column_l ()))
+    {
+      gh_set_car_x (extremal_pair, it->self_scm ());
+    }
+  if (!i2 || r > Paper_column::rank_i (i2->column_l ()))
+    {
+      gh_set_cdr_x (extremal_pair, it->self_scm ());
+    }
+}
+
+static void
+extend_spanner_over_elements (SCM value, SCM extremal_pair)
+{
+  if (gh_pair_p (value))
+    {
+      extend_spanner_over_elements (gh_car (value), extremal_pair);
+      extend_spanner_over_elements (gh_cdr (value), extremal_pair);
+    }
+  else if (unsmob_element (value))
+    {
+      if (Spanner * sp = dynamic_cast<Spanner*> (unsmob_element(value)))
+       {
+         extend_spanner_over_item (sp->get_bound (LEFT), extremal_pair);
+         extend_spanner_over_item (sp->get_bound (RIGHT), extremal_pair);
+       }
+      else if (Item * it= dynamic_cast<Item*> (unsmob_element(value)))
+       extend_spanner_over_item (it, extremal_pair);
+    }
+}
+
+
+/*
+  Make sure that the left and right bounds encompasses all objects it
+  points to.
+
+  TODO: maybe be more specific. Most probably fucks up if someone sets
+  a pointer to the staffsymbol in S
+*/
+void
+extend_spanner_over_elements (Score_element*s)
+{
+  Spanner*sp = dynamic_cast<Spanner*> (s);
+
+  SCM s1 = sp->get_bound (LEFT) ? sp->get_bound (LEFT)->self_scm () : SCM_EOL;
+  SCM s2 = sp->get_bound (RIGHT) ? sp->get_bound (RIGHT)->self_scm () : SCM_EOL;  
+  
+  SCM pair = gh_cons (s1,s2);
+  extend_spanner_over_elements (sp->mutable_property_alist_, pair);
+
+  Score_element *p1 =  unsmob_element (gh_car (pair));
+  Score_element* p2 = unsmob_element (gh_cdr (pair));
+  sp->set_bound (LEFT,p1);
+  sp->set_bound (RIGHT, p2);
+
+  //extra precaution.
+}
+
index 430a9939bc06ce60475a7c59cb10be0f27619d48..b69a614ea046223aeccc86d24bc6123e0779e580 100644 (file)
@@ -38,40 +38,40 @@ Timing_translator::do_try_music (Music*r)
              return false;
            }
        }
+
+      /*
+       We have to do this soon enough. Maybe we'd better disguise
+       \time as a \property. Then all settings will be `immediate'.
+       */
+      if (Time_signature_change_req *c
+         = dynamic_cast <Time_signature_change_req *> (t))
+       set_time_signature (c->beats_i_, c->one_beat_i_);
     
       timing_req_l_arr_.push(t);
       return true;
     }
   return false;
 }
+
 void
 Timing_translator::do_process_music()
 {
   for (int i=0; i < timing_req_l_arr_.size (); i++)
     {
-      Timing_req * tr_l = timing_req_l_arr_[i];
-
-      if (Time_signature_change_req *m_l = dynamic_cast <Time_signature_change_req *> (tr_l))
-       {
-         int b_i= m_l->beats_i_;
-         int o_i = m_l->one_beat_i_;
-         set_time_signature (b_i, o_i);
-       }
-      else if (dynamic_cast <Barcheck_req *> (tr_l))
+      if (!dynamic_cast <Barcheck_req *> (timing_req_l_arr_[i]))
+       continue;
+      if (measure_position ())
        {
-         if (measure_position ())
-           {
-             Moment nm; 
-             tr_l ->origin ()->warning (_f ("barcheck failed at: %s", 
-                                 measure_position ().str ()));
-             // resync
-             daddy_trans_l_->set_property("measurePosition", nm.make_scm ());
-           }
+         timing_req_l_arr_[i]->origin ()->warning (_f ("barcheck failed at: %s", 
+                                                       measure_position ().str ()));
+         Moment zero; 
+
+         // resync
+         daddy_trans_l_->set_property("measurePosition", zero.make_scm ());
        }
     }
 }
 
-
 void
 Timing_translator::do_pre_move_processing()
 {
index 38d3df23ec1be8292c53ead1749acb4845f5389b..69e8b375739d2dbc191d78cdb553caaaae254700 100644 (file)
@@ -1,15 +1,15 @@
 Begin3
 Title: LilyPond
-Version: 1.3.83
-Entered-date: 01SEP00
+Version: 1.3.84
+Entered-date: 02SEP00
 Description: 
 Keywords: music notation typesetting midi fonts engraving
 Author: hanwen@cs.uu.nl (Han-Wen Nienhuys)
        janneke@gnu.org (Jan Nieuwenhuizen)
 Maintained-by: hanwen@stack.nl (Han-Wen Nienhuys)
 Primary-site: sunsite.unc.edu /pub/Linux/apps/sound/convert
-       1000k lilypond-1.3.83.tar.gz 
+       1000k lilypond-1.3.84.tar.gz 
 Original-site: ftp.cs.uu.nl /pub/GNU/LilyPond/development/
-       1000k lilypond-1.3.83.tar.gz 
+       1000k lilypond-1.3.84.tar.gz 
 Copying-policy: GPL
 End
index 71d33beb679785048c5cca7280d247aa240fc2e6..bc861526dae0034a02c0f8652b2cb2217fd67a64 100644 (file)
@@ -1,9 +1,9 @@
 Name: lilypond
-Version: 1.3.83
+Version: 1.3.84
 Release: 1
 Copyright: GPL
 Group: Applications/Publishing
-Source0: ftp.cs.uu.nl:/pub/GNU/LilyPond/development/lilypond-1.3.83.tar.gz
+Source0: ftp.cs.uu.nl:/pub/GNU/LilyPond/development/lilypond-1.3.84.tar.gz
 Summary: A program for printing sheet music.
 URL: http://www.cs.uu.nl/~hanwen/lilypond
 # Icon: lilypond-icon.gif
index 1835fd340be29a25cf771c6152ff2c5ec6cf7c0c..366e497e7d777181a9ad7c9d5193a2960e0cefc8 100644 (file)
@@ -87,6 +87,36 @@ def find_scale (transposition):
        trscale = map(lambda x, k=transposition: transpose(x, k), cscale)
 
        return trscale
+def EDU_to_duration (edu):
+       log = 1
+       d = 4096
+       while d > edu:
+               d = d >> 1
+               log = log << 1
+
+       edu = edu - d
+       dots = 0
+       if edu == d /2:
+               dots = 1
+       elif edu == d*3/4:
+               dots = 2
+       return (log, dots)      
+
+def rat_to_lily_duration (rat):
+       (n,d) = rat
+
+       basedur = 1
+       while d and  d % 2 == 0:
+               basedur = basedur << 1
+               d = d >> 1
+
+       str = 's%d' % basedur
+       if n <> 1:
+               str = str + '*%d' % n
+       if d <> 1:
+               str = str + '/%d' % d
+
+       return str
 
 def gcd (a,b):
        if b == 0:
@@ -126,6 +156,8 @@ def rat_neg (a):
        (p,q) = a
        return (-p,q)
 
+
+
 def rat_subtract (a,b ):
        return rat_add (a, rat_neg (b))
 
@@ -481,37 +513,7 @@ class Staff:
                return str
 
                                
-def EDU_to_duration (edu):
-       log = 1
-       d = 4096
-       while d > edu:
-               d = d >> 1
-               log = log << 1
-
-       edu = edu - d
-       dots = 0
-       if edu == d /2:
-               dots = 1
-       elif edu == d*3/4:
-               dots = 2
-       return (log, dots)      
-
-def rat_to_lily_duration (rat):
-       (n,d) = rat
 
-       basedur = 1
-       while d and  d % 2 == 0:
-               basedur = basedur << 1
-               d = d >> 1
-
-       str = 's%d' % basedur
-       if n <> 1:
-               str = str + '*%d' % n
-       if d <> 1:
-               str = str + '/%d' % d
-
-       return str
-               
 
 class Chord:
        def __init__ (self, finale_entry):
@@ -939,34 +941,37 @@ class Etf_file:
                        e.prev = self.chords[e.finale[0][1]]
                        e.next = self.chords[e.finale[0][2]]
 
-
-        
-
-
-
 def identify():
        sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
 
 def help ():
-       print r"""
-Convert ETF to LilyPond.
+       print """Usage: etf2ly [OPTION]... ETF-FILE
 
-Usage: etf2ly [OPTION]... ETF-FILE
+Convert ETF to LilyPond.
 
 Options:
-  -h, --help          this help
-  -o, --output=FILE   set output filename to FILE
-  -v, --version       version information
+  -h,--help          this help
+  -o,--output=FILE   set output filename to FILE
+  -v,--version       version information
 
 Enigma Transport Format is a format used by Coda Music Technology's
 Finale product. This program will convert a subset of ETF to a
 ready-to-use lilypond file.
 
+Report bugs to bug-gnu-music@gnu.org
 
+Written by  Han-Wen Nienhuys <hanwen@cs.uu.nl>
 """
 
 def print_version ():
-       print r"""etf2ly (GNU lilypond) %s""" % version
+       print r"""etf2ly (GNU lilypond) %s
+
+This is free software.  It is covered by the GNU General Public License,
+and you are welcome to change it and/or distribute copies of it under
+certain conditions.  Invoke as `midi2ly --warranty' for more information.
+
+Copyright (c) 2000 by Han-Wen Nienhuys <hanwen@cs.uu.nl>
+""" % version
 
 
 
diff --git a/scripts/musedata2ly.py b/scripts/musedata2ly.py
new file mode 100644 (file)
index 0000000..ec139d4
--- /dev/null
@@ -0,0 +1,616 @@
+#!@PYTHON@
+
+# musedata = musedata.stanford.edu
+# musedata = COBOL for musicians.
+
+
+# TODO
+#
+# * clefs,
+# * keys,
+# * staffs,
+# * multiple voices (they use `Backspace' (shudder)
+# * tuplets
+#
+
+import re
+import sys
+import string
+import getopt
+import os
+program_name = 'musedata2ly'
+version = '@TOPLEVEL_VERSION@'
+if version == '@' + 'TOPLEVEL_VERSION' + '@':
+       version = '(unknown version)'      # uGUHGUHGHGUGH
+
+
+
+ref_header_dict = {
+       'COM': 'composer',
+       'OPR': 'collection',
+       'OTL': 'title',
+       'OMV': 'subtitle',
+       'YOR': 'source',
+       'AGN': 'instrument',
+       'END': 'encodingdate',
+       'CDT': 'date',
+       'OCY': 'composedin',
+       'AST': 'genre',
+       'YEC': 'copyright',
+       'YEM': 'license',
+       'YEN': 'encodingcountry',
+       'EED': 'editor',
+       'SCA': 'opus',
+       'ONM': 'onm',
+       'ENC': 'musedataencoder',
+       'KEY': 'musedatakey',
+       'AFT': 'musedatastage'
+       }
+
+
+class Ref_parser:
+       def __init__ (self, fn):
+               self.dict = {}
+               
+               ls = open (fn).readlines ()
+               self.parse (ls)
+       def parse (self,ls):
+               for l in ls:
+                       m = re.match('!!!([A-Z]+):[ \t]+(.*)$',l)
+                       if m:
+                               key = m.group(1)
+                               val = m.group (2)
+                               val = re.sub ('[ \t]+', ' ', val)
+                               try:
+                                       
+                                       key =ref_header_dict [key]
+                               except KeyError:
+                                       sys.stderr.write ('\nUnknown ref key \`%s\'' % key) 
+                               s = ''
+                               try:
+                                       s = self.dict[key]
+                               except KeyError:
+                                       pass
+
+                               s = s + val
+                               self.dict[key] = s
+       def dump( self):
+               str = ''
+               for (k,v) in self.dict.items ():
+                       str = str +'  %s = "%s";\n' % (k,v)
+               str = '\\header {\n%s}' % str
+               return str
+       
+verbose = 0
+
+
+actab = {-2: 'eses', -1: 'es', 0 : '', 1: 'is', 2:'isis'}
+
+def pitch_to_lily_string (tup):
+       (o,n,a) = tup
+
+       nm = chr((n + 2) % 7 + ord ('a'))
+       nm = nm + actab[a]
+       if o > 0:
+               nm = nm + "'" * o
+       elif o < 0:
+               nm = nm + "," * -o
+       return nm
+
+def get_key (s):
+       i = string.atoi (s)
+       return ''
+
+def get_timesig (s):
+       return '\\time %s;\n' % s
+
+
+divisions = 4
+def get_divisions_per_quarter (s):
+       divisions = string.atoi (s) 
+       return ''
+
+def get_directive (s):
+       return '%% %s\n' % s
+
+def get_transposing (s):
+       return ''
+
+def get_num_instruments (s):
+       return ''
+
+def get_mudela_notename (p, ac):
+       if p > 5:
+               p = p - 7
+       s = chr (p + ord ('c'))
+       infix = 'i'
+       if ac < 0:
+               infix = 'e'
+               ac = -ac
+
+       while ac:
+               s = s + infix + 's'
+               ac = ac - 1
+       return s
+def get_clef ():
+       return ''
+
+SPACES = ' '
+DIGITS = "0123456789"
+
+
+clef_dict = {
+04: 'treble',
+13 : 'alto',
+22: 'bass',
+}
+attr_dict = {
+       'C' : get_clef,
+       'K' : get_key ,
+       'T' : get_timesig,
+       'Q' : get_divisions_per_quarter,
+       'D' : get_directive,
+       'X' : get_transposing,
+       'I': get_num_instruments,
+       }
+
+
+script_table = {
+'v': '\\upbow',
+'n': '\\downbow',
+'o': '\\harmonic',
+'0': '"openstring',
+'Q': '\\thumb',
+'>': '^',
+'V': '^',
+'.': '.',
+'_': '-',
+'=': '"det leg"',
+'i': '|',
+'s': '"\\\\textsharp"',
+'n': '"\\\\textnatural"',
+'b': '"\\\\textflat"',
+'F': '\\fermata',
+'E': '\\fermata',
+}
+
+class Attribute_set:
+       def __init__ (self, dict):
+               self.dict = dict
+       def dump (self):
+               return ''
+
+class Chord:
+       def __init__ (self):
+               self.pitches = []
+               self.grace = 0
+               self.cue = 0
+               self.slurstart = []
+               self.slurstop  = []
+               self.scripts = []
+               self.syllables = []
+               self.dots = 0
+               self.basic_duration = 4
+               self.tied = 0
+
+               self.note_suffix = self.note_prefix = ''
+               self.chord_suffix = self.chord_prefix = ''
+               
+       def add_script (self,s):
+               self.scripts.append (s)
+       def set_duration (self, d):
+               self.basic_duration = d
+       def add_syllable (self, s):
+               self.syllables.append (s)
+       def add_pitch (self,t):
+               self.pitches.append (t)
+               
+       def dump (self):
+               str = ''
+
+               sd = ''
+               if self.basic_duration == 0.5:
+                       sd = '\\breve'
+               else:
+                       sd = '%d' % self.basic_duration
+                       
+               sd = sd + '.' * self.dots 
+               for p in self.pitches:
+                       if str:
+                               str = str + ' ' 
+                       str = str + pitch_to_lily_string (p) + sd
+
+               for s in self.scripts:
+                       str = str + '-' + s
+
+               str = self.note_prefix +str  + self.note_suffix
+               
+               if len (self.pitches) > 1:
+                       str = '<%s>' % str
+               elif len (self.pitches) == 0:
+                       str = 'r' + sd
+
+               str = self.chord_prefix + str + self.chord_suffix
+               
+               return str
+       
+class Parser:
+       def append_entry (self, e):
+               self.entries.append (e)
+       def append_chord (self,c ):
+               self.chords.append (c)
+               self.entries.append (c)
+       def last_chord (self):
+               return self.chords[-1]
+       def __init__ (self, fn):
+               self.divs_per_q = 1
+               self.header_dict = {
+                       'tagline' :'automatically converted from Musedata',
+                       'copyright' : 'all rights reserved -- free for noncommercial use'
+                       #  musedata license (argh)
+                       }
+               self.entries = []
+               self.chords = []
+               
+               lines = open (fn).readlines ()
+               lines = self.parse_header (lines)
+               lines = self.append_lines (lines)
+               str = string.join (lines, '\n')
+               lines = re.split ('[\n\r]+', str)
+               self.parse_body (lines)
+               
+       def parse_header (self, lines):
+               enter = string.split (lines[3], ' ')
+               self.header_dict['enteredby']  = string.join (enter[1:])
+               self.header_dict['enteredon'] = enter[0]
+               self.header_dict['opus'] = lines[4]
+               self.header_dict['source'] = lines[5]
+               self.header_dict['title'] = lines[6]
+               self.header_dict['subtitle'] = lines[7]
+               self.header_dict['instrument']= lines[8]
+               self.header_dict['musedatamisc'] =lines[9]
+               self.header_dict['musedatagroups'] =lines[10]
+               self.header_dict['musedatagroupnumber']=lines[11]
+               lines =  lines[12:]
+               comment = 0
+               while lines:
+                       if lines[0][0]  == '$':
+                               break                   
+                       lines = lines[1:]
+               return lines
+       
+       def parse_musical_attributes (self,l):
+               atts = re.split('([A-Z][0-9]?):', l)
+               atts = atts[1:]
+               found = {}
+               while len (atts):
+                       id = atts[0]
+                       val = atts[1]
+                       atts = atts[2:]
+                       found[id] = val
+
+               try:
+                       self.divs_per_q = string.atoi (found['Q'])
+               except KeyError:
+                       pass
+               
+               self.append_entry (Attribute_set (found))
+       def append_entry (self, e):
+               self.entries.append (e)
+
+       def parse_line_comment (self,l):
+               pass
+
+       def parse_note_line (self,l):
+               ch = None
+               if verbose:
+                       print DIGITS+DIGITS+DIGITS 
+                       print l
+               pi = l[0:5]
+               di = l[5:8]
+               tied = l[8:9] == '-'
+
+               cue = grace = 0
+               if (pi[0] == 'g'):
+                       grace = 1
+                       pi = pi[1:]
+               elif (pi[0] == 'c'):
+                       cue = 1
+                       pi = pi[1:]
+               
+               if pi[0] == ' ':
+                       ch = self.last_chord ()
+                       pi = pi[1:]
+               else:
+                       ch = Chord ()
+                       self.append_chord (ch)
+
+
+               ch.cue = ch.cue or cue
+               ch.grace = ch.grace or grace
+
+               while pi[0] in SPACES:
+                       pi = pi[1:]
+
+               if pi[0] <> 'r':
+                       name =  ((ord (pi[0]) -ord('A')) + 5) % 7
+                       alter = 0
+                       pi = pi[1:]
+                       while pi and pi[0] in '#f':
+                               if pi[0] == '#':
+                                       alter = alter + 1
+                               else:
+                                       alter = alter - 1
+                               pi = pi[1:]
+
+                       oct = string.atoi (pi) - 3
+
+                       pittup = (oct, name ,alter)
+                       ch.add_pitch (pittup)
+
+               base_dur = None
+               if ch.cue or ch.grace:
+                       c = di[2]
+                       if c == '0':
+                               ch.accaciatura = 1
+                       elif c == 'A':
+                               base_dur = 0.5
+                       else:
+                               base_dur = 1 << (9 - (ord (c) - ord ('0')))
+               else:           
+                       base_dur =  (4 * self.divs_per_q) / string.atoi (di)
+                       ch.set_duration (base_dur)
+
+               ch.tied = ch.tied or tied 
+               dots = 0
+               
+               
+               if l[18:19] == '.':
+                       dots = 1
+               elif l[18:19] == ':':
+                       dots = 2
+
+               if l[26:27] == '[':
+                       ch.start_beam = 1
+               elif l[26:27] == ']':
+                       ch.end_beam = 1
+
+
+               additional = l[32:44]
+               for c in additional:
+                       try:
+                               s = list('([{z').index (c)
+                               ch.slurstart.append( s)
+                               sluropen = list(')]}x').index (c)
+                               ch.slurstop.append( s)
+                       except ValueError:
+                               pass
+                       if c == '*':
+                               ch.start_tuplet = 1
+                               continue
+                       elif c == '!':
+                               ch.stop_tuplet = 1
+                               continue
+
+                       if c in DIGITS:
+                               ch.add_script (c)
+                               continue
+
+                       if c == ' ' :
+                               continue
+                       
+                       try:
+                               scr = script_table[c]
+                               ch.add_script (scr)
+                               c = None
+                       except KeyError:
+                               sys.stderr.write ("\nFixme: script `%s' not done\n" % c)
+
+               text = l[40:81]
+               sylls = string.split (text,'|')
+
+               for syl in sylls:
+                       ch.add_syllable (syl)
+
+                       
+       def parse_measure_line (self,l):
+               pass
+
+
+       def parse_duration (l):
+               s = ''
+               while l[0] in '0123456789':
+                       s = s + l[0]
+                       l= l[1:]
+               print l
+               num = string.atoi (s)
+               den = 4 * divisions 
+
+               current_dots = 0
+               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
+
+               if num <> 1:
+                       sys.stderr.write ('huh. Durations left')
+               return '%s%s' % (den, '.' * current_dots)
+       
+       def append_lines (self,ls):
+               nls = []
+               for l in ls:
+                       if l[0] == 'a':
+                               nls[-1] = nls[-1]+l[1:]
+                       else:
+                               nls.append(l)
+               return nls
+       def dump (self):
+               s = ''
+               ln = ''
+               for e in self.entries:
+                       
+                       next = ' ' + e.dump()
+                       if len (ln) + len (next) > 72:
+                               s = s +ln +  '\n'
+                               ln = ''
+                       ln = ln + next
+                       
+               s = s + ln
+
+               s = '\\notes {\n %s \n}' % s
+               return s
+       
+       def parse_body (self,lines):
+               comment_switch = 0
+               for l in lines:
+                       
+                       
+                       c = l[0]
+                       if c == '&':
+                               comment_switch = not comment_switch
+                               continue
+                       
+                       if comment_switch:
+                               continue
+
+                       if 0:
+                               pass
+                       elif c == '$':
+                               self.parse_musical_attributes (l)
+                       elif c == '@':
+                               self.parse_line_comment (l)
+                       elif c == '*':
+                               self.parse_musical_directions (l)
+                       elif c in 'ABCDEFGr ':
+                               self.parse_note_line (l)
+                       elif c == 'm':
+                               self.parse_measure_line (l)
+                       elif c == '/':
+                               break
+                       elif c in 'PS':
+                               pass                    # ignore sound & print
+                       else:
+                               sys.stderr.write ("\nUnrecognized record `%s'\n"%  l)
+
+
+
+
+
+def help ():
+       sys.stdout.write (
+"""Usage: musedata2ly [OPTION]... FILE1 [FILE2 ...]
+
+Convert musedata to LilyPond.
+
+Options:
+  -h,--help          this help
+  -o,--output=FILE   set output filename to FILE
+  -v,--version       version information
+  -r,--ref=REF       read background information from ref-file REF     
+
+Musedata (http://www.ccarh.org/musedata/) is an electronic library of
+classical music scores, currently comprising XXX scores.  The music is
+encoded in so-called Musedata format
+(http://www.ccarh.org/publications/books/beyondmidi/online/musedata).
+musedata2ly converts a set of musedata files to one .ly file, and will
+include a \header field if a .ref file is supplied 
+
+Report bugs to bug-gnu-music@gnu.org.
+
+Written by Han-Wen Nienhuys <hanwen@cs.uu.nl>
+""")
+
+
+def print_version ():
+       sys.stdout.write ("""musedata2ly (GNU LilyPond) %s
+
+This is free software.  It is covered by the GNU General Public License,
+and you are welcome to change it and/or distribute copies of it under
+certain conditions.  Invoke as `midi2ly --warranty' for more information.
+
+Copyright (c) 2000 by Han-Wen Nienhuys <hanwen@cs.uu.nl>
+""" % version)
+def identify():
+       sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
+
+
+
+(options, files) = getopt.getopt (sys.argv[1:], 'r:vo:h', ['ref=', 'help','version', 'output='])
+out_filename = None
+ref_file = None
+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 == '--ref' or o == '-r':
+               ref_file = a 
+       elif o == '--output' or o == '-o':
+               out_filename = a
+       else:
+               print o
+               raise getopt.error
+
+identify()
+
+
+
+ly = ''
+
+
+found_ids = ''
+for f in files:
+       if f == '-':
+               f = ''
+
+       sys.stderr.write ('Processing `%s\'\n' % f)
+       
+       e = Parser(f)
+
+       id = os.path.basename (f)
+       id = re.sub ('[^a-zA-Z0-9]', 'x', id)
+       id = 'voice%s' % id
+       ly =ly + '\n\n%s = \\context Staff = "%s" %s\n\n' % (id, id, e.dump ())
+
+       found_ids = found_ids + '\\%s\n' % id
+
+found_ids = '\n\n\n\\score { < %s > } ' % found_ids 
+
+ly_head = ''
+if ref_file:
+       head = Ref_parser (ref_file)
+       if not out_filename:
+               t = ''
+               st = ''
+               try:
+                       t = head.dict['title']
+                       st= head.dict['subtitle']
+               except KeyError:
+                       pass
+                       
+               t = t + '-' +st
+               
+               t = re.sub ("^ +(.*) +$", r"\1", t)
+               t = re.sub ("\\.", '', t)
+               out_filename = re.sub ('[^a-zA-Z0-9-]', '-', t)
+               out_filename = out_filename+ '.ly'
+       ly_head = head.dump ()
+       
+if not out_filename:
+       out_filename = 'musedata.ly'
+       
+sys.stderr.write ('Writing `%s\'\n' % out_filename)
+
+fo = open (out_filename, 'w')
+fo.write ('%% lily was here -- automatically converted by musedata.ly\n')
+fo.write(ly_head + ly + found_ids)
+fo.close ()
+
diff --git a/scripts/pmx2ly.py b/scripts/pmx2ly.py
new file mode 100644 (file)
index 0000000..0f656fe
--- /dev/null
@@ -0,0 +1,766 @@
+#!@PYTHON@
+
+# (urg! wat een pokkeformaat (pokkenformaat?))  
+import os
+import string
+import sys
+import re
+import getopt
+
+program_name = 'pmx2ly'
+version = '@TOPLEVEL_VERSION@'
+if version == '@' + 'TOPLEVEL_VERSION' + '@':
+       version = '(unknown version)'      # uGUHGUHGHGUGH
+
+
+def encodeint (i):
+       return chr ( i  + ord ('A'))
+
+def stripcomment (l):
+       return re.sub ('[ \t]*%.*$\n', '', l)
+       
+def stripwhite (l):
+       return re.sub ('[ \n\t]+', ' ', l)
+       
+def stripeols (l):
+       return re.sub ('^ ',  '', re.sub (' $', '', l))
+       
+actab = {-2: 'eses', -1: 'es', 0 : '', 1: 'is', 2:'isis'}
+
+def pitch_to_lily_string (tup):
+       (o,n,a) = tup
+
+       nm = chr((n + 2) % 7 + ord ('a'))
+       nm = nm + actab[a]
+       if o > 0:
+               nm = nm + "'" * o
+       elif o < 0:
+               nm = nm + "," * -o
+       return nm
+
+def gcd (a,b):
+       if b == 0:
+               return a
+       c = a
+       while c: 
+               c = a % b
+               a = b
+               b = c
+       return a
+
+def rat_simplify (r):
+       (n,d) = r
+       if d < 0:
+               d = -d
+               n = -n
+       if n == 0:
+               return (0,1)
+       else:
+               g = gcd (n, d)
+               return (n/g, d/g)
+       
+def rat_multiply (a,b):
+       (x,y) = a
+       (p,q) = b
+
+       return rat_simplify ((x*p, y*q))
+
+def rat_divide (a,b):
+       (p,q) = b
+       return rat_multiply (a, (q,p))
+
+tuplet_table = {
+       2: 3,
+       3: 2,
+       5: 4
+}
+
+
+def rat_add (a,b):
+       (x,y) = a
+       (p,q) = b
+
+       return rat_simplify ((x*q + p*y, y*q))
+
+def rat_neg (a):
+       (p,q) = a
+       return (-p,q)
+
+
+def rat_larger (a,b):
+       return rat_subtract (a, b )[0] > 0
+
+def rat_subtract (a,b ):
+       return rat_add (a, rat_neg (b))
+
+def rat_to_duration (frac):
+       log = 1
+       d = (1,1)
+       while rat_larger (d, frac):
+               d = rat_multiply (d, (1,2))
+               log = log << 1
+
+       frac = rat_subtract (frac, d)
+       dots = 0
+       if frac == rat_multiply (d, (1,2)):
+               dots = 1
+       elif frac == rat_multiply (d, (3,4)):
+               dots = 2
+       return (log, dots)      
+
+
+class Barcheck :
+       def __init__ (self):
+               pass
+       def dump (self):
+               return '|\n'
+
+class Beam:
+       def __init__ (self, ch):
+               self.char = ch
+       def dump (self):
+               return self.char
+
+class Slur:
+       def __init__ (self,id):
+               self.id = id
+               self.start_chord = None
+               self.end_chord = None
+       def calculate (self):
+               s =self.start_chord
+               e= self.end_chord
+
+               if e and s:
+                       s.note_suffix = s.note_suffix + '('
+                       e.note_prefix = ')' + e.note_prefix
+               else:
+                       sys.stderr.write ("\nOrphaned slur")
+                       
+class Voice:
+       def __init__ (self):
+               self.entries = []
+               self.chords = []
+               self.staff = None
+               self.current_slurs = []
+               self.slurs = []
+       def toggle_slur (self, id):
+               
+               for s in self.current_slurs:
+                       if s.id == id:
+                               self.current_slurs.remove (s)
+                               s.end_chord = self.chords[-1]
+                               return
+               s = Slur (id)
+               s.start_chord = self.chords[-1]
+               self.current_slurs.append (s)
+               self.slurs.append (s)
+               
+       def last_chord (self):
+               return self.chords[-1]
+       def add_chord (self, ch):
+               self.chords.append (ch)
+               self.entries.append (ch)
+       def add_nonchord (self, nch):
+               self.entries.append (nch)
+
+       def idstring (self):
+               return 'staff%svoice%s ' % (encodeint (self.staff.number) , encodeint(self.number))
+       def dump (self):
+               str = ''
+               ln = ''
+               for e in self.entries:
+                       next = ' ' + e.dump ()
+                       if next[-1] == '\n':
+                               str  = str + ln + next
+                               ln = ''
+                               continue
+                       
+                       if len (ln) +len (next) > 72:
+                               str = str+ ln + '\n'
+                               ln = ''
+                       ln = ln + next
+                       
+                       
+               str = str  + ln
+               id = self.idstring ()
+                       
+               str = '%s =  \\notes { \n %s }\n '% (id, str)
+               return str
+       def calculate_graces (self):
+               lastgr = 0
+               lastc = None
+               for c in self.chords:
+                       if c.grace and  not lastgr:
+                               c.chord_prefix = c.chord_prefix + '\\grace { '
+                       elif not c.grace and lastgr:
+                               lastc.chord_suffix = lastc.chord_suffix + ' } '
+                       lastgr = c.grace
+                       lastc = c
+       def calculate (self):
+               self.calculate_graces ()
+               for s in self.slurs:
+                       s.calculate ()
+
+class Clef:
+       def __init__ (self, cl):
+               self.type = cl
+       def dump(self):
+               return '\\clef %s;' % self.type
+
+clef_table = {
+       'b':'bass'  ,
+       'r':'baritone',
+       'n':'tenor',
+       'a':'alto',
+       'm':'mezzosoprano',
+       's':'soprano',
+       't':'treble',
+       'f':'frenchviolin',
+       } 
+class Staff:
+       def __init__ (self): 
+               self.voices = (Voice (), Voice())
+               self.clef = None
+               self.instrument = 0
+               self.voice_idx = 0
+               self.number = None
+               
+               i = 0
+               for v  in self.voices:
+                       v.staff = self
+                       v.number = i
+                       i = i+1
+       def set_clef (self, letter):
+
+               clstr = clef_table[letter]
+               self.voices[0].add_nonchord (Clef (clstr))
+               
+       def current_voice (self):
+               return self.voices[self.voice_idx]
+       def next_voice (self):
+               self.voice_idx = (self.voice_idx + 1)%len (self.voices)
+
+       def calculate (self):
+               for v in self.voices:
+                       v.calculate ()
+       def idstring (self):
+               return 'staff%s' % encodeint (self.number)
+       def dump (self):
+               str = ''
+
+               refs = ''
+               for v in self.voices:
+                       str = str + v.dump()
+                       refs = refs + '\\' + v.idstring ()+  ' '
+               
+               str = str + '\n\n%s = \\context Staff = %s \n  < \n %s >\n\n\n'% (self.idstring (), self.idstring (), refs)
+               return str
+
+class Tuplet:
+       def __init__ (self, number, base, dots):
+               self.chords = []
+               self.number = number
+               self.replaces = tuplet_table[number]
+               self.base = base
+               self.dots = dots
+               
+               length = (1,base)
+               if dots == 1:
+                       length = rat_multiply (length, (3,2))
+               elif dots == 2:
+                       length = rat_multiply (length, (7,4))
+
+               length = rat_multiply (length, (1,self.replaces))
+
+               (nb,nd) =rat_to_duration (length)
+
+               self.note_base = nb
+               self.note_dots = nd
+
+       def add_chord (self, ch):
+               ch.dots = self.note_dots
+               ch.basic_duration = self.note_base
+               self.chords.append (ch)
+
+               if len (self.chords) == 1:
+                       ch.chord_prefix = '\\times %d/%d { ' % (self.replaces, self.number)
+               elif len (self.chords) == self.number:
+                       ch.chord_suffix = ' }' 
+               
+class Chord:
+       def __init__ (self):
+               self.pitches = []
+               self.dots = 0
+               self.basic_duration = 0
+               self.scripts = []
+               self.grace = 0
+               self.chord_prefix = ''
+               self.chord_suffix = ''
+               self.note_prefix = ''
+               self.note_suffix = ''
+               
+       def dump (self):
+               str = ''
+
+               sd = ''
+               if self.basic_duration == 0.5:
+                       sd = '\\breve'
+               else:
+                       sd = '%d' % self.basic_duration
+               sd = sd + '.' * self.dots 
+               for p in self.pitches:
+                       if str:
+                               str = str + ' ' 
+                       str = str + pitch_to_lily_string (p) + sd
+
+               for s in self.scripts:
+                       str = str + '-' + s
+
+               str = self.note_prefix +str  + self.note_suffix
+               
+               if len (self.pitches) > 1:
+                       str = '<%s>' % str
+               elif len (self.pitches) == 0:
+                       str = 'r' + sd
+
+               str = self.chord_prefix + str + self.chord_suffix
+               
+               return str
+               
+SPACE=' \t\n'
+DIGITS ='0123456789'
+basicdur_table = {
+       9: 0.5,
+       0: 0 ,
+       2: 2 ,
+       4: 4 ,
+       8: 8 ,
+       1: 16,
+       3: 32,
+       6: 64
+       }
+
+
+ornament_table = {
+       't': '\\prall',
+       'm': '\\mordent',
+       'x': '"x"',
+       '+': '+',
+       'u': '"pizz"',
+       'p': '|',
+       '(': '"paren"',
+       ')': '"paren"',
+       'g': '"segno"',
+       '.': '.',
+       'fd': '\\fermata',
+       'f': '\\fermata',
+       '_': '-',
+       'T': '\\trill',
+       '>': '>',
+       '^': '^',
+       }
+
+class Parser:
+       def __init__ (self, filename):
+               self.staffs = []
+               self.forced_duration = None
+               self.last_name = 0
+               self.last_oct = 0               
+               self.tuplets_expected = 0
+               self.tuplets = []
+               self.last_basic_duration = 4
+
+               self.parse (filename)
+               
+       def set_staffs (self, number):
+               self.staffs = map (lambda x: Staff (), range(0, number))
+               
+               self.staff_idx = 0
+
+               i =0
+               for s in self.staffs:
+                       s.number = i
+                       i = i+1
+       def current_staff (self):
+               return self.staffs[self.staff_idx]
+
+       def current_voice (self):
+               return self.current_staff ().current_voice ()
+       
+       def next_staff (self):
+               self.staff_idx = (self.staff_idx + 1)% len (self.staffs)
+               
+       def parse_note (self, str):
+               name = None
+               ch = None
+
+               grace = 0
+               if str[0] == 'G':
+                       grace = 1
+                       str = str[1:]
+                       
+               if str[0] == 'z':
+                       ch = self.current_voice().last_chord()
+                       str = str[1:]
+               else:
+                       ch = Chord ()
+                       self.current_voice().add_chord (ch)                     
+               if str[0] <> 'r':
+                       name = (ord (str[0]) - ord('a') + 5) % 7
+
+               str = str[1:]
+
+               ch.grace = ch.grace or grace 
+               
+               forced_duration  = 0
+               alteration = 0
+               dots = 0
+               oct = None
+               durdigit = None
+               multibar = 0
+               tupnumber = 0
+               extra_oct = 0
+               while str[0] in 'dsfmnul0123456789.,+-':
+                       c = str[0]
+                       str = str[1:]
+                       if c == 'f':
+                               alteration = alteration -1
+                       elif c == 'n':
+                               alteration = 0
+                       elif c == 'm':
+                               multibar = 1
+                       elif c == 's':
+                               alteration = alteration +1
+                       elif c == 'd':
+                               dots = dots + 1
+                       elif c in DIGITS and durdigit == None and \
+                            self.tuplets_expected == 0:
+                               durdigit = string.atoi (c)
+                       elif c in DIGITS:
+                               oct = string.atoi (c) - 3
+                       elif c == '+':
+                               extra_oct = extra_oct + 1
+                       elif c == '-':
+                               extra_oct = extra_oct - 1
+                       elif c == '.':
+                               dots = dots+ 1
+                               forced_duration = 2
+                       elif c == ',':
+                               forced_duration = 2
+
+               if str[0] == 'x':
+                       str = str[1:]
+                       tupnumber = string.atoi (str[0])
+                       str = str[1:]
+                       str=re.sub (r'^n?f?[+-0-9.]+', '' , str)
+
+               
+               if durdigit:
+                       try:
+                               basic_duration =  basicdur_table[durdigit]
+                               self.last_basic_duration = basic_duration
+                       except KeyError:
+                               sys.stderr.write ("""
+Huh? expected duration, found %d Left was `%s'""" % (durdigit, str[:20]))
+
+                               basic_duration = 4
+               else:
+                       basic_duration = self.last_basic_duration
+
+
+               
+               if name <> None and oct == None:
+                       e = 0
+                       if self.last_name < name and name -self.last_name > 3:
+                               e = -1
+                       elif self.last_name > name and self.last_name -name > 3:
+                               e = 1
+
+                       oct = self.last_oct  +e + extra_oct
+
+               if name <> None:
+                       self.last_oct = oct
+                       self.last_name = name
+                               
+               if name <> None:
+                       ch.pitches.append ((oct, name,  alteration))
+
+               # do before adding to tuplet.
+               ch.basic_duration = basic_duration
+               ch.dots = dots
+
+               if forced_duration:
+                       self.forced_duration = ch.basic_duration / forced_duration
+
+               if tupnumber:
+                       tup =Tuplet (tupnumber, basic_duration, dots)
+                       self.tuplets_expected = tupnumber
+                       self.tuplets.append (tup)
+
+               if self.tuplets_expected > 0:
+                       self.tuplets[-1].add_chord (ch)
+                       self.tuplets_expected = self.tuplets_expected - 1
+                       
+               return str
+       def parse_basso_continuo (self, str):
+               while str[0] in DIGITS +'#n-':
+                       scr = str[0]
+
+                       if scr == '#':
+                               scr = '\\\\textsharp'
+                       
+                       if len(scr)>1 or scr not in DIGITS:
+                               scr = '"%s"' % scr
+                               
+                       self.current_voice().last_chord ().scripts.append (scr)
+                       str=str[1:]
+               return str
+       def parse_beams (self,str):
+               c = str[0]
+       #       self.current_voice().add_nonchord (Beam(c))
+               if str[0] == '[':
+                       str = str[1:]
+                       while str[0] in '+-0123456789':
+                               str=str[1:]
+               else:
+                       str = str[1:]
+                                       
+               return str
+       
+       def clean (self, ls):
+               ls = map (stripcomment, ls)
+               ls = map (stripwhite, ls)
+               ls = map (stripeols, ls)
+
+
+               ls = filter (lambda x: x <> '', ls)
+               return ls
+
+       def parse_header  (self, ls):
+
+               opening = ls[0]
+               ls = ls[1:]
+
+
+               opening = map (string.atoi, re.split ('[\t ]+', opening))
+
+               (no_staffs, no_instruments, timesig_num,timesig_den, ptimesig_num,
+                ptimesig_den, pickup_beats,keysig_number) = tuple (opening)
+
+               opening = ls[0]
+               ls = ls[1:]
+
+               # ignore this.
+               # opening = map (string.atoi, re.split ('[\t ]+', opening))
+               # (no_pages,no_systems, musicsize, fracindent) = tuple (opening)
+
+               instruments = []
+               while len (instruments) < no_instruments:
+                       instruments.append (ls[0])
+                       ls = ls[1:]
+
+
+               l = ls[0]
+               ls = ls[1:]
+
+
+               self.set_staffs (no_staffs)
+               for s in self.staffs:
+                       s.set_clef(l[0])
+                       l = l[1:]
+
+               # dump path 
+               ls = ls[1:] 
+
+               # dump more ?
+               ls = ls[2:]
+
+               return ls
+
+       def parse_ornament (self, left):
+               left = left[1:]
+               e = self.current_voice ().last_chord ()
+
+               id = left[0]
+               left = left[1:]
+               if left[0] == 'd':
+                       id = id +'d'
+                       left = left [1:]
+
+               orn = '"orn"'
+               try:
+                       orn = ornament_table[id]
+               except KeyError:
+                       sys.stderr.write ("unknown ornament `%s'\n" % id)
+                       
+               e.scripts.append (orn)
+               return left
+       def parse_barcheck (self, left):
+               self.current_voice ().add_nonchord (Barcheck ())
+               
+               return left [1:]
+
+       def parse_slur (self, left):
+               left = left[1:]
+
+               id = None
+
+               if re.match ('[A-Z0-9]', left[0]):
+                       id = left[0]
+                       left= left[1:]
+               while left[0] in 'uld0123456789+-.':
+                       left= left[1:]
+                       
+               self.current_voice ().toggle_slur (id)
+               return left
+
+       def parse_mumbo_jumbo (self,left):
+               left = left[1:]
+               while left and  left[0] <> '\\':
+                       left = left[1:]
+
+               left  = left[1:]
+               return left
+       def parsex (self,left):
+               left = left[1:]
+               while left[0] in DIGITS:
+                       left = left[1:]
+
+               return left
+       
+       def parse_body (self, left):
+               left = re.sub ('[ \t\n]+',   ' ', left)
+
+               while left:
+                       c = left[0]
+                       if c in 'Gzabcdefgr':
+                               left = self.parse_note (left)
+                       elif c in DIGITS + 'n#-':
+                               left = self.parse_basso_continuo (left)
+                       elif c in SPACE:
+                               left = left[1:]
+                       elif c == 's':
+                               left = self.parse_slur (left)
+                       elif c == '|':
+                               left = self.parse_barcheck (left)
+                       elif c == 'o':
+                               left = self.parse_ornament (left)
+                       elif c == 'x':
+                               left = self.parsex (left)
+                       elif c in "[]":
+                               left = self.parse_beams (left)
+                       elif left[:2] == "//":
+                               self.current_staff().next_voice ()
+                               left = left[2:]
+                       elif c == '/':
+                               self.next_staff ()
+                               left = left[1:]
+                       elif c == '\\':
+                               left = self.parse_mumbo_jumbo(left)
+                       else:
+                               sys.stderr.write ("""
+Huh? Unknown directive `%s', before `%s'""" % (c, left[:20] ))
+                               left = left[1:]
+
+               for c in self.staffs:
+                       c.calculate ()
+
+       def dump (self):
+               str = ''
+
+               refs = ''
+               for s in self.staffs:
+                       str = str +  s.dump ()
+                       refs = '\\' + s.idstring() + refs
+
+               str = str + "\n\n\\score { <\n %s\n > }" % refs 
+               return str
+                       
+
+       def parse (self,fn):
+               ls = open (fn).readlines ()
+               ls = self.clean (ls)
+               ls = self.parse_header (ls)
+               left = string.join (ls, ' ')
+               self.parse_body (left)
+               
+
+
+
+def help ():
+       sys.stdout.write (
+"""Usage: pmx2ly [OPTION]... PMX-FILE
+
+Convert PMX to LilyPond.
+
+Options:
+  -h, --help          this help
+  -o, --output=FILE   set output filename to FILE
+  -v, --version       version information
+
+PMX is a Musixtex preprocessor written by Don Simons, see
+http://www.gmd.de/Misc/Music/musixtex/software/pmx/
+
+Report bugs to bug-gnu-music@gnu.org.
+
+Written by Han-Wen Nienhuys <hanwen@cs.uu.nl>
+""")
+
+
+def print_version ():
+       sys.stdout.write ("""pmx2ly (GNU LilyPond) %s
+
+This is free software.  It is covered by the GNU General Public License,
+and you are welcome to change it and/or distribute copies of it under
+certain conditions.  Invoke as `midi2ly --warranty' for more information.
+
+Copyright (c) 2000 by Han-Wen Nienhuys <hanwen@cs.uu.nl>
+""" % version)
+def identify():
+       sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
+
+
+
+(options, files) = getopt.getopt (sys.argv[1:], 'vo:h', ['help','version', 'output='])
+out_filename = None
+for opt in options:
+       o = opt[0]
+       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()
+
+for f in files:
+       if f == '-':
+               f = ''
+
+       sys.stderr.write ('Processing `%s\'\n' % f)
+       e = Parser(f)
+       if not out_filename:
+               out_filename = os.path.basename (re.sub ('(?i).pmx$', '.ly', f))
+               
+       if out_filename == f:
+               out_filename = os.path.basename (f + '.ly')
+               
+       sys.stderr.write ('Writing `%s\'' % out_filename)
+       ly = e.dump()
+
+       
+       
+       fo = open (out_filename, 'w')
+       fo.write ('%% lily was here -- automatically converted by pmx2ly from %s\n' % f)
+       fo.write(ly)
+       fo.close ()
+       
+