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_duration_info (self):
406 log = self.get_duration_log ()
408 dots = len (self.get_typed_children (Dot))
413 def get_factor (self):
416 def get_pitches (self):
417 return self.get_typed_children (get_class (u'pitch'))
419 class Part_list (Music_xml_node):
421 Music_xml_node.__init__ (self)
422 self._id_instrument_name_dict = {}
424 def generate_id_instrument_dict (self):
426 ## not empty to make sure this happens only once.
428 for score_part in self.get_named_children ('score-part'):
429 for instr in score_part.get_named_children ('score-instrument'):
431 name = instr.get_named_child ("instrument-name")
432 mapping[id] = name.get_text ()
434 self._id_instrument_name_dict = mapping
436 def get_instrument (self, id):
437 if not self._id_instrument_name_dict:
438 self.generate_id_instrument_dict()
440 instrument_name = self._id_instrument_name_dict.get (id)
442 return instrument_name
444 ly.stderr_write (_ ("Unable to find instrument for ID=%s\n") % id)
447 class Part_group (Music_xml_node):
449 class Score_part (Music_xml_node):
452 class Measure (Music_xml_node):
454 Music_xml_node.__init__ (self)
456 def is_implicit (self):
457 return hasattr (self, 'implicit') and self.implicit == 'yes'
458 def get_notes (self):
459 return self.get_typed_children (get_class (u'note'))
461 class Syllabic (Music_xml_node):
462 def continued (self):
463 text = self.get_text()
464 return (text == "begin") or (text == "middle")
465 class Text (Music_xml_node):
468 class Lyric (Music_xml_node):
469 def get_number (self):
470 if hasattr (self, 'number'):
475 def lyric_to_text (self):
477 syllabic = self.get_maybe_exist_typed_child (Syllabic)
479 continued = syllabic.continued ()
480 text = self.get_maybe_exist_typed_child (Text)
483 text = text.get_text()
484 # We need to convert soft hyphens to -, otherwise the ascii codec as well
485 # as lilypond will barf on that character
486 text = string.replace( text, u'\xad', '-' )
488 if text == "-" and continued:
490 elif text == "_" and continued:
492 elif continued and text:
493 return escape_ly_output_string (text) + " --"
497 return escape_ly_output_string (text)
501 class Musicxml_voice:
505 self._start_staff = None
507 self._has_lyrics = False
509 def add_element (self, e):
510 self._elements.append (e)
511 if (isinstance (e, Note)
512 and e.get_maybe_exist_typed_child (Staff)):
513 name = e.get_maybe_exist_typed_child (Staff).get_text ()
515 if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
516 self._start_staff = name
517 self._staves[name] = True
519 lyrics = e.get_typed_children (Lyric)
520 if not self._has_lyrics:
521 self.has_lyrics = len (lyrics) > 0
525 if (nr > 0) and not (nr in self._lyrics):
526 self._lyrics.append (nr)
528 def insert (self, idx, e):
529 self._elements.insert (idx, e)
531 def get_lyrics_numbers (self):
532 if (len (self._lyrics) == 0) and self._has_lyrics:
533 #only happens if none of the <lyric> tags has a number attribute
539 def graces_to_aftergraces (pending_graces):
540 for gr in pending_graces:
541 gr._when = gr._prev_when
542 gr._measure_position = gr._prev_measure_position
543 gr._after_grace = True
546 class Part (Music_xml_node):
548 Music_xml_node.__init__ (self)
550 self._staff_attributes_dict = {}
552 def get_part_list (self):
554 while n and n.get_name() != 'score-partwise':
557 return n.get_named_child ('part-list')
559 def interpret (self):
560 """Set durations and starting points."""
561 """The starting point of the very first note is 0!"""
563 part_list = self.get_part_list ()
566 factor = Rational (1)
568 attributes_object = None
569 measures = self.get_typed_children (Measure)
570 last_moment = Rational (-1)
571 last_measure_position = Rational (-1)
572 measure_position = Rational (0)
573 measure_start_moment = now
574 is_first_measure = True
575 previous_measure = None
576 # Graces at the end of a measure need to have their position set to the
580 # implicit measures are used for artificial measures, e.g. when
581 # a repeat bar line splits a bar into two halves. In this case,
582 # don't reset the measure position to 0. They are also used for
583 # upbeats (initial value of 0 fits these, too).
584 # Also, don't reset the measure position at the end of the loop,
585 # but rather when starting the next measure (since only then do we
586 # know if the next measure is implicit and continues that measure)
587 if not m.is_implicit ():
588 # Warn about possibly overfull measures and reset the position
589 if attributes_object and previous_measure and previous_measure.partial == 0:
590 length = attributes_object.get_measure_length ()
591 new_now = measure_start_moment + length
593 problem = 'incomplete'
596 ## only for verbose operation.
597 if problem <> 'incomplete' and previous_measure:
598 previous_measure.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
600 measure_start_moment = now
601 measure_position = Rational (0)
603 for n in m.get_all_children ():
604 # figured bass has a duration, but applies to the next note
605 # and should not change the current measure position!
606 if isinstance (n, FiguredBass):
607 n._divisions = factor.denominator ()
609 n._measure_position = measure_position
612 if isinstance (n, Hash_text):
616 if n.__class__ == Attributes:
617 n.set_attributes_from_previous (attributes_dict)
619 attributes_dict = n._dict.copy ()
620 attributes_object = n
622 factor = Rational (1,
623 int (attributes_dict.get ('divisions').get_text ()))
626 if (n.get_maybe_exist_typed_child (Duration)):
627 mxl_dur = n.get_maybe_exist_typed_child (Duration)
628 dur = mxl_dur.get_length () * factor
630 if n.get_name() == 'backup':
632 # reset all graces before the backup to after-graces:
633 graces_to_aftergraces (pending_graces)
635 if n.get_maybe_exist_typed_child (Grace):
638 rest = n.get_maybe_exist_typed_child (Rest)
640 and attributes_object
641 and attributes_object.get_measure_length () == dur):
643 rest._is_whole_measure = True
645 if (dur > Rational (0)
646 and n.get_maybe_exist_typed_child (Chord)):
648 measure_position = last_measure_position
651 n._measure_position = measure_position
653 # For all grace notes, store the previous note, in case need
654 # to turn the grace note into an after-grace later on!
655 if isinstance(n, Note) and n.is_grace ():
656 n._prev_when = last_moment
657 n._prev_measure_position = last_measure_position
658 # After-graces are placed at the same position as the previous note
659 if isinstance(n, Note) and n.is_after_grace ():
660 # TODO: We should do the same for grace notes at the end of
661 # a measure with no following note!!!
662 n._when = last_moment
663 n._measure_position = last_measure_position
664 elif isinstance(n, Note) and n.is_grace ():
665 pending_graces.append (n)
666 elif (dur > Rational (0)):
670 if dur > Rational (0):
672 last_measure_position = measure_position
674 measure_position += dur
675 elif dur < Rational (0):
676 # backup element, reset measure position
678 measure_position += dur
679 if measure_position < 0:
680 # backup went beyond the measure start => reset to 0
681 now -= measure_position
684 last_measure_position = measure_position
685 if n._name == 'note':
686 instrument = n.get_maybe_exist_named_child ('instrument')
688 n.instrument_name = part_list.get_instrument (instrument.id)
690 # reset all graces at the end of the measure to after-graces:
691 graces_to_aftergraces (pending_graces)
693 # Incomplete first measures are not padded, but registered as partial
695 is_first_measure = False
696 # upbeats are marked as implicit measures
697 if attributes_object and m.is_implicit ():
698 length = attributes_object.get_measure_length ()
699 measure_end = measure_start_moment + length
700 if measure_end <> now:
704 # modify attributes so that only those applying to the given staff remain
705 def extract_attributes_for_staff (part, attr, staff):
706 attributes = copy.copy (attr)
707 attributes._children = [];
708 attributes._dict = attr._dict.copy ()
709 attributes._original_tag = attr
710 # copy only the relevant children over for the given staff
711 for c in attr._children:
712 if (not (hasattr (c, 'number') and (c.number != staff)) and
713 not (isinstance (c, Hash_text))):
714 attributes._children.append (c)
715 if not attributes._children:
720 def extract_voices (part):
722 measures = part.get_typed_children (Measure)
726 elements.append (Partial (m.partial))
727 elements.extend (m.get_all_children ())
728 # make sure we know all voices already so that dynamics, clefs, etc.
729 # can be assigned to the correct voices
730 voice_to_staff_dict = {}
732 voice_id = n.get_maybe_exist_named_child (u'voice')
735 vid = voice_id.get_text ()
736 elif isinstance (n, Note):
739 staff_id = n.get_maybe_exist_named_child (u'staff')
742 sid = staff_id.get_text ()
745 if vid and not voices.has_key (vid):
746 voices[vid] = Musicxml_voice()
747 if vid and sid and not n.get_maybe_exist_typed_child (Grace):
748 if not voice_to_staff_dict.has_key (vid):
749 voice_to_staff_dict[vid] = sid
750 # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
751 # need to assign staff-assigned objects like clefs, times, etc. to
752 # all the correct voices. This will never work entirely correct due
753 # to staff-switches, but that's the best we can do!
754 staff_to_voice_dict = {}
755 for (v,s) in voice_to_staff_dict.items ():
756 if not staff_to_voice_dict.has_key (s):
757 staff_to_voice_dict[s] = [v]
759 staff_to_voice_dict[s].append (v)
763 assign_to_next_note = []
766 voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
768 id = voice_id.get_text ()
772 # We don't need backup/forward any more, since we have already
773 # assigned the correct onset times.
774 # TODO: Let Grouping through. Also: link, print, bokmark sound
775 if not (isinstance (n, Note) or isinstance (n, Attributes) or
776 isinstance (n, Direction) or isinstance (n, Partial) or
777 isinstance (n, Barline) or isinstance (n, Harmony) or
778 isinstance (n, FiguredBass) ):
781 if isinstance (n, Attributes) and not start_attr:
785 if isinstance (n, Attributes):
786 # assign these only to the voices they really belongs to!
787 for (s, vids) in staff_to_voice_dict.items ():
788 staff_attributes = part.extract_attributes_for_staff (n, s)
791 voices[v].add_element (staff_attributes)
794 if isinstance (n, Partial) or isinstance (n, Barline):
795 for v in voices.keys ():
796 voices[v].add_element (n)
799 if isinstance (n, Direction):
800 staff_id = n.get_maybe_exist_named_child (u'staff')
802 staff_id = staff_id.get_text ()
804 dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
806 dir_voices = voices.keys ()
808 voices[v].add_element (n)
811 if isinstance (n, Harmony) or isinstance (n, FiguredBass):
812 # store the harmony or figured bass element until we encounter
813 # the next note and assign it only to that one voice.
814 assign_to_next_note.append (n)
817 if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
821 for i in assign_to_next_note:
822 voices[id].add_element (i)
823 assign_to_next_note = []
824 voices[id].add_element (n)
826 # Assign all remaining elements from assign_to_next_note to the voice
827 # of the previous note:
828 for i in assign_to_next_note:
829 voices[id].add_element (i)
830 assign_to_next_note = []
833 for (s, vids) in staff_to_voice_dict.items ():
834 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
835 staff_attributes.read_self ()
836 part._staff_attributes_dict[s] = staff_attributes
838 voices[v].insert (0, staff_attributes)
839 voices[v]._elements[0].read_self()
841 part._voices = voices
843 def get_voices (self):
845 def get_staff_attributes (self):
846 return self._staff_attributes_dict
848 class Notations (Music_xml_node):
850 ts = self.get_named_children ('tied')
851 starts = [t for t in ts if t.type == 'start']
857 def get_tuplets (self):
858 return self.get_typed_children (Tuplet)
860 class Time_modification(Music_xml_node):
861 def get_fraction (self):
862 b = self.get_maybe_exist_named_child ('actual-notes')
863 a = self.get_maybe_exist_named_child ('normal-notes')
864 return (int(a.get_text ()), int (b.get_text ()))
866 def get_normal_type (self):
867 tuplet_type = self.get_maybe_exist_named_child ('normal-type')
869 dots = self.get_named_children ('normal-dot')
870 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
871 return (log , len (dots))
876 class Accidental (Music_xml_node):
878 Music_xml_node.__init__ (self)
879 self.editorial = False
880 self.cautionary = False
882 class Music_xml_spanner (Music_xml_node):
884 if hasattr (self, 'type'):
889 if hasattr (self, 'size'):
890 return string.atoi (self.size)
894 class Wedge (Music_xml_spanner):
897 class Tuplet (Music_xml_spanner):
898 def duration_info_from_tuplet_note (self, tuplet_note):
899 tuplet_type = tuplet_note.get_maybe_exist_named_child ('tuplet-type')
901 dots = tuplet_note.get_named_children ('tuplet-dot')
902 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
903 return (log, len (dots))
907 # Return tuplet note type as (log, dots)
908 def get_normal_type (self):
909 tuplet = self.get_maybe_exist_named_child ('tuplet-normal')
911 return self.duration_info_from_tuplet_note (tuplet)
915 def get_actual_type (self):
916 tuplet = self.get_maybe_exist_named_child ('tuplet-actual')
918 return self.duration_info_from_tuplet_note (tuplet)
922 def get_tuplet_note_count (self, tuplet_note):
924 tuplet_nr = tuplet_note.get_maybe_exist_named_child ('tuplet-number')
926 return int (tuplet_nr.get_text ())
928 def get_normal_nr (self):
929 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-normal'))
930 def get_actual_nr (self):
931 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-actual'))
933 class Bracket (Music_xml_spanner):
936 class Dashes (Music_xml_spanner):
939 class Slur (Music_xml_spanner):
943 class Beam (Music_xml_spanner):
945 return self.get_text ()
946 def is_primary (self):
947 return self.number == "1"
949 class Wavy_line (Music_xml_spanner):
952 class Pedal (Music_xml_spanner):
955 class Glissando (Music_xml_spanner):
958 class Slide (Music_xml_spanner):
961 class Octave_shift (Music_xml_spanner):
962 # default is 8 for the octave-shift!
964 if hasattr (self, 'size'):
965 return string.atoi (self.size)
969 class Chord (Music_xml_node):
972 class Dot (Music_xml_node):
975 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
976 # for the inner <rest> element, not the whole rest block.
977 class Rest (Music_xml_node):
979 Music_xml_node.__init__ (self)
980 self._is_whole_measure = False
981 def is_whole_measure (self):
982 return self._is_whole_measure
984 ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
986 step = ch.get_text ().strip ()
990 def get_octave (self):
991 ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
993 step = ch.get_text ().strip ()
998 class Type (Music_xml_node):
1000 class Grace (Music_xml_node):
1002 class Staff (Music_xml_node):
1005 class Direction (Music_xml_node):
1007 class DirType (Music_xml_node):
1010 class Bend (Music_xml_node):
1011 def bend_alter (self):
1012 alter = self.get_maybe_exist_named_child ('bend-alter')
1014 return alter.get_text()
1018 class Words (Music_xml_node):
1021 class Harmony (Music_xml_node):
1024 class ChordPitch (Music_xml_node):
1025 def step_class_name (self):
1027 def alter_class_name (self):
1028 return u'root-alter'
1029 def get_step (self):
1030 ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
1031 return ch.get_text ().strip ()
1032 def get_alteration (self):
1033 ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
1036 alter = int (ch.get_text ().strip ())
1039 class Root (ChordPitch):
1042 class Bass (ChordPitch):
1043 def step_class_name (self):
1045 def alter_class_name (self):
1046 return u'bass-alter'
1048 class ChordModification (Music_xml_node):
1049 def get_type (self):
1050 ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
1051 return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
1052 def get_value (self):
1053 ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
1056 value = int (ch.get_text ().strip ())
1058 def get_alter (self):
1059 ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
1062 value = int (ch.get_text ().strip ())
1066 class Frame (Music_xml_node):
1067 def get_frets (self):
1068 return self.get_named_child_value_number ('frame-frets', 4)
1069 def get_strings (self):
1070 return self.get_named_child_value_number ('frame-strings', 6)
1071 def get_first_fret (self):
1072 return self.get_named_child_value_number ('first-fret', 1)
1074 class Frame_Note (Music_xml_node):
1075 def get_string (self):
1076 return self.get_named_child_value_number ('string', 1)
1077 def get_fret (self):
1078 return self.get_named_child_value_number ('fret', 0)
1079 def get_fingering (self):
1080 return self.get_named_child_value_number ('fingering', -1)
1081 def get_barre (self):
1082 n = self.get_maybe_exist_named_child ('barre')
1084 return getattr (n, 'type', '')
1088 class FiguredBass (Music_xml_node):
1091 class BeatUnit (Music_xml_node):
1094 class BeatUnitDot (Music_xml_node):
1097 class PerMinute (Music_xml_node):
1102 ## need this, not all classes are instantiated
1103 ## for every input file. Only add those classes, that are either directly
1104 ## used by class name or extend Music_xml_node in some way!
1106 '#comment': Hash_comment,
1108 'accidental': Accidental,
1109 'attributes': Attributes,
1111 'bar-style': BarStyle,
1114 'beat-unit': BeatUnit,
1115 'beat-unit-dot': BeatUnitDot,
1117 'bracket' : Bracket,
1120 'degree' : ChordModification,
1122 'direction': Direction,
1123 'direction-type': DirType,
1124 'duration': Duration,
1126 'frame-note': Frame_Note,
1127 'figured-bass': FiguredBass,
1128 'glissando': Glissando,
1131 'identification': Identification,
1134 'notations': Notations,
1136 'octave-shift': Octave_shift,
1138 'part-group': Part_group,
1139 'part-list': Part_list,
1141 'per-minute': PerMinute,
1145 'score-part': Score_part,
1149 'syllabic': Syllabic,
1151 'time-modification': Time_modification,
1154 'unpitched': Unpitched,
1155 'wavy-line': Wavy_line,
1161 def name2class_name (name):
1162 name = name.replace ('-', '_')
1163 name = name.replace ('#', 'hash_')
1164 name = name[0].upper() + name[1:].lower()
1168 def get_class (name):
1169 classname = class_dict.get (name)
1173 class_name = name2class_name (name)
1174 klass = new.classobj (class_name, (Music_xml_node,) , {})
1175 class_dict[name] = klass
1178 def lxml_demarshal_node (node):
1181 # Ignore comment nodes, which are also returned by the etree parser!
1182 if name is None or node.__class__.__name__ == "_Comment":
1184 klass = get_class (name)
1187 py_node._original = node
1188 py_node._name = name
1189 py_node._data = node.text
1190 py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1191 py_node._children = filter (lambda x: x, py_node._children)
1193 for c in py_node._children:
1196 for (k, v) in node.items ():
1197 py_node.__dict__[k] = v
1198 py_node._attribute_dict[k] = v
1202 def minidom_demarshal_node (node):
1203 name = node.nodeName
1205 klass = get_class (name)
1207 py_node._name = name
1208 py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1209 for c in py_node._children:
1213 for (nm, value) in node.attributes.items ():
1214 py_node.__dict__[nm] = value
1215 py_node._attribute_dict[nm] = value
1217 py_node._data = None
1218 if node.nodeType == node.TEXT_NODE and node.data:
1219 py_node._data = node.data
1221 py_node._original = node
1225 if __name__ == '__main__':
1228 tree = lxml.etree.parse ('beethoven.xml')
1229 mxl_tree = lxml_demarshal_node (tree.getroot ())
1230 ks = class_dict.keys ()
1232 print '\n'.join (ks)