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)
45 self._name = 'xml_node'
47 self._attribute_dict = {}
49 def get_parent (self):
53 return self._parent.get_typed_children (self.__class__)[0] == self
64 if not self._children:
67 return ''.join ([c.get_text () for c in self._children])
69 def message (self, msg):
70 ly.stderr_write (msg+'\n')
74 sys.stderr.write (' In: <%s %s>\n' % (p._name, ' '.join (['%s=%s' % item for item in p._attribute_dict.items ()])))
77 def get_typed_children (self, klass):
81 return [c for c in self._children if isinstance(c, klass)]
83 def get_named_children (self, nm):
84 return self.get_typed_children (get_class (nm))
86 def get_named_child (self, nm):
87 return self.get_maybe_exist_named_child (nm)
89 def get_children (self, predicate):
90 return [c for c in self._children if predicate(c)]
92 def get_all_children (self):
95 def get_maybe_exist_named_child (self, name):
96 return self.get_maybe_exist_typed_child (get_class (name))
98 def get_maybe_exist_typed_child (self, klass):
99 cn = self.get_typed_children (klass)
105 raise "More than 1 child", klass
107 def get_unique_typed_child (self, klass):
108 cn = self.get_typed_children(klass)
110 sys.stderr.write (self.__dict__ + '\n')
111 raise 'Child is not unique for', (klass, 'found', cn)
115 def get_named_child_value_number (self, name, default):
116 n = self.get_maybe_exist_named_child (name)
118 return string.atoi (n.get_text())
123 class Music_xml_node (Xml_node):
125 Xml_node.__init__ (self)
126 self.duration = Rational (0)
127 self.start = Rational (0)
129 class Work (Xml_node):
130 def get_work_information (self, tag):
131 wt = self.get_maybe_exist_named_child (tag)
133 return wt.get_text ()
137 def get_work_title (self):
138 return self.get_work_information ('work-title')
139 def get_work_number (self):
140 return self.get_work_information ('work-number')
142 return self.get_work_information ('opus')
144 class Identification (Xml_node):
145 def get_rights (self):
146 rights = self.get_named_children ('rights')
149 ret.append (r.get_text ())
150 return string.join (ret, "\n")
152 def get_creator (self, type):
153 creators = self.get_named_children ('creator')
154 # return the first creator tag that has the particular type
156 if hasattr (i, 'type') and i.type == type:
160 def get_composer (self):
161 c = self.get_creator ('composer')
164 creators = self.get_named_children ('creator')
165 # return the first creator tag that has no type at all
167 if not hasattr (i, 'type'):
170 def get_arranger (self):
171 return self.get_creator ('arranger')
172 def get_editor (self):
173 return self.get_creator ('editor')
175 v = self.get_creator ('lyricist')
178 v = self.get_creator ('poet')
181 def get_encoding_information (self, type):
182 enc = self.get_named_children ('encoding')
184 children = enc[0].get_named_children (type)
186 return children[0].get_text ()
190 def get_encoding_software (self):
191 return self.get_encoding_information ('software')
192 def get_encoding_date (self):
193 return self.get_encoding_information ('encoding-date')
194 def get_encoding_person (self):
195 return self.get_encoding_information ('encoder')
196 def get_encoding_description (self):
197 return self.get_encoding_information ('encoding-description')
199 def get_encoding_software_list (self):
200 enc = self.get_named_children ('encoding')
203 softwares = e.get_named_children ('software')
205 software.append (s.get_text ())
208 def get_file_description (self):
209 misc = self.get_named_children ('miscellaneous')
211 misc_fields = m.get_named_children ('miscellaneous-field')
212 for mf in misc_fields:
213 if hasattr (mf, 'name') and mf.name == 'description':
214 return mf.get_text ()
219 class Duration (Music_xml_node):
220 def get_length (self):
221 dur = int (self.get_text ()) * Rational (1,4)
224 class Hash_comment (Music_xml_node):
226 class Hash_text (Music_xml_node):
229 class Pitch (Music_xml_node):
231 ch = self.get_unique_typed_child (get_class (u'step'))
232 step = ch.get_text ().strip ()
234 def get_octave (self):
235 ch = self.get_unique_typed_child (get_class (u'octave'))
237 step = ch.get_text ().strip ()
240 def get_alteration (self):
241 ch = self.get_maybe_exist_typed_child (get_class (u'alter'))
244 alter = int (ch.get_text ().strip ())
247 class Unpitched (Music_xml_node):
249 ch = self.get_unique_typed_child (get_class (u'display-step'))
250 step = ch.get_text ().strip ()
253 def get_octave (self):
254 ch = self.get_unique_typed_child (get_class (u'display-octave'))
257 octave = ch.get_text ().strip ()
262 class Measure_element (Music_xml_node):
263 def get_voice_id (self):
264 voice_id = self.get_maybe_exist_named_child ('voice')
266 return voice_id.get_text ()
271 # Look at all measure elements (previously we had self.__class__, which
272 # only looked at objects of the same type!
273 cn = self._parent.get_typed_children (Measure_element)
274 # But only look at the correct voice; But include Attributes, too, which
275 # are not tied to any particular voice
276 cn = [c for c in cn if (c.get_voice_id () == self.get_voice_id ()) or isinstance (c, Attributes)]
279 class Attributes (Measure_element):
281 Measure_element.__init__ (self)
283 self._original_tag = None
286 cn = self._parent.get_typed_children (self.__class__)
287 if self._original_tag:
288 return cn[0] == self._original_tag
292 def set_attributes_from_previous (self, dict):
293 self._dict.update (dict)
295 def read_self (self):
296 for c in self.get_all_children ():
297 self._dict[c.get_name()] = c
299 def get_named_attribute (self, name):
300 return self._dict.get (name)
302 def get_measure_length (self):
303 (n,d) = self.get_time_signature ()
304 return Rational (n,d)
306 def get_time_signature (self):
307 "return time sig as a (beat, beat-type) tuple"
310 mxl = self.get_named_attribute ('time')
312 beats = mxl.get_maybe_exist_named_child ('beats')
313 type = mxl.get_maybe_exist_named_child ('beat-type')
314 return (int (beats.get_text ()),
315 int (type.get_text ()))
319 error (_ ("requested time signature, but time sig is unknown"))
322 # returns clef information in the form ("cleftype", position, octave-shift)
323 def get_clef_information (self):
324 clefinfo = ['G', 2, 0]
325 mxl = self.get_named_attribute ('clef')
328 sign = mxl.get_maybe_exist_named_child ('sign')
330 clefinfo[0] = sign.get_text()
331 line = mxl.get_maybe_exist_named_child ('line')
333 clefinfo[1] = string.atoi (line.get_text ())
334 octave = mxl.get_maybe_exist_named_child ('clef-octave-change')
336 clefinfo[2] = string.atoi (octave.get_text ())
339 def get_key_signature (self):
340 "return (fifths, mode) tuple"
342 key = self.get_named_attribute ('key')
343 mode_node = key.get_maybe_exist_named_child ('mode')
346 mode = mode_node.get_text ()
347 if not mode or mode == '':
350 fifths = int (key.get_maybe_exist_named_child ('fifths').get_text ())
351 return (fifths, mode)
353 class Barline (Measure_element):
355 class BarStyle (Music_xml_node):
357 class Partial (Measure_element):
358 def __init__ (self, partial):
359 Measure_element.__init__ (self)
360 self.partial = partial
362 class Note (Measure_element):
364 Measure_element.__init__ (self)
365 self.instrument_name = ''
366 self._after_grace = False
368 return self.get_maybe_exist_named_child (u'grace')
369 def is_after_grace (self):
370 if not self.is_grace():
372 gr = self.get_maybe_exist_typed_child (Grace)
373 return self._after_grace or hasattr (gr, 'steal-time-previous');
375 def get_duration_log (self):
376 ch = self.get_maybe_exist_named_child (u'type')
379 log = ch.get_text ().strip()
380 return musicxml_duration_to_log (log)
381 elif self.get_maybe_exist_named_child (u'grace'):
382 # FIXME: is it ok to default to eight note for grace notes?
387 def get_factor (self):
390 def get_pitches (self):
391 return self.get_typed_children (get_class (u'pitch'))
393 class Part_list (Music_xml_node):
395 Music_xml_node.__init__ (self)
396 self._id_instrument_name_dict = {}
398 def generate_id_instrument_dict (self):
400 ## not empty to make sure this happens only once.
402 for score_part in self.get_named_children ('score-part'):
403 for instr in score_part.get_named_children ('score-instrument'):
405 name = instr.get_named_child ("instrument-name")
406 mapping[id] = name.get_text ()
408 self._id_instrument_name_dict = mapping
410 def get_instrument (self, id):
411 if not self._id_instrument_name_dict:
412 self.generate_id_instrument_dict()
414 instrument_name = self._id_instrument_name_dict.get (id)
416 return instrument_name
418 ly.stderr_write (_ ("Unable to find instrument for ID=%s\n") % id)
421 class Part_group (Music_xml_node):
423 class Score_part (Music_xml_node):
426 class Measure (Music_xml_node):
428 Music_xml_node.__init__ (self)
430 def is_implicit (self):
431 return hasattr (self, 'implicit') and self.implicit == 'yes'
432 def get_notes (self):
433 return self.get_typed_children (get_class (u'note'))
435 class Syllabic (Music_xml_node):
436 def continued (self):
437 text = self.get_text()
438 return (text == "begin") or (text == "middle")
439 class Text (Music_xml_node):
442 class Lyric (Music_xml_node):
443 def get_number (self):
444 if hasattr (self, 'number'):
449 def lyric_to_text (self):
451 syllabic = self.get_maybe_exist_typed_child (Syllabic)
453 continued = syllabic.continued ()
454 text = self.get_maybe_exist_typed_child (Text)
457 text = text.get_text()
458 # We need to convert soft hyphens to -, otherwise the ascii codec as well
459 # as lilypond will barf on that character
460 text = string.replace( text, u'\xad', '-' )
462 if text == "-" and continued:
464 elif text == "_" and continued:
466 elif continued and text:
467 return escape_ly_output_string (text) + " --"
471 return escape_ly_output_string (text)
475 class Musicxml_voice:
479 self._start_staff = None
481 self._has_lyrics = False
483 def add_element (self, e):
484 self._elements.append (e)
485 if (isinstance (e, Note)
486 and e.get_maybe_exist_typed_child (Staff)):
487 name = e.get_maybe_exist_typed_child (Staff).get_text ()
489 if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
490 self._start_staff = name
491 self._staves[name] = True
493 lyrics = e.get_typed_children (Lyric)
494 if not self._has_lyrics:
495 self.has_lyrics = len (lyrics) > 0
499 if (nr > 0) and not (nr in self._lyrics):
500 self._lyrics.append (nr)
502 def insert (self, idx, e):
503 self._elements.insert (idx, e)
505 def get_lyrics_numbers (self):
506 if (len (self._lyrics) == 0) and self._has_lyrics:
507 #only happens if none of the <lyric> tags has a number attribute
513 class Part (Music_xml_node):
515 Music_xml_node.__init__ (self)
517 self._staff_attributes_dict = {}
519 def get_part_list (self):
521 while n and n.get_name() != 'score-partwise':
524 return n.get_named_child ('part-list')
526 def interpret (self):
527 """Set durations and starting points."""
528 """The starting point of the very first note is 0!"""
530 part_list = self.get_part_list ()
533 factor = Rational (1)
535 attributes_object = None
536 measures = self.get_typed_children (Measure)
537 last_moment = Rational (-1)
538 last_measure_position = Rational (-1)
539 measure_position = Rational (0)
540 measure_start_moment = now
541 is_first_measure = True
542 previous_measure = None
543 # Graces at the end of a measure need to have their position set to the
547 # implicit measures are used for artificial measures, e.g. when
548 # a repeat bar line splits a bar into two halves. In this case,
549 # don't reset the measure position to 0. They are also used for
550 # upbeats (initial value of 0 fits these, too).
551 # Also, don't reset the measure position at the end of the loop,
552 # but rather when starting the next measure (since only then do we
553 # know if the next measure is implicit and continues that measure)
554 if not m.is_implicit ():
555 # Warn about possibly overfull measures and reset the position
556 if attributes_object and previous_measure and previous_measure.partial == 0:
557 length = attributes_object.get_measure_length ()
558 new_now = measure_start_moment + length
560 problem = 'incomplete'
563 ## only for verbose operation.
564 if problem <> 'incomplete' and previous_measure:
565 previous_measure.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
567 measure_start_moment = now
568 measure_position = Rational (0)
570 for n in m.get_all_children ():
571 # figured bass has a duration, but applies to the next note
572 # and should not change the current measure position!
573 if isinstance (n, FiguredBass):
574 n._divisions = factor.denominator ()
576 n._measure_position = measure_position
579 if isinstance (n, Hash_text):
583 if n.__class__ == Attributes:
584 n.set_attributes_from_previous (attributes_dict)
586 attributes_dict = n._dict.copy ()
587 attributes_object = n
589 factor = Rational (1,
590 int (attributes_dict.get ('divisions').get_text ()))
593 if (n.get_maybe_exist_typed_child (Duration)):
594 mxl_dur = n.get_maybe_exist_typed_child (Duration)
595 dur = mxl_dur.get_length () * factor
597 if n.get_name() == 'backup':
599 # reset all graces before the backup to after-graces:
600 for n in pending_graces:
601 n._when = n._prev_when
602 n._measure_position = n._prev_measure_position
603 n._after_grace = True
605 if n.get_maybe_exist_typed_child (Grace):
608 rest = n.get_maybe_exist_typed_child (Rest)
610 and attributes_object
611 and attributes_object.get_measure_length () == dur):
613 rest._is_whole_measure = True
615 if (dur > Rational (0)
616 and n.get_maybe_exist_typed_child (Chord)):
618 measure_position = last_measure_position
621 n._measure_position = measure_position
623 # For all grace notes, store the previous note, in case need
624 # to turn the grace note into an after-grace later on!
625 if isinstance(n, Note) and n.is_grace ():
626 n._prev_when = last_moment
627 n._prev_measure_position = last_measure_position
628 # After-graces are placed at the same position as the previous note
629 if isinstance(n, Note) and n.is_after_grace ():
630 # TODO: We should do the same for grace notes at the end of
631 # a measure with no following note!!!
632 n._when = last_moment
633 n._measure_position = last_measure_position
634 elif isinstance(n, Note) and n.is_grace ():
635 pending_graces.append (n)
636 elif (dur > Rational (0)):
640 if dur > Rational (0):
642 last_measure_position = measure_position
644 measure_position += dur
645 elif dur < Rational (0):
646 # backup element, reset measure position
648 measure_position += dur
649 if measure_position < 0:
650 # backup went beyond the measure start => reset to 0
651 now -= measure_position
654 last_measure_position = measure_position
655 if n._name == 'note':
656 instrument = n.get_maybe_exist_named_child ('instrument')
658 n.instrument_name = part_list.get_instrument (instrument.id)
660 # reset all graces at the end of the measure to after-graces:
661 for n in pending_graces:
662 n._when = n._prev_when
663 n._measure_position = n._prev_measure_position
664 n._after_grace = True
666 # Incomplete first measures are not padded, but registered as partial
668 is_first_measure = False
669 # upbeats are marked as implicit measures
670 if attributes_object and m.is_implicit ():
671 length = attributes_object.get_measure_length ()
672 measure_end = measure_start_moment + length
673 if measure_end <> now:
677 # modify attributes so that only those applying to the given staff remain
678 def extract_attributes_for_staff (part, attr, staff):
679 attributes = copy.copy (attr)
680 attributes._children = [];
681 attributes._dict = attr._dict.copy ()
682 attributes._original_tag = attr
683 # copy only the relevant children over for the given staff
684 for c in attr._children:
685 if (not (hasattr (c, 'number') and (c.number != staff)) and
686 not (isinstance (c, Hash_text))):
687 attributes._children.append (c)
688 if not attributes._children:
693 def extract_voices (part):
695 measures = part.get_typed_children (Measure)
699 elements.append (Partial (m.partial))
700 elements.extend (m.get_all_children ())
701 # make sure we know all voices already so that dynamics, clefs, etc.
702 # can be assigned to the correct voices
703 voice_to_staff_dict = {}
705 voice_id = n.get_maybe_exist_named_child (u'voice')
708 vid = voice_id.get_text ()
709 elif isinstance (n, Note):
712 staff_id = n.get_maybe_exist_named_child (u'staff')
715 sid = staff_id.get_text ()
718 if vid and not voices.has_key (vid):
719 voices[vid] = Musicxml_voice()
720 if vid and sid and not n.get_maybe_exist_typed_child (Grace):
721 if not voice_to_staff_dict.has_key (vid):
722 voice_to_staff_dict[vid] = sid
723 # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
724 # need to assign staff-assigned objects like clefs, times, etc. to
725 # all the correct voices. This will never work entirely correct due
726 # to staff-switches, but that's the best we can do!
727 staff_to_voice_dict = {}
728 for (v,s) in voice_to_staff_dict.items ():
729 if not staff_to_voice_dict.has_key (s):
730 staff_to_voice_dict[s] = [v]
732 staff_to_voice_dict[s].append (v)
736 assign_to_next_note = []
739 voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
741 id = voice_id.get_text ()
745 # We don't need backup/forward any more, since we have already
746 # assigned the correct onset times.
747 # TODO: Let Grouping through. Also: link, print, bokmark sound
748 if not (isinstance (n, Note) or isinstance (n, Attributes) or
749 isinstance (n, Direction) or isinstance (n, Partial) or
750 isinstance (n, Barline) or isinstance (n, Harmony) or
751 isinstance (n, FiguredBass) ):
754 if isinstance (n, Attributes) and not start_attr:
758 if isinstance (n, Attributes):
759 # assign these only to the voices they really belongs to!
760 for (s, vids) in staff_to_voice_dict.items ():
761 staff_attributes = part.extract_attributes_for_staff (n, s)
764 voices[v].add_element (staff_attributes)
767 if isinstance (n, Partial) or isinstance (n, Barline):
768 for v in voices.keys ():
769 voices[v].add_element (n)
772 if isinstance (n, Direction):
773 staff_id = n.get_maybe_exist_named_child (u'staff')
775 staff_id = staff_id.get_text ()
777 dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
779 dir_voices = voices.keys ()
781 voices[v].add_element (n)
784 if isinstance (n, Harmony) or isinstance (n, FiguredBass):
785 # store the harmony or figured bass element until we encounter
786 # the next note and assign it only to that one voice.
787 assign_to_next_note.append (n)
790 if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
794 for i in assign_to_next_note:
795 voices[id].add_element (i)
796 assign_to_next_note = []
797 voices[id].add_element (n)
799 # Assign all remaining elements from assign_to_next_note to the voice
800 # of the previous note:
801 for i in assign_to_next_note:
802 voices[id].add_element (i)
803 assign_to_next_note = []
806 for (s, vids) in staff_to_voice_dict.items ():
807 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
808 staff_attributes.read_self ()
809 part._staff_attributes_dict[s] = staff_attributes
811 voices[v].insert (0, staff_attributes)
812 voices[v]._elements[0].read_self()
814 part._voices = voices
816 def get_voices (self):
818 def get_staff_attributes (self):
819 return self._staff_attributes_dict
821 class Notations (Music_xml_node):
823 ts = self.get_named_children ('tied')
824 starts = [t for t in ts if t.type == 'start']
830 def get_tuplets (self):
831 return self.get_typed_children (Tuplet)
833 class Time_modification(Music_xml_node):
834 def get_fraction (self):
835 b = self.get_maybe_exist_named_child ('actual-notes')
836 a = self.get_maybe_exist_named_child ('normal-notes')
837 return (int(a.get_text ()), int (b.get_text ()))
839 class Accidental (Music_xml_node):
841 Music_xml_node.__init__ (self)
842 self.editorial = False
843 self.cautionary = False
845 class Music_xml_spanner (Music_xml_node):
847 if hasattr (self, 'type'):
852 if hasattr (self, 'size'):
853 return string.atoi (self.size)
857 class Wedge (Music_xml_spanner):
860 class Tuplet (Music_xml_spanner):
863 class Bracket (Music_xml_spanner):
866 class Dashes (Music_xml_spanner):
869 class Slur (Music_xml_spanner):
873 class Beam (Music_xml_spanner):
875 return self.get_text ()
876 def is_primary (self):
877 return self.number == "1"
879 class Wavy_line (Music_xml_spanner):
882 class Pedal (Music_xml_spanner):
885 class Glissando (Music_xml_spanner):
888 class Slide (Music_xml_spanner):
891 class Octave_shift (Music_xml_spanner):
892 # default is 8 for the octave-shift!
894 if hasattr (self, 'size'):
895 return string.atoi (self.size)
899 class Chord (Music_xml_node):
902 class Dot (Music_xml_node):
905 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
906 # for the inner <rest> element, not the whole rest block.
907 class Rest (Music_xml_node):
909 Music_xml_node.__init__ (self)
910 self._is_whole_measure = False
911 def is_whole_measure (self):
912 return self._is_whole_measure
914 ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
916 step = ch.get_text ().strip ()
920 def get_octave (self):
921 ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
923 step = ch.get_text ().strip ()
928 class Type (Music_xml_node):
930 class Grace (Music_xml_node):
932 class Staff (Music_xml_node):
935 class Direction (Music_xml_node):
937 class DirType (Music_xml_node):
940 class Bend (Music_xml_node):
941 def bend_alter (self):
942 alter = self.get_maybe_exist_named_child ('bend-alter')
944 return alter.get_text()
948 class Words (Music_xml_node):
951 class Harmony (Music_xml_node):
954 class ChordPitch (Music_xml_node):
955 def step_class_name (self):
957 def alter_class_name (self):
960 ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
961 return ch.get_text ().strip ()
962 def get_alteration (self):
963 ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
966 alter = int (ch.get_text ().strip ())
969 class Root (ChordPitch):
972 class Bass (ChordPitch):
973 def step_class_name (self):
975 def alter_class_name (self):
978 class ChordModification (Music_xml_node):
980 ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
981 return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
982 def get_value (self):
983 ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
986 value = int (ch.get_text ().strip ())
988 def get_alter (self):
989 ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
992 value = int (ch.get_text ().strip ())
996 class Frame (Music_xml_node):
997 def get_frets (self):
998 return self.get_named_child_value_number ('frame-frets', 4)
999 def get_strings (self):
1000 return self.get_named_child_value_number ('frame-strings', 6)
1001 def get_first_fret (self):
1002 return self.get_named_child_value_number ('first-fret', 1)
1004 class Frame_Note (Music_xml_node):
1005 def get_string (self):
1006 return self.get_named_child_value_number ('string', 1)
1007 def get_fret (self):
1008 return self.get_named_child_value_number ('fret', 0)
1009 def get_fingering (self):
1010 return self.get_named_child_value_number ('fingering', -1)
1011 def get_barre (self):
1012 n = self.get_maybe_exist_named_child ('barre')
1014 return getattr (n, 'type', '')
1018 class FiguredBass (Music_xml_node):
1021 class BeatUnit (Music_xml_node):
1024 class BeatUnitDot (Music_xml_node):
1027 class PerMinute (Music_xml_node):
1032 ## need this, not all classes are instantiated
1033 ## for every input file. Only add those classes, that are either directly
1034 ## used by class name or extend Music_xml_node in some way!
1036 '#comment': Hash_comment,
1038 'accidental': Accidental,
1039 'attributes': Attributes,
1041 'bar-style': BarStyle,
1044 'beat-unit': BeatUnit,
1045 'beat-unit-dot': BeatUnitDot,
1047 'bracket' : Bracket,
1050 'degree' : ChordModification,
1052 'direction': Direction,
1053 'direction-type': DirType,
1054 'duration': Duration,
1056 'frame-note': Frame_Note,
1057 'figured-bass': FiguredBass,
1058 'glissando': Glissando,
1061 'identification': Identification,
1064 'notations': Notations,
1066 'octave-shift': Octave_shift,
1068 'part-group': Part_group,
1069 'part-list': Part_list,
1071 'per-minute': PerMinute,
1075 'score-part': Score_part,
1079 'syllabic': Syllabic,
1081 'time-modification': Time_modification,
1084 'unpitched': Unpitched,
1085 'wavy-line': Wavy_line,
1091 def name2class_name (name):
1092 name = name.replace ('-', '_')
1093 name = name.replace ('#', 'hash_')
1094 name = name[0].upper() + name[1:].lower()
1098 def get_class (name):
1099 classname = class_dict.get (name)
1103 class_name = name2class_name (name)
1104 klass = new.classobj (class_name, (Music_xml_node,) , {})
1105 class_dict[name] = klass
1108 def lxml_demarshal_node (node):
1111 # Ignore comment nodes, which are also returned by the etree parser!
1112 if name is None or node.__class__.__name__ == "_Comment":
1114 klass = get_class (name)
1117 py_node._original = node
1118 py_node._name = name
1119 py_node._data = node.text
1120 py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1121 py_node._children = filter (lambda x: x, py_node._children)
1123 for c in py_node._children:
1126 for (k, v) in node.items ():
1127 py_node.__dict__[k] = v
1128 py_node._attribute_dict[k] = v
1132 def minidom_demarshal_node (node):
1133 name = node.nodeName
1135 klass = get_class (name)
1137 py_node._name = name
1138 py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1139 for c in py_node._children:
1143 for (nm, value) in node.attributes.items ():
1144 py_node.__dict__[nm] = value
1145 py_node._attribute_dict[nm] = value
1147 py_node._data = None
1148 if node.nodeType == node.TEXT_NODE and node.data:
1149 py_node._data = node.data
1151 py_node._original = node
1155 if __name__ == '__main__':
1158 tree = lxml.etree.parse ('beethoven.xml')
1159 mxl_tree = lxml_demarshal_node (tree.getroot ())
1160 ks = class_dict.keys ()
1162 print '\n'.join (ks)