1 # -*- coding: utf-8 -*-
13 ly.stderr_write ((_ ("error: %s") % str) + "\n")
16 def escape_ly_output_string (input_string):
17 return_string = input_string
18 needs_quotes = not re.match (u"^[a-zA-ZäöüÜÄÖß,\.!:ñ]*$", return_string);
20 return_string = "\"" + string.replace (return_string, "\"", "\\\"") + "\""
24 def musicxml_duration_to_log (dur):
36 'long': -2}.get (dur, 0)
38 def interpret_alter_element (alter_elm):
41 val = eval(alter_elm.get_text ())
42 if type (val) in (int, float):
52 self._name = 'xml_node'
54 self._attribute_dict = {}
56 def get_parent (self):
60 return self._parent.get_typed_children (self.__class__)[0] == self
71 if not self._children:
74 return ''.join ([c.get_text () for c in self._children])
76 def message (self, msg):
77 ly.stderr_write (msg+'\n')
81 sys.stderr.write (' In: <%s %s>\n' % (p._name, ' '.join (['%s=%s' % item for item in p._attribute_dict.items ()])))
84 def dump (self, indent = ''):
85 sys.stderr.write ('%s<%s%s>' % (indent, self._name, ''.join ([' %s=%s' % item for item in self._attribute_dict.items ()])))
86 non_text_children = [c for c in self._children if not isinstance (c, Hash_text)]
88 sys.stderr.write ('\n')
89 for c in self._children:
92 sys.stderr.write (indent)
93 sys.stderr.write ('</%s>\n' % self._name)
96 def get_typed_children (self, klass):
100 return [c for c in self._children if isinstance(c, klass)]
102 def get_named_children (self, nm):
103 return self.get_typed_children (get_class (nm))
105 def get_named_child (self, nm):
106 return self.get_maybe_exist_named_child (nm)
108 def get_children (self, predicate):
109 return [c for c in self._children if predicate(c)]
111 def get_all_children (self):
112 return self._children
114 def get_maybe_exist_named_child (self, name):
115 return self.get_maybe_exist_typed_child (get_class (name))
117 def get_maybe_exist_typed_child (self, klass):
118 cn = self.get_typed_children (klass)
124 raise "More than 1 child", klass
126 def get_unique_typed_child (self, klass):
127 cn = self.get_typed_children(klass)
129 sys.stderr.write (self.__dict__ + '\n')
130 raise 'Child is not unique for', (klass, 'found', cn)
134 def get_named_child_value_number (self, name, default):
135 n = self.get_maybe_exist_named_child (name)
137 return string.atoi (n.get_text())
142 class Music_xml_node (Xml_node):
144 Xml_node.__init__ (self)
145 self.duration = Rational (0)
146 self.start = Rational (0)
148 class Work (Xml_node):
149 def get_work_information (self, tag):
150 wt = self.get_maybe_exist_named_child (tag)
152 return wt.get_text ()
156 def get_work_title (self):
157 return self.get_work_information ('work-title')
158 def get_work_number (self):
159 return self.get_work_information ('work-number')
161 return self.get_work_information ('opus')
163 class Identification (Xml_node):
164 def get_rights (self):
165 rights = self.get_named_children ('rights')
168 ret.append (r.get_text ())
169 return string.join (ret, "\n")
171 def get_creator (self, type):
172 creators = self.get_named_children ('creator')
173 # return the first creator tag that has the particular type
175 if hasattr (i, 'type') and i.type == type:
179 def get_composer (self):
180 c = self.get_creator ('composer')
183 creators = self.get_named_children ('creator')
184 # return the first creator tag that has no type at all
186 if not hasattr (i, 'type'):
189 def get_arranger (self):
190 return self.get_creator ('arranger')
191 def get_editor (self):
192 return self.get_creator ('editor')
194 v = self.get_creator ('lyricist')
197 v = self.get_creator ('poet')
200 def get_encoding_information (self, type):
201 enc = self.get_named_children ('encoding')
203 children = enc[0].get_named_children (type)
205 return children[0].get_text ()
209 def get_encoding_software (self):
210 return self.get_encoding_information ('software')
211 def get_encoding_date (self):
212 return self.get_encoding_information ('encoding-date')
213 def get_encoding_person (self):
214 return self.get_encoding_information ('encoder')
215 def get_encoding_description (self):
216 return self.get_encoding_information ('encoding-description')
218 def get_encoding_software_list (self):
219 enc = self.get_named_children ('encoding')
222 softwares = e.get_named_children ('software')
224 software.append (s.get_text ())
227 def get_file_description (self):
228 misc = self.get_named_children ('miscellaneous')
230 misc_fields = m.get_named_children ('miscellaneous-field')
231 for mf in misc_fields:
232 if hasattr (mf, 'name') and mf.name == 'description':
233 return mf.get_text ()
238 class Duration (Music_xml_node):
239 def get_length (self):
240 dur = int (self.get_text ()) * Rational (1,4)
243 class Hash_comment (Music_xml_node):
245 class Hash_text (Music_xml_node):
246 def dump (self, indent = ''):
247 sys.stderr.write ('%s' % string.strip (self._data))
249 class Pitch (Music_xml_node):
251 ch = self.get_unique_typed_child (get_class (u'step'))
252 step = ch.get_text ().strip ()
254 def get_octave (self):
255 ch = self.get_unique_typed_child (get_class (u'octave'))
257 step = ch.get_text ().strip ()
260 def get_alteration (self):
261 ch = self.get_maybe_exist_typed_child (get_class (u'alter'))
262 return interpret_alter_element (ch)
264 class Unpitched (Music_xml_node):
266 ch = self.get_unique_typed_child (get_class (u'display-step'))
267 step = ch.get_text ().strip ()
270 def get_octave (self):
271 ch = self.get_unique_typed_child (get_class (u'display-octave'))
274 octave = ch.get_text ().strip ()
279 class Measure_element (Music_xml_node):
280 def get_voice_id (self):
281 voice_id = self.get_maybe_exist_named_child ('voice')
283 return voice_id.get_text ()
288 # Look at all measure elements (previously we had self.__class__, which
289 # only looked at objects of the same type!
290 cn = self._parent.get_typed_children (Measure_element)
291 # But only look at the correct voice; But include Attributes, too, which
292 # are not tied to any particular voice
293 cn = [c for c in cn if (c.get_voice_id () == self.get_voice_id ()) or isinstance (c, Attributes)]
296 class Attributes (Measure_element):
298 Measure_element.__init__ (self)
300 self._original_tag = None
301 self._time_signature_cache = None
304 cn = self._parent.get_typed_children (self.__class__)
305 if self._original_tag:
306 return cn[0] == self._original_tag
310 def set_attributes_from_previous (self, dict):
311 self._dict.update (dict)
313 def read_self (self):
314 for c in self.get_all_children ():
315 self._dict[c.get_name()] = c
317 def get_named_attribute (self, name):
318 return self._dict.get (name)
320 def single_time_sig_to_fraction (self, sig):
326 return Rational (n, sig[-1])
328 def get_measure_length (self):
329 sig = self.get_time_signature ()
330 if not sig or len (sig) == 0:
332 if isinstance (sig[0], list):
333 # Complex compound time signature
336 l += self.single_time_sig_to_fraction (i)
339 # Simple (maybe compound) time signature of the form (beat, ..., type)
340 return self.single_time_sig_to_fraction (sig)
343 def get_time_signature (self):
344 "Return time sig as a (beat, beat-type) tuple. For compound signatures,"
345 "return either (beat, beat,..., beat-type) or ((beat,..., type), "
346 "(beat,..., type), ...)."
347 if self._time_signature_cache:
348 return self._time_signature_cache
351 mxl = self.get_named_attribute ('time')
355 if mxl.get_maybe_exist_named_child ('senza-misura'):
356 # TODO: Handle pieces without a time signature!
357 error (_ ("Senza-misura time signatures are not yet supported!"))
362 for i in mxl.get_all_children ():
363 if isinstance (i, Beats):
364 beats = string.split (i.get_text ().strip (), "+")
365 current_sig = [int (j) for j in beats]
366 elif isinstance (i, BeatType):
367 current_sig.append (int (i.get_text ()))
368 signature.append (current_sig)
370 if isinstance (signature[0], list) and len (signature) == 1:
371 signature = signature[0]
372 self._time_signature_cache = signature
374 except (KeyError, ValueError):
375 self.message (_ ("Unable to interpret time signature! Falling back to 4/4."))
378 # returns clef information in the form ("cleftype", position, octave-shift)
379 def get_clef_information (self):
380 clefinfo = ['G', 2, 0]
381 mxl = self.get_named_attribute ('clef')
384 sign = mxl.get_maybe_exist_named_child ('sign')
386 clefinfo[0] = sign.get_text()
387 line = mxl.get_maybe_exist_named_child ('line')
389 clefinfo[1] = string.atoi (line.get_text ())
390 octave = mxl.get_maybe_exist_named_child ('clef-octave-change')
392 clefinfo[2] = string.atoi (octave.get_text ())
395 def get_key_signature (self):
396 "return (fifths, mode) tuple if the key signatures is given as "
397 "major/minor in the Circle of fifths. Otherwise return an alterations"
398 "list of the form [[step,alter<,octave>], [step,alter<,octave>], ...], "
399 "where the octave values are optional."
401 key = self.get_named_attribute ('key')
404 fifths_elm = key.get_maybe_exist_named_child ('fifths')
406 mode_node = key.get_maybe_exist_named_child ('mode')
409 mode = mode_node.get_text ()
410 if not mode or mode == '':
412 fifths = int (fifths_elm.get_text ())
413 # TODO: Shall we try to convert the key-octave and the cancel, too?
414 return (fifths, mode)
418 for i in key.get_all_children ():
419 if isinstance (i, KeyStep):
420 current_step = int (i.get_text ())
421 elif isinstance (i, KeyAlter):
422 alterations.append ([current_step, interpret_alter_element (i)])
423 elif isinstance (i, KeyOctave):
425 if hasattr (i, 'number'):
427 if (nr > 0) and (nr <= len (alterations)):
428 # MusicXML Octave 4 is middle C -> shift to 0
429 alterations[nr-1].append (int (i.get_text ())-4)
431 i.message (_ ("Key alteration octave given for a "
432 "non-existing alteration nr. %s, available numbers: %s!") % (nr, len(alterations)))
435 def get_transposition (self):
436 return self.get_named_attribute ('transpose')
438 class KeyAlter (Music_xml_node):
440 class KeyStep (Music_xml_node):
442 class KeyOctave (Music_xml_node):
446 class Barline (Measure_element):
448 class BarStyle (Music_xml_node):
450 class Partial (Measure_element):
451 def __init__ (self, partial):
452 Measure_element.__init__ (self)
453 self.partial = partial
455 class Note (Measure_element):
457 Measure_element.__init__ (self)
458 self.instrument_name = ''
459 self._after_grace = False
461 return self.get_maybe_exist_named_child (u'grace')
462 def is_after_grace (self):
463 if not self.is_grace():
465 gr = self.get_maybe_exist_typed_child (Grace)
466 return self._after_grace or hasattr (gr, 'steal-time-previous');
468 def get_duration_log (self):
469 ch = self.get_maybe_exist_named_child (u'type')
472 log = ch.get_text ().strip()
473 return musicxml_duration_to_log (log)
474 elif self.get_maybe_exist_named_child (u'grace'):
475 # FIXME: is it ok to default to eight note for grace notes?
480 def get_duration_info (self):
481 log = self.get_duration_log ()
483 dots = len (self.get_typed_children (Dot))
488 def get_factor (self):
491 def get_pitches (self):
492 return self.get_typed_children (get_class (u'pitch'))
494 class Part_list (Music_xml_node):
496 Music_xml_node.__init__ (self)
497 self._id_instrument_name_dict = {}
499 def generate_id_instrument_dict (self):
501 ## not empty to make sure this happens only once.
503 for score_part in self.get_named_children ('score-part'):
504 for instr in score_part.get_named_children ('score-instrument'):
506 name = instr.get_named_child ("instrument-name")
507 mapping[id] = name.get_text ()
509 self._id_instrument_name_dict = mapping
511 def get_instrument (self, id):
512 if not self._id_instrument_name_dict:
513 self.generate_id_instrument_dict()
515 instrument_name = self._id_instrument_name_dict.get (id)
517 return instrument_name
519 ly.stderr_write (_ ("Unable to find instrument for ID=%s\n") % id)
522 class Part_group (Music_xml_node):
524 class Score_part (Music_xml_node):
527 class Measure (Music_xml_node):
529 Music_xml_node.__init__ (self)
531 def is_implicit (self):
532 return hasattr (self, 'implicit') and self.implicit == 'yes'
533 def get_notes (self):
534 return self.get_typed_children (get_class (u'note'))
536 class Syllabic (Music_xml_node):
537 def continued (self):
538 text = self.get_text()
539 return (text == "begin") or (text == "middle")
540 class Elision (Music_xml_node):
542 class Extend (Music_xml_node):
544 class Text (Music_xml_node):
547 class Lyric (Music_xml_node):
548 def get_number (self):
549 if hasattr (self, 'number'):
554 class Musicxml_voice:
558 self._start_staff = None
560 self._has_lyrics = False
562 def add_element (self, e):
563 self._elements.append (e)
564 if (isinstance (e, Note)
565 and e.get_maybe_exist_typed_child (Staff)):
566 name = e.get_maybe_exist_typed_child (Staff).get_text ()
568 if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
569 self._start_staff = name
570 self._staves[name] = True
572 lyrics = e.get_typed_children (Lyric)
573 if not self._has_lyrics:
574 self.has_lyrics = len (lyrics) > 0
578 if (nr > 0) and not (nr in self._lyrics):
579 self._lyrics.append (nr)
581 def insert (self, idx, e):
582 self._elements.insert (idx, e)
584 def get_lyrics_numbers (self):
585 if (len (self._lyrics) == 0) and self._has_lyrics:
586 #only happens if none of the <lyric> tags has a number attribute
592 def graces_to_aftergraces (pending_graces):
593 for gr in pending_graces:
594 gr._when = gr._prev_when
595 gr._measure_position = gr._prev_measure_position
596 gr._after_grace = True
599 class Part (Music_xml_node):
601 Music_xml_node.__init__ (self)
603 self._staff_attributes_dict = {}
605 def get_part_list (self):
607 while n and n.get_name() != 'score-partwise':
610 return n.get_named_child ('part-list')
612 def interpret (self):
613 """Set durations and starting points."""
614 """The starting point of the very first note is 0!"""
616 part_list = self.get_part_list ()
619 factor = Rational (1)
621 attributes_object = None
622 measures = self.get_typed_children (Measure)
623 last_moment = Rational (-1)
624 last_measure_position = Rational (-1)
625 measure_position = Rational (0)
626 measure_start_moment = now
627 is_first_measure = True
628 previous_measure = None
629 # Graces at the end of a measure need to have their position set to the
633 # implicit measures are used for artificial measures, e.g. when
634 # a repeat bar line splits a bar into two halves. In this case,
635 # don't reset the measure position to 0. They are also used for
636 # upbeats (initial value of 0 fits these, too).
637 # Also, don't reset the measure position at the end of the loop,
638 # but rather when starting the next measure (since only then do we
639 # know if the next measure is implicit and continues that measure)
640 if not m.is_implicit ():
641 # Warn about possibly overfull measures and reset the position
642 if attributes_object and previous_measure and previous_measure.partial == 0:
643 length = attributes_object.get_measure_length ()
644 new_now = measure_start_moment + length
646 problem = 'incomplete'
649 ## only for verbose operation.
650 if problem <> 'incomplete' and previous_measure:
651 previous_measure.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
653 measure_start_moment = now
654 measure_position = Rational (0)
656 for n in m.get_all_children ():
657 # figured bass has a duration, but applies to the next note
658 # and should not change the current measure position!
659 if isinstance (n, FiguredBass):
660 n._divisions = factor.denominator ()
662 n._measure_position = measure_position
665 if isinstance (n, Hash_text):
669 if n.__class__ == Attributes:
670 n.set_attributes_from_previous (attributes_dict)
672 attributes_dict = n._dict.copy ()
673 attributes_object = n
675 factor = Rational (1,
676 int (attributes_dict.get ('divisions').get_text ()))
679 if (n.get_maybe_exist_typed_child (Duration)):
680 mxl_dur = n.get_maybe_exist_typed_child (Duration)
681 dur = mxl_dur.get_length () * factor
683 if n.get_name() == 'backup':
685 # reset all graces before the backup to after-graces:
686 graces_to_aftergraces (pending_graces)
688 if n.get_maybe_exist_typed_child (Grace):
691 rest = n.get_maybe_exist_typed_child (Rest)
693 and attributes_object
694 and attributes_object.get_measure_length () == dur):
696 rest._is_whole_measure = True
698 if (dur > Rational (0)
699 and n.get_maybe_exist_typed_child (Chord)):
701 measure_position = last_measure_position
704 n._measure_position = measure_position
706 # For all grace notes, store the previous note, in case need
707 # to turn the grace note into an after-grace later on!
708 if isinstance(n, Note) and n.is_grace ():
709 n._prev_when = last_moment
710 n._prev_measure_position = last_measure_position
711 # After-graces are placed at the same position as the previous note
712 if isinstance(n, Note) and n.is_after_grace ():
713 # TODO: We should do the same for grace notes at the end of
714 # a measure with no following note!!!
715 n._when = last_moment
716 n._measure_position = last_measure_position
717 elif isinstance(n, Note) and n.is_grace ():
718 pending_graces.append (n)
719 elif (dur > Rational (0)):
723 if dur > Rational (0):
725 last_measure_position = measure_position
727 measure_position += dur
728 elif dur < Rational (0):
729 # backup element, reset measure position
731 measure_position += dur
732 if measure_position < 0:
733 # backup went beyond the measure start => reset to 0
734 now -= measure_position
737 last_measure_position = measure_position
738 if n._name == 'note':
739 instrument = n.get_maybe_exist_named_child ('instrument')
741 n.instrument_name = part_list.get_instrument (instrument.id)
743 # reset all graces at the end of the measure to after-graces:
744 graces_to_aftergraces (pending_graces)
746 # Incomplete first measures are not padded, but registered as partial
748 is_first_measure = False
749 # upbeats are marked as implicit measures
750 if attributes_object and m.is_implicit ():
751 length = attributes_object.get_measure_length ()
752 measure_end = measure_start_moment + length
753 if measure_end <> now:
757 # modify attributes so that only those applying to the given staff remain
758 def extract_attributes_for_staff (part, attr, staff):
759 attributes = copy.copy (attr)
760 attributes._children = [];
761 attributes._dict = attr._dict.copy ()
762 attributes._original_tag = attr
763 # copy only the relevant children over for the given staff
764 for c in attr._children:
765 if (not (hasattr (c, 'number') and (c.number != staff)) and
766 not (isinstance (c, Hash_text))):
767 attributes._children.append (c)
768 if not attributes._children:
773 def extract_voices (part):
775 measures = part.get_typed_children (Measure)
779 elements.append (Partial (m.partial))
780 elements.extend (m.get_all_children ())
781 # make sure we know all voices already so that dynamics, clefs, etc.
782 # can be assigned to the correct voices
783 voice_to_staff_dict = {}
785 voice_id = n.get_maybe_exist_named_child (u'voice')
788 vid = voice_id.get_text ()
789 elif isinstance (n, Note):
792 staff_id = n.get_maybe_exist_named_child (u'staff')
795 sid = staff_id.get_text ()
798 if vid and not voices.has_key (vid):
799 voices[vid] = Musicxml_voice()
800 if vid and sid and not n.get_maybe_exist_typed_child (Grace):
801 if not voice_to_staff_dict.has_key (vid):
802 voice_to_staff_dict[vid] = sid
803 # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
804 # need to assign staff-assigned objects like clefs, times, etc. to
805 # all the correct voices. This will never work entirely correct due
806 # to staff-switches, but that's the best we can do!
807 staff_to_voice_dict = {}
808 for (v,s) in voice_to_staff_dict.items ():
809 if not staff_to_voice_dict.has_key (s):
810 staff_to_voice_dict[s] = [v]
812 staff_to_voice_dict[s].append (v)
816 assign_to_next_note = []
819 voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
821 id = voice_id.get_text ()
825 # We don't need backup/forward any more, since we have already
826 # assigned the correct onset times.
827 # TODO: Let Grouping through. Also: link, print, bokmark sound
828 if not (isinstance (n, Note) or isinstance (n, Attributes) or
829 isinstance (n, Direction) or isinstance (n, Partial) or
830 isinstance (n, Barline) or isinstance (n, Harmony) or
831 isinstance (n, FiguredBass) or isinstance (n, Print)):
834 if isinstance (n, Attributes) and not start_attr:
838 if isinstance (n, Attributes):
839 # assign these only to the voices they really belongs to!
840 for (s, vids) in staff_to_voice_dict.items ():
841 staff_attributes = part.extract_attributes_for_staff (n, s)
844 voices[v].add_element (staff_attributes)
847 if isinstance (n, Partial) or isinstance (n, Barline) or isinstance (n, Print):
848 for v in voices.keys ():
849 voices[v].add_element (n)
852 if isinstance (n, Direction):
853 staff_id = n.get_maybe_exist_named_child (u'staff')
855 staff_id = staff_id.get_text ()
857 dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
859 dir_voices = voices.keys ()
861 voices[v].add_element (n)
864 if isinstance (n, Harmony) or isinstance (n, FiguredBass):
865 # store the harmony or figured bass element until we encounter
866 # the next note and assign it only to that one voice.
867 assign_to_next_note.append (n)
870 if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
874 for i in assign_to_next_note:
875 voices[id].add_element (i)
876 assign_to_next_note = []
877 voices[id].add_element (n)
879 # Assign all remaining elements from assign_to_next_note to the voice
880 # of the previous note:
881 for i in assign_to_next_note:
882 voices[id].add_element (i)
883 assign_to_next_note = []
886 for (s, vids) in staff_to_voice_dict.items ():
887 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
888 staff_attributes.read_self ()
889 part._staff_attributes_dict[s] = staff_attributes
891 voices[v].insert (0, staff_attributes)
892 voices[v]._elements[0].read_self()
894 part._voices = voices
896 def get_voices (self):
898 def get_staff_attributes (self):
899 return self._staff_attributes_dict
901 class Notations (Music_xml_node):
903 ts = self.get_named_children ('tied')
904 starts = [t for t in ts if t.type == 'start']
910 def get_tuplets (self):
911 return self.get_typed_children (Tuplet)
913 class Time_modification(Music_xml_node):
914 def get_fraction (self):
915 b = self.get_maybe_exist_named_child ('actual-notes')
916 a = self.get_maybe_exist_named_child ('normal-notes')
917 return (int(a.get_text ()), int (b.get_text ()))
919 def get_normal_type (self):
920 tuplet_type = self.get_maybe_exist_named_child ('normal-type')
922 dots = self.get_named_children ('normal-dot')
923 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
924 return (log , len (dots))
929 class Accidental (Music_xml_node):
931 Music_xml_node.__init__ (self)
932 self.editorial = False
933 self.cautionary = False
935 class Music_xml_spanner (Music_xml_node):
937 if hasattr (self, 'type'):
942 if hasattr (self, 'size'):
943 return string.atoi (self.size)
947 class Wedge (Music_xml_spanner):
950 class Tuplet (Music_xml_spanner):
951 def duration_info_from_tuplet_note (self, tuplet_note):
952 tuplet_type = tuplet_note.get_maybe_exist_named_child ('tuplet-type')
954 dots = tuplet_note.get_named_children ('tuplet-dot')
955 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
956 return (log, len (dots))
960 # Return tuplet note type as (log, dots)
961 def get_normal_type (self):
962 tuplet = self.get_maybe_exist_named_child ('tuplet-normal')
964 return self.duration_info_from_tuplet_note (tuplet)
968 def get_actual_type (self):
969 tuplet = self.get_maybe_exist_named_child ('tuplet-actual')
971 return self.duration_info_from_tuplet_note (tuplet)
975 def get_tuplet_note_count (self, tuplet_note):
977 tuplet_nr = tuplet_note.get_maybe_exist_named_child ('tuplet-number')
979 return int (tuplet_nr.get_text ())
981 def get_normal_nr (self):
982 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-normal'))
983 def get_actual_nr (self):
984 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-actual'))
986 class Bracket (Music_xml_spanner):
989 class Dashes (Music_xml_spanner):
992 class Slur (Music_xml_spanner):
996 class Beam (Music_xml_spanner):
998 return self.get_text ()
999 def is_primary (self):
1000 return self.number == "1"
1002 class Wavy_line (Music_xml_spanner):
1005 class Pedal (Music_xml_spanner):
1008 class Glissando (Music_xml_spanner):
1011 class Slide (Music_xml_spanner):
1014 class Octave_shift (Music_xml_spanner):
1015 # default is 8 for the octave-shift!
1016 def get_size (self):
1017 if hasattr (self, 'size'):
1018 return string.atoi (self.size)
1022 class Chord (Music_xml_node):
1025 class Dot (Music_xml_node):
1028 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
1029 # for the inner <rest> element, not the whole rest block.
1030 class Rest (Music_xml_node):
1031 def __init__ (self):
1032 Music_xml_node.__init__ (self)
1033 self._is_whole_measure = False
1034 def is_whole_measure (self):
1035 return self._is_whole_measure
1036 def get_step (self):
1037 ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
1039 step = ch.get_text ().strip ()
1043 def get_octave (self):
1044 ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
1046 step = ch.get_text ().strip ()
1051 class Type (Music_xml_node):
1053 class Grace (Music_xml_node):
1055 class Staff (Music_xml_node):
1058 class Direction (Music_xml_node):
1060 class DirType (Music_xml_node):
1063 class Bend (Music_xml_node):
1064 def bend_alter (self):
1065 alter = self.get_maybe_exist_named_child ('bend-alter')
1066 return interpret_alter_element (alter)
1068 class Words (Music_xml_node):
1071 class Harmony (Music_xml_node):
1074 class ChordPitch (Music_xml_node):
1075 def step_class_name (self):
1077 def alter_class_name (self):
1078 return u'root-alter'
1079 def get_step (self):
1080 ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
1081 return ch.get_text ().strip ()
1082 def get_alteration (self):
1083 ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
1084 return interpret_alter_element (ch)
1086 class Root (ChordPitch):
1089 class Bass (ChordPitch):
1090 def step_class_name (self):
1092 def alter_class_name (self):
1093 return u'bass-alter'
1095 class ChordModification (Music_xml_node):
1096 def get_type (self):
1097 ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
1098 return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
1099 def get_value (self):
1100 ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
1103 value = int (ch.get_text ().strip ())
1105 def get_alter (self):
1106 ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
1107 return interpret_alter_element (ch)
1110 class Frame (Music_xml_node):
1111 def get_frets (self):
1112 return self.get_named_child_value_number ('frame-frets', 4)
1113 def get_strings (self):
1114 return self.get_named_child_value_number ('frame-strings', 6)
1115 def get_first_fret (self):
1116 return self.get_named_child_value_number ('first-fret', 1)
1118 class Frame_Note (Music_xml_node):
1119 def get_string (self):
1120 return self.get_named_child_value_number ('string', 1)
1121 def get_fret (self):
1122 return self.get_named_child_value_number ('fret', 0)
1123 def get_fingering (self):
1124 return self.get_named_child_value_number ('fingering', -1)
1125 def get_barre (self):
1126 n = self.get_maybe_exist_named_child ('barre')
1128 return getattr (n, 'type', '')
1132 class FiguredBass (Music_xml_node):
1135 class Beats (Music_xml_node):
1138 class BeatType (Music_xml_node):
1141 class BeatUnit (Music_xml_node):
1144 class BeatUnitDot (Music_xml_node):
1147 class PerMinute (Music_xml_node):
1150 class Print (Music_xml_node):
1155 ## need this, not all classes are instantiated
1156 ## for every input file. Only add those classes, that are either directly
1157 ## used by class name or extend Music_xml_node in some way!
1159 '#comment': Hash_comment,
1161 'accidental': Accidental,
1162 'attributes': Attributes,
1164 'bar-style': BarStyle,
1168 'beat-type': BeatType,
1169 'beat-unit': BeatUnit,
1170 'beat-unit-dot': BeatUnitDot,
1172 'bracket' : Bracket,
1175 'degree' : ChordModification,
1177 'direction': Direction,
1178 'direction-type': DirType,
1179 'duration': Duration,
1183 'frame-note': Frame_Note,
1184 'figured-bass': FiguredBass,
1185 'glissando': Glissando,
1188 'identification': Identification,
1189 'key-alter': KeyAlter,
1190 'key-octave': KeyOctave,
1191 'key-step': KeyStep,
1194 'notations': Notations,
1196 'octave-shift': Octave_shift,
1198 'part-group': Part_group,
1199 'part-list': Part_list,
1201 'per-minute': PerMinute,
1206 'score-part': Score_part,
1210 'syllabic': Syllabic,
1212 'time-modification': Time_modification,
1215 'unpitched': Unpitched,
1216 'wavy-line': Wavy_line,
1222 def name2class_name (name):
1223 name = name.replace ('-', '_')
1224 name = name.replace ('#', 'hash_')
1225 name = name[0].upper() + name[1:].lower()
1229 def get_class (name):
1230 classname = class_dict.get (name)
1234 class_name = name2class_name (name)
1235 klass = new.classobj (class_name, (Music_xml_node,) , {})
1236 class_dict[name] = klass
1239 def lxml_demarshal_node (node):
1242 # Ignore comment nodes, which are also returned by the etree parser!
1243 if name is None or node.__class__.__name__ == "_Comment":
1245 klass = get_class (name)
1248 py_node._original = node
1249 py_node._name = name
1250 py_node._data = node.text
1251 py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1252 py_node._children = filter (lambda x: x, py_node._children)
1254 for c in py_node._children:
1257 for (k, v) in node.items ():
1258 py_node.__dict__[k] = v
1259 py_node._attribute_dict[k] = v
1263 def minidom_demarshal_node (node):
1264 name = node.nodeName
1266 klass = get_class (name)
1268 py_node._name = name
1269 py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1270 for c in py_node._children:
1274 for (nm, value) in node.attributes.items ():
1275 py_node.__dict__[nm] = value
1276 py_node._attribute_dict[nm] = value
1278 py_node._data = None
1279 if node.nodeType == node.TEXT_NODE and node.data:
1280 py_node._data = node.data
1282 py_node._original = node
1286 if __name__ == '__main__':
1289 tree = lxml.etree.parse ('beethoven.xml')
1290 mxl_tree = lxml_demarshal_node (tree.getroot ())
1291 ks = class_dict.keys ()
1293 print '\n'.join (ks)