]> git.donarmstrong.com Git - lilypond.git/commitdiff
MusicXML: Implement after-grace notes
authorReinhold Kainhofer <reinhold@kainhofer.com>
Fri, 7 Nov 2008 19:22:40 +0000 (20:22 +0100)
committerReinhold Kainhofer <reinhold@kainhofer.com>
Wed, 12 Nov 2008 14:36:50 +0000 (15:36 +0100)
also convert all grace notes at the end of a measure to after-graces, otherwise
they won't appear at all...

input/regression/musicxml/04g-Trill-EndingOnGraceNote-Finale.xml [new file with mode: 0644]
input/regression/musicxml/13d-AfterGrace.xml [new file with mode: 0644]
python/musicexp.py
python/musicxml.py
scripts/musicxml2ly.py

diff --git a/input/regression/musicxml/04g-Trill-EndingOnGraceNote-Finale.xml b/input/regression/musicxml/04g-Trill-EndingOnGraceNote-Finale.xml
new file mode 100644 (file)
index 0000000..5576ce7
--- /dev/null
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 1.0 Partwise//EN"\r
+                                "http://www.musicxml.org/dtds/partwise.dtd">\r
+<score-partwise>\r
+  <movement-title>Sonata No. 2</movement-title>\r
+  <identification>\r
+    <creator type="composer">Simon Bielman</creator>\r
+    <encoding>\r
+      <software>Finale 2007 for Windows</software>\r
+      <software>Dolet Light for Finale 2007</software>\r
+      <encoding-date>2008-08-20</encoding-date>\r
+    </encoding>\r
+  </identification>\r
+  <part-list>\r
+    <score-part id="P1">\r
+      <part-name>Piano</part-name>\r
+      <score-instrument id="P1-I1">\r
+        <instrument-name>Acoustic Grand Piano</instrument-name>\r
+      </score-instrument>\r
+      <midi-instrument id="P1-I1">\r
+        <midi-channel>1</midi-channel>\r
+        <midi-program>1</midi-program>\r
+      </midi-instrument>\r
+    </score-part>\r
+  </part-list>\r
+  <!--=========================================================-->\r
+  <part id="P1">\r
+    <measure number="25">\r
+      <attributes>\r
+        <divisions>336</divisions>\r
+        <key>\r
+          <fifths>0</fifths>\r
+          <mode>major</mode>\r
+        </key>\r
+        <time>\r
+          <beats>6</beats>\r
+          <beat-type>8</beat-type>\r
+        </time>\r
+        <staves>2</staves>\r
+        <clef number="1">\r
+          <sign>G</sign>\r
+          <line>2</line>\r
+        </clef>\r
+        <clef number="2">\r
+          <sign>F</sign>\r
+          <line>4</line>\r
+        </clef>\r
+      </attributes>\r
+      <note>\r
+        <pitch>\r
+          <step>E</step>\r
+          <octave>5</octave>\r
+        </pitch>\r
+        <duration>336</duration>\r
+        <voice>1</voice>\r
+        <type>quarter</type>\r
+        <stem>down</stem>\r
+        <staff>1</staff>\r
+        <notations>\r
+          <slur number="1" placement="above" type="start"/>\r
+        </notations>\r
+      </note>\r
+      <note>\r
+        <pitch>\r
+          <step>F</step>\r
+          <alter>1</alter>\r
+          <octave>5</octave>\r
+        </pitch>\r
+        <duration>84</duration>\r
+        <voice>1</voice>\r
+        <type>16th</type>\r
+        <accidental>sharp</accidental>\r
+        <stem>down</stem>\r
+        <staff>1</staff>\r
+        <beam number="1">begin</beam>\r
+        <beam number="2">begin</beam>\r
+      </note>\r
+      <note>\r
+        <pitch>\r
+          <step>G</step>\r
+          <octave>5</octave>\r
+        </pitch>\r
+        <duration>84</duration>\r
+        <voice>1</voice>\r
+        <type>16th</type>\r
+        <stem>down</stem>\r
+        <staff>1</staff>\r
+        <beam number="1">end</beam>\r
+        <beam number="2">end</beam>\r
+        <notations>\r
+          <slur number="1" type="stop"/>\r
+          <ornaments>\r
+            <trill-mark default-y="39"/>\r
+            <wavy-line default-y="39" number="1" type="start"/>\r
+          </ornaments>\r
+        </notations>\r
+      </note>\r
+      <note>\r
+        <grace/>\r
+        <pitch>\r
+          <step>B</step>\r
+          <octave>5</octave>\r
+        </pitch>\r
+        <voice>1</voice>\r
+        <type>16th</type>\r
+        <stem>up</stem>\r
+        <staff>1</staff>\r
+      </note>\r
+      <note>\r
+        <pitch>\r
+          <step>A</step>\r
+          <octave>5</octave>\r
+        </pitch>\r
+        <duration>504</duration>\r
+        <voice>1</voice>\r
+        <type>quarter</type>\r
+        <dot/>\r
+        <stem>down</stem>\r
+        <staff>1</staff>\r
+      </note>\r
+      <note>\r
+        <grace/>\r
+        <pitch>\r
+          <step>G</step>\r
+          <octave>5</octave>\r
+        </pitch>\r
+        <voice>1</voice>\r
+        <type>16th</type>\r
+        <stem>up</stem>\r
+        <staff>1</staff>\r
+        <beam number="1">begin</beam>\r
+        <beam number="2">begin</beam>\r
+      </note>\r
+      <note>\r
+        <grace/>\r
+        <pitch>\r
+          <step>A</step>\r
+          <octave>5</octave>\r
+        </pitch>\r
+        <voice>1</voice>\r
+        <type>16th</type>\r
+        <stem>up</stem>\r
+        <staff>1</staff>\r
+        <beam number="1">end</beam>\r
+        <beam number="2">end</beam>\r
+        <notations>\r
+          <ornaments>\r
+            <wavy-line number="1" type="stop"/>\r
+          </ornaments>\r
+        </notations>\r
+      </note>\r
+    </measure>\r
+  </part>\r
+  <!--=========================================================-->\r
+</score-partwise>\r
diff --git a/input/regression/musicxml/13d-AfterGrace.xml b/input/regression/musicxml/13d-AfterGrace.xml
new file mode 100644 (file)
index 0000000..a6662a4
--- /dev/null
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 1.0 Partwise//EN"\r
+                                "http://www.musicxml.org/dtds/partwise.dtd">\r
+<score-partwise>\r
+  <movement-title>Sonata No. 2</movement-title>\r
+  <identification>\r
+    <creator type="composer">Simon Bielman</creator>\r
+    <encoding>\r
+      <software>Finale 2007 for Windows</software>\r
+      <software>Dolet Light for Finale 2007</software>\r
+      <encoding-date>2008-08-20</encoding-date>\r
+    </encoding>\r
+  </identification>\r
+  <part-list>\r
+    <score-part id="P1">\r
+      <part-name>Piano</part-name>\r
+      <score-instrument id="P1-I1">\r
+        <instrument-name>Acoustic Grand Piano</instrument-name>\r
+      </score-instrument>\r
+      <midi-instrument id="P1-I1">\r
+        <midi-channel>1</midi-channel>\r
+        <midi-program>1</midi-program>\r
+      </midi-instrument>\r
+    </score-part>\r
+  </part-list>\r
+  <!--=========================================================-->\r
+  <part id="P1">\r
+    <measure number="25">\r
+      <attributes>\r
+        <divisions>32</divisions>\r
+        <key>\r
+          <fifths>0</fifths>\r
+          <mode>major</mode>\r
+        </key>\r
+        <time>\r
+          <beats>4</beats>\r
+          <beat-type>4</beat-type>\r
+        </time>\r
+        <clef>\r
+          <sign>G</sign>\r
+          <line>2</line>\r
+        </clef>\r
+      </attributes>\r
+      <note>\r
+        <pitch>\r
+          <step>E</step>\r
+          <octave>5</octave>\r
+        </pitch>\r
+        <duration>64</duration>\r
+        <voice>1</voice>\r
+        <type>half</type>\r
+        <stem>down</stem>\r
+        <staff>1</staff>\r
+      </note>\r
+      <note>\r
+        <grace steal-time-previous="20"/>\r
+        <pitch>\r
+          <step>G</step>\r
+          <octave>5</octave>\r
+        </pitch>\r
+        <voice>1</voice>\r
+        <type>16th</type>\r
+        <stem>up</stem>\r
+        <staff>1</staff>\r
+      </note>\r
+      <note>\r
+        <grace steal-time-next="20"/>\r
+        <pitch>\r
+          <step>A</step>\r
+          <octave>5</octave>\r
+        </pitch>\r
+        <voice>1</voice>\r
+        <type>16th</type>\r
+        <stem>up</stem>\r
+        <staff>1</staff>\r
+      </note>\r
+      <note>\r
+        <grace/>\r
+        <pitch>\r
+          <step>A</step>\r
+          <octave>5</octave>\r
+        </pitch>\r
+        <voice>1</voice>\r
+        <type>16th</type>\r
+        <stem>up</stem>\r
+        <staff>1</staff>\r
+      </note>\r
+      <note>\r
+        <pitch>\r
+          <step>E</step>\r
+          <octave>5</octave>\r
+        </pitch>\r
+        <duration>64</duration>\r
+        <voice>1</voice>\r
+        <type>half</type>\r
+        <stem>down</stem>\r
+        <staff>1</staff>\r
+      </note>\r
+      <note>\r
+        <grace/>\r
+        <pitch>\r
+          <step>G</step>\r
+          <octave>5</octave>\r
+        </pitch>\r
+        <voice>1</voice>\r
+        <type>16th</type>\r
+        <stem>up</stem>\r
+        <staff>1</staff>\r
+        <beam number="1">begin</beam>\r
+        <beam number="2">begin</beam>\r
+      </note>\r
+      <note>\r
+        <grace/>\r
+        <pitch>\r
+          <step>A</step>\r
+          <octave>5</octave>\r
+        </pitch>\r
+        <voice>1</voice>\r
+        <type>16th</type>\r
+        <stem>up</stem>\r
+        <staff>1</staff>\r
+        <beam number="1">end</beam>\r
+        <beam number="2">end</beam>\r
+      </note>\r
+    </measure>\r
+  </part>\r
+  <!--=========================================================-->\r
+</score-partwise>\r
index 9d9ea96dc674ea70817a04c21ee856863d1ce418..22cfcdcd53888461e0020fd778ed0eae861b8cf2 100644 (file)
@@ -706,6 +706,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):
@@ -713,6 +714,16 @@ 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)
@@ -739,6 +750,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)
@@ -746,6 +760,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:
@@ -777,6 +800,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):
index 295bdb326b3aa94137c931305872fc478f086943..4bee0f10dc199e0361b77bf3a090db256693cd10 100644 (file)
@@ -352,7 +352,15 @@ class Note (Measure_element):
     def __init__ (self):
         Measure_element.__init__ (self)
         self.instrument_name = ''
-        
+        self._after_grace = False
+    def is_grace (self):
+        return self.get_maybe_exist_named_child (u'grace')
+    def is_after_grace (self):
+        if not self.is_grace():
+            return False;
+        gr = self.get_maybe_exist_typed_child (Grace)
+        return self._after_grace or hasattr (gr, 'steal-time-previous');
+
     def get_duration_log (self):
         ch = self.get_maybe_exist_named_child (u'type')
 
@@ -522,6 +530,9 @@ class Part (Music_xml_node):
         measure_start_moment = now
         is_first_measure = True
         previous_measure = None
+        # Graces at the end of a measure need to have their position set to the
+        # previous number!
+        pending_graces = []
        for m in measures:
             # implicit measures are used for artificial measures, e.g. when
             # a repeat bar line splits a bar into two halves. In this case,
@@ -575,6 +586,12 @@ class Part (Music_xml_node):
                     
                    if n.get_name() == 'backup':
                        dur = - dur
+                        # reset all graces before the backup to after-graces:
+                        for n in pending_graces:
+                            n._when = n._prev_when
+                            n._measure_position = n._prev_measure_position
+                            n._after_grace = True
+                        pending_graces = []
                    if n.get_maybe_exist_typed_child (Grace):
                        dur = Rational (0)
 
@@ -592,6 +609,23 @@ class Part (Music_xml_node):
 
                 n._when = now
                 n._measure_position = measure_position
+
+                # For all grace notes, store the previous note,  in case need
+                # to turn the grace note into an after-grace later on!
+                if isinstance(n, Note) and n.is_grace ():
+                    n._prev_when = last_moment
+                    n._prev_measure_position = last_measure_position
+                # After-graces are placed at the same position as the previous note
+                if isinstance(n, Note) and  n.is_after_grace ():
+                    # TODO: We should do the same for grace notes at the end of 
+                    # a measure with no following note!!!
+                    n._when = last_moment
+                    n._measure_position = last_measure_position
+                elif isinstance(n, Note) and n.is_grace ():
+                    pending_graces.append (n)
+                elif (dur > Rational (0)):
+                    pending_graces = [];
+
                 n._duration = dur
                 if dur > Rational (0):
                     last_moment = now
@@ -613,6 +647,12 @@ class Part (Music_xml_node):
                     if instrument:
                         n.instrument_name = part_list.get_instrument (instrument.id)
 
+            # reset all graces at the end of the measure to after-graces:
+            for n in pending_graces:
+                n._when = n._prev_when
+                n._measure_position = n._prev_measure_position
+                n._after_grace = True
+            pending_graces = []
             # Incomplete first measures are not padded, but registered as partial
             if is_first_measure:
                 is_first_measure = False
index 695f855d4996f3ec34ce2994c416a2b13ed14e73..50d2a0b67d2827ff697dbf7994c7e10b856ce7f5 100644 (file)
@@ -1763,8 +1763,10 @@ def musicxml_voice_to_lily_voice (voice):
             if a:
                 voice_builder.add_partial (a)
             continue
+
         is_chord = n.get_maybe_exist_named_child ('chord')
-        if not is_chord:
+        is_after_grace = (isinstance (n, musicxml.Note) and n.is_after_grace ());
+        if not is_chord and not is_after_grace:
             try:
                 voice_builder.jumpto (n._when)
             except NegativeSkip, neg:
@@ -1857,15 +1859,31 @@ def musicxml_voice_to_lily_voice (voice):
             ev_chord = musicexp.ChordEvent()
             voice_builder.add_music (ev_chord, n._duration)
 
+        # For grace notes:
         grace = n.get_maybe_exist_typed_child (musicxml.Grace)
-        if grace:
+        if n.is_grace ():
+            is_after_grace = ev_chord.has_elements () or n.is_after_grace ();
+            is_chord = n.get_maybe_exist_typed_child (musicxml.Chord)
+
             grace_chord = None
-            if n.get_maybe_exist_typed_child (musicxml.Chord) and ev_chord.grace_elements:
-                grace_chord = ev_chord.grace_elements.get_last_event_chord ()
-            if not grace_chord:
-                grace_chord = musicexp.ChordEvent ()
-                ev_chord.append_grace (grace_chord)
-            if hasattr (grace, 'slash'):
+
+            # after-graces and other graces use different lists; Depending on
+            # whether we have a chord or not, obtain either a new ChordEvent or 
+            # the previous one to create a chord
+            if is_after_grace:
+                if ev_chord.after_grace_elements and n.get_maybe_exist_typed_child (musicxml.Chord):
+                    grace_chord = ev_chord.after_grace_elements.get_last_event_chord ()
+                if not grace_chord:
+                    grace_chord = musicexp.ChordEvent ()
+                    ev_chord.append_after_grace (grace_chord)
+            elif n.is_grace ():
+                if ev_chord.grace_elements and n.get_maybe_exist_typed_child (musicxml.Chord):
+                    grace_chord = ev_chord.grace_elements.get_last_event_chord ()
+                if not grace_chord:
+                    grace_chord = musicexp.ChordEvent ()
+                    ev_chord.append_grace (grace_chord)
+
+            if hasattr (grace, 'slash') and not is_after_grace:
                 # TODO: use grace_type = "appoggiatura" for slurred grace notes
                 if grace.slash == "yes":
                     ev_chord.grace_type = "acciaccatura"
@@ -1926,7 +1944,7 @@ def musicxml_voice_to_lily_voice (voice):
                 frac = (1,1)
                 if mod:
                     frac = mod.get_fraction ()
-                
+
                 tuplet_events.append ((ev_chord, tuplet_event, frac))
 
             # First, close all open slurs, only then start any new slur
@@ -2020,7 +2038,7 @@ def musicxml_voice_to_lily_voice (voice):
             for a in ornaments:
                 for ch in a.get_all_children ():
                     ev = musicxml_articulation_to_lily_event (ch)
-                    if ev: 
+                    if ev:
                         ev_chord.append (ev)
 
             dynamics = notations.get_named_children ('dynamics')