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 ()
348 fifths = int (key.get_maybe_exist_named_child ('fifths').get_text ())
349 return (fifths, mode)
351 class Barline (Measure_element):
353 class BarStyle (Music_xml_node):
355 class Partial (Measure_element):
356 def __init__ (self, partial):
357 Measure_element.__init__ (self)
358 self.partial = partial
360 class Note (Measure_element):
362 Measure_element.__init__ (self)
363 self.instrument_name = ''
364 self._after_grace = False
366 return self.get_maybe_exist_named_child (u'grace')
367 def is_after_grace (self):
368 if not self.is_grace():
370 gr = self.get_maybe_exist_typed_child (Grace)
371 return self._after_grace or hasattr (gr, 'steal-time-previous');
373 def get_duration_log (self):
374 ch = self.get_maybe_exist_named_child (u'type')
377 log = ch.get_text ().strip()
378 return musicxml_duration_to_log (log)
379 elif self.get_maybe_exist_named_child (u'grace'):
380 # FIXME: is it ok to default to eight note for grace notes?
383 self.message (_ ("Encountered note at %s with %s duration (no <type> element):") % (self.start, self.duration) )
386 def get_factor (self):
389 def get_pitches (self):
390 return self.get_typed_children (get_class (u'pitch'))
392 class Part_list (Music_xml_node):
394 Music_xml_node.__init__ (self)
395 self._id_instrument_name_dict = {}
397 def generate_id_instrument_dict (self):
399 ## not empty to make sure this happens only once.
401 for score_part in self.get_named_children ('score-part'):
402 for instr in score_part.get_named_children ('score-instrument'):
404 name = instr.get_named_child ("instrument-name")
405 mapping[id] = name.get_text ()
407 self._id_instrument_name_dict = mapping
409 def get_instrument (self, id):
410 if not self._id_instrument_name_dict:
411 self.generate_id_instrument_dict()
413 instrument_name = self._id_instrument_name_dict.get (id)
415 return instrument_name
417 ly.stderr_write (_ ("Unable to find instrument for ID=%s\n") % id)
420 class Part_group (Music_xml_node):
422 class Score_part (Music_xml_node):
425 class Measure (Music_xml_node):
427 Music_xml_node.__init__ (self)
429 def is_implicit (self):
430 return hasattr (self, 'implicit') and self.implicit == 'yes'
431 def get_notes (self):
432 return self.get_typed_children (get_class (u'note'))
434 class Syllabic (Music_xml_node):
435 def continued (self):
436 text = self.get_text()
437 return (text == "begin") or (text == "middle")
438 class Text (Music_xml_node):
441 class Lyric (Music_xml_node):
442 def get_number (self):
443 if hasattr (self, 'number'):
448 def lyric_to_text (self):
450 syllabic = self.get_maybe_exist_typed_child (Syllabic)
452 continued = syllabic.continued ()
453 text = self.get_maybe_exist_typed_child (Text)
456 text = text.get_text()
457 # We need to convert soft hyphens to -, otherwise the ascii codec as well
458 # as lilypond will barf on that character
459 text = string.replace( text, u'\xad', '-' )
461 if text == "-" and continued:
463 elif text == "_" and continued:
465 elif continued and text:
466 return escape_ly_output_string (text) + " --"
470 return escape_ly_output_string (text)
474 class Musicxml_voice:
478 self._start_staff = None
480 self._has_lyrics = False
482 def add_element (self, e):
483 self._elements.append (e)
484 if (isinstance (e, Note)
485 and e.get_maybe_exist_typed_child (Staff)):
486 name = e.get_maybe_exist_typed_child (Staff).get_text ()
488 if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
489 self._start_staff = name
490 self._staves[name] = True
492 lyrics = e.get_typed_children (Lyric)
493 if not self._has_lyrics:
494 self.has_lyrics = len (lyrics) > 0
498 if (nr > 0) and not (nr in self._lyrics):
499 self._lyrics.append (nr)
501 def insert (self, idx, e):
502 self._elements.insert (idx, e)
504 def get_lyrics_numbers (self):
505 if (len (self._lyrics) == 0) and self._has_lyrics:
506 #only happens if none of the <lyric> tags has a number attribute
512 class Part (Music_xml_node):
514 Music_xml_node.__init__ (self)
516 self._staff_attributes_dict = {}
518 def get_part_list (self):
520 while n and n.get_name() != 'score-partwise':
523 return n.get_named_child ('part-list')
525 def interpret (self):
526 """Set durations and starting points."""
527 """The starting point of the very first note is 0!"""
529 part_list = self.get_part_list ()
532 factor = Rational (1)
534 attributes_object = None
535 measures = self.get_typed_children (Measure)
536 last_moment = Rational (-1)
537 last_measure_position = Rational (-1)
538 measure_position = Rational (0)
539 measure_start_moment = now
540 is_first_measure = True
541 previous_measure = None
542 # Graces at the end of a measure need to have their position set to the
546 # implicit measures are used for artificial measures, e.g. when
547 # a repeat bar line splits a bar into two halves. In this case,
548 # don't reset the measure position to 0. They are also used for
549 # upbeats (initial value of 0 fits these, too).
550 # Also, don't reset the measure position at the end of the loop,
551 # but rather when starting the next measure (since only then do we
552 # know if the next measure is implicit and continues that measure)
553 if not m.is_implicit ():
554 # Warn about possibly overfull measures and reset the position
555 if attributes_object and previous_measure and previous_measure.partial == 0:
556 length = attributes_object.get_measure_length ()
557 new_now = measure_start_moment + length
559 problem = 'incomplete'
562 ## only for verbose operation.
563 if problem <> 'incomplete' and previous_measure:
564 previous_measure.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
566 measure_start_moment = now
567 measure_position = Rational (0)
569 for n in m.get_all_children ():
570 # figured bass has a duration, but applies to the next note
571 # and should not change the current measure position!
572 if isinstance (n, FiguredBass):
573 n._divisions = factor.denominator ()
575 n._measure_position = measure_position
578 if isinstance (n, Hash_text):
582 if n.__class__ == Attributes:
583 n.set_attributes_from_previous (attributes_dict)
585 attributes_dict = n._dict.copy ()
586 attributes_object = n
588 factor = Rational (1,
589 int (attributes_dict.get ('divisions').get_text ()))
592 if (n.get_maybe_exist_typed_child (Duration)):
593 mxl_dur = n.get_maybe_exist_typed_child (Duration)
594 dur = mxl_dur.get_length () * factor
596 if n.get_name() == 'backup':
598 # reset all graces before the backup to after-graces:
599 for n in pending_graces:
600 n._when = n._prev_when
601 n._measure_position = n._prev_measure_position
602 n._after_grace = True
604 if n.get_maybe_exist_typed_child (Grace):
607 rest = n.get_maybe_exist_typed_child (Rest)
609 and attributes_object
610 and attributes_object.get_measure_length () == dur):
612 rest._is_whole_measure = True
614 if (dur > Rational (0)
615 and n.get_maybe_exist_typed_child (Chord)):
617 measure_position = last_measure_position
620 n._measure_position = measure_position
622 # For all grace notes, store the previous note, in case need
623 # to turn the grace note into an after-grace later on!
624 if isinstance(n, Note) and n.is_grace ():
625 n._prev_when = last_moment
626 n._prev_measure_position = last_measure_position
627 # After-graces are placed at the same position as the previous note
628 if isinstance(n, Note) and n.is_after_grace ():
629 # TODO: We should do the same for grace notes at the end of
630 # a measure with no following note!!!
631 n._when = last_moment
632 n._measure_position = last_measure_position
633 elif isinstance(n, Note) and n.is_grace ():
634 pending_graces.append (n)
635 elif (dur > Rational (0)):
639 if dur > Rational (0):
641 last_measure_position = measure_position
643 measure_position += dur
644 elif dur < Rational (0):
645 # backup element, reset measure position
647 measure_position += dur
648 if measure_position < 0:
649 # backup went beyond the measure start => reset to 0
650 now -= measure_position
653 last_measure_position = measure_position
654 if n._name == 'note':
655 instrument = n.get_maybe_exist_named_child ('instrument')
657 n.instrument_name = part_list.get_instrument (instrument.id)
659 # reset all graces at the end of the measure to after-graces:
660 for n in pending_graces:
661 n._when = n._prev_when
662 n._measure_position = n._prev_measure_position
663 n._after_grace = True
665 # Incomplete first measures are not padded, but registered as partial
667 is_first_measure = False
668 # upbeats are marked as implicit measures
669 if attributes_object and m.is_implicit ():
670 length = attributes_object.get_measure_length ()
671 measure_end = measure_start_moment + length
672 if measure_end <> now:
676 # modify attributes so that only those applying to the given staff remain
677 def extract_attributes_for_staff (part, attr, staff):
678 attributes = copy.copy (attr)
679 attributes._children = [];
680 attributes._dict = attr._dict.copy ()
681 attributes._original_tag = attr
682 # copy only the relevant children over for the given staff
683 for c in attr._children:
684 if (not (hasattr (c, 'number') and (c.number != staff)) and
685 not (isinstance (c, Hash_text))):
686 attributes._children.append (c)
687 if not attributes._children:
692 def extract_voices (part):
694 measures = part.get_typed_children (Measure)
698 elements.append (Partial (m.partial))
699 elements.extend (m.get_all_children ())
700 # make sure we know all voices already so that dynamics, clefs, etc.
701 # can be assigned to the correct voices
702 voice_to_staff_dict = {}
704 voice_id = n.get_maybe_exist_named_child (u'voice')
707 vid = voice_id.get_text ()
708 elif isinstance (n, Note):
711 staff_id = n.get_maybe_exist_named_child (u'staff')
714 sid = staff_id.get_text ()
717 if vid and not voices.has_key (vid):
718 voices[vid] = Musicxml_voice()
719 if vid and sid and not n.get_maybe_exist_typed_child (Grace):
720 if not voice_to_staff_dict.has_key (vid):
721 voice_to_staff_dict[vid] = sid
722 # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
723 # need to assign staff-assigned objects like clefs, times, etc. to
724 # all the correct voices. This will never work entirely correct due
725 # to staff-switches, but that's the best we can do!
726 staff_to_voice_dict = {}
727 for (v,s) in voice_to_staff_dict.items ():
728 if not staff_to_voice_dict.has_key (s):
729 staff_to_voice_dict[s] = [v]
731 staff_to_voice_dict[s].append (v)
735 assign_to_next_note = []
738 voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
740 id = voice_id.get_text ()
744 # We don't need backup/forward any more, since we have already
745 # assigned the correct onset times.
746 # TODO: Let Grouping through. Also: link, print, bokmark sound
747 if not (isinstance (n, Note) or isinstance (n, Attributes) or
748 isinstance (n, Direction) or isinstance (n, Partial) or
749 isinstance (n, Barline) or isinstance (n, Harmony) or
750 isinstance (n, FiguredBass) ):
753 if isinstance (n, Attributes) and not start_attr:
757 if isinstance (n, Attributes):
758 # assign these only to the voices they really belongs to!
759 for (s, vids) in staff_to_voice_dict.items ():
760 staff_attributes = part.extract_attributes_for_staff (n, s)
763 voices[v].add_element (staff_attributes)
766 if isinstance (n, Partial) or isinstance (n, Barline):
767 for v in voices.keys ():
768 voices[v].add_element (n)
771 if isinstance (n, Direction):
772 staff_id = n.get_maybe_exist_named_child (u'staff')
774 staff_id = staff_id.get_text ()
776 dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
778 dir_voices = voices.keys ()
780 voices[v].add_element (n)
783 if isinstance (n, Harmony) or isinstance (n, FiguredBass):
784 # store the harmony or figured bass element until we encounter
785 # the next note and assign it only to that one voice.
786 assign_to_next_note.append (n)
789 if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
793 for i in assign_to_next_note:
794 voices[id].add_element (i)
795 assign_to_next_note = []
796 voices[id].add_element (n)
798 # Assign all remaining elements from assign_to_next_note to the voice
799 # of the previous note:
800 for i in assign_to_next_note:
801 voices[id].add_element (i)
802 assign_to_next_note = []
805 for (s, vids) in staff_to_voice_dict.items ():
806 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
807 staff_attributes.read_self ()
808 part._staff_attributes_dict[s] = staff_attributes
810 voices[v].insert (0, staff_attributes)
811 voices[v]._elements[0].read_self()
813 part._voices = voices
815 def get_voices (self):
817 def get_staff_attributes (self):
818 return self._staff_attributes_dict
820 class Notations (Music_xml_node):
822 ts = self.get_named_children ('tied')
823 starts = [t for t in ts if t.type == 'start']
829 def get_tuplets (self):
830 return self.get_typed_children (Tuplet)
832 class Time_modification(Music_xml_node):
833 def get_fraction (self):
834 b = self.get_maybe_exist_named_child ('actual-notes')
835 a = self.get_maybe_exist_named_child ('normal-notes')
836 return (int(a.get_text ()), int (b.get_text ()))
838 class Accidental (Music_xml_node):
840 Music_xml_node.__init__ (self)
841 self.editorial = False
842 self.cautionary = False
844 class Music_xml_spanner (Music_xml_node):
846 if hasattr (self, 'type'):
851 if hasattr (self, 'size'):
852 return string.atoi (self.size)
856 class Wedge (Music_xml_spanner):
859 class Tuplet (Music_xml_spanner):
862 class Bracket (Music_xml_spanner):
865 class Dashes (Music_xml_spanner):
868 class Slur (Music_xml_spanner):
872 class Beam (Music_xml_spanner):
874 return self.get_text ()
875 def is_primary (self):
876 return self.number == "1"
878 class Wavy_line (Music_xml_spanner):
881 class Pedal (Music_xml_spanner):
884 class Glissando (Music_xml_spanner):
887 class Slide (Music_xml_spanner):
890 class Octave_shift (Music_xml_spanner):
891 # default is 8 for the octave-shift!
893 if hasattr (self, 'size'):
894 return string.atoi (self.size)
898 class Chord (Music_xml_node):
901 class Dot (Music_xml_node):
904 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
905 # for the inner <rest> element, not the whole rest block.
906 class Rest (Music_xml_node):
908 Music_xml_node.__init__ (self)
909 self._is_whole_measure = False
910 def is_whole_measure (self):
911 return self._is_whole_measure
913 ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
915 step = ch.get_text ().strip ()
919 def get_octave (self):
920 ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
922 step = ch.get_text ().strip ()
927 class Type (Music_xml_node):
929 class Grace (Music_xml_node):
931 class Staff (Music_xml_node):
934 class Direction (Music_xml_node):
936 class DirType (Music_xml_node):
939 class Bend (Music_xml_node):
940 def bend_alter (self):
941 alter = self.get_maybe_exist_named_child ('bend-alter')
943 return alter.get_text()
947 class Words (Music_xml_node):
950 class Harmony (Music_xml_node):
953 class ChordPitch (Music_xml_node):
954 def step_class_name (self):
956 def alter_class_name (self):
959 ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
960 return ch.get_text ().strip ()
961 def get_alteration (self):
962 ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
965 alter = int (ch.get_text ().strip ())
968 class Root (ChordPitch):
971 class Bass (ChordPitch):
972 def step_class_name (self):
974 def alter_class_name (self):
977 class ChordModification (Music_xml_node):
979 ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
980 return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
981 def get_value (self):
982 ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
985 value = int (ch.get_text ().strip ())
987 def get_alter (self):
988 ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
991 value = int (ch.get_text ().strip ())
995 class Frame (Music_xml_node):
996 def get_frets (self):
997 return self.get_named_child_value_number ('frame-frets', 4)
998 def get_strings (self):
999 return self.get_named_child_value_number ('frame-strings', 6)
1000 def get_first_fret (self):
1001 return self.get_named_child_value_number ('first-fret', 1)
1003 class Frame_Note (Music_xml_node):
1004 def get_string (self):
1005 return self.get_named_child_value_number ('string', 1)
1006 def get_fret (self):
1007 return self.get_named_child_value_number ('fret', 0)
1008 def get_fingering (self):
1009 return self.get_named_child_value_number ('fingering', -1)
1010 def get_barre (self):
1011 n = self.get_maybe_exist_named_child ('barre')
1013 return getattr (n, 'type', '')
1017 class FiguredBass (Music_xml_node):
1020 class BeatUnit (Music_xml_node):
1023 class BeatUnitDot (Music_xml_node):
1026 class PerMinute (Music_xml_node):
1031 ## need this, not all classes are instantiated
1032 ## for every input file. Only add those classes, that are either directly
1033 ## used by class name or extend Music_xml_node in some way!
1035 '#comment': Hash_comment,
1037 'accidental': Accidental,
1038 'attributes': Attributes,
1040 'bar-style': BarStyle,
1043 'beat-unit': BeatUnit,
1044 'beat-unit-dot': BeatUnitDot,
1046 'bracket' : Bracket,
1049 'degree' : ChordModification,
1051 'direction': Direction,
1052 'direction-type': DirType,
1053 'duration': Duration,
1055 'frame-note': Frame_Note,
1056 'figured-bass': FiguredBass,
1057 'glissando': Glissando,
1060 'identification': Identification,
1063 'notations': Notations,
1065 'octave-shift': Octave_shift,
1067 'part-group': Part_group,
1068 'part-list': Part_list,
1070 'per-minute': PerMinute,
1074 'score-part': Score_part,
1078 'syllabic': Syllabic,
1080 'time-modification': Time_modification,
1083 'unpitched': Unpitched,
1084 'wavy-line': Wavy_line,
1090 def name2class_name (name):
1091 name = name.replace ('-', '_')
1092 name = name.replace ('#', 'hash_')
1093 name = name[0].upper() + name[1:].lower()
1097 def get_class (name):
1098 classname = class_dict.get (name)
1102 class_name = name2class_name (name)
1103 klass = new.classobj (class_name, (Music_xml_node,) , {})
1104 class_dict[name] = klass
1107 def lxml_demarshal_node (node):
1110 # Ignore comment nodes, which are also returned by the etree parser!
1111 if name is None or node.__class__.__name__ == "_Comment":
1113 klass = get_class (name)
1116 py_node._original = node
1117 py_node._name = name
1118 py_node._data = node.text
1119 py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1120 py_node._children = filter (lambda x: x, py_node._children)
1122 for c in py_node._children:
1125 for (k, v) in node.items ():
1126 py_node.__dict__[k] = v
1127 py_node._attribute_dict[k] = v
1131 def minidom_demarshal_node (node):
1132 name = node.nodeName
1134 klass = get_class (name)
1136 py_node._name = name
1137 py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1138 for c in py_node._children:
1142 for (nm, value) in node.attributes.items ():
1143 py_node.__dict__[nm] = value
1144 py_node._attribute_dict[nm] = value
1146 py_node._data = None
1147 if node.nodeType == node.TEXT_NODE and node.data:
1148 py_node._data = node.data
1150 py_node._original = node
1154 if __name__ == '__main__':
1157 tree = lxml.etree.parse ('beethoven.xml')
1158 mxl_tree = lxml_demarshal_node (tree.getroot ())
1159 ks = class_dict.keys ()
1161 print '\n'.join (ks)