]> git.donarmstrong.com Git - lilypond.git/blobdiff - python/musicexp.py
Merge branch 'master' of ssh+git://git.sv.gnu.org/srv/git/lilypond
[lilypond.git] / python / musicexp.py
index 5c3b91a804cda65ce59d45f26418ea4c217f6ce5..2ae8a6784f3ea480f3343d6539323fd79ec706d0 100644 (file)
@@ -2,9 +2,32 @@ import inspect
 import sys
 import string
 import re
+import lilylib as ly
+
+_ = ly._
 
 from rational import Rational
 
+# Store previously converted pitch for \relative conversion as a global state variable
+previous_pitch = None
+relative_pitches = False
+
+def warning (str):
+    ly.stderr_write ((_ ("warning: %s") % str) + "\n")
+
+
+def escape_instrument_string (input_string):
+    retstring = string.replace (input_string, "\"", "\\\"")
+    if re.match ('.*[\r\n]+.*', retstring):
+        rx = re.compile (r'[\n\r]+')
+        strings = rx.split (retstring)
+        retstring = "\\markup { \\column { "
+        for s in strings:
+            retstring += "\\line {\"" + s + "\"} "
+        retstring += "} }"
+    else:
+        retstring = "\"" + retstring + "\""
+    return retstring
 
 class Output_stack_element:
     def __init__ (self):
@@ -63,8 +86,14 @@ class Output_printer:
         self._line += str
 
     def unformatted_output (self, str):
-        self._nesting += str.count ('<') + str.count ('{')
-        self._nesting -= str.count ('>') + str.count ('}')
+        # don't indent on \< and indent only once on <<
+        self._nesting += ( str.count ('<') 
+                         - str.count ('\<') - str.count ('<<') 
+                         + str.count ('{') )
+        self._nesting -= ( str.count ('>') - str.count ('\>') - str.count ('>>')
+                                           - str.count ('->') - str.count ('_>')
+                                           - str.count ('^>')
+                         + str.count ('}') )
         self.print_verbatim (str)
         
     def print_duration_string (self, str):
@@ -108,8 +137,8 @@ class Output_printer:
         self.newline ()
         self._file.close ()
         self._file = None
-        
-        
+
+
 class Duration:
     def __init__ (self):
         self.duration_log = 0
@@ -126,11 +155,18 @@ class Duration:
     def ly_expression (self, factor = None):
         if not factor:
             factor = self.factor
-            
-        str = '%d%s' % (1 << self.duration_log, '.'*self.dots)
+
+        if self.duration_log < 0:
+            str = {-1: "\\breve", -2: "\\longa"}.get (self.duration_log, "1")
+        else:
+            str = '%d' % (1 << self.duration_log)
+        str += '.'*self.dots
 
         if factor <> Rational (1,1):
-            str += '*%d/%d' % (factor.numerator (), factor.denominator ())
+            if factor.denominator () <> 1:
+                str += '*%d/%d' % (factor.numerator (), factor.denominator ())
+            else:
+                str += '*%d' % factor.numerator ()
 
         return str
     
@@ -161,7 +197,71 @@ class Duration:
 
         return base * dot_fact * self.factor
 
-    
+
+# Implement the different note names for the various languages
+def pitch_generic (pitch, notenames, accidentals):
+    str = notenames[pitch.step]
+    if pitch.alteration < 0:
+        str += accidentals[0] * (-pitch.alteration)
+    elif pitch.alteration > 0:
+        str += accidentals[3] * (pitch.alteration)
+    return str
+
+def pitch_general (pitch):
+    str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'b'], ['es', 'eh', 'ih', 'is'])
+    return str.replace ('aes', 'as').replace ('ees', 'es')
+
+def pitch_nederlands (pitch):
+    return pitch_general (pitch)
+
+def pitch_english (pitch):
+    str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'b'], ['f', 'qf', 'qs', 's'])
+    return str.replace ('aes', 'as').replace ('ees', 'es')
+
+def pitch_deutsch (pitch):
+    str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'h'], ['es', 'eh', 'ih', 'is'])
+    return str.replace ('hes', 'b').replace ('aes', 'as').replace ('ees', 'es')
+
+def pitch_norsk (pitch):
+    return pitch_deutsch (pitch)
+
+def pitch_svenska (pitch):
+    str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'h'], ['ess', '', '', 'iss'])
+    return str.replace ('hess', 'b').replace ('aes', 'as').replace ('ees', 'es')
+
+def pitch_italiano (pitch):
+    str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', 'sb', 'sd', 'd'])
+    return str
+
+def pitch_catalan (pitch):
+    return pitch_italiano (pitch)
+
+def pitch_espanol (pitch):
+    str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', '', '', 's'])
+    return str
+
+def pitch_vlaams (pitch):
+    str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', '', '', 'k'])
+    return str
+
+def set_pitch_language (language):
+    global pitch_generating_function
+    function_dict = {
+        "nederlands": pitch_nederlands,
+        "english": pitch_english,
+        "deutsch": pitch_deutsch,
+        "norsk": pitch_norsk,
+        "svenska": pitch_svenska,
+        "italiano": pitch_italiano,
+        "catalan": pitch_catalan,
+        "espanol": pitch_espanol,
+        "vlaams": pitch_vlaams}
+    pitch_generating_function = function_dict.get (language, pitch_general)
+
+# global variable to hold the formatting function.
+pitch_generating_function = pitch_general
+
+
 class Pitch:
     def __init__ (self):
         self.alteration = 0
@@ -208,21 +308,39 @@ class Pitch:
         return self.octave * 12 + [0,2,4,5,7,9,11][self.step] + self.alteration
     
     def ly_step_expression (self): 
-        str = 'cdefgab'[self.step]
-        if self.alteration > 0:
-            str += 'is'* (self.alteration)
-        elif self.alteration < 0:
-            str += 'es'* (-self.alteration)
+        return pitch_generating_function (self)
 
-        return str.replace ('aes', 'as').replace ('ees', 'es')
-    
-    def ly_expression (self):
-        str = self.ly_step_expression ()
+    def absolute_pitch (self):
         if self.octave >= 0:
-            str += "'" * (self.octave + 1) 
+            return "'" * (self.octave + 1)
         elif self.octave < -1:
-            str += "," * (-self.octave - 1) 
-            
+            return "," * (-self.octave - 1)
+        else:
+            return ''
+
+    def relative_pitch (self):
+        global previous_pitch
+        if not previous_pitch:
+            previous_pitch = self
+            return self.absolute_pitch ()
+        previous_pitch_steps = previous_pitch.octave * 7 + previous_pitch.step
+        this_pitch_steps = self.octave * 7 + self.step
+        pitch_diff = (this_pitch_steps - previous_pitch_steps)
+        previous_pitch = self
+        if pitch_diff > 3:
+            return "'" * ((pitch_diff + 3) / 7)
+        elif pitch_diff < -3:
+            return "," * ((-pitch_diff + 3) / 7)
+        else:
+            return ""
+
+    def ly_expression (self):
+        str = self.ly_step_expression ()
+        if relative_pitches:
+            str += self.relative_pitch ()
+        else:
+            str += self.absolute_pitch ()
+
         return str
     
     def print_ly (self, outputter):
@@ -310,6 +428,24 @@ class ModeChangingMusicWrapper (MusicWrapper):
         func ('\\%s' % self.mode)
         MusicWrapper.print_ly (self, func)
 
+class RelativeMusic (MusicWrapper):
+    def __init__ (self):
+        MusicWrapper.__init__ (self)
+        self.basepitch = None
+
+    def print_ly (self, func):
+        global previous_pitch
+        global relative_pitches
+        prev_relative_pitches = relative_pitches
+        relative_pitches = True
+        previous_pitch = self.basepitch
+        if not previous_pitch:
+            previous_pitch = Pitch ()
+        func ('\\relative %s%s' % (pitch_generating_function (previous_pitch), 
+                                   previous_pitch.absolute_pitch ()))
+        MusicWrapper.print_ly (self, func)
+        relative_pitches = prev_relative_pitches
+
 class TimeScaledMusic (MusicWrapper):
     def print_ly (self, func):
         func ('\\times %d/%d ' %
@@ -390,17 +526,31 @@ class NestedMusic(Music):
         return None
         
 class SequentialMusic (NestedMusic):
-    def print_ly (self, printer):
+    def get_last_event_chord (self):
+        value = None
+        at = len( self.elements ) - 1
+        while (at >= 0 and
+               not isinstance (self.elements[at], ChordEvent) and
+               not isinstance (self.elements[at], BarLine)):
+            at -= 1
+
+        if (at >= 0 and isinstance (self.elements[at], ChordEvent)):
+            value = self.elements[at]
+        return value
+
+    def print_ly (self, printer, newline = True):
         printer ('{')
         if self.comment:
             self.print_comment (printer)
 
-        printer.newline()
+        if newline:
+            printer.newline()
         for e in self.elements:
             e.print_ly (printer)
 
         printer ('}')
-        printer.newline()
+        if newline:
+            printer.newline()
             
     def lisp_sub_expression (self, pred):
         name = self.name()
@@ -415,6 +565,37 @@ class SequentialMusic (NestedMusic):
             e.set_start (start)
             start += e.get_length()
 
+class RepeatedMusic:
+    def __init__ (self):
+        self.repeat_type = "volta"
+        self.repeat_count = 2
+        self.endings = []
+        self.music = None
+    def set_music (self, music):
+        if isinstance (music, Music):
+            self.music = music
+        elif isinstance (music, list):
+            self.music = SequentialMusic ()
+            self.music.elements = music
+        else:
+            warning (_ ("unable to set the music %(music)s for the repeat %(repeat)s" % \
+                            {'music':music, 'repeat':self}))
+    def add_ending (self, music):
+        self.endings.append (music)
+    def print_ly (self, printer):
+        printer.dump ('\\repeat %s %s' % (self.repeat_type, self.repeat_count))
+        if self.music:
+            self.music.print_ly (printer)
+        else:
+            warning (_ ("encountered repeat without body"))
+            printer.dump ('{}')
+        if self.endings:
+            printer.dump ('\\alternative {')
+            for e in self.endings:
+                e.print_ly (printer)
+            printer.dump ('}')
+
+
 class Lyrics:
     def __init__ (self):
         self.lyrics_syllables = []
@@ -433,7 +614,102 @@ class Lyrics:
         return lstr
 
 
-class EventChord (NestedMusic):
+class Header:
+    def __init__ (self):
+        self.header_fields = {}
+    def set_field (self, field, value):
+        self.header_fields[field] = value
+
+    def print_ly (self, printer):
+        printer.dump ("\header {")
+        printer.newline ()
+        for (k,v) in self.header_fields.items ():
+            if v:
+                printer.dump ('%s = %s' % (k,v))
+                printer.newline ()
+        printer.dump ("}")
+        printer.newline ()
+        printer.newline ()
+
+
+class Paper:
+    def __init__ (self):
+        self.global_staff_size = -1
+        # page size
+        self.page_width = -1
+        self.page_height = -1
+        # page margins
+        self.top_margin = -1
+        self.bottom_margin = -1
+        self.left_margin = -1
+        self.right_margin = -1
+        self.system_left_margin = -1
+        self.system_right_margin = -1
+        self.system_distance = -1
+        self.top_system_distance = -1
+
+    def print_length_field (self, printer, field, value):
+        if value >= 0:
+            printer.dump ("%s = %s\\cm" % (field, value))
+            printer.newline ()
+    def print_ly (self, printer):
+        if self.global_staff_size > 0:
+            printer.dump ('#(set-global-staff-size %s)' % self.global_staff_size)
+            printer.newline ()
+        printer.dump ('\\paper {')
+        printer.newline ()
+        self.print_length_field (printer, "paper-width", self.page_width)
+        self.print_length_field (printer, "paper-height", self.page_height)
+        self.print_length_field (printer, "top-margin", self.top_margin)
+        self.print_length_field (printer, "botton-margin", self.bottom_margin)
+        self.print_length_field (printer, "left-margin", self.left_margin)
+        # TODO: maybe set line-width instead of right-margin?
+        self.print_length_field (printer, "right-margin", self.right_margin)
+        # TODO: What's the corresponding setting for system_left_margin and
+        #        system_right_margin in Lilypond?
+        self.print_length_field (printer, "between-system-space", self.system_distance)
+        self.print_length_field (printer, "page-top-space", self.top_system_distance)
+
+        printer.dump ('}')
+        printer.newline ()
+
+class Layout:
+    def __init__ (self):
+        self.context_dict = {}
+    def add_context (self, context):
+        if not self.context_dict.has_key (context):
+            self.context_dict[context] = []
+    def set_context_item (self, context, item):
+        self.add_context (context)
+        if not item in self.context_dict[context]:
+            self.context_dict[context].append (item)
+    def print_ly (self, printer):
+        if self.context_dict.items ():
+            printer.dump ('\\layout {')
+            printer.newline ()
+            for (context, defs) in self.context_dict.items ():
+                printer.dump ('\\context { \\%s' % context)
+                printer.newline ()
+                for d in defs:
+                    printer.dump (d)
+                    printer.newline ()
+                printer.dump ('}')
+                printer.newline ()
+            printer.dump ('}')
+            printer.newline ()
+
+
+class ChordEvent (NestedMusic):
+    def __init__ (self):
+        NestedMusic.__init__ (self)
+        self.grace_elements = None
+        self.grace_type = None
+    def append_grace (self, element):
+        if element:
+            if not self.grace_elements:
+                self.grace_elements = SequentialMusic ()
+            self.grace_elements.append (element)
+
     def get_length (self):
         l = Rational (0)
         for e in self.elements:
@@ -451,13 +727,32 @@ class EventChord (NestedMusic):
         other_events = [e for e in self.elements if
                 not isinstance (e, RhythmicEvent)]
 
+        if self.grace_elements and self.elements:
+            if self.grace_type:
+                printer ('\\%s' % self.grace_type)
+            else:
+                printer ('\\grace')
+            # don't print newlines after the { and } braces
+            self.grace_elements.print_ly (printer, False)
+        # Print all overrides and other settings needed by the 
+        # articulations/ornaments before the note
+        for e in other_events:
+            e.print_before_note (printer)
+
         if rest_events:
             rest_events[0].print_ly (printer)
         elif len (note_events) == 1:
             note_events[0].print_ly (printer)
         elif note_events:
-            pitches = [x.pitch.ly_expression () for x in note_events]
+            global previous_pitch
+            pitches = []
+            basepitch = None
+            for x in note_events:
+                pitches.append (x.pitch.ly_expression ())
+                if not basepitch:
+                    basepitch = previous_pitch
             printer ('<%s>' % string.join (pitches))
+            previous_pitch = basepitch
             note_events[0].duration.print_ly (printer)
         else:
             pass
@@ -465,117 +760,175 @@ class EventChord (NestedMusic):
         for e in other_events:
             e.print_ly (printer)
 
+        for e in other_events:
+            e.print_after_note (printer)
+
         self.print_comment (printer)
             
+class Partial (Music):
+    def __init__ (self):
+        Music.__init__ (self)
+        self.partial = None
+    def print_ly (self, printer):
+        if self.partial:
+            printer.dump ("\\partial %s" % self.partial.ly_expression ())
 
-class BarCheck (Music):
+class BarLine (Music):
     def __init__ (self):
         Music.__init__ (self)
         self.bar_number = 0
+        self.type = None
         
     def print_ly (self, printer):
+        bar_symbol = { 'regular': "|", 'dotted': ":", 'dashed': ":",
+                       'heavy': "|", 'light-light': "||", 'light-heavy': "|.",
+                       'heavy-light': ".|", 'heavy-heavy': ".|.", 'tick': "'",
+                       'short': "'", 'none': "" }.get (self.type, None)
+        if bar_symbol <> None:
+            printer.dump ('\\bar "%s"' % bar_symbol)
+        else:
+            printer.dump ("|")
+
         if self.bar_number > 0 and (self.bar_number % 10) == 0:
-            printer.dump ("|  \\barNumberCheck #%d " % self.bar_number)
-            printer.newline ()
+            printer.dump ("\\barNumberCheck #%d " % self.bar_number)
         else:
-            printer.dump ("| ")
             printer.print_verbatim (' %% %d' % self.bar_number)
-            printer.newline ()
+        printer.newline ()
 
     def ly_expression (self):
         return " | "
 
 class Event(Music):
+    def __init__ (self):
+        # strings to print before the note to which an event is attached.
+        # Ignored for notes etc.
+        self.before_note = None
+        self.after_note = None
+   # print something before the note to which an event is attached, e.g. overrides
+    def print_before_note (self, printer):
+        if self.before_note:
+            printer.dump (self.before_note)
+   # print something after the note to which an event is attached, e.g. resetting
+    def print_after_note (self, printer):
+        if self.after_note:
+            printer.dump (self.after_note)
     pass
 
 class SpanEvent (Event):
-    def __init__(self):
+    def __init__ (self):
         Event.__init__ (self)
-        self.span_direction = 0
-        self.line_type = 0
-        self.size = 0
+        self.span_direction = 0 # start/stop
+        self.line_type = 'solid'
+        self.span_type = 0 # e.g. cres/decrescendo, ottava up/down
+        self.size = 0 # size of e.g. ocrave shift
     def wait_for_note (self):
         return True
     def get_properties(self):
         return "'span-direction  %d" % self.span_direction
-    
+    def set_span_type (self, type):
+        self.span_type = type
+
 class SlurEvent (SpanEvent):
+    def print_before_note (self, printer):
+        command = {'dotted': '\\slurDotted', 
+                  'dashed' : '\\slurDashed'}.get (self.line_type, '')
+        if command and self.span_direction == -1:
+            printer.dump (command)
+    def print_after_note (self, printer):
+        # reset non-solid slur types!
+        command = {'dotted': '\\slurSolid', 
+                  'dashed' : '\\slurSolid'}.get (self.line_type, '')
+        if command and self.span_direction == -1:
+            printer.dump (command)
     def ly_expression (self):
-        before = ''
-        after = ''
-        # TODO: setting dashed/dotted line style does not work, because that
-        #       command needs to be written before the note, not when the
-        #       event is observed after the note!
-        #if self.line_type == 1:
-            #before = '\\slurDotted'
-        #elif self.line_type == 2:
-            #before = '\\slurDashed'
-        #if before:
-            #after = '\\slurSolid'
-
-        return {-1: before + '(' + after,
-            0:'',
-            1:')'}.get (self.span_direction, '')
+        return {-1: '(', 1:')'}.get (self.span_direction, '')
 
 class BeamEvent (SpanEvent):
     def ly_expression (self):
-        return {-1: '[',
-            0:'',
-            1:']'}.get (self.span_direction, '')
+        return {-1: '[', 1:']'}.get (self.span_direction, '')
 
 class PedalEvent (SpanEvent):
     def ly_expression (self):
         return {-1: '\\sustainDown',
-            0:'',
+            0:'\\sustainUp\\sustainDown',
             1:'\\sustainUp'}.get (self.span_direction, '')
 
-# type==-1 means octave up, type==-2 means octave down
+class TextSpannerEvent (SpanEvent):
+    def ly_expression (self):
+        return {-1: '\\startTextSpan',
+            1:'\\stopTextSpan'}.get (self.span_direction, '')
+
+class BracketSpannerEvent (SpanEvent):
+    # Ligature brackets use prefix-notation!!!
+    def print_before_note (self, printer):
+        if self.span_direction == -1:
+            printer.dump ('\[')
+    # the the bracket after the last note
+    def print_after_note (self, printer):
+        if self.span_direction == 1:
+            printer.dump ('\]')
+    # we're printing everything in print_(before|after)_note...
+    def ly_expression (self):
+        return '';
+
+
 class OctaveShiftEvent (SpanEvent):
     def wait_for_note (self):
-        return False;
+        return False
+    def set_span_type (self, type):
+        self.span_type = {'up': 1, 'down': -1}.get (type, 0)
     def ly_octave_shift_indicator (self):
-        if self.size == 8:
-            value = 1
-        elif self.size == 15:
-            value = 2
-        else:
-            value = 0
-        # -2 means up
-        if self.span_direction == -2:
-            value = -value
+        # convert 8/15 to lilypond indicators (+-1/+-2)
+        value = {8: 1, 15: 2}.get (self.size, 0)
+        # negative values go up!
+        value *= -1*self.span_type
         return value
     def ly_expression (self):
         dir = self.ly_octave_shift_indicator ()
         value = ''
         if dir:
             value = '#(set-octavation %s)' % dir
-        return {-2: value,
-           -1: value,
-            0: '',
+        return { 
+            -1: value,
             1: '#(set-octavation 0)'}.get (self.span_direction, '')
 
 class TrillSpanEvent (SpanEvent):
     def ly_expression (self):
         return {-1: '\\startTrillSpan',
-            0:'',
+            0: '', # no need to write out anything for type='continue'
             1:'\\stopTrillSpan'}.get (self.span_direction, '')
 
 class GlissandoEvent (SpanEvent):
+    def print_before_note (self, printer):
+        if self.span_direction == -1:
+            style= {
+                "dashed" : "dashed-line",
+                "dotted" : "dotted-line",
+                "wavy"   : "zigzag" 
+            }. get (self.line_type, None)
+            if style:
+                printer.dump ("\once \override Glissando #'style = #'%s" % style)
     def ly_expression (self):
-        style = ''
-        # TODO: wavy-line glissandos don't work, becasue the style has to be
-        #       set before the note, at the \glissando it's already too late!
-        #if self.line_type == 3: # wavy-line:
-            #style = "\once\override Glissando #'style = #'zigzag"
-        # In lilypond, glissando is NOT a spanner, unlike MusicXML.
-        return {-1: style + '\\glissando',
-            0:'',
+        return {-1: '\\glissando',
             1:''}.get (self.span_direction, '')
 
 class ArpeggioEvent(Event):
+    def __init__ (self):
+        Event.__init__ (self)
+        self.direction = 0
+        self.non_arpeggiate = False
     def wait_for_note (self):
-        return True;
+        return True
+    def print_before_note (self, printer):
+        if self.non_arpeggiate:
+            printer.dump ("\\arpeggioBracket")
+        else:
+          dir = { -1: "\\arpeggioDown", 1: "\\arpeggioUp" }.get (self.direction, '')
+          if dir:
+              printer.dump (dir)
+    def print_after_note (self, printer):
+        if self.non_arpeggiate or self.direction:
+            printer.dump ("\\arpeggioNeutral")
     def ly_expression (self):
         return ('\\arpeggio')
 
@@ -586,10 +939,13 @@ class TieEvent(Event):
 
 
 class HairpinEvent (SpanEvent):
-    def __init__ (self, type):
-        self.type = type
+    def set_span_type (self, type):
+        self.span_type = {'crescendo' : 1, 'decrescendo' : -1, 'diminuendo' : -1 }.get (type, 0)
     def hairpin_to_ly (self):
-        return { 0: '\!', 1: '\<', -1: '\>' }.get (self.type, '')
+        if self.span_direction == 1:
+            return '\!'
+        else:
+            return {1: '\<', -1: '\>'}.get (self.span_type, '')
     
     def ly_expression (self):
         return self.hairpin_to_ly ()
@@ -603,34 +959,67 @@ class HairpinEvent (SpanEvent):
 
 class DynamicsEvent (Event):
     def __init__ (self):
+        Event.__init__ (self)
         self.type = None
-        self.available_commands = [ "ppppp", "pppp", "ppp", "pp", "p", 
-                                    "mp", "mf", 
-                                    "f", "ff", "fff", "ffff", 
-                                    "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" ];
     def wait_for_note (self):
-        return True;
+        return True
     def ly_expression (self):
-        if self.type == None:
-            return;
-        elif self.type in self.available_commands:
+        if self.type:
             return '\%s' % self.type
         else:
-            return '-\markup{ \dynamic %s }' % self.type
-        
-    def print_ly (self, printer):
-        if self.type == None:
             return
-        elif self.type in self.available_commands:
+
+    def print_ly (self, printer):
+        if self.type:
             printer.dump ("\\%s" % self.type)
+
+class MarkEvent (Event):
+    def __init__ (self, text="\\default"):
+        Event.__init__ (self)
+        self.mark = text
+    def wait_for_note (self):
+        return False
+    def ly_contents (self):
+        if self.mark:
+            return '%s' % self.mark
         else:
-            printer.dump ("-\\markup{ \\dynamic %s }" % self.type)
+            return "\"ERROR\""
+    def ly_expression (self):
+        return '\\mark %s' % self.ly_contents ()
+
+class MusicGlyphMarkEvent (MarkEvent):
+    def ly_contents (self):
+        if self.mark:
+            return '\\markup { \\musicglyph #"scripts.%s" }' % self.mark
+        else:
+            return ''
+
+
+class TextEvent (Event):
+    def __init__ (self):
+        Event.__init__ (self)
+        self.Text = None
+        self.force_direction = None
+        self.markup = ''
+    def wait_for_note (self):
+        return True
 
+    def direction_mod (self):
+        return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
+
+    def ly_expression (self):
+        base_string = '%s\"%s\"'
+        if self.markup:
+            base_string = '%s\markup{ ' + self.markup + ' {%s} }'
+        return base_string % (self.direction_mod (), self.text)
 
 class ArticulationEvent (Event):
     def __init__ (self):
+        Event.__init__ (self)
         self.type = None
         self.force_direction = None
+    def wait_for_note (self):
+        return True
 
     def direction_mod (self):
         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
@@ -643,20 +1032,71 @@ class ShortArticulationEvent (ArticulationEvent):
         # default is -
         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
     def ly_expression (self):
-        return '%s%s' % (self.direction_mod (), self.type)
+        if self.type:
+            return '%s%s' % (self.direction_mod (), self.type)
+        else:
+            return ''
 
-class TremoloEvent (Event):
+class NoDirectionArticulationEvent (ArticulationEvent):
+    def ly_expression (self):
+        if self.type:
+            return '\\%s' % self.type
+        else:
+            return ''
+
+class MarkupEvent (ShortArticulationEvent):
+    def __init__ (self):
+        ArticulationEvent.__init__ (self)
+        self.contents = None
+    def ly_expression (self):
+        if self.contents:
+            return "%s\\markup { %s }" % (self.direction_mod (), self.contents)
+        else:
+            return ''
+
+class FretEvent (MarkupEvent):
+    def __init__ (self):
+        MarkupEvent.__init__ (self)
+        self.force_direction = 1
+        self.strings = 6
+        self.frets = 4
+        self.barre = None
+        self.elements = []
+    def ly_expression (self):
+        val = ""
+        if self.strings <> 6:
+            val += "w:%s;" % self.strings
+        if self.frets <> 4:
+            val += "h:%s;" % self.frets
+        if self.barre and len (self.barre) >= 3:
+            val += "c:%s-%s-%s;" % (self.barre[0], self.barre[1], self.barre[2])
+        have_fingering = False
+        for i in self.elements:
+            if len (i) > 1:
+                val += "%s-%s" % (i[0], i[1])
+            if len (i) > 2:
+                have_fingering = True
+                val += "-%s" % i[2]
+            val += ";"
+        if have_fingering:
+            val = "f:1;" + val
+        if val:
+            return "%s\\markup { \\fret-diagram #\"%s\" }" % (self.direction_mod (), val)
+        else:
+            return ''
+
+class TremoloEvent (ArticulationEvent):
     def __init__ (self):
         Event.__init__ (self)
         self.bars = 0
 
     def ly_expression (self):
         str=''
-        if self.bars > 0:
+        if self.bars and self.bars > 0:
             str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
         return str
 
-class BendEvent (Event):
+class BendEvent (ArticulationEvent):
     def __init__ (self):
         Event.__init__ (self)
         self.alter = 0
@@ -679,12 +1119,23 @@ class RhythmicEvent(Event):
                 % self.duration.lisp_expression ())
     
 class RestEvent (RhythmicEvent):
+    def __init__ (self):
+        RhythmicEvent.__init__ (self)
+        self.pitch = None
     def ly_expression (self):
-        return 'r%s' % self.duration.ly_expression ()
+        if self.pitch:
+            return "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ())
+        else:
+            return 'r%s' % self.duration.ly_expression ()
     
     def print_ly (self, printer):
-        printer('r')
-        self.duration.print_ly (printer)
+        if self.pitch:
+            self.pitch.print_ly (printer)
+            self.duration.print_ly (printer)
+            printer ('\\rest')
+        else:
+            printer('r')
+            self.duration.print_ly (printer)
 
 class SkipEvent (RhythmicEvent):
     def ly_expression (self):
@@ -809,6 +1260,17 @@ class ClefChange (Music):
         return clefsetting
 
 
+class StaffChange (Music):
+    def __init__ (self, staff):
+        Music.__init__ (self)
+        self.staff = staff
+    def ly_expression (self):
+        if self.staff:
+            return "\\change Staff=\"%s\"" % self.staff
+        else:
+            return ''
+
+
 class MultiMeasureRest(Music):
 
     def lisp_expression (self):
@@ -818,7 +1280,7 @@ class MultiMeasureRest(Music):
   'elements
   (list (make-music (quote BarCheck))
         (make-music
-          'EventChord
+          'ChordEvent
           'elements
           (list (make-music
                   'MultiMeasureRestEvent
@@ -831,6 +1293,156 @@ class MultiMeasureRest(Music):
         return 'R%s' % self.duration.ly_expression ()
 
 
+class StaffGroup:
+    def __init__ (self, command = "StaffGroup"):
+        self.stafftype = command
+        self.id = None
+        self.instrument_name = None
+        self.short_instrument_name = None
+        self.symbol = None
+        self.spanbar = None
+        self.children = []
+        self.is_group = True
+        # part_information is a list with entries of the form
+        #     [staffid, voicelist]
+        # where voicelist is a list with entries of the form
+        #     [voiceid1, [lyricsid11, lyricsid12,...] ]
+        self.part_information = None
+
+    def append_staff (self, staff):
+        self.children.append (staff)
+
+    def set_part_information (self, part_name, staves_info):
+        if part_name == self.id:
+            self.part_information = staves_info
+        else:
+            for c in self.children:
+                c.set_part_information (part_name, staves_info)
+
+    def print_ly_contents (self, printer):
+        for c in self.children:
+            if c:
+                c.print_ly (printer)
+    def print_ly_overrides (self, printer):
+        needs_with = False
+        needs_with |= self.spanbar == "no"
+        needs_with |= self.instrument_name != None
+        needs_with |= self.short_instrument_name != None
+        needs_with |= (self.symbol != None) and (self.symbol != "bracket")
+        if needs_with:
+            printer.dump ("\\with {")
+            if self.instrument_name or self.short_instrument_name:
+                printer.dump ("\\consists \"Instrument_name_engraver\"")
+            if self.spanbar == "no":
+                printer.dump ("\\override SpanBar #'transparent = ##t")
+            brack = {"brace": "SystemStartBrace",
+                     "none": "f",
+                     "line": "SystemStartSquare"}.get (self.symbol, None)
+            if brack:
+                printer.dump ("systemStartDelimiter = #'%s" % brack)
+            printer.dump ("}")
+
+    def print_ly (self, printer):
+        if self.stafftype:
+            printer.dump ("\\new %s" % self.stafftype)
+        self.print_ly_overrides (printer)
+        printer.dump ("<<")
+        printer.newline ()
+        if self.stafftype and self.instrument_name:
+            printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype, 
+                    escape_instrument_string (self.instrument_name)))
+            printer.newline ()
+        if self.stafftype and self.short_instrument_name:
+            printer.dump ("\\set %s.shortInstrumentName = %s" % (self.stafftype,
+                    escape_instrument_string (self.short_instrument_name)))
+            printer.newline ()
+        self.print_ly_contents (printer)
+        printer.newline ()
+        printer.dump (">>")
+        printer.newline ()
+
+
+class Staff (StaffGroup):
+    def __init__ (self, command = "Staff"):
+        StaffGroup.__init__ (self, command)
+        self.is_group = False
+        self.part = None
+        self.voice_command = "Voice"
+        self.substafftype = None
+
+    def print_ly_overrides (self, printer):
+        pass
+
+    def print_ly_contents (self, printer):
+        if not self.id or not self.part_information:
+            return
+        sub_staff_type = self.substafftype
+        if not sub_staff_type:
+            sub_staff_type = self.stafftype
+
+        for [staff_id, voices] in self.part_information:
+            if staff_id:
+                printer ('\\context %s = "%s" << ' % (sub_staff_type, staff_id))
+            else:
+                printer ('\\context %s << ' % sub_staff_type)
+            printer.newline ()
+            n = 0
+            nr_voices = len (voices)
+            for [v, lyrics] in voices:
+                n += 1
+                voice_count_text = ''
+                if nr_voices > 1:
+                    voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
+                                        3: ' \\voiceThree'}.get (n, ' \\voiceFour')
+                printer ('\\context %s = "%s" {%s \\%s }' % (self.voice_command, v, voice_count_text, v))
+                printer.newline ()
+
+                for l in lyrics:
+                    printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
+                    printer.newline()
+            printer ('>>')
+
+    def print_ly (self, printer):
+        if self.part_information and len (self.part_information) > 1:
+            self.stafftype = "PianoStaff"
+            self.substafftype = "Staff"
+        StaffGroup.print_ly (self, printer)
+
+class TabStaff (Staff):
+    def __init__ (self, command = "TabStaff"):
+        Staff.__init__ (self, command)
+        self.string_tunings = []
+        self.tablature_format = None
+        self.voice_command = "TabVoice"
+    def print_ly_overrides (self, printer):
+        if self.string_tunings or self.tablature_format:
+            printer.dump ("\\with {")
+            if self.string_tunings:
+                printer.dump ("stringTunings = #'(")
+                for i in self.string_tunings:
+                    printer.dump ("%s" % i.semitones ())
+                printer.dump (")")
+            if self.tablature_format:
+                printer.dump ("tablatureFormat = #%s" % self.tablature_format)
+            printer.dump ("}")
+
+
+class DrumStaff (Staff):
+    def __init__ (self, command = "DrumStaff"):
+        Staff.__init__ (self, command)
+        self.drum_style_table = None
+        self.voice_command = "DrumVoice"
+    def print_ly_overrides (self, printer):
+        if self.drum_style_table:
+            printer.dump ("\with {")
+            printer.dump ("drumStyleTable = #%s" % self.drum_style_table)
+            printer.dump ("}")
+
+class RhythmicStaff (Staff):
+    def __init__ (self, command = "RhythmicStaff"):
+        Staff.__init__ (self, command)
+
+
 def test_pitch ():
     bflat = Pitch()
     bflat.alteration = -1
@@ -856,7 +1468,7 @@ def test_pitch ():
 
 def test_printer ():
     def make_note ():
-        evc = EventChord()
+        evc = ChordEvent()
         n = NoteEvent()
         evc.append (n)
         return n
@@ -886,21 +1498,21 @@ def test_printer ():
 def test_expr ():
     m = SequentialMusic()
     l = 2  
-    evc = EventChord()
+    evc = ChordEvent()
     n = NoteEvent()
     n.duration.duration_log = l
     n.pitch.step = 1
     evc.insert_around (None, n, 0)
     m.insert_around (None, evc, 0)
 
-    evc = EventChord()
+    evc = ChordEvent()
     n = NoteEvent()
     n.duration.duration_log = l
     n.pitch.step = 3
     evc.insert_around (None, n, 0)
     m.insert_around (None, evc, 0)
 
-    evc = EventChord()
+    evc = ChordEvent()
     n = NoteEvent()
     n.duration.duration_log = l
     n.pitch.step = 2 
@@ -911,7 +1523,7 @@ def test_expr ():
     evc.type = 'treble'
     m.insert_around (None, evc, 0)
 
-    evc = EventChord()
+    evc = ChordEvent()
     tonic = Pitch ()
     tonic.step = 2
     tonic.alteration = -2