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?
385 self.message (_ ("Encountered note at %s with %s duration (no <type> element):") % (self.start, self.duration) )
388 def get_factor (self):
391 def get_pitches (self):
392 return self.get_typed_children (get_class (u'pitch'))
394 class Part_list (Music_xml_node):
396 Music_xml_node.__init__ (self)
397 self._id_instrument_name_dict = {}
399 def generate_id_instrument_dict (self):
401 ## not empty to make sure this happens only once.
403 for score_part in self.get_named_children ('score-part'):
404 for instr in score_part.get_named_children ('score-instrument'):
406 name = instr.get_named_child ("instrument-name")
407 mapping[id] = name.get_text ()
409 self._id_instrument_name_dict = mapping
411 def get_instrument (self, id):
412 if not self._id_instrument_name_dict:
413 self.generate_id_instrument_dict()
415 instrument_name = self._id_instrument_name_dict.get (id)
417 return instrument_name
419 ly.stderr_write (_ ("Unable to find instrument for ID=%s\n") % id)
422 class Part_group (Music_xml_node):
424 class Score_part (Music_xml_node):
427 class Measure (Music_xml_node):
429 Music_xml_node.__init__ (self)
431 def is_implicit (self):
432 return hasattr (self, 'implicit') and self.implicit == 'yes'
433 def get_notes (self):
434 return self.get_typed_children (get_class (u'note'))
436 class Syllabic (Music_xml_node):
437 def continued (self):
438 text = self.get_text()
439 return (text == "begin") or (text == "middle")
440 class Text (Music_xml_node):
443 class Lyric (Music_xml_node):
444 def get_number (self):
445 if hasattr (self, 'number'):
450 def lyric_to_text (self):
452 syllabic = self.get_maybe_exist_typed_child (Syllabic)
454 continued = syllabic.continued ()
455 text = self.get_maybe_exist_typed_child (Text)
458 text = text.get_text()
459 # We need to convert soft hyphens to -, otherwise the ascii codec as well
460 # as lilypond will barf on that character
461 text = string.replace( text, u'\xad', '-' )
463 if text == "-" and continued:
465 elif text == "_" and continued:
467 elif continued and text:
468 return escape_ly_output_string (text) + " --"
472 return escape_ly_output_string (text)
476 class Musicxml_voice:
480 self._start_staff = None
482 self._has_lyrics = False
484 def add_element (self, e):
485 self._elements.append (e)
486 if (isinstance (e, Note)
487 and e.get_maybe_exist_typed_child (Staff)):
488 name = e.get_maybe_exist_typed_child (Staff).get_text ()
490 if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
491 self._start_staff = name
492 self._staves[name] = True
494 lyrics = e.get_typed_children (Lyric)
495 if not self._has_lyrics:
496 self.has_lyrics = len (lyrics) > 0
500 if (nr > 0) and not (nr in self._lyrics):
501 self._lyrics.append (nr)
503 def insert (self, idx, e):
504 self._elements.insert (idx, e)
506 def get_lyrics_numbers (self):
507 if (len (self._lyrics) == 0) and self._has_lyrics:
508 #only happens if none of the <lyric> tags has a number attribute
514 class Part (Music_xml_node):
516 Music_xml_node.__init__ (self)
518 self._staff_attributes_dict = {}
520 def get_part_list (self):
522 while n and n.get_name() != 'score-partwise':
525 return n.get_named_child ('part-list')
527 def interpret (self):
528 """Set durations and starting points."""
529 """The starting point of the very first note is 0!"""
531 part_list = self.get_part_list ()
534 factor = Rational (1)
536 attributes_object = None
537 measures = self.get_typed_children (Measure)
538 last_moment = Rational (-1)
539 last_measure_position = Rational (-1)
540 measure_position = Rational (0)
541 measure_start_moment = now
542 is_first_measure = True
543 previous_measure = None
544 # Graces at the end of a measure need to have their position set to the
548 # implicit measures are used for artificial measures, e.g. when
549 # a repeat bar line splits a bar into two halves. In this case,
550 # don't reset the measure position to 0. They are also used for
551 # upbeats (initial value of 0 fits these, too).
552 # Also, don't reset the measure position at the end of the loop,
553 # but rather when starting the next measure (since only then do we
554 # know if the next measure is implicit and continues that measure)
555 if not m.is_implicit ():
556 # Warn about possibly overfull measures and reset the position
557 if attributes_object and previous_measure and previous_measure.partial == 0:
558 length = attributes_object.get_measure_length ()
559 new_now = measure_start_moment + length
561 problem = 'incomplete'
564 ## only for verbose operation.
565 if problem <> 'incomplete' and previous_measure:
566 previous_measure.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
568 measure_start_moment = now
569 measure_position = Rational (0)
571 for n in m.get_all_children ():
572 # figured bass has a duration, but applies to the next note
573 # and should not change the current measure position!
574 if isinstance (n, FiguredBass):
575 n._divisions = factor.denominator ()
577 n._measure_position = measure_position
580 if isinstance (n, Hash_text):
584 if n.__class__ == Attributes:
585 n.set_attributes_from_previous (attributes_dict)
587 attributes_dict = n._dict.copy ()
588 attributes_object = n
590 factor = Rational (1,
591 int (attributes_dict.get ('divisions').get_text ()))
594 if (n.get_maybe_exist_typed_child (Duration)):
595 mxl_dur = n.get_maybe_exist_typed_child (Duration)
596 dur = mxl_dur.get_length () * factor
598 if n.get_name() == 'backup':
600 # reset all graces before the backup to after-graces:
601 for n in pending_graces:
602 n._when = n._prev_when
603 n._measure_position = n._prev_measure_position
604 n._after_grace = True
606 if n.get_maybe_exist_typed_child (Grace):
609 rest = n.get_maybe_exist_typed_child (Rest)
611 and attributes_object
612 and attributes_object.get_measure_length () == dur):
614 rest._is_whole_measure = True
616 if (dur > Rational (0)
617 and n.get_maybe_exist_typed_child (Chord)):
619 measure_position = last_measure_position
622 n._measure_position = measure_position
624 # For all grace notes, store the previous note, in case need
625 # to turn the grace note into an after-grace later on!
626 if isinstance(n, Note) and n.is_grace ():
627 n._prev_when = last_moment
628 n._prev_measure_position = last_measure_position
629 # After-graces are placed at the same position as the previous note
630 if isinstance(n, Note) and n.is_after_grace ():
631 # TODO: We should do the same for grace notes at the end of
632 # a measure with no following note!!!
633 n._when = last_moment
634 n._measure_position = last_measure_position
635 elif isinstance(n, Note) and n.is_grace ():
636 pending_graces.append (n)
637 elif (dur > Rational (0)):
641 if dur > Rational (0):
643 last_measure_position = measure_position
645 measure_position += dur
646 elif dur < Rational (0):
647 # backup element, reset measure position
649 measure_position += dur
650 if measure_position < 0:
651 # backup went beyond the measure start => reset to 0
652 now -= measure_position
655 last_measure_position = measure_position
656 if n._name == 'note':
657 instrument = n.get_maybe_exist_named_child ('instrument')
659 n.instrument_name = part_list.get_instrument (instrument.id)
661 # reset all graces at the end of the measure to after-graces:
662 for n in pending_graces:
663 n._when = n._prev_when
664 n._measure_position = n._prev_measure_position
665 n._after_grace = True
667 # Incomplete first measures are not padded, but registered as partial
669 is_first_measure = False
670 # upbeats are marked as implicit measures
671 if attributes_object and m.is_implicit ():
672 length = attributes_object.get_measure_length ()
673 measure_end = measure_start_moment + length
674 if measure_end <> now:
678 # modify attributes so that only those applying to the given staff remain
679 def extract_attributes_for_staff (part, attr, staff):
680 attributes = copy.copy (attr)
681 attributes._children = [];
682 attributes._dict = attr._dict.copy ()
683 attributes._original_tag = attr
684 # copy only the relevant children over for the given staff
685 for c in attr._children:
686 if (not (hasattr (c, 'number') and (c.number != staff)) and
687 not (isinstance (c, Hash_text))):
688 attributes._children.append (c)
689 if not attributes._children:
694 def extract_voices (part):
696 measures = part.get_typed_children (Measure)
700 elements.append (Partial (m.partial))
701 elements.extend (m.get_all_children ())
702 # make sure we know all voices already so that dynamics, clefs, etc.
703 # can be assigned to the correct voices
704 voice_to_staff_dict = {}
706 voice_id = n.get_maybe_exist_named_child (u'voice')
709 vid = voice_id.get_text ()
710 elif isinstance (n, Note):
713 staff_id = n.get_maybe_exist_named_child (u'staff')
716 sid = staff_id.get_text ()
719 if vid and not voices.has_key (vid):
720 voices[vid] = Musicxml_voice()
721 if vid and sid and not n.get_maybe_exist_typed_child (Grace):
722 if not voice_to_staff_dict.has_key (vid):
723 voice_to_staff_dict[vid] = sid
724 # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
725 # need to assign staff-assigned objects like clefs, times, etc. to
726 # all the correct voices. This will never work entirely correct due
727 # to staff-switches, but that's the best we can do!
728 staff_to_voice_dict = {}
729 for (v,s) in voice_to_staff_dict.items ():
730 if not staff_to_voice_dict.has_key (s):
731 staff_to_voice_dict[s] = [v]
733 staff_to_voice_dict[s].append (v)
737 assign_to_next_note = []
740 voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
742 id = voice_id.get_text ()
746 # We don't need backup/forward any more, since we have already
747 # assigned the correct onset times.
748 # TODO: Let Grouping through. Also: link, print, bokmark sound
749 if not (isinstance (n, Note) or isinstance (n, Attributes) or
750 isinstance (n, Direction) or isinstance (n, Partial) or
751 isinstance (n, Barline) or isinstance (n, Harmony) or
752 isinstance (n, FiguredBass) ):
755 if isinstance (n, Attributes) and not start_attr:
759 if isinstance (n, Attributes):
760 # assign these only to the voices they really belongs to!
761 for (s, vids) in staff_to_voice_dict.items ():
762 staff_attributes = part.extract_attributes_for_staff (n, s)
765 voices[v].add_element (staff_attributes)
768 if isinstance (n, Partial) or isinstance (n, Barline):
769 for v in voices.keys ():
770 voices[v].add_element (n)
773 if isinstance (n, Direction):
774 staff_id = n.get_maybe_exist_named_child (u'staff')
776 staff_id = staff_id.get_text ()
778 dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
780 dir_voices = voices.keys ()
782 voices[v].add_element (n)
785 if isinstance (n, Harmony) or isinstance (n, FiguredBass):
786 # store the harmony or figured bass element until we encounter
787 # the next note and assign it only to that one voice.
788 assign_to_next_note.append (n)
791 if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
795 for i in assign_to_next_note:
796 voices[id].add_element (i)
797 assign_to_next_note = []
798 voices[id].add_element (n)
800 # Assign all remaining elements from assign_to_next_note to the voice
801 # of the previous note:
802 for i in assign_to_next_note:
803 voices[id].add_element (i)
804 assign_to_next_note = []
807 for (s, vids) in staff_to_voice_dict.items ():
808 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
809 staff_attributes.read_self ()
810 part._staff_attributes_dict[s] = staff_attributes
812 voices[v].insert (0, staff_attributes)
813 voices[v]._elements[0].read_self()
815 part._voices = voices
817 def get_voices (self):
819 def get_staff_attributes (self):
820 return self._staff_attributes_dict
822 class Notations (Music_xml_node):
824 ts = self.get_named_children ('tied')
825 starts = [t for t in ts if t.type == 'start']
831 def get_tuplets (self):
832 return self.get_typed_children (Tuplet)
834 class Time_modification(Music_xml_node):
835 def get_fraction (self):
836 b = self.get_maybe_exist_named_child ('actual-notes')
837 a = self.get_maybe_exist_named_child ('normal-notes')
838 return (int(a.get_text ()), int (b.get_text ()))
840 class Accidental (Music_xml_node):
842 Music_xml_node.__init__ (self)
843 self.editorial = False
844 self.cautionary = False
846 class Music_xml_spanner (Music_xml_node):
848 if hasattr (self, 'type'):
853 if hasattr (self, 'size'):
854 return string.atoi (self.size)
858 class Wedge (Music_xml_spanner):
861 class Tuplet (Music_xml_spanner):
864 class Bracket (Music_xml_spanner):
867 class Dashes (Music_xml_spanner):
870 class Slur (Music_xml_spanner):
874 class Beam (Music_xml_spanner):
876 return self.get_text ()
877 def is_primary (self):
878 return self.number == "1"
880 class Wavy_line (Music_xml_spanner):
883 class Pedal (Music_xml_spanner):
886 class Glissando (Music_xml_spanner):
889 class Slide (Music_xml_spanner):
892 class Octave_shift (Music_xml_spanner):
893 # default is 8 for the octave-shift!
895 if hasattr (self, 'size'):
896 return string.atoi (self.size)
900 class Chord (Music_xml_node):
903 class Dot (Music_xml_node):
906 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
907 # for the inner <rest> element, not the whole rest block.
908 class Rest (Music_xml_node):
910 Music_xml_node.__init__ (self)
911 self._is_whole_measure = False
912 def is_whole_measure (self):
913 return self._is_whole_measure
915 ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
917 step = ch.get_text ().strip ()
921 def get_octave (self):
922 ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
924 step = ch.get_text ().strip ()
929 class Type (Music_xml_node):
931 class Grace (Music_xml_node):
933 class Staff (Music_xml_node):
936 class Direction (Music_xml_node):
938 class DirType (Music_xml_node):
941 class Bend (Music_xml_node):
942 def bend_alter (self):
943 alter = self.get_maybe_exist_named_child ('bend-alter')
945 return alter.get_text()
949 class Words (Music_xml_node):
952 class Harmony (Music_xml_node):
955 class ChordPitch (Music_xml_node):
956 def step_class_name (self):
958 def alter_class_name (self):
961 ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
962 return ch.get_text ().strip ()
963 def get_alteration (self):
964 ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
967 alter = int (ch.get_text ().strip ())
970 class Root (ChordPitch):
973 class Bass (ChordPitch):
974 def step_class_name (self):
976 def alter_class_name (self):
979 class ChordModification (Music_xml_node):
981 ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
982 return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
983 def get_value (self):
984 ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
987 value = int (ch.get_text ().strip ())
989 def get_alter (self):
990 ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
993 value = int (ch.get_text ().strip ())
997 class Frame (Music_xml_node):
998 def get_frets (self):
999 return self.get_named_child_value_number ('frame-frets', 4)
1000 def get_strings (self):
1001 return self.get_named_child_value_number ('frame-strings', 6)
1002 def get_first_fret (self):
1003 return self.get_named_child_value_number ('first-fret', 1)
1005 class Frame_Note (Music_xml_node):
1006 def get_string (self):
1007 return self.get_named_child_value_number ('string', 1)
1008 def get_fret (self):
1009 return self.get_named_child_value_number ('fret', 0)
1010 def get_fingering (self):
1011 return self.get_named_child_value_number ('fingering', -1)
1012 def get_barre (self):
1013 n = self.get_maybe_exist_named_child ('barre')
1015 return getattr (n, 'type', '')
1019 class FiguredBass (Music_xml_node):
1022 class BeatUnit (Music_xml_node):
1025 class BeatUnitDot (Music_xml_node):
1028 class PerMinute (Music_xml_node):
1033 ## need this, not all classes are instantiated
1034 ## for every input file. Only add those classes, that are either directly
1035 ## used by class name or extend Music_xml_node in some way!
1037 '#comment': Hash_comment,
1039 'accidental': Accidental,
1040 'attributes': Attributes,
1042 'bar-style': BarStyle,
1045 'beat-unit': BeatUnit,
1046 'beat-unit-dot': BeatUnitDot,
1048 'bracket' : Bracket,
1051 'degree' : ChordModification,
1053 'direction': Direction,
1054 'direction-type': DirType,
1055 'duration': Duration,
1057 'frame-note': Frame_Note,
1058 'figured-bass': FiguredBass,
1059 'glissando': Glissando,
1062 'identification': Identification,
1065 'notations': Notations,
1067 'octave-shift': Octave_shift,
1069 'part-group': Part_group,
1070 'part-list': Part_list,
1072 'per-minute': PerMinute,
1076 'score-part': Score_part,
1080 'syllabic': Syllabic,
1082 'time-modification': Time_modification,
1085 'unpitched': Unpitched,
1086 'wavy-line': Wavy_line,
1092 def name2class_name (name):
1093 name = name.replace ('-', '_')
1094 name = name.replace ('#', 'hash_')
1095 name = name[0].upper() + name[1:].lower()
1099 def get_class (name):
1100 classname = class_dict.get (name)
1104 class_name = name2class_name (name)
1105 klass = new.classobj (class_name, (Music_xml_node,) , {})
1106 class_dict[name] = klass
1109 def lxml_demarshal_node (node):
1112 # Ignore comment nodes, which are also returned by the etree parser!
1113 if name is None or node.__class__.__name__ == "_Comment":
1115 klass = get_class (name)
1118 py_node._original = node
1119 py_node._name = name
1120 py_node._data = node.text
1121 py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1122 py_node._children = filter (lambda x: x, py_node._children)
1124 for c in py_node._children:
1127 for (k, v) in node.items ():
1128 py_node.__dict__[k] = v
1129 py_node._attribute_dict[k] = v
1133 def minidom_demarshal_node (node):
1134 name = node.nodeName
1136 klass = get_class (name)
1138 py_node._name = name
1139 py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1140 for c in py_node._children:
1144 for (nm, value) in node.attributes.items ():
1145 py_node.__dict__[nm] = value
1146 py_node._attribute_dict[nm] = value
1148 py_node._data = None
1149 if node.nodeType == node.TEXT_NODE and node.data:
1150 py_node._data = node.data
1152 py_node._original = node
1156 if __name__ == '__main__':
1159 tree = lxml.etree.parse ('beethoven.xml')
1160 mxl_tree = lxml_demarshal_node (tree.getroot ())
1161 ks = class_dict.keys ()
1163 print '\n'.join (ks)