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 dump (self, indent = ''):
78 sys.stderr.write ('%s<%s%s>' % (indent, self._name, ''.join ([' %s=%s' % item for item in self._attribute_dict.items ()])))
79 non_text_children = [c for c in self._children if not isinstance (c, Hash_text)]
81 sys.stderr.write ('\n')
82 for c in self._children:
85 sys.stderr.write (indent)
86 sys.stderr.write ('</%s>\n' % self._name)
89 def get_typed_children (self, klass):
93 return [c for c in self._children if isinstance(c, klass)]
95 def get_named_children (self, nm):
96 return self.get_typed_children (get_class (nm))
98 def get_named_child (self, nm):
99 return self.get_maybe_exist_named_child (nm)
101 def get_children (self, predicate):
102 return [c for c in self._children if predicate(c)]
104 def get_all_children (self):
105 return self._children
107 def get_maybe_exist_named_child (self, name):
108 return self.get_maybe_exist_typed_child (get_class (name))
110 def get_maybe_exist_typed_child (self, klass):
111 cn = self.get_typed_children (klass)
117 raise "More than 1 child", klass
119 def get_unique_typed_child (self, klass):
120 cn = self.get_typed_children(klass)
122 sys.stderr.write (self.__dict__ + '\n')
123 raise 'Child is not unique for', (klass, 'found', cn)
127 def get_named_child_value_number (self, name, default):
128 n = self.get_maybe_exist_named_child (name)
130 return string.atoi (n.get_text())
135 class Music_xml_node (Xml_node):
137 Xml_node.__init__ (self)
138 self.duration = Rational (0)
139 self.start = Rational (0)
141 class Work (Xml_node):
142 def get_work_information (self, tag):
143 wt = self.get_maybe_exist_named_child (tag)
145 return wt.get_text ()
149 def get_work_title (self):
150 return self.get_work_information ('work-title')
151 def get_work_number (self):
152 return self.get_work_information ('work-number')
154 return self.get_work_information ('opus')
156 class Identification (Xml_node):
157 def get_rights (self):
158 rights = self.get_named_children ('rights')
161 ret.append (r.get_text ())
162 return string.join (ret, "\n")
164 def get_creator (self, type):
165 creators = self.get_named_children ('creator')
166 # return the first creator tag that has the particular type
168 if hasattr (i, 'type') and i.type == type:
172 def get_composer (self):
173 c = self.get_creator ('composer')
176 creators = self.get_named_children ('creator')
177 # return the first creator tag that has no type at all
179 if not hasattr (i, 'type'):
182 def get_arranger (self):
183 return self.get_creator ('arranger')
184 def get_editor (self):
185 return self.get_creator ('editor')
187 v = self.get_creator ('lyricist')
190 v = self.get_creator ('poet')
193 def get_encoding_information (self, type):
194 enc = self.get_named_children ('encoding')
196 children = enc[0].get_named_children (type)
198 return children[0].get_text ()
202 def get_encoding_software (self):
203 return self.get_encoding_information ('software')
204 def get_encoding_date (self):
205 return self.get_encoding_information ('encoding-date')
206 def get_encoding_person (self):
207 return self.get_encoding_information ('encoder')
208 def get_encoding_description (self):
209 return self.get_encoding_information ('encoding-description')
211 def get_encoding_software_list (self):
212 enc = self.get_named_children ('encoding')
215 softwares = e.get_named_children ('software')
217 software.append (s.get_text ())
220 def get_file_description (self):
221 misc = self.get_named_children ('miscellaneous')
223 misc_fields = m.get_named_children ('miscellaneous-field')
224 for mf in misc_fields:
225 if hasattr (mf, 'name') and mf.name == 'description':
226 return mf.get_text ()
231 class Duration (Music_xml_node):
232 def get_length (self):
233 dur = int (self.get_text ()) * Rational (1,4)
236 class Hash_comment (Music_xml_node):
238 class Hash_text (Music_xml_node):
239 def dump (self, indent = ''):
240 sys.stderr.write ('%s' % string.strip (self._data))
242 class Pitch (Music_xml_node):
244 ch = self.get_unique_typed_child (get_class (u'step'))
245 step = ch.get_text ().strip ()
247 def get_octave (self):
248 ch = self.get_unique_typed_child (get_class (u'octave'))
250 step = ch.get_text ().strip ()
253 def get_alteration (self):
254 ch = self.get_maybe_exist_typed_child (get_class (u'alter'))
257 alter = int (ch.get_text ().strip ())
260 class Unpitched (Music_xml_node):
262 ch = self.get_unique_typed_child (get_class (u'display-step'))
263 step = ch.get_text ().strip ()
266 def get_octave (self):
267 ch = self.get_unique_typed_child (get_class (u'display-octave'))
270 octave = ch.get_text ().strip ()
275 class Measure_element (Music_xml_node):
276 def get_voice_id (self):
277 voice_id = self.get_maybe_exist_named_child ('voice')
279 return voice_id.get_text ()
284 # Look at all measure elements (previously we had self.__class__, which
285 # only looked at objects of the same type!
286 cn = self._parent.get_typed_children (Measure_element)
287 # But only look at the correct voice; But include Attributes, too, which
288 # are not tied to any particular voice
289 cn = [c for c in cn if (c.get_voice_id () == self.get_voice_id ()) or isinstance (c, Attributes)]
292 class Attributes (Measure_element):
294 Measure_element.__init__ (self)
296 self._original_tag = None
299 cn = self._parent.get_typed_children (self.__class__)
300 if self._original_tag:
301 return cn[0] == self._original_tag
305 def set_attributes_from_previous (self, dict):
306 self._dict.update (dict)
308 def read_self (self):
309 for c in self.get_all_children ():
310 self._dict[c.get_name()] = c
312 def get_named_attribute (self, name):
313 return self._dict.get (name)
315 def get_measure_length (self):
316 (n,d) = self.get_time_signature ()
317 return Rational (n,d)
319 def get_time_signature (self):
320 "return time sig as a (beat, beat-type) tuple"
323 mxl = self.get_named_attribute ('time')
325 beats = mxl.get_maybe_exist_named_child ('beats')
326 type = mxl.get_maybe_exist_named_child ('beat-type')
327 return (int (beats.get_text ()),
328 int (type.get_text ()))
332 error (_ ("requested time signature, but time sig is unknown"))
335 # returns clef information in the form ("cleftype", position, octave-shift)
336 def get_clef_information (self):
337 clefinfo = ['G', 2, 0]
338 mxl = self.get_named_attribute ('clef')
341 sign = mxl.get_maybe_exist_named_child ('sign')
343 clefinfo[0] = sign.get_text()
344 line = mxl.get_maybe_exist_named_child ('line')
346 clefinfo[1] = string.atoi (line.get_text ())
347 octave = mxl.get_maybe_exist_named_child ('clef-octave-change')
349 clefinfo[2] = string.atoi (octave.get_text ())
352 def get_key_signature (self):
353 "return (fifths, mode) tuple"
355 key = self.get_named_attribute ('key')
356 mode_node = key.get_maybe_exist_named_child ('mode')
359 mode = mode_node.get_text ()
360 if not mode or mode == '':
363 fifths = int (key.get_maybe_exist_named_child ('fifths').get_text ())
364 return (fifths, mode)
366 def get_transposition (self):
367 return self.get_named_attribute ('transpose')
371 class Barline (Measure_element):
373 class BarStyle (Music_xml_node):
375 class Partial (Measure_element):
376 def __init__ (self, partial):
377 Measure_element.__init__ (self)
378 self.partial = partial
380 class Note (Measure_element):
382 Measure_element.__init__ (self)
383 self.instrument_name = ''
384 self._after_grace = False
386 return self.get_maybe_exist_named_child (u'grace')
387 def is_after_grace (self):
388 if not self.is_grace():
390 gr = self.get_maybe_exist_typed_child (Grace)
391 return self._after_grace or hasattr (gr, 'steal-time-previous');
393 def get_duration_log (self):
394 ch = self.get_maybe_exist_named_child (u'type')
397 log = ch.get_text ().strip()
398 return musicxml_duration_to_log (log)
399 elif self.get_maybe_exist_named_child (u'grace'):
400 # FIXME: is it ok to default to eight note for grace notes?
405 def get_factor (self):
408 def get_pitches (self):
409 return self.get_typed_children (get_class (u'pitch'))
411 class Part_list (Music_xml_node):
413 Music_xml_node.__init__ (self)
414 self._id_instrument_name_dict = {}
416 def generate_id_instrument_dict (self):
418 ## not empty to make sure this happens only once.
420 for score_part in self.get_named_children ('score-part'):
421 for instr in score_part.get_named_children ('score-instrument'):
423 name = instr.get_named_child ("instrument-name")
424 mapping[id] = name.get_text ()
426 self._id_instrument_name_dict = mapping
428 def get_instrument (self, id):
429 if not self._id_instrument_name_dict:
430 self.generate_id_instrument_dict()
432 instrument_name = self._id_instrument_name_dict.get (id)
434 return instrument_name
436 ly.stderr_write (_ ("Unable to find instrument for ID=%s\n") % id)
439 class Part_group (Music_xml_node):
441 class Score_part (Music_xml_node):
444 class Measure (Music_xml_node):
446 Music_xml_node.__init__ (self)
448 def is_implicit (self):
449 return hasattr (self, 'implicit') and self.implicit == 'yes'
450 def get_notes (self):
451 return self.get_typed_children (get_class (u'note'))
453 class Syllabic (Music_xml_node):
454 def continued (self):
455 text = self.get_text()
456 return (text == "begin") or (text == "middle")
457 class Text (Music_xml_node):
460 class Lyric (Music_xml_node):
461 def get_number (self):
462 if hasattr (self, 'number'):
467 def lyric_to_text (self):
469 syllabic = self.get_maybe_exist_typed_child (Syllabic)
471 continued = syllabic.continued ()
472 text = self.get_maybe_exist_typed_child (Text)
475 text = text.get_text()
476 # We need to convert soft hyphens to -, otherwise the ascii codec as well
477 # as lilypond will barf on that character
478 text = string.replace( text, u'\xad', '-' )
480 if text == "-" and continued:
482 elif text == "_" and continued:
484 elif continued and text:
485 return escape_ly_output_string (text) + " --"
489 return escape_ly_output_string (text)
493 class Musicxml_voice:
497 self._start_staff = None
499 self._has_lyrics = False
501 def add_element (self, e):
502 self._elements.append (e)
503 if (isinstance (e, Note)
504 and e.get_maybe_exist_typed_child (Staff)):
505 name = e.get_maybe_exist_typed_child (Staff).get_text ()
507 if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
508 self._start_staff = name
509 self._staves[name] = True
511 lyrics = e.get_typed_children (Lyric)
512 if not self._has_lyrics:
513 self.has_lyrics = len (lyrics) > 0
517 if (nr > 0) and not (nr in self._lyrics):
518 self._lyrics.append (nr)
520 def insert (self, idx, e):
521 self._elements.insert (idx, e)
523 def get_lyrics_numbers (self):
524 if (len (self._lyrics) == 0) and self._has_lyrics:
525 #only happens if none of the <lyric> tags has a number attribute
531 class Part (Music_xml_node):
533 Music_xml_node.__init__ (self)
535 self._staff_attributes_dict = {}
537 def get_part_list (self):
539 while n and n.get_name() != 'score-partwise':
542 return n.get_named_child ('part-list')
544 def interpret (self):
545 """Set durations and starting points."""
546 """The starting point of the very first note is 0!"""
548 part_list = self.get_part_list ()
551 factor = Rational (1)
553 attributes_object = None
554 measures = self.get_typed_children (Measure)
555 last_moment = Rational (-1)
556 last_measure_position = Rational (-1)
557 measure_position = Rational (0)
558 measure_start_moment = now
559 is_first_measure = True
560 previous_measure = None
561 # Graces at the end of a measure need to have their position set to the
565 # implicit measures are used for artificial measures, e.g. when
566 # a repeat bar line splits a bar into two halves. In this case,
567 # don't reset the measure position to 0. They are also used for
568 # upbeats (initial value of 0 fits these, too).
569 # Also, don't reset the measure position at the end of the loop,
570 # but rather when starting the next measure (since only then do we
571 # know if the next measure is implicit and continues that measure)
572 if not m.is_implicit ():
573 # Warn about possibly overfull measures and reset the position
574 if attributes_object and previous_measure and previous_measure.partial == 0:
575 length = attributes_object.get_measure_length ()
576 new_now = measure_start_moment + length
578 problem = 'incomplete'
581 ## only for verbose operation.
582 if problem <> 'incomplete' and previous_measure:
583 previous_measure.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
585 measure_start_moment = now
586 measure_position = Rational (0)
588 for n in m.get_all_children ():
589 # figured bass has a duration, but applies to the next note
590 # and should not change the current measure position!
591 if isinstance (n, FiguredBass):
592 n._divisions = factor.denominator ()
594 n._measure_position = measure_position
597 if isinstance (n, Hash_text):
601 if n.__class__ == Attributes:
602 n.set_attributes_from_previous (attributes_dict)
604 attributes_dict = n._dict.copy ()
605 attributes_object = n
607 factor = Rational (1,
608 int (attributes_dict.get ('divisions').get_text ()))
611 if (n.get_maybe_exist_typed_child (Duration)):
612 mxl_dur = n.get_maybe_exist_typed_child (Duration)
613 dur = mxl_dur.get_length () * factor
615 if n.get_name() == 'backup':
617 # reset all graces before the backup to after-graces:
618 for n in pending_graces:
619 n._when = n._prev_when
620 n._measure_position = n._prev_measure_position
621 n._after_grace = True
623 if n.get_maybe_exist_typed_child (Grace):
626 rest = n.get_maybe_exist_typed_child (Rest)
628 and attributes_object
629 and attributes_object.get_measure_length () == dur):
631 rest._is_whole_measure = True
633 if (dur > Rational (0)
634 and n.get_maybe_exist_typed_child (Chord)):
636 measure_position = last_measure_position
639 n._measure_position = measure_position
641 # For all grace notes, store the previous note, in case need
642 # to turn the grace note into an after-grace later on!
643 if isinstance(n, Note) and n.is_grace ():
644 n._prev_when = last_moment
645 n._prev_measure_position = last_measure_position
646 # After-graces are placed at the same position as the previous note
647 if isinstance(n, Note) and n.is_after_grace ():
648 # TODO: We should do the same for grace notes at the end of
649 # a measure with no following note!!!
650 n._when = last_moment
651 n._measure_position = last_measure_position
652 elif isinstance(n, Note) and n.is_grace ():
653 pending_graces.append (n)
654 elif (dur > Rational (0)):
658 if dur > Rational (0):
660 last_measure_position = measure_position
662 measure_position += dur
663 elif dur < Rational (0):
664 # backup element, reset measure position
666 measure_position += dur
667 if measure_position < 0:
668 # backup went beyond the measure start => reset to 0
669 now -= measure_position
672 last_measure_position = measure_position
673 if n._name == 'note':
674 instrument = n.get_maybe_exist_named_child ('instrument')
676 n.instrument_name = part_list.get_instrument (instrument.id)
678 # reset all graces at the end of the measure to after-graces:
679 for n in pending_graces:
680 n._when = n._prev_when
681 n._measure_position = n._prev_measure_position
682 n._after_grace = True
684 # Incomplete first measures are not padded, but registered as partial
686 is_first_measure = False
687 # upbeats are marked as implicit measures
688 if attributes_object and m.is_implicit ():
689 length = attributes_object.get_measure_length ()
690 measure_end = measure_start_moment + length
691 if measure_end <> now:
695 # modify attributes so that only those applying to the given staff remain
696 def extract_attributes_for_staff (part, attr, staff):
697 attributes = copy.copy (attr)
698 attributes._children = [];
699 attributes._dict = attr._dict.copy ()
700 attributes._original_tag = attr
701 # copy only the relevant children over for the given staff
702 for c in attr._children:
703 if (not (hasattr (c, 'number') and (c.number != staff)) and
704 not (isinstance (c, Hash_text))):
705 attributes._children.append (c)
706 if not attributes._children:
711 def extract_voices (part):
713 measures = part.get_typed_children (Measure)
717 elements.append (Partial (m.partial))
718 elements.extend (m.get_all_children ())
719 # make sure we know all voices already so that dynamics, clefs, etc.
720 # can be assigned to the correct voices
721 voice_to_staff_dict = {}
723 voice_id = n.get_maybe_exist_named_child (u'voice')
726 vid = voice_id.get_text ()
727 elif isinstance (n, Note):
730 staff_id = n.get_maybe_exist_named_child (u'staff')
733 sid = staff_id.get_text ()
736 if vid and not voices.has_key (vid):
737 voices[vid] = Musicxml_voice()
738 if vid and sid and not n.get_maybe_exist_typed_child (Grace):
739 if not voice_to_staff_dict.has_key (vid):
740 voice_to_staff_dict[vid] = sid
741 # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
742 # need to assign staff-assigned objects like clefs, times, etc. to
743 # all the correct voices. This will never work entirely correct due
744 # to staff-switches, but that's the best we can do!
745 staff_to_voice_dict = {}
746 for (v,s) in voice_to_staff_dict.items ():
747 if not staff_to_voice_dict.has_key (s):
748 staff_to_voice_dict[s] = [v]
750 staff_to_voice_dict[s].append (v)
754 assign_to_next_note = []
757 voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
759 id = voice_id.get_text ()
763 # We don't need backup/forward any more, since we have already
764 # assigned the correct onset times.
765 # TODO: Let Grouping through. Also: link, print, bokmark sound
766 if not (isinstance (n, Note) or isinstance (n, Attributes) or
767 isinstance (n, Direction) or isinstance (n, Partial) or
768 isinstance (n, Barline) or isinstance (n, Harmony) or
769 isinstance (n, FiguredBass) ):
772 if isinstance (n, Attributes) and not start_attr:
776 if isinstance (n, Attributes):
777 # assign these only to the voices they really belongs to!
778 for (s, vids) in staff_to_voice_dict.items ():
779 staff_attributes = part.extract_attributes_for_staff (n, s)
782 voices[v].add_element (staff_attributes)
785 if isinstance (n, Partial) or isinstance (n, Barline):
786 for v in voices.keys ():
787 voices[v].add_element (n)
790 if isinstance (n, Direction):
791 staff_id = n.get_maybe_exist_named_child (u'staff')
793 staff_id = staff_id.get_text ()
795 dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
797 dir_voices = voices.keys ()
799 voices[v].add_element (n)
802 if isinstance (n, Harmony) or isinstance (n, FiguredBass):
803 # store the harmony or figured bass element until we encounter
804 # the next note and assign it only to that one voice.
805 assign_to_next_note.append (n)
808 if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
812 for i in assign_to_next_note:
813 voices[id].add_element (i)
814 assign_to_next_note = []
815 voices[id].add_element (n)
817 # Assign all remaining elements from assign_to_next_note to the voice
818 # of the previous note:
819 for i in assign_to_next_note:
820 voices[id].add_element (i)
821 assign_to_next_note = []
824 for (s, vids) in staff_to_voice_dict.items ():
825 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
826 staff_attributes.read_self ()
827 part._staff_attributes_dict[s] = staff_attributes
829 voices[v].insert (0, staff_attributes)
830 voices[v]._elements[0].read_self()
832 part._voices = voices
834 def get_voices (self):
836 def get_staff_attributes (self):
837 return self._staff_attributes_dict
839 class Notations (Music_xml_node):
841 ts = self.get_named_children ('tied')
842 starts = [t for t in ts if t.type == 'start']
848 def get_tuplets (self):
849 return self.get_typed_children (Tuplet)
851 class Time_modification(Music_xml_node):
852 def get_fraction (self):
853 b = self.get_maybe_exist_named_child ('actual-notes')
854 a = self.get_maybe_exist_named_child ('normal-notes')
855 return (int(a.get_text ()), int (b.get_text ()))
857 class Accidental (Music_xml_node):
859 Music_xml_node.__init__ (self)
860 self.editorial = False
861 self.cautionary = False
863 class Music_xml_spanner (Music_xml_node):
865 if hasattr (self, 'type'):
870 if hasattr (self, 'size'):
871 return string.atoi (self.size)
875 class Wedge (Music_xml_spanner):
878 class Tuplet (Music_xml_spanner):
881 class Bracket (Music_xml_spanner):
884 class Dashes (Music_xml_spanner):
887 class Slur (Music_xml_spanner):
891 class Beam (Music_xml_spanner):
893 return self.get_text ()
894 def is_primary (self):
895 return self.number == "1"
897 class Wavy_line (Music_xml_spanner):
900 class Pedal (Music_xml_spanner):
903 class Glissando (Music_xml_spanner):
906 class Slide (Music_xml_spanner):
909 class Octave_shift (Music_xml_spanner):
910 # default is 8 for the octave-shift!
912 if hasattr (self, 'size'):
913 return string.atoi (self.size)
917 class Chord (Music_xml_node):
920 class Dot (Music_xml_node):
923 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
924 # for the inner <rest> element, not the whole rest block.
925 class Rest (Music_xml_node):
927 Music_xml_node.__init__ (self)
928 self._is_whole_measure = False
929 def is_whole_measure (self):
930 return self._is_whole_measure
932 ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
934 step = ch.get_text ().strip ()
938 def get_octave (self):
939 ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
941 step = ch.get_text ().strip ()
946 class Type (Music_xml_node):
948 class Grace (Music_xml_node):
950 class Staff (Music_xml_node):
953 class Direction (Music_xml_node):
955 class DirType (Music_xml_node):
958 class Bend (Music_xml_node):
959 def bend_alter (self):
960 alter = self.get_maybe_exist_named_child ('bend-alter')
962 return alter.get_text()
966 class Words (Music_xml_node):
969 class Harmony (Music_xml_node):
972 class ChordPitch (Music_xml_node):
973 def step_class_name (self):
975 def alter_class_name (self):
978 ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
979 return ch.get_text ().strip ()
980 def get_alteration (self):
981 ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
984 alter = int (ch.get_text ().strip ())
987 class Root (ChordPitch):
990 class Bass (ChordPitch):
991 def step_class_name (self):
993 def alter_class_name (self):
996 class ChordModification (Music_xml_node):
998 ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
999 return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
1000 def get_value (self):
1001 ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
1004 value = int (ch.get_text ().strip ())
1006 def get_alter (self):
1007 ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
1010 value = int (ch.get_text ().strip ())
1014 class Frame (Music_xml_node):
1015 def get_frets (self):
1016 return self.get_named_child_value_number ('frame-frets', 4)
1017 def get_strings (self):
1018 return self.get_named_child_value_number ('frame-strings', 6)
1019 def get_first_fret (self):
1020 return self.get_named_child_value_number ('first-fret', 1)
1022 class Frame_Note (Music_xml_node):
1023 def get_string (self):
1024 return self.get_named_child_value_number ('string', 1)
1025 def get_fret (self):
1026 return self.get_named_child_value_number ('fret', 0)
1027 def get_fingering (self):
1028 return self.get_named_child_value_number ('fingering', -1)
1029 def get_barre (self):
1030 n = self.get_maybe_exist_named_child ('barre')
1032 return getattr (n, 'type', '')
1036 class FiguredBass (Music_xml_node):
1039 class BeatUnit (Music_xml_node):
1042 class BeatUnitDot (Music_xml_node):
1045 class PerMinute (Music_xml_node):
1050 ## need this, not all classes are instantiated
1051 ## for every input file. Only add those classes, that are either directly
1052 ## used by class name or extend Music_xml_node in some way!
1054 '#comment': Hash_comment,
1056 'accidental': Accidental,
1057 'attributes': Attributes,
1059 'bar-style': BarStyle,
1062 'beat-unit': BeatUnit,
1063 'beat-unit-dot': BeatUnitDot,
1065 'bracket' : Bracket,
1068 'degree' : ChordModification,
1070 'direction': Direction,
1071 'direction-type': DirType,
1072 'duration': Duration,
1074 'frame-note': Frame_Note,
1075 'figured-bass': FiguredBass,
1076 'glissando': Glissando,
1079 'identification': Identification,
1082 'notations': Notations,
1084 'octave-shift': Octave_shift,
1086 'part-group': Part_group,
1087 'part-list': Part_list,
1089 'per-minute': PerMinute,
1093 'score-part': Score_part,
1097 'syllabic': Syllabic,
1099 'time-modification': Time_modification,
1102 'unpitched': Unpitched,
1103 'wavy-line': Wavy_line,
1109 def name2class_name (name):
1110 name = name.replace ('-', '_')
1111 name = name.replace ('#', 'hash_')
1112 name = name[0].upper() + name[1:].lower()
1116 def get_class (name):
1117 classname = class_dict.get (name)
1121 class_name = name2class_name (name)
1122 klass = new.classobj (class_name, (Music_xml_node,) , {})
1123 class_dict[name] = klass
1126 def lxml_demarshal_node (node):
1129 # Ignore comment nodes, which are also returned by the etree parser!
1130 if name is None or node.__class__.__name__ == "_Comment":
1132 klass = get_class (name)
1135 py_node._original = node
1136 py_node._name = name
1137 py_node._data = node.text
1138 py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1139 py_node._children = filter (lambda x: x, py_node._children)
1141 for c in py_node._children:
1144 for (k, v) in node.items ():
1145 py_node.__dict__[k] = v
1146 py_node._attribute_dict[k] = v
1150 def minidom_demarshal_node (node):
1151 name = node.nodeName
1153 klass = get_class (name)
1155 py_node._name = name
1156 py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1157 for c in py_node._children:
1161 for (nm, value) in node.attributes.items ():
1162 py_node.__dict__[nm] = value
1163 py_node._attribute_dict[nm] = value
1165 py_node._data = None
1166 if node.nodeType == node.TEXT_NODE and node.data:
1167 py_node._data = node.data
1169 py_node._original = node
1173 if __name__ == '__main__':
1176 tree = lxml.etree.parse ('beethoven.xml')
1177 mxl_tree = lxml_demarshal_node (tree.getroot ())
1178 ks = class_dict.keys ()
1180 print '\n'.join (ks)