X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=python%2Fmusicxml.py;h=d2af5d5f2e10717d236b4e8f7ad0a6f753e2b3ba;hb=e5f3079f431c5db45b960cb02ef7f5536acf9bec;hp=5afd4f27952e6b3d6701c2e3700991b2b7a7fd29;hpb=aaba12ab37f85486fd3432f7ab7dbdec1dd14639;p=lilypond.git diff --git a/python/musicxml.py b/python/musicxml.py index 5afd4f2795..d2af5d5f2e 100644 --- a/python/musicxml.py +++ b/python/musicxml.py @@ -71,7 +71,7 @@ class Xml_node: p = self while p: - sys.stderr.write (' In: <%s %s>\n' % (p._name, ' '.join (['%s=%s' % item for item in p._attribute_dict.items()]))) + sys.stderr.write (' In: <%s %s>\n' % (p._name, ' '.join (['%s=%s' % item for item in p._attribute_dict.items ()]))) p = p.get_parent () def get_typed_children (self, klass): @@ -205,6 +205,15 @@ class Identification (Xml_node): software.append (s.get_text ()) return software + def get_file_description (self): + misc = self.get_named_children ('miscellaneous') + for m in misc: + misc_fields = m.get_named_children ('miscellaneous-field') + for mf in misc_fields: + if hasattr (mf, 'name') and mf.name == 'description': + return mf.get_text () + return None + class Duration (Music_xml_node): @@ -259,15 +268,27 @@ class Measure_element (Music_xml_node): return None def is_first (self): - cn = self._parent.get_typed_children (self.__class__) - cn = [c for c in cn if c.get_voice_id () == self.get_voice_id ()] + # Look at all measure elements (previously we had self.__class__, which + # only looked at objects of the same type! + cn = self._parent.get_typed_children (Measure_element) + # But only look at the correct voice; But include Attributes, too, which + # are not tied to any particular voice + cn = [c for c in cn if (c.get_voice_id () == self.get_voice_id ()) or isinstance (c, Attributes)] return cn[0] == self class Attributes (Measure_element): def __init__ (self): Measure_element.__init__ (self) self._dict = {} + self._original_tag = None + def is_first (self): + cn = self._parent.get_typed_children (self.__class__) + if self._original_tag: + return cn[0] == self._original_tag + else: + return cn[0] == self + def set_attributes_from_previous (self, dict): self._dict.update (dict) @@ -340,7 +361,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') @@ -510,6 +539,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, @@ -539,6 +571,8 @@ class Part (Music_xml_node): # and should not change the current measure position! if isinstance (n, FiguredBass): n._divisions = factor.denominator () + n._when = now + n._measure_position = measure_position continue if isinstance (n, Hash_text): @@ -561,6 +595,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) @@ -578,6 +618,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 @@ -599,6 +656,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 @@ -613,12 +676,18 @@ class Part (Music_xml_node): # modify attributes so that only those applying to the given staff remain def extract_attributes_for_staff (part, attr, staff): attributes = copy.copy (attr) - attributes._children = copy.copy (attr._children) + attributes._children = []; attributes._dict = attr._dict.copy () - for c in attributes._children: - if hasattr (c, 'number') and c.number != staff: - attributes._children.remove (c) - return attributes + attributes._original_tag = attr + # copy only the relevant children over for the given staff + for c in attr._children: + if (not (hasattr (c, 'number') and (c.number != staff)) and + not (isinstance (c, Hash_text))): + attributes._children.append (c) + if not attributes._children: + return None + else: + return attributes def extract_voices (part): voices = {} @@ -680,8 +749,9 @@ class Part (Music_xml_node): # assign these only to the voices they really belongs to! for (s, vids) in staff_to_voice_dict.items (): staff_attributes = part.extract_attributes_for_staff (n, s) - for v in vids: - voices[v].add_element (staff_attributes) + if staff_attributes: + for v in vids: + voices[v].add_element (staff_attributes) continue if isinstance (n, Partial) or isinstance (n, Barline): @@ -1044,7 +1114,7 @@ def lxml_demarshal_node (node): for c in py_node._children: c._parent = py_node - for (k,v) in node.items (): + for (k, v) in node.items (): py_node.__dict__[k] = v py_node._attribute_dict[k] = v @@ -1054,14 +1124,14 @@ def minidom_demarshal_node (node): name = node.nodeName klass = get_class (name) - py_node = klass() + py_node = klass () py_node._name = name py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes] for c in py_node._children: c._parent = py_node if node.attributes: - for (nm, value) in node.attributes.items(): + for (nm, value) in node.attributes.items (): py_node.__dict__[nm] = value py_node._attribute_dict[nm] = value @@ -1074,10 +1144,10 @@ def minidom_demarshal_node (node): if __name__ == '__main__': - import lxml.etree + import lxml.etree - tree = lxml.etree.parse ('beethoven.xml') - mxl_tree = lxml_demarshal_node (tree.getroot ()) - ks = class_dict.keys() - ks.sort() - print '\n'.join (ks) + tree = lxml.etree.parse ('beethoven.xml') + mxl_tree = lxml_demarshal_node (tree.getroot ()) + ks = class_dict.keys () + ks.sort () + print '\n'.join (ks)