+ def __init__ (self):
+ Music_xml_node.__init__ (self)
+ self._voices = {}
+ self._staff_attributes_dict = {}
+
+ def get_part_list (self):
+ n = self
+ while n and n.get_name() != 'score-partwise':
+ n = n._parent
+
+ return n.get_named_child ('part-list')
+
+ def interpret (self):
+ """Set durations and starting points."""
+ """The starting point of the very first note is 0!"""
+
+ part_list = self.get_part_list ()
+
+ now = Rational (0)
+ factor = Rational (1)
+ attributes_dict = {}
+ attributes_object = None
+ 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
+ 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,
+ # 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 and previous_measure and previous_measure.partial == 0:
+ 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 ():
+ # 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 ()
+ n._when = now
+ n._measure_position = measure_position
+ continue
+
+ if isinstance (n, Hash_text):
+ continue
+ dur = Rational (0)
+
+ if n.__class__ == Attributes:
+ n.set_attributes_from_previous (attributes_dict)
+ n.read_self ()
+ attributes_dict = n._dict.copy ()
+ attributes_object = n
+
+ factor = Rational (1,
+ int (attributes_dict.get ('divisions').get_text ()))
+
+
+ if (n.get_maybe_exist_typed_child (Duration)):
+ mxl_dur = n.get_maybe_exist_typed_child (Duration)
+ dur = mxl_dur.get_length () * factor
+
+ if n.get_name() == 'backup':
+ dur = - dur
+ # reset all graces before the backup to after-graces:
+ graces_to_aftergraces (pending_graces)
+ pending_graces = []
+ if n.get_maybe_exist_typed_child (Grace):
+ dur = Rational (0)
+
+ rest = n.get_maybe_exist_typed_child (Rest)
+ if (rest
+ and attributes_object
+ and attributes_object.get_measure_length () == dur):
+
+ rest._is_whole_measure = True
+
+ if (dur > Rational (0)
+ and n.get_maybe_exist_typed_child (Chord)):
+ now = last_moment
+ measure_position = last_measure_position
+
+ 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
+ last_measure_position = measure_position
+ now += dur
+ measure_position += dur
+ elif dur < Rational (0):
+ # backup element, reset measure position
+ now += dur
+ measure_position += dur
+ if measure_position < 0:
+ # backup went beyond the measure start => reset to 0
+ now -= measure_position
+ measure_position = 0
+ last_moment = now
+ last_measure_position = measure_position
+ if n._name == 'note':
+ instrument = n.get_maybe_exist_named_child ('instrument')
+ if instrument:
+ n.instrument_name = part_list.get_instrument (instrument.id)
+
+ # reset all graces at the end of the measure to after-graces:
+ graces_to_aftergraces (pending_graces)
+ pending_graces = []
+ # 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):
+ attributes = copy.copy (attr)
+ attributes._children = [];
+ attributes._dict = attr._dict.copy ()
+ 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 = {}
+ 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
+ voice_to_staff_dict = {}
+ for n in elements:
+ voice_id = n.get_maybe_exist_named_child (u'voice')
+ vid = None
+ if voice_id:
+ vid = voice_id.get_text ()
+ elif isinstance (n, Note):
+ vid = "None"
+
+ staff_id = n.get_maybe_exist_named_child (u'staff')
+ sid = None
+ if staff_id:
+ sid = staff_id.get_text ()
+ else:
+ sid = "None"
+ if vid and not voices.has_key (vid):
+ voices[vid] = Musicxml_voice()
+ if vid and sid and not n.get_maybe_exist_typed_child (Grace):
+ if not voice_to_staff_dict.has_key (vid):
+ voice_to_staff_dict[vid] = sid
+ # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
+ # need to assign staff-assigned objects like clefs, times, etc. to
+ # all the correct voices. This will never work entirely correct due
+ # to staff-switches, but that's the best we can do!
+ staff_to_voice_dict = {}
+ for (v,s) in voice_to_staff_dict.items ():
+ if not staff_to_voice_dict.has_key (s):
+ staff_to_voice_dict[s] = [v]
+ else:
+ staff_to_voice_dict[s].append (v)
+
+
+ start_attr = None
+ assign_to_next_note = []
+ id = None
+ for n in elements:
+ voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
+ if voice_id:
+ id = voice_id.get_text ()
+ else:
+ id = "None"
+
+ # We don't need backup/forward any more, since we have already
+ # assigned the correct onset times.
+ # TODO: Let Grouping through. Also: link, print, bokmark sound
+ if not (isinstance (n, Note) or isinstance (n, Attributes) or
+ isinstance (n, Direction) or isinstance (n, Partial) or
+ isinstance (n, Barline) or isinstance (n, Harmony) or
+ isinstance (n, FiguredBass) or isinstance (n, Print)):
+ continue
+
+ if isinstance (n, Attributes) and not start_attr:
+ start_attr = n
+ continue
+
+ if isinstance (n, Attributes):
+ # 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)
+ if staff_attributes:
+ for v in vids:
+ voices[v].add_element (staff_attributes)
+ continue
+
+ if isinstance (n, Partial) or isinstance (n, Barline) or isinstance (n, Print):
+ 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:
+ staff_id = staff_id.get_text ()
+ if staff_id:
+ dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
+ else:
+ dir_voices = voices.keys ()
+ for v in dir_voices:
+ voices[v].add_element (n)
+ continue
+
+ 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
+
+ if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
+ #Skip this note.
+ pass
+ else:
+ for i in assign_to_next_note:
+ voices[id].add_element (i)
+ assign_to_next_note = []
+ voices[id].add_element (n)
+
+ # Assign all remaining elements from assign_to_next_note to the voice
+ # of the previous note:
+ for i in assign_to_next_note:
+ voices[id].add_element (i)
+ assign_to_next_note = []
+
+ if start_attr:
+ 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()
+
+ part._voices = voices
+
+ 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):
+ ts = self.get_named_children ('tied')
+ starts = [t for t in ts if t.type == 'start']
+ if starts:
+ return starts[0]
+ else:
+ return None
+
+ def get_tuplets (self):
+ return self.get_typed_children (Tuplet)
+
+class Time_modification(Music_xml_node):
+ def get_fraction (self):
+ b = self.get_maybe_exist_named_child ('actual-notes')
+ a = self.get_maybe_exist_named_child ('normal-notes')
+ return (int(a.get_text ()), int (b.get_text ()))
+
+ def get_normal_type (self):
+ tuplet_type = self.get_maybe_exist_named_child ('normal-type')
+ if tuplet_type:
+ dots = self.get_named_children ('normal-dot')
+ log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
+ return (log , len (dots))
+ else:
+ return None
+
+
+class Accidental (Music_xml_node):
+ def __init__ (self):
+ Music_xml_node.__init__ (self)
+ self.editorial = False
+ self.cautionary = False
+
+class Music_xml_spanner (Music_xml_node):
+ def get_type (self):
+ if hasattr (self, 'type'):
+ return self.type
+ else:
+ return 0
+ def get_size (self):
+ if hasattr (self, 'size'):
+ return string.atoi (self.size)
+ else:
+ return 0
+
+class Wedge (Music_xml_spanner):
+ pass
+
+class Tuplet (Music_xml_spanner):
+ def duration_info_from_tuplet_note (self, tuplet_note):
+ tuplet_type = tuplet_note.get_maybe_exist_named_child ('tuplet-type')
+ if tuplet_type:
+ dots = tuplet_note.get_named_children ('tuplet-dot')
+ log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
+ return (log, len (dots))
+ else:
+ return None
+
+ # Return tuplet note type as (log, dots)
+ def get_normal_type (self):
+ tuplet = self.get_maybe_exist_named_child ('tuplet-normal')
+ if tuplet:
+ return self.duration_info_from_tuplet_note (tuplet)
+ else:
+ return None
+
+ def get_actual_type (self):
+ tuplet = self.get_maybe_exist_named_child ('tuplet-actual')
+ if tuplet:
+ return self.duration_info_from_tuplet_note (tuplet)
+ else:
+ return None
+
+ def get_tuplet_note_count (self, tuplet_note):
+ if tuplet_note:
+ tuplet_nr = tuplet_note.get_maybe_exist_named_child ('tuplet-number')
+ if tuplet_nr:
+ return int (tuplet_nr.get_text ())
+ return None
+ def get_normal_nr (self):
+ return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-normal'))
+ def get_actual_nr (self):
+ return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-actual'))
+
+class Bracket (Music_xml_spanner):
+ pass
+
+class Dashes (Music_xml_spanner):
+ pass
+
+class Slur (Music_xml_spanner):
+ def get_type (self):
+ return self.type
+
+class Beam (Music_xml_spanner):
+ def get_type (self):
+ return self.get_text ()
+ def is_primary (self):
+ if hasattr (self, 'number'):
+ return self.number == "1"
+ else:
+ return True
+
+class Wavy_line (Music_xml_spanner):
+ pass
+
+class Pedal (Music_xml_spanner):
+ pass
+
+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):
+ if hasattr (self, 'size'):
+ return string.atoi (self.size)
+ else:
+ return 8
+