]> git.donarmstrong.com Git - lilypond.git/blobdiff - python/musicexp.py
MusicXML: Cleanup of span start/end and direction, coding style
[lilypond.git] / python / musicexp.py
index b543dbc2e6056d5267406d485c13137d4dc25d15..f4efdd54db16e37ff1649640405dda87baec6121 100644 (file)
@@ -5,6 +5,7 @@ import re
 
 from rational import Rational
 
+
 class Output_stack_element:
     def __init__ (self):
         self.factor = Rational (1)
@@ -413,8 +414,26 @@ class SequentialMusic (NestedMusic):
         for e in self.elements:
             e.set_start (start)
             start += e.get_length()
-            
-class EventChord(NestedMusic):
+
+class Lyrics:
+    def __init__ (self):
+        self.lyrics_syllables = []
+
+    def print_ly (self, printer):
+        printer.dump ("\lyricmode {")
+        for l in self.lyrics_syllables:
+            printer.dump ( "%s " % l )
+        printer.dump ("}")
+
+    def ly_expression (self):
+        lstr = "\lyricmode {\n  "
+        for l in self.lyrics_syllables:
+            lstr += l + " "
+        lstr += "\n}"
+        return lstr
+
+
+class EventChord (NestedMusic):
     def get_length (self):
         l = Rational (0)
         for e in self.elements:
@@ -473,23 +492,83 @@ class Event(Music):
 class SpanEvent (Event):
     def __init__(self):
         Event.__init__ (self)
-        self.span_direction = 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 ly_expression (self):
-        return {-1: '(',
-            0:'',
-            1:')'}[self.span_direction]
+        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!
+        #before = {'dotted': '\\slurDotted', 
+        #          'dashed' : '\\slurDashed'}.get (self.line_type, '')
+        #if before:
+            #after = '\\slurSolid'
+
+        return {-1: before + '(' + after,
+            1:')'}.get (self.span_direction, '')
 
 class BeamEvent (SpanEvent):
     def ly_expression (self):
         return {-1: '[',
-            0:'',
-            1:']'}[self.span_direction]
+            1:']'}.get (self.span_direction, '')
+
+class PedalEvent (SpanEvent):
+    def ly_expression (self):
+        return {-1: '\\sustainDown',
+            1:'\\sustainUp'}.get (self.span_direction, '')
+
+# type==-1 means octave up, type==-2 means octave down
+class OctaveShiftEvent (SpanEvent):
+    def wait_for_note (self):
+        return False;
+    def set_span_type (self, type):
+        self.span_type = {'up': 1, 'down': -1}.get (type, 0)
+    def ly_octave_shift_indicator (self):
+        # 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 { 
+            -1: value,
+            1: '#(set-octavation 0)'}.get (self.span_direction, '')
+
+class TrillSpanEvent (SpanEvent):
+    def ly_expression (self):
+        return {-1: '\\startTrillSpan',
+            0: '', # no need to write out anything for type='continue'
+            1:'\\stopTrillSpan'}.get (self.span_direction, '')
+
+class GlissandoEvent (SpanEvent):
+    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 == 'wavy':
+            #style = "\once\override Glissando #'style = #'zigzag"
+        # In lilypond, glissando is NOT a spanner, unlike MusicXML.
+        return {-1: style + '\\glissando',
+            1:''}.get (self.span_direction, '')
 
 class ArpeggioEvent(Event):
+    def wait_for_note (self):
+        return True;
     def ly_expression (self):
         return ('\\arpeggio')
 
@@ -499,15 +578,14 @@ class TieEvent(Event):
         return '~'
 
 
-class HairpinEvent (Event):
-    def __init__ (self, type):
-        self.type = type
+class HairpinEvent (SpanEvent):
+    def set_span_type (self, type):
+        self.span_type = {'crescendo' : 1, 'decrescendo' : -1, 'diminuendo' : -1 }.get (type, 0)
     def hairpin_to_ly (self):
-        val = ''
-        tp = { 0: '\!', 1: '\<', -1: '\>' }.get (self.type)
-        if tp:
-            val += tp
-        return val
+        if self.span_direction == 1:
+            return '\!'
+        else:
+            return {1: '\<', -1: '\>'}.get (self.span_type, '')
     
     def ly_expression (self):
         return self.hairpin_to_ly ()
@@ -526,13 +604,15 @@ class DynamicsEvent (Event):
                                     "mp", "mf", 
                                     "f", "ff", "fff", "ffff", 
                                     "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" ];
+    def wait_for_note (self):
+        return True;
     def ly_expression (self):
         if self.type == None:
             return;
         elif self.type in self.available_commands:
             return '\%s' % self.type
         else:
-            return '\markup{ \dynamic %s }' % self.type
+            return '-\markup{ \dynamic %s }' % self.type
         
     def print_ly (self, printer):
         if self.type == None:
@@ -540,7 +620,7 @@ class DynamicsEvent (Event):
         elif self.type in self.available_commands:
             printer.dump ("\\%s" % self.type)
         else:
-            printer.dump ("\\markup{ \\dynamic %s }" % self.type)
+            printer.dump ("-\\markup{ \\dynamic %s }" % self.type)
 
 
 class ArticulationEvent (Event):
@@ -549,19 +629,22 @@ class ArticulationEvent (Event):
         self.force_direction = None
 
     def direction_mod (self):
-        dirstr = { 1: '^', -1: '_', 0: '-' }.get (self.force_direction)
-        if dirstr:
-            return dirstr
-        else:
-            return ''
+        return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
 
     def ly_expression (self):
         return '%s\\%s' % (self.direction_mod (), self.type)
 
+class ShortArticulationEvent (ArticulationEvent):
+    def direction_mod (self):
+        # default is -
+        return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
+    def ly_expression (self):
+        return '%s%s' % (self.direction_mod (), self.type)
 
 class TremoloEvent (Event):
     def __init__ (self):
-        self.bars = 0;
+        Event.__init__ (self)
+        self.bars = 0
 
     def ly_expression (self):
         str=''
@@ -569,6 +652,15 @@ class TremoloEvent (Event):
             str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
         return str
 
+class BendEvent (Event):
+    def __init__ (self):
+        Event.__init__ (self)
+        self.alter = 0
+    def ly_expression (self):
+        if self.alter:
+            return "-\\bendAfter #%s" % self.alter
+        else:
+            return ''
 
 class RhythmicEvent(Event):
     def __init__ (self):
@@ -603,7 +695,7 @@ class NoteEvent(RhythmicEvent):
         self.forced_accidental = False
         
     def get_properties (self):
-        str = RhythmicEvent.get_properties ()
+        str = RhythmicEvent.get_properties (self)
         
         if self.pitch:
             str += self.pitch.lisp_expression ()
@@ -668,10 +760,27 @@ class ClefChange (Music):
     def __init__ (self):
         Music.__init__ (self)
         self.type = 'G'
-        
-    
+        self.position = 2
+        self.octave = 0
+
+    def octave_modifier (self):
+        return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
+    def clef_name (self):
+        return {('G', 2): "treble",
+                ('G', 1): "french",
+                ('C', 1): "soprano",
+                ('C', 2): "mezzosoprano",
+                ('C', 3): "alto",
+                ('C', 4): "tenor",
+                ('C', 5): "baritone",
+                ('F', 3): "varbaritone",
+                ('F', 4): "bass",
+                ('F', 5): "subbass",
+                ("percussion", 2): "percussion",
+                ("TAB", 5): "tab"}.get ((self.type, self.position), None)
     def ly_expression (self):
-        return '\\clef "%s"' % self.type
+        return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
+
     clef_dict = {
         "G": ("clefs.G", -2, -6),
         "C": ("clefs.C", 0, 0),
@@ -679,7 +788,10 @@ class ClefChange (Music):
         }
     
     def lisp_expression (self):
-        (glyph, pos, c0) = self.clef_dict [self.type]
+        try:
+            (glyph, pos, c0) = self.clef_dict[self.type]
+        except KeyError:
+            return ""
         clefsetting = """
         (make-music 'SequentialMusic
         'elements (list