]> git.donarmstrong.com Git - lilypond.git/blobdiff - python/musicexp.py
MusicXML: Implement different styles of tuplet brackets and numbers
[lilypond.git] / python / musicexp.py
index cdd0271486c2944f6242030b9fbf1f104d963f0e..bdd707476198a031ce5ca783dad876d03cda5fb3 100644 (file)
@@ -152,12 +152,16 @@ class Duration:
                              self.factor.denominator ())
 
 
-    def ly_expression (self, factor = None):
+    def ly_expression (self, factor = None, scheme_mode = False):
         if not factor:
             factor = self.factor
 
         if self.duration_log < 0:
-            str = {-1: "\\breve", -2: "\\longa"}.get (self.duration_log, "1")
+            if scheme_mode:
+                longer_dict = {-1: "breve", -2: "longa"}
+            else:
+                longer_dict = {-1: "\\breve", -2: "\\longa"}
+            str = longer_dict.get (self.duration_log, "1")
         else:
             str = '%d' % (1 << self.duration_log)
         str += '.'*self.dots
@@ -267,6 +271,7 @@ class Pitch:
         self.alteration = 0
         self.step = 0
         self.octave = 0
+        self._force_absolute_pitch = False
         
     def __repr__(self):
         return self.ly_expression()
@@ -336,7 +341,7 @@ class Pitch:
 
     def ly_expression (self):
         str = self.ly_step_expression ()
-        if relative_pitches:
+        if relative_pitches and not self._force_absolute_pitch:
             str += self.relative_pitch ()
         else:
             str += self.absolute_pitch ()
@@ -447,7 +452,47 @@ class RelativeMusic (MusicWrapper):
         relative_pitches = prev_relative_pitches
 
 class TimeScaledMusic (MusicWrapper):
+    def __init__ (self):
+        MusicWrapper.__init__ (self)
+        self.display_number = "actual" # valid values "actual" | "both" | None
+        # Display the basic note length for the tuplet:
+        self.display_type = None       # value values "actual" | "both" | None
+        self.display_bracket = "bracket" # valid values "bracket" | "curved" | None
+
     def print_ly (self, func):
+        if self.display_bracket == None:
+            func ("\\once \\override TupletBracket #'stencil = ##f")
+            func.newline ()
+        elif self.display_bracket == "curved":
+            warning (_ ("Tuplet brackets of curved shape are not correctly implemented"))
+            func ("\\once \\override TupletBracket #'stencil = #ly:slur::print")
+            func.newline ()
+
+        base_number_function = {None: "#f", 
+             "actual": "tuplet-number::calc-denominator-text", 
+             "both": "tuplet-number::calc-fraction-text"}.get (self.display_number, None)
+
+        if self.display_type == "actual":
+            base_duration = "8" # TODO!!!
+            func ("\\once \\override TupletNumber #'text = #(tuplet-number::append-note-wrapper %s \"%s\")" %
+                (base_number_function, base_duration))
+            func.newline ()
+        elif self.display_type == None:
+            if self.display_number == None:
+                func ("\\once \\override TupletNumber #'stencil = ##f")
+                func.newline ()
+            elif self.display_number == "both":
+                func ("\\once \\override TupletNumber #'text = #%s" % base_number_function)
+                func.newline ()
+        elif self.display_type == "both":
+            warning (_ ("Tuplet brackets displaying both note durations are not implemented, using default"))
+            if self.display_number == None:
+                func ("\\once \\override TupletNumber #'stencil = ##f")
+                func.newline ()
+            elif self.display_number == "both":
+                func ("\\once \\override TupletNumber #'text = #%s" % base_number_function)
+                func.newline ()
+
         func ('\\times %d/%d ' %
            (self.numerator, self.denominator))
         func.add_factor (Rational (self.numerator, self.denominator))
@@ -578,8 +623,8 @@ class RepeatedMusic:
             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}))
+            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):
@@ -702,6 +747,7 @@ class Layout:
 class ChordEvent (NestedMusic):
     def __init__ (self):
         NestedMusic.__init__ (self)
+        self.after_grace_elements = None
         self.grace_elements = None
         self.grace_type = None
     def append_grace (self, element):
@@ -709,13 +755,31 @@ class ChordEvent (NestedMusic):
             if not self.grace_elements:
                 self.grace_elements = SequentialMusic ()
             self.grace_elements.append (element)
+    def append_after_grace (self, element):
+        if element:
+            if not self.after_grace_elements:
+                self.after_grace_elements = SequentialMusic ()
+            self.after_grace_elements.append (element)
+
+    def has_elements (self):
+        return [e for e in self.elements if
+               isinstance (e, NoteEvent) or isinstance (e, RestEvent)] != []
+
 
     def get_length (self):
         l = Rational (0)
         for e in self.elements:
             l = max(l, e.get_length())
         return l
-    
+
+    def get_duration (self):
+        note_events = [e for e in self.elements if
+               isinstance (e, NoteEvent) or isinstance (e, RestEvent)]
+        if note_events:
+            return note_events[0].duration
+        else:
+            return None
+
     def print_ly (self, printer):
         note_events = [e for e in self.elements if
                isinstance (e, NoteEvent)]
@@ -727,6 +791,9 @@ class ChordEvent (NestedMusic):
         other_events = [e for e in self.elements if
                 not isinstance (e, RhythmicEvent)]
 
+        if self.after_grace_elements:
+            printer ('\\afterGrace {')
+
         if self.grace_elements and self.elements:
             if self.grace_type:
                 printer ('\\%s' % self.grace_type)
@@ -734,6 +801,15 @@ class ChordEvent (NestedMusic):
                 printer ('\\grace')
             # don't print newlines after the { and } braces
             self.grace_elements.print_ly (printer, False)
+        elif self.grace_elements: # no self.elements!
+            warning (_ ("Grace note with no following music: %s") % self.grace_elements)
+            if self.grace_type:
+                printer ('\\%s' % self.grace_type)
+            else:
+                printer ('\\grace')
+            self.grace_elements.print_ly (printer, False)
+            printer ('{}')
+
         # Print all overrides and other settings needed by the 
         # articulations/ornaments before the note
         for e in other_events:
@@ -753,7 +829,9 @@ class ChordEvent (NestedMusic):
                     basepitch = previous_pitch
             printer ('<%s>' % string.join (pitches))
             previous_pitch = basepitch
-            note_events[0].duration.print_ly (printer)
+            duration = self.get_duration ()
+            if duration:
+                duration.print_ly (printer)
         else:
             pass
         
@@ -763,6 +841,10 @@ class ChordEvent (NestedMusic):
         for e in other_events:
             e.print_after_note (printer)
 
+        if self.after_grace_elements:
+            printer ('}')
+            self.after_grace_elements.print_ly (printer, False)
+
         self.print_comment (printer)
             
 class Partial (Music):
@@ -791,7 +873,7 @@ class BarLine (Music):
 
         if self.bar_number > 0 and (self.bar_number % 10) == 0:
             printer.dump ("\\barNumberCheck #%d " % self.bar_number)
-        else:
+        elif self.bar_number > 0:
             printer.print_verbatim (' %% %d' % self.bar_number)
         printer.newline ()
 
@@ -849,9 +931,9 @@ class BeamEvent (SpanEvent):
 
 class PedalEvent (SpanEvent):
     def ly_expression (self):
-        return {-1: '\\sustainDown',
-            0:'\\sustainUp\\sustainDown',
-            1:'\\sustainUp'}.get (self.span_direction, '')
+        return {-1: '\\sustainOn',
+            0:'\\sustainOff\\sustainOn',
+            1:'\\sustainOff'}.get (self.span_direction, '')
 
 class TextSpannerEvent (SpanEvent):
     def ly_expression (self):
@@ -887,10 +969,10 @@ class OctaveShiftEvent (SpanEvent):
         dir = self.ly_octave_shift_indicator ()
         value = ''
         if dir:
-            value = '#(set-octavation %s)' % dir
+            value = '\ottava #%s' % dir
         return { 
             -1: value,
-            1: '#(set-octavation 0)'}.get (self.span_direction, '')
+            1: '\ottava #0'}.get (self.span_direction, '')
 
 class TrillSpanEvent (SpanEvent):
     def ly_expression (self):
@@ -923,12 +1005,12 @@ class ArpeggioEvent(Event):
         if self.non_arpeggiate:
             printer.dump ("\\arpeggioBracket")
         else:
-          dir = { -1: "\\arpeggioDown", 1: "\\arpeggioUp" }.get (self.direction, '')
+          dir = { -1: "\\arpeggioArrowDown", 1: "\\arpeggioArrowUp" }.get (self.direction, '')
           if dir:
               printer.dump (dir)
     def print_after_note (self, printer):
         if self.non_arpeggiate or self.direction:
-            printer.dump ("\\arpeggioNeutral")
+            printer.dump ("\\arpeggioNormal")
     def ly_expression (self):
         return ('\\arpeggio')
 
@@ -1085,6 +1167,60 @@ class FretEvent (MarkupEvent):
         else:
             return ''
 
+class ChordPitch:
+    def __init__ (self):
+        self.alteration = 0
+        self.step = 0
+    def __repr__(self):
+        return self.ly_expression()
+    def ly_expression (self): 
+        return pitch_generating_function (self)
+
+class ChordModification:
+    def __init__ (self):
+        self.alteration = 0
+        self.step = 0
+        self.type = 0
+    def ly_expression (self):
+        if self.type:
+            val = {1: ".", -1: "^" }.get (self.type, "")
+            val += "%s" % self.step
+            val += {1: "+", -1: "-"}.get (self.alteration, "")
+            return val
+        else:
+            return ''
+
+class ChordNameEvent (Event):
+    def __init__ (self):
+        Event.__init__ (self)
+        self.root = None
+        self.kind = None
+        self.duration = None
+        self.modifications = []
+        self.bass = None
+    def add_modification (self, mod):
+        self.modifications.append (mod)
+    def ly_expression (self):
+        if not self.root:
+            return ''
+        value = self.root.ly_expression ()
+        if self.duration:
+            value += self.duration.ly_expression ()
+        if self.kind:
+            value += ":"
+            value += self.kind
+        # First print all additions/changes, and only afterwards all subtractions
+        for m in self.modifications:
+            if m.type == 1:
+              value += m.ly_expression ()
+        for m in self.modifications:
+            if m.type == -1:
+              value += m.ly_expression ()
+        if self.bass:
+            value += "/+%s" % self.bass.ly_expression ()
+        return value
+
+
 class TremoloEvent (ArticulationEvent):
     def __init__ (self):
         Event.__init__ (self)
@@ -1259,6 +1395,13 @@ class ClefChange (Music):
 """ % (glyph, pos, c0)
         return clefsetting
 
+class Transposition (Music):
+    def __init__ (self):
+        Music.__init__ (self)
+        self.pitch = None
+    def ly_expression (self):
+        self.pitch._force_absolute_pitch = True
+        return '\\transposition %s' % self.pitch.ly_expression ()
 
 class StaffChange (Music):
     def __init__ (self, staff):
@@ -1270,6 +1413,54 @@ class StaffChange (Music):
         else:
             return ''
 
+
+class TempoMark (Music):
+    def __init__ (self):
+        Music.__init__ (self)
+        self.baseduration = None
+        self.newduration = None
+        self.beats = None
+        self.parentheses = False
+    def set_base_duration (self, dur):
+        self.baseduration = dur
+    def set_new_duration (self, dur):
+        self.newduration = dur
+    def set_beats_per_minute (self, beats):
+        self.beats = beats
+    def set_parentheses (self, parentheses):
+        self.parentheses = parentheses
+    def wait_for_note (self):
+        return False
+    def duration_to_markup (self, dur):
+        if dur:
+            # Generate the markup to print the note, use scheme mode for 
+            # ly_expression to get longa and not \longa (which causes an error)
+            return "\\general-align #Y #DOWN \\smaller \\note #\"%s\" #UP" % dur.ly_expression(None, True)
+        else:
+            return ''
+    def tempo_markup_template (self):
+        return "\\mark\\markup { \\fontsize #-2 \\line { %s } }"
+    def ly_expression (self):
+        res = ''
+        if not self.baseduration:
+            return res
+        if self.beats:
+            if self.parentheses:
+                res += "\\tempo \"\" %s=%s" % (self.baseduration.ly_expression(), self.beats)
+            else:
+                res += "\\tempo %s=%s" % (self.baseduration.ly_expression(), self.beats)
+        elif self.newduration:
+            dm = self.duration_to_markup (self.baseduration)
+            ndm = self.duration_to_markup (self.newduration)
+            if self.parentheses:
+                contents = "\"(\" %s = %s \")\"" % (dm, ndm)
+            else:
+                contents = " %s = %s " % (dm, ndm)
+            res += self.tempo_markup_template() % contents
+        else:
+            return ''
+        return res
+
 class FiguredBassNote (Music):
     def __init__ (self):
         Music.__init__ (self)
@@ -1433,6 +1624,12 @@ class Staff (StaffGroup):
             sub_staff_type = self.stafftype
 
         for [staff_id, voices] in self.part_information:
+            # Chord names need to come before the staff itself!
+            for [v, lyrics, figuredbass, chordnames] in voices:
+                if chordnames:
+                    printer ('\context ChordNames = "%s" \\%s' % (chordnames, chordnames))
+
+            # now comes the real staff definition:
             if staff_id:
                 printer ('\\context %s = "%s" << ' % (sub_staff_type, staff_id))
             else:
@@ -1440,7 +1637,7 @@ class Staff (StaffGroup):
             printer.newline ()
             n = 0
             nr_voices = len (voices)
-            for [v, lyrics, figuredbass] in voices:
+            for [v, lyrics, figuredbass, chordnames] in voices:
                 n += 1
                 voice_count_text = ''
                 if nr_voices > 1:
@@ -1495,6 +1692,34 @@ class DrumStaff (Staff):
 class RhythmicStaff (Staff):
     def __init__ (self, command = "RhythmicStaff"):
         Staff.__init__ (self, command)
+        
+class Score:
+    def __init__ (self):
+        self.contents = None
+        self.create_midi = False
+
+    def set_contents (self, contents):
+        self.contents = contents
+    
+    def set_part_information (self, part_id, staves_info):
+        if self.contents:
+          self.contents.set_part_information (part_id, staves_info)
+
+    def print_ly (self, printer):
+        printer.dump ("\\score {");
+        printer.newline ()
+        if self.contents:
+            self.contents.print_ly (printer);
+        printer.dump ("\\layout {}");
+        printer.newline ()
+        if not self.create_midi:
+            printer.dump ("% To create MIDI output, uncomment the following line:");
+            printer.newline ();
+            printer.dump ("% ");
+        printer.dump ("\\midi {}");
+        printer.newline ()
+        printer.dump ("}");
+        printer.newline ()
 
 
 def test_pitch ():