X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=python%2Fmusicxml.py;h=194fffbd9c709dbf418114b5de59a3be266ae3f7;hb=6a69250b0fa3f880fc3763201a74a73080b813d9;hp=a251afa5f313427575a4a80defcac1b87b9fb7ed;hpb=89e7453c7578abc708d45253705f626439d8bfe1;p=lilypond.git diff --git a/python/musicxml.py b/python/musicxml.py index a251afa5f3..194fffbd9c 100644 --- a/python/musicxml.py +++ b/python/musicxml.py @@ -5,6 +5,13 @@ from rational import * import re import sys import copy +import lilylib as ly + +_ = ly._ + +def error (str): + ly.stderr_write ((_ ("error: %s") % str) + "\n") + def escape_ly_output_string (input_string): return_string = input_string @@ -14,6 +21,22 @@ def escape_ly_output_string (input_string): return return_string +def musicxml_duration_to_log (dur): + return {'256th': 8, + '128th': 7, + '64th': 6, + '32nd': 5, + '16th': 4, + 'eighth': 3, + 'quarter': 2, + 'half': 1, + 'whole': 0, + 'breve': -1, + 'longa': -2, + 'long': -2}.get (dur, 0) + + + class Xml_node: def __init__ (self): self._children = [] @@ -44,7 +67,7 @@ class Xml_node: return ''.join ([c.get_text () for c in self._children]) def message (self, msg): - sys.stderr.write (msg+'\n') + ly.stderr_write (msg+'\n') p = self while p: @@ -172,6 +195,16 @@ class Identification (Xml_node): return self.get_encoding_information ('encoder') def get_encoding_description (self): return self.get_encoding_information ('encoding-description') + + def get_encoding_software_list (self): + enc = self.get_named_children ('encoding') + software = [] + for e in enc: + softwares = e.get_named_children ('software') + for s in softwares: + software.append (s.get_text ()) + return software + class Duration (Music_xml_node): @@ -202,6 +235,21 @@ class Pitch (Music_xml_node): alter = int (ch.get_text ().strip ()) return alter +class Unpitched (Music_xml_node): + def get_step (self): + ch = self.get_unique_typed_child (get_class (u'display-step')) + step = ch.get_text ().strip () + return step + + def get_octave (self): + ch = self.get_unique_typed_child (get_class (u'display-octave')) + + if ch: + octave = ch.get_text ().strip () + return int (octave) + else: + return None + class Measure_element (Music_xml_node): def get_voice_id (self): voice_id = self.get_maybe_exist_named_child ('voice') @@ -247,7 +295,7 @@ class Attributes (Measure_element): else: return (4, 4) except KeyError: - sys.stderr.write ('error: requested time signature, but time sig unknown\n') + error (_ ("requested time signature, but time sig is unknown")) return (4, 4) # returns clef information in the form ("cleftype", position, octave-shift) @@ -298,19 +346,12 @@ class Note (Measure_element): if ch: log = ch.get_text ().strip() - return {'256th': 8, - '128th': 7, - '64th': 6, - '32nd': 5, - '16th': 4, - 'eighth': 3, - 'quarter': 2, - 'half': 1, - 'whole': 0, - 'breve': -1, - 'long': -2}.get (log, 0) + return musicxml_duration_to_log (log) + elif self.get_maybe_exist_named_child (u'grace'): + # FIXME: is it ok to default to eight note for grace notes? + return 3 else: - self.message ("Encountered note at %s without %s duration (no element):" % (self.start, self.duration) ) + self.message (_ ("Encountered note at %s with %s duration (no element):") % (self.start, self.duration) ) return 0 def get_factor (self): @@ -344,7 +385,7 @@ class Part_list (Music_xml_node): if instrument_name: return instrument_name else: - sys.stderr.write ("Opps, couldn't find instrument for ID=%s\n" % id) + ly.stderr_write (_ ("Unable to find instrument for ID=%s\n") % id) return "Grand Piano" class Part_group (Music_xml_node): @@ -439,11 +480,11 @@ class Musicxml_voice: return self._lyrics - class Part (Music_xml_node): def __init__ (self): Music_xml_node.__init__ (self) - self._voices = [] + self._voices = {} + self._staff_attributes_dict = {} def get_part_list (self): n = self @@ -454,6 +495,7 @@ class Part (Music_xml_node): def interpret (self): """Set durations and starting points.""" + """The starting point of the very first note is 0!""" part_list = self.get_part_list () @@ -467,7 +509,7 @@ class Part (Music_xml_node): measure_position = Rational (0) measure_start_moment = now is_first_measure = True - prvious_measure = None + previous_measure = None 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, @@ -478,7 +520,7 @@ class Part (Music_xml_node): # 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: + if attributes_object and previous_measure and previous_measure.partial == 0: length = attributes_object.get_measure_length () new_now = measure_start_moment + length if now <> new_now: @@ -493,6 +535,12 @@ class Part (Music_xml_node): measure_position = Rational (0) for n in m.get_all_children (): + # figured bass has a duration, but applies to the next note + # and should not change the current measure position! + if isinstance (n, FiguredBass): + n._divisions = factor.denominator () + continue + if isinstance (n, Hash_text): continue dur = Rational (0) @@ -620,7 +668,8 @@ class Part (Music_xml_node): if not (voice_id or isinstance (n, Attributes) or isinstance (n, Direction) or isinstance (n, Partial) or - isinstance (n, Barline) or isinstance (n, Harmony) ): + isinstance (n, Barline) or isinstance (n, Harmony) or + isinstance (n, FiguredBass) ): continue if isinstance (n, Attributes) and not start_attr: @@ -652,9 +701,9 @@ class Part (Music_xml_node): voices[v].add_element (n) continue - if isinstance (n, Harmony): - # store the harmony element until we encounter the next note - # and assign it only to that one voice. + if isinstance (n, Harmony) or isinstance (n, FiguredBass): + # store the harmony or figured bass element until we encounter + # the next note and assign it only to that one voice. assign_to_next_note.append (n) continue @@ -678,6 +727,7 @@ class Part (Music_xml_node): for (s, vids) in staff_to_voice_dict.items (): staff_attributes = part.extract_attributes_for_staff (start_attr, s) staff_attributes.read_self () + part._staff_attributes_dict[s] = staff_attributes for v in vids: voices[v].insert (0, staff_attributes) voices[v]._elements[0].read_self() @@ -686,6 +736,8 @@ class Part (Music_xml_node): def get_voices (self): return self._voices + def get_staff_attributes (self): + return self._staff_attributes_dict class Notations (Music_xml_node): def get_tie (self): @@ -696,8 +748,8 @@ class Notations (Music_xml_node): else: return None - def get_tuplet (self): - return self.get_maybe_exist_typed_child (Tuplet) + def get_tuplets (self): + return self.get_typed_children (Tuplet) class Time_modification(Music_xml_node): def get_fraction (self): @@ -729,6 +781,12 @@ class Wedge (Music_xml_spanner): class Tuplet (Music_xml_spanner): pass +class Bracket (Music_xml_spanner): + pass + +class Dashes (Music_xml_spanner): + pass + class Slur (Music_xml_spanner): def get_type (self): return self.type @@ -748,6 +806,9 @@ class Pedal (Music_xml_spanner): class Glissando (Music_xml_spanner): pass +class Slide (Music_xml_spanner): + pass + class Octave_shift (Music_xml_spanner): # default is 8 for the octave-shift! def get_size (self): @@ -832,6 +893,19 @@ class Frame_Note (Music_xml_node): else: return '' +class FiguredBass (Music_xml_node): + pass + +class BeatUnit (Music_xml_node): + pass + +class BeatUnitDot (Music_xml_node): + pass + +class PerMinute (Music_xml_node): + pass + + ## need this, not all classes are instantiated ## for every input file. Only add those classes, that are either directly @@ -844,14 +918,19 @@ class_dict = { 'barline': Barline, 'bar-style': BarStyle, 'beam' : Beam, + 'beat-unit': BeatUnit, + 'beat-unit-dot': BeatUnitDot, 'bend' : Bend, + 'bracket' : Bracket, 'chord': Chord, + 'dashes' : Dashes, 'dot': Dot, 'direction': Direction, 'direction-type': DirType, 'duration': Duration, 'frame': Frame, 'frame-note': Frame_Note, + 'figured-bass': FiguredBass, 'glissando': Glissando, 'grace': Grace, 'harmony': Harmony, @@ -865,9 +944,11 @@ class_dict = { 'part-group': Part_group, 'part-list': Part_list, 'pedal': Pedal, + 'per-minute': PerMinute, 'pitch': Pitch, 'rest': Rest, 'score-part': Score_part, + 'slide': Slide, 'slur': Slur, 'staff': Staff, 'syllabic': Syllabic, @@ -875,6 +956,7 @@ class_dict = { 'time-modification': Time_modification, 'tuplet': Tuplet, 'type': Type, + 'unpitched': Unpitched, 'wavy-line': Wavy_line, 'wedge': Wedge, 'words': Words, @@ -901,7 +983,8 @@ def get_class (name): def lxml_demarshal_node (node): name = node.tag - if name is None: + # Ignore comment nodes, which are also returned by the etree parser! + if name is None or node.__class__.__name__ == "_Comment": return None klass = get_class (name) py_node = klass()