From: Reinhold Kainhofer Date: Tue, 9 Oct 2007 22:23:55 +0000 (+0200) Subject: MusicXML: Support for upbeats and partial/implicit measures X-Git-Tag: release/2.11.35-1~90 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=f53d31d764297c61bba51584449fe48f1da3c711;p=lilypond.git MusicXML: Support for upbeats and partial/implicit measures -) 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 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 --- diff --git a/input/regression/musicxml/07e-Upbeats-ImplicitMeasures-Finale.xml b/input/regression/musicxml/07e-Upbeats-ImplicitMeasures-Finale.xml new file mode 100644 index 0000000000..c81bd38e73 --- /dev/null +++ b/input/regression/musicxml/07e-Upbeats-ImplicitMeasures-Finale.xml @@ -0,0 +1,148 @@ + + + + Upbeats, implicit measure, incomplete final measure + + + Finale 2007 for Windows + Dolet Light for Finale 2007 + 2007-10-09 + + + + + MusicXML Part + + Grand Piano + + + 1 + 1 + + + + + + + + 2 + + 0 + major + + + + G + 2 + + + + + + E + 4 + + 2 + 1 + quarter + up + + + + E + 4 + + 1 + 1 + eighth + up + + + + + + + F + 4 + + 2 + 1 + quarter + up + + + + G + 4 + + 2 + 1 + quarter + up + + + none + + + + + + + A + 4 + + 2 + 1 + quarter + up + + + + B + 4 + + 2 + 1 + quarter + down + + + + + + + C + 5 + + 2 + 1 + quarter + down + + + + D + 5 + + 2 + 1 + quarter + down + + + + 2 + 1 + quarter + + + light-heavy + + + + + diff --git a/python/musicexp.py b/python/musicexp.py index 8ae3105cad..a78885dd3c 100644 --- a/python/musicexp.py +++ b/python/musicexp.py @@ -148,7 +148,10 @@ class Duration: 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 @@ -537,6 +540,13 @@ class EventChord (NestedMusic): 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): diff --git a/python/musicxml.py b/python/musicxml.py index 80b332bf64..9a5d43eeec 100644 --- a/python/musicxml.py +++ b/python/musicxml.py @@ -271,6 +271,10 @@ class Attributes (Measure_element): 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): @@ -337,6 +341,11 @@ class Score_part (Music_xml_node): 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')) @@ -443,10 +452,35 @@ class Part (Music_xml_node): 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) @@ -505,20 +539,16 @@ class Part (Music_xml_node): 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): @@ -535,6 +565,8 @@ class Part (Music_xml_node): 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 @@ -572,7 +604,7 @@ class Part (Music_xml_node): 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: @@ -587,6 +619,11 @@ class Part (Music_xml_node): 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: diff --git a/scripts/musicxml2ly.py b/scripts/musicxml2ly.py index fd13721d96..b31511a291 100644 --- a/scripts/musicxml2ly.py +++ b/scripts/musicxml2ly.py @@ -150,7 +150,21 @@ def musicxml_duration_to_lily (mxl_note): 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): @@ -535,6 +549,7 @@ class LilyPondVoiceBuilder: 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 () @@ -575,6 +590,9 @@ class LilyPondVoiceBuilder: 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: @@ -593,7 +611,7 @@ class LilyPondVoiceBuilder: 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 @@ -601,7 +619,10 @@ class LilyPondVoiceBuilder: 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 @@ -646,6 +667,12 @@ def musicxml_voice_to_lily_voice (voice): 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 (): @@ -667,8 +694,9 @@ def musicxml_voice_to_lily_voice (voice): 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 @@ -689,7 +717,8 @@ def musicxml_voice_to_lily_voice (voice): 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)