-) In the Duration class, don't write out e.g. *3/1 for the factor.
In this case, *3 is much better
-) Add support for implicit measures, i.e. measures in MusicXML that do
not count. These are typically used for upbeats and for places where
a repeat or clef/key change occurs inside a measure. In this case,
the measure is split into to <measure> elements, the second one with
the implicit attribute set to "yes". Now we don't start a measure
in lilypond for those implicit measures.
-) Implicit measures at the very beginning are now converted as \partial X
-) Don't print out | %0 at the very beginning (i.e. for the measure with
number 0)
Signed-off-by: Reinhold Kainhofer <reinhold@kainhofer.com>
--- /dev/null
+<?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>Upbeats, implicit measure, incomplete final measure</movement-title>\r
+ <identification>\r
+ <encoding>\r
+ <software>Finale 2007 for Windows</software>\r
+ <software>Dolet Light for Finale 2007</software>\r
+ <encoding-date>2007-10-09</encoding-date>\r
+ </encoding>\r
+ </identification>\r
+ <part-list>\r
+ <score-part id="P1">\r
+ <part-name>MusicXML Part</part-name>\r
+ <score-instrument id="P1-I1">\r
+ <instrument-name>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 implicit="yes" number="0">\r
+ <attributes>\r
+ <divisions>2</divisions>\r
+ <key>\r
+ <fifths>0</fifths>\r
+ <mode>major</mode>\r
+ </key>\r
+ <time symbol="common">\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
+ <sound tempo="120"/>\r
+ <note>\r
+ <pitch>\r
+ <step>E</step>\r
+ <octave>4</octave>\r
+ </pitch>\r
+ <duration>2</duration>\r
+ <voice>1</voice>\r
+ <type>quarter</type>\r
+ <stem>up</stem>\r
+ </note>\r
+ <note>\r
+ <pitch>\r
+ <step>E</step>\r
+ <octave>4</octave>\r
+ </pitch>\r
+ <duration>1</duration>\r
+ <voice>1</voice>\r
+ <type>eighth</type>\r
+ <stem>up</stem>\r
+ </note>\r
+ </measure>\r
+ <!--=======================================================-->\r
+ <measure number="1">\r
+ <note>\r
+ <pitch>\r
+ <step>F</step>\r
+ <octave>4</octave>\r
+ </pitch>\r
+ <duration>2</duration>\r
+ <voice>1</voice>\r
+ <type>quarter</type>\r
+ <stem>up</stem>\r
+ </note>\r
+ <note>\r
+ <pitch>\r
+ <step>G</step>\r
+ <octave>4</octave>\r
+ </pitch>\r
+ <duration>2</duration>\r
+ <voice>1</voice>\r
+ <type>quarter</type>\r
+ <stem>up</stem>\r
+ </note>\r
+ <barline location="right">\r
+ <bar-style>none</bar-style>\r
+ </barline>\r
+ </measure>\r
+ <!--=======================================================-->\r
+ <measure implicit="yes" number="X1">\r
+ <note>\r
+ <pitch>\r
+ <step>A</step>\r
+ <octave>4</octave>\r
+ </pitch>\r
+ <duration>2</duration>\r
+ <voice>1</voice>\r
+ <type>quarter</type>\r
+ <stem>up</stem>\r
+ </note>\r
+ <note>\r
+ <pitch>\r
+ <step>B</step>\r
+ <octave>4</octave>\r
+ </pitch>\r
+ <duration>2</duration>\r
+ <voice>1</voice>\r
+ <type>quarter</type>\r
+ <stem>down</stem>\r
+ </note>\r
+ </measure>\r
+ <!--=======================================================-->\r
+ <measure number="2">\r
+ <note>\r
+ <pitch>\r
+ <step>C</step>\r
+ <octave>5</octave>\r
+ </pitch>\r
+ <duration>2</duration>\r
+ <voice>1</voice>\r
+ <type>quarter</type>\r
+ <stem>down</stem>\r
+ </note>\r
+ <note>\r
+ <pitch>\r
+ <step>D</step>\r
+ <octave>5</octave>\r
+ </pitch>\r
+ <duration>2</duration>\r
+ <voice>1</voice>\r
+ <type>quarter</type>\r
+ <stem>down</stem>\r
+ </note>\r
+ <note>\r
+ <rest/>\r
+ <duration>2</duration>\r
+ <voice>1</voice>\r
+ <type>quarter</type>\r
+ </note>\r
+ <barline location="right">\r
+ <bar-style>light-heavy</bar-style>\r
+ </barline>\r
+ </measure>\r
+ </part>\r
+ <!--=========================================================-->\r
+</score-partwise>\r
str = '%d%s' % (1 << self.duration_log, '.'*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
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):
def __init__ (self):
fifths = int (key.get_maybe_exist_named_child ('fifths').get_text ())
return (fifths, mode)
+class Partial (Measure_element):
+ def __init__ (self, partial):
+ Measure_element.__init__ (self)
+ self.partial = partial
class Note (Measure_element):
def __init__ (self):
pass
class Measure (Music_xml_node):
+ def __init__ (self):
+ Music_xml_node.__init__ (self)
+ self.partial = 0
+ def is_implicit (self):
+ return hasattr (self, 'implicit') and self.implicit == 'yes'
def get_notes (self):
return self.get_typed_children (get_class (u'note'))
measures = self.get_typed_children (Measure)
last_moment = Rational (-1)
last_measure_position = Rational (-1)
+ measure_position = Rational (0)
+ measure_start_moment = now
+ is_first_measure = True
+ prvious_measure = None
for m in measures:
- measure_start_moment = now
- measure_position = Rational (0)
- for n in m.get_all_children ():
+ # implicit measures are used for artificial measures, e.g. when
+ # a repeat bar line splits a bar into two halves. In this case,
+ # don't reset the measure position to 0. They are also used for
+ # upbeats (initial value of 0 fits these, too).
+ # Also, don't reset the measure position at the end of the loop,
+ # but rather when starting the next measure (since only then do we
+ # know if the next measure is implicit and continues that measure)
+ if not m.is_implicit ():
+ # Warn about possibly overfull measures and reset the position
+ if attributes_object:
+ length = attributes_object.get_measure_length ()
+ new_now = measure_start_moment + length
+ if now <> new_now:
+ problem = 'incomplete'
+ if now > new_now:
+ problem = 'overfull'
+ ## only for verbose operation.
+ if problem <> 'incomplete' and previous_measure:
+ previous_measure.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
+ now = new_now
+ measure_start_moment = now
+ measure_position = Rational (0)
+
+ for n in m.get_all_children ():
if isinstance (n, Hash_text):
continue
dur = Rational (0)
if instrument:
n.instrument_name = part_list.get_instrument (instrument.id)
- if attributes_object:
- length = attributes_object.get_measure_length ()
- new_now = measure_start_moment + length
-
- if now <> new_now:
- problem = 'incomplete'
- if now > new_now:
- problem = 'overfull'
-
- ## only for verbose operation.
- if problem <> 'incomplete':
- m.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
-
- now = new_now
+ # Incomplete first measures are not padded, but registered as partial
+ if is_first_measure:
+ is_first_measure = False
+ # upbeats are marked as implicit measures
+ if attributes_object and m.is_implicit ():
+ length = attributes_object.get_measure_length ()
+ measure_end = measure_start_moment + length
+ if measure_end <> now:
+ m.partial = now
+ previous_measure = m
# modify attributes so that only those applying to the given staff remain
def extract_attributes_for_staff (part, attr, staff):
measures = part.get_typed_children (Measure)
elements = []
for m in measures:
+ if m.partial > 0:
+ elements.append (Partial (m.partial))
elements.extend (m.get_all_children ())
# make sure we know all voices already so that dynamics, clefs, etc.
# can be assigned to the correct voices
for n in elements:
voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
- if not (voice_id or isinstance (n, Attributes) or isinstance (n, Direction) ):
+ if not (voice_id or isinstance (n, Attributes) or isinstance (n, Direction) or isinstance (n, Partial) ):
continue
if isinstance (n, Attributes) and not start_attr:
voices[v].add_element (staff_attributes)
continue
+ if isinstance (n, Partial):
+ for v in voices.keys ():
+ voices[v].add_element (n)
+ continue
+
if isinstance (n, Direction):
staff_id = n.get_maybe_exist_named_child (u'staff')
if staff_id:
if not mxl_note.get_maybe_exist_typed_child (musicxml.Grace):
d.factor = mxl_note._duration / d.get_length ()
- return d
+ return d
+
+def rational_to_lily_duration (rational_len):
+ d = musicexp.Duration ()
+ d.duration_log = {1: 0, 2: 1, 4:2, 8:3, 16:4, 32:5, 64:6, 128:7, 256:8, 512:9}.get (rational_len.denominator (), -1)
+ d.factor = Rational (rational_len.numerator ())
+ return d
+
+def musicxml_partial_to_lily (partial_len):
+ if partial_len > 0:
+ p = musicexp.Partial ()
+ p.partial = rational_to_lily_duration (partial_len)
+ return p
+ else:
+ return Null
def group_tuplets (music_list, events):
self.end_moment = Rational (0)
self.begin_moment = Rational (0)
self.pending_multibar = Rational (0)
+ self.ignore_skips = False
def _insert_multibar (self):
r = musicexp.MultiMeasureRest ()
if self.pending_multibar > Rational (0):
self._insert_multibar ()
self.elements.append (command)
+ def add_partial (self, command):
+ self.ignore_skips = True
+ self.add_command (command)
def add_dynamics (self, dynamic):
# store the dynamic item(s) until we encounter the next note/rest:
error_message ('Negative skip %s' % diff)
diff = Rational (0)
- if diff > Rational (0):
+ if diff > Rational (0) and not self.ignore_skips:
skip = musicexp.SkipEvent()
skip.duration.duration_log = 0
skip.duration.factor = diff
evc = musicexp.EventChord ()
evc.elements.append (skip)
self.add_music (evc, diff)
-
+
+ if diff > Rational (0) and moment == 0:
+ self.ignore_skips = False
+
def last_event_chord (self, starting_at):
value = None
if n.get_name () == 'forward':
continue
+ if isinstance (n, musicxml.Partial) and n.partial > 0:
+ a = musicxml_partial_to_lily (n.partial)
+ if a:
+ voice_builder.add_partial (a)
+ continue
+
if isinstance (n, musicxml.Direction):
for a in musicxml_direction_to_lily (n):
if a.wait_for_note ():
number = int (n.get_parent ().number)
except ValueError:
number = 0
+ if number > 0:
+ voice_builder.add_bar_check (number)
- voice_builder.add_bar_check (number)
for a in musicxml_attributes_to_lily (n):
voice_builder.add_music (a, Rational (0))
continue
num = int (n.get_parent ().number)
except ValueError:
num = 0
- voice_builder.add_bar_check (num)
+ if num > 0:
+ voice_builder.add_bar_check (num)
main_event = musicxml_note_to_lily_main_event (n)