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
297 self._time_signature_cache = None
300 cn = self._parent.get_typed_children (self.__class__)
301 if self._original_tag:
302 return cn[0] == self._original_tag
306 def set_attributes_from_previous (self, dict):
307 self._dict.update (dict)
309 def read_self (self):
310 for c in self.get_all_children ():
311 self._dict[c.get_name()] = c
313 def get_named_attribute (self, name):
314 return self._dict.get (name)
316 def single_time_sig_to_fraction (self, sig):
322 return Rational (n, sig[-1])
324 def get_measure_length (self):
325 sig = self.get_time_signature ()
326 if not sig or len (sig) == 0:
328 if isinstance (sig[0], list):
329 # Complex compound time signature
332 l += self.single_time_sig_to_fraction (i)
335 # Simple (maybe compound) time signature of the form (beat, ..., type)
336 return self.single_time_sig_to_fraction (sig)
339 def get_time_signature (self):
340 "Return time sig as a (beat, beat-type) tuple. For compound signatures,"
341 "return either (beat, beat,..., beat-type) or ((beat,..., type), "
342 "(beat,..., type), ...)."
343 if self._time_signature_cache:
344 return self._time_signature_cache
347 mxl = self.get_named_attribute ('time')
351 if mxl.get_maybe_exist_named_child ('senza-misura'):
352 # TODO: Handle pieces without a time signature!
353 error (_ ("Senza-misura time signatures are not yet supported!"))
358 for i in mxl.get_all_children ():
359 if isinstance (i, Beats):
360 beats = string.split (i.get_text ().strip (), "+")
361 current_sig = [int (j) for j in beats]
362 elif isinstance (i, BeatType):
363 current_sig.append (int (i.get_text ()))
364 signature.append (current_sig)
366 if isinstance (signature[0], list) and len (signature) == 1:
367 signature = signature[0]
368 self._time_signature_cache = signature
370 except (KeyError, ValueError):
371 self.message (_ ("Unable to interpret time signature! Falling back to 4/4."))
374 # returns clef information in the form ("cleftype", position, octave-shift)
375 def get_clef_information (self):
376 clefinfo = ['G', 2, 0]
377 mxl = self.get_named_attribute ('clef')
380 sign = mxl.get_maybe_exist_named_child ('sign')
382 clefinfo[0] = sign.get_text()
383 line = mxl.get_maybe_exist_named_child ('line')
385 clefinfo[1] = string.atoi (line.get_text ())
386 octave = mxl.get_maybe_exist_named_child ('clef-octave-change')
388 clefinfo[2] = string.atoi (octave.get_text ())
391 def get_key_signature (self):
392 "return (fifths, mode) tuple"
394 key = self.get_named_attribute ('key')
395 mode_node = key.get_maybe_exist_named_child ('mode')
398 mode = mode_node.get_text ()
399 if not mode or mode == '':
402 fifths = int (key.get_maybe_exist_named_child ('fifths').get_text ())
403 return (fifths, mode)
405 def get_transposition (self):
406 return self.get_named_attribute ('transpose')
410 class Barline (Measure_element):
412 class BarStyle (Music_xml_node):
414 class Partial (Measure_element):
415 def __init__ (self, partial):
416 Measure_element.__init__ (self)
417 self.partial = partial
419 class Note (Measure_element):
421 Measure_element.__init__ (self)
422 self.instrument_name = ''
423 self._after_grace = False
425 return self.get_maybe_exist_named_child (u'grace')
426 def is_after_grace (self):
427 if not self.is_grace():
429 gr = self.get_maybe_exist_typed_child (Grace)
430 return self._after_grace or hasattr (gr, 'steal-time-previous');
432 def get_duration_log (self):
433 ch = self.get_maybe_exist_named_child (u'type')
436 log = ch.get_text ().strip()
437 return musicxml_duration_to_log (log)
438 elif self.get_maybe_exist_named_child (u'grace'):
439 # FIXME: is it ok to default to eight note for grace notes?
444 def get_duration_info (self):
445 log = self.get_duration_log ()
447 dots = len (self.get_typed_children (Dot))
452 def get_factor (self):
455 def get_pitches (self):
456 return self.get_typed_children (get_class (u'pitch'))
458 class Part_list (Music_xml_node):
460 Music_xml_node.__init__ (self)
461 self._id_instrument_name_dict = {}
463 def generate_id_instrument_dict (self):
465 ## not empty to make sure this happens only once.
467 for score_part in self.get_named_children ('score-part'):
468 for instr in score_part.get_named_children ('score-instrument'):
470 name = instr.get_named_child ("instrument-name")
471 mapping[id] = name.get_text ()
473 self._id_instrument_name_dict = mapping
475 def get_instrument (self, id):
476 if not self._id_instrument_name_dict:
477 self.generate_id_instrument_dict()
479 instrument_name = self._id_instrument_name_dict.get (id)
481 return instrument_name
483 ly.stderr_write (_ ("Unable to find instrument for ID=%s\n") % id)
486 class Part_group (Music_xml_node):
488 class Score_part (Music_xml_node):
491 class Measure (Music_xml_node):
493 Music_xml_node.__init__ (self)
495 def is_implicit (self):
496 return hasattr (self, 'implicit') and self.implicit == 'yes'
497 def get_notes (self):
498 return self.get_typed_children (get_class (u'note'))
500 class Syllabic (Music_xml_node):
501 def continued (self):
502 text = self.get_text()
503 return (text == "begin") or (text == "middle")
504 class Elision (Music_xml_node):
506 class Text (Music_xml_node):
509 class Lyric (Music_xml_node):
510 def get_number (self):
511 if hasattr (self, 'number'):
516 class Musicxml_voice:
520 self._start_staff = None
522 self._has_lyrics = False
524 def add_element (self, e):
525 self._elements.append (e)
526 if (isinstance (e, Note)
527 and e.get_maybe_exist_typed_child (Staff)):
528 name = e.get_maybe_exist_typed_child (Staff).get_text ()
530 if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
531 self._start_staff = name
532 self._staves[name] = True
534 lyrics = e.get_typed_children (Lyric)
535 if not self._has_lyrics:
536 self.has_lyrics = len (lyrics) > 0
540 if (nr > 0) and not (nr in self._lyrics):
541 self._lyrics.append (nr)
543 def insert (self, idx, e):
544 self._elements.insert (idx, e)
546 def get_lyrics_numbers (self):
547 if (len (self._lyrics) == 0) and self._has_lyrics:
548 #only happens if none of the <lyric> tags has a number attribute
554 def graces_to_aftergraces (pending_graces):
555 for gr in pending_graces:
556 gr._when = gr._prev_when
557 gr._measure_position = gr._prev_measure_position
558 gr._after_grace = True
561 class Part (Music_xml_node):
563 Music_xml_node.__init__ (self)
565 self._staff_attributes_dict = {}
567 def get_part_list (self):
569 while n and n.get_name() != 'score-partwise':
572 return n.get_named_child ('part-list')
574 def interpret (self):
575 """Set durations and starting points."""
576 """The starting point of the very first note is 0!"""
578 part_list = self.get_part_list ()
581 factor = Rational (1)
583 attributes_object = None
584 measures = self.get_typed_children (Measure)
585 last_moment = Rational (-1)
586 last_measure_position = Rational (-1)
587 measure_position = Rational (0)
588 measure_start_moment = now
589 is_first_measure = True
590 previous_measure = None
591 # Graces at the end of a measure need to have their position set to the
595 # implicit measures are used for artificial measures, e.g. when
596 # a repeat bar line splits a bar into two halves. In this case,
597 # don't reset the measure position to 0. They are also used for
598 # upbeats (initial value of 0 fits these, too).
599 # Also, don't reset the measure position at the end of the loop,
600 # but rather when starting the next measure (since only then do we
601 # know if the next measure is implicit and continues that measure)
602 if not m.is_implicit ():
603 # Warn about possibly overfull measures and reset the position
604 if attributes_object and previous_measure and previous_measure.partial == 0:
605 length = attributes_object.get_measure_length ()
606 new_now = measure_start_moment + length
608 problem = 'incomplete'
611 ## only for verbose operation.
612 if problem <> 'incomplete' and previous_measure:
613 previous_measure.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
615 measure_start_moment = now
616 measure_position = Rational (0)
618 for n in m.get_all_children ():
619 # figured bass has a duration, but applies to the next note
620 # and should not change the current measure position!
621 if isinstance (n, FiguredBass):
622 n._divisions = factor.denominator ()
624 n._measure_position = measure_position
627 if isinstance (n, Hash_text):
631 if n.__class__ == Attributes:
632 n.set_attributes_from_previous (attributes_dict)
634 attributes_dict = n._dict.copy ()
635 attributes_object = n
637 factor = Rational (1,
638 int (attributes_dict.get ('divisions').get_text ()))
641 if (n.get_maybe_exist_typed_child (Duration)):
642 mxl_dur = n.get_maybe_exist_typed_child (Duration)
643 dur = mxl_dur.get_length () * factor
645 if n.get_name() == 'backup':
647 # reset all graces before the backup to after-graces:
648 graces_to_aftergraces (pending_graces)
650 if n.get_maybe_exist_typed_child (Grace):
653 rest = n.get_maybe_exist_typed_child (Rest)
655 and attributes_object
656 and attributes_object.get_measure_length () == dur):
658 rest._is_whole_measure = True
660 if (dur > Rational (0)
661 and n.get_maybe_exist_typed_child (Chord)):
663 measure_position = last_measure_position
666 n._measure_position = measure_position
668 # For all grace notes, store the previous note, in case need
669 # to turn the grace note into an after-grace later on!
670 if isinstance(n, Note) and n.is_grace ():
671 n._prev_when = last_moment
672 n._prev_measure_position = last_measure_position
673 # After-graces are placed at the same position as the previous note
674 if isinstance(n, Note) and n.is_after_grace ():
675 # TODO: We should do the same for grace notes at the end of
676 # a measure with no following note!!!
677 n._when = last_moment
678 n._measure_position = last_measure_position
679 elif isinstance(n, Note) and n.is_grace ():
680 pending_graces.append (n)
681 elif (dur > Rational (0)):
685 if dur > Rational (0):
687 last_measure_position = measure_position
689 measure_position += dur
690 elif dur < Rational (0):
691 # backup element, reset measure position
693 measure_position += dur
694 if measure_position < 0:
695 # backup went beyond the measure start => reset to 0
696 now -= measure_position
699 last_measure_position = measure_position
700 if n._name == 'note':
701 instrument = n.get_maybe_exist_named_child ('instrument')
703 n.instrument_name = part_list.get_instrument (instrument.id)
705 # reset all graces at the end of the measure to after-graces:
706 graces_to_aftergraces (pending_graces)
708 # Incomplete first measures are not padded, but registered as partial
710 is_first_measure = False
711 # upbeats are marked as implicit measures
712 if attributes_object and m.is_implicit ():
713 length = attributes_object.get_measure_length ()
714 measure_end = measure_start_moment + length
715 if measure_end <> now:
719 # modify attributes so that only those applying to the given staff remain
720 def extract_attributes_for_staff (part, attr, staff):
721 attributes = copy.copy (attr)
722 attributes._children = [];
723 attributes._dict = attr._dict.copy ()
724 attributes._original_tag = attr
725 # copy only the relevant children over for the given staff
726 for c in attr._children:
727 if (not (hasattr (c, 'number') and (c.number != staff)) and
728 not (isinstance (c, Hash_text))):
729 attributes._children.append (c)
730 if not attributes._children:
735 def extract_voices (part):
737 measures = part.get_typed_children (Measure)
741 elements.append (Partial (m.partial))
742 elements.extend (m.get_all_children ())
743 # make sure we know all voices already so that dynamics, clefs, etc.
744 # can be assigned to the correct voices
745 voice_to_staff_dict = {}
747 voice_id = n.get_maybe_exist_named_child (u'voice')
750 vid = voice_id.get_text ()
751 elif isinstance (n, Note):
754 staff_id = n.get_maybe_exist_named_child (u'staff')
757 sid = staff_id.get_text ()
760 if vid and not voices.has_key (vid):
761 voices[vid] = Musicxml_voice()
762 if vid and sid and not n.get_maybe_exist_typed_child (Grace):
763 if not voice_to_staff_dict.has_key (vid):
764 voice_to_staff_dict[vid] = sid
765 # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
766 # need to assign staff-assigned objects like clefs, times, etc. to
767 # all the correct voices. This will never work entirely correct due
768 # to staff-switches, but that's the best we can do!
769 staff_to_voice_dict = {}
770 for (v,s) in voice_to_staff_dict.items ():
771 if not staff_to_voice_dict.has_key (s):
772 staff_to_voice_dict[s] = [v]
774 staff_to_voice_dict[s].append (v)
778 assign_to_next_note = []
781 voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
783 id = voice_id.get_text ()
787 # We don't need backup/forward any more, since we have already
788 # assigned the correct onset times.
789 # TODO: Let Grouping through. Also: link, print, bokmark sound
790 if not (isinstance (n, Note) or isinstance (n, Attributes) or
791 isinstance (n, Direction) or isinstance (n, Partial) or
792 isinstance (n, Barline) or isinstance (n, Harmony) or
793 isinstance (n, FiguredBass) ):
796 if isinstance (n, Attributes) and not start_attr:
800 if isinstance (n, Attributes):
801 # assign these only to the voices they really belongs to!
802 for (s, vids) in staff_to_voice_dict.items ():
803 staff_attributes = part.extract_attributes_for_staff (n, s)
806 voices[v].add_element (staff_attributes)
809 if isinstance (n, Partial) or isinstance (n, Barline):
810 for v in voices.keys ():
811 voices[v].add_element (n)
814 if isinstance (n, Direction):
815 staff_id = n.get_maybe_exist_named_child (u'staff')
817 staff_id = staff_id.get_text ()
819 dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
821 dir_voices = voices.keys ()
823 voices[v].add_element (n)
826 if isinstance (n, Harmony) or isinstance (n, FiguredBass):
827 # store the harmony or figured bass element until we encounter
828 # the next note and assign it only to that one voice.
829 assign_to_next_note.append (n)
832 if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
836 for i in assign_to_next_note:
837 voices[id].add_element (i)
838 assign_to_next_note = []
839 voices[id].add_element (n)
841 # Assign all remaining elements from assign_to_next_note to the voice
842 # of the previous note:
843 for i in assign_to_next_note:
844 voices[id].add_element (i)
845 assign_to_next_note = []
848 for (s, vids) in staff_to_voice_dict.items ():
849 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
850 staff_attributes.read_self ()
851 part._staff_attributes_dict[s] = staff_attributes
853 voices[v].insert (0, staff_attributes)
854 voices[v]._elements[0].read_self()
856 part._voices = voices
858 def get_voices (self):
860 def get_staff_attributes (self):
861 return self._staff_attributes_dict
863 class Notations (Music_xml_node):
865 ts = self.get_named_children ('tied')
866 starts = [t for t in ts if t.type == 'start']
872 def get_tuplets (self):
873 return self.get_typed_children (Tuplet)
875 class Time_modification(Music_xml_node):
876 def get_fraction (self):
877 b = self.get_maybe_exist_named_child ('actual-notes')
878 a = self.get_maybe_exist_named_child ('normal-notes')
879 return (int(a.get_text ()), int (b.get_text ()))
881 def get_normal_type (self):
882 tuplet_type = self.get_maybe_exist_named_child ('normal-type')
884 dots = self.get_named_children ('normal-dot')
885 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
886 return (log , len (dots))
891 class Accidental (Music_xml_node):
893 Music_xml_node.__init__ (self)
894 self.editorial = False
895 self.cautionary = False
897 class Music_xml_spanner (Music_xml_node):
899 if hasattr (self, 'type'):
904 if hasattr (self, 'size'):
905 return string.atoi (self.size)
909 class Wedge (Music_xml_spanner):
912 class Tuplet (Music_xml_spanner):
913 def duration_info_from_tuplet_note (self, tuplet_note):
914 tuplet_type = tuplet_note.get_maybe_exist_named_child ('tuplet-type')
916 dots = tuplet_note.get_named_children ('tuplet-dot')
917 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
918 return (log, len (dots))
922 # Return tuplet note type as (log, dots)
923 def get_normal_type (self):
924 tuplet = self.get_maybe_exist_named_child ('tuplet-normal')
926 return self.duration_info_from_tuplet_note (tuplet)
930 def get_actual_type (self):
931 tuplet = self.get_maybe_exist_named_child ('tuplet-actual')
933 return self.duration_info_from_tuplet_note (tuplet)
937 def get_tuplet_note_count (self, tuplet_note):
939 tuplet_nr = tuplet_note.get_maybe_exist_named_child ('tuplet-number')
941 return int (tuplet_nr.get_text ())
943 def get_normal_nr (self):
944 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-normal'))
945 def get_actual_nr (self):
946 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-actual'))
948 class Bracket (Music_xml_spanner):
951 class Dashes (Music_xml_spanner):
954 class Slur (Music_xml_spanner):
958 class Beam (Music_xml_spanner):
960 return self.get_text ()
961 def is_primary (self):
962 return self.number == "1"
964 class Wavy_line (Music_xml_spanner):
967 class Pedal (Music_xml_spanner):
970 class Glissando (Music_xml_spanner):
973 class Slide (Music_xml_spanner):
976 class Octave_shift (Music_xml_spanner):
977 # default is 8 for the octave-shift!
979 if hasattr (self, 'size'):
980 return string.atoi (self.size)
984 class Chord (Music_xml_node):
987 class Dot (Music_xml_node):
990 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
991 # for the inner <rest> element, not the whole rest block.
992 class Rest (Music_xml_node):
994 Music_xml_node.__init__ (self)
995 self._is_whole_measure = False
996 def is_whole_measure (self):
997 return self._is_whole_measure
999 ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
1001 step = ch.get_text ().strip ()
1005 def get_octave (self):
1006 ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
1008 step = ch.get_text ().strip ()
1013 class Type (Music_xml_node):
1015 class Grace (Music_xml_node):
1017 class Staff (Music_xml_node):
1020 class Direction (Music_xml_node):
1022 class DirType (Music_xml_node):
1025 class Bend (Music_xml_node):
1026 def bend_alter (self):
1027 alter = self.get_maybe_exist_named_child ('bend-alter')
1029 return alter.get_text()
1033 class Words (Music_xml_node):
1036 class Harmony (Music_xml_node):
1039 class ChordPitch (Music_xml_node):
1040 def step_class_name (self):
1042 def alter_class_name (self):
1043 return u'root-alter'
1044 def get_step (self):
1045 ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
1046 return ch.get_text ().strip ()
1047 def get_alteration (self):
1048 ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
1051 alter = int (ch.get_text ().strip ())
1054 class Root (ChordPitch):
1057 class Bass (ChordPitch):
1058 def step_class_name (self):
1060 def alter_class_name (self):
1061 return u'bass-alter'
1063 class ChordModification (Music_xml_node):
1064 def get_type (self):
1065 ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
1066 return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
1067 def get_value (self):
1068 ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
1071 value = int (ch.get_text ().strip ())
1073 def get_alter (self):
1074 ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
1077 value = int (ch.get_text ().strip ())
1081 class Frame (Music_xml_node):
1082 def get_frets (self):
1083 return self.get_named_child_value_number ('frame-frets', 4)
1084 def get_strings (self):
1085 return self.get_named_child_value_number ('frame-strings', 6)
1086 def get_first_fret (self):
1087 return self.get_named_child_value_number ('first-fret', 1)
1089 class Frame_Note (Music_xml_node):
1090 def get_string (self):
1091 return self.get_named_child_value_number ('string', 1)
1092 def get_fret (self):
1093 return self.get_named_child_value_number ('fret', 0)
1094 def get_fingering (self):
1095 return self.get_named_child_value_number ('fingering', -1)
1096 def get_barre (self):
1097 n = self.get_maybe_exist_named_child ('barre')
1099 return getattr (n, 'type', '')
1103 class FiguredBass (Music_xml_node):
1106 class Beats (Music_xml_node):
1109 class BeatType (Music_xml_node):
1112 class BeatUnit (Music_xml_node):
1115 class BeatUnitDot (Music_xml_node):
1118 class PerMinute (Music_xml_node):
1123 ## need this, not all classes are instantiated
1124 ## for every input file. Only add those classes, that are either directly
1125 ## used by class name or extend Music_xml_node in some way!
1127 '#comment': Hash_comment,
1129 'accidental': Accidental,
1130 'attributes': Attributes,
1132 'bar-style': BarStyle,
1136 'beat-type': BeatType,
1137 'beat-unit': BeatUnit,
1138 'beat-unit-dot': BeatUnitDot,
1140 'bracket' : Bracket,
1143 'degree' : ChordModification,
1145 'direction': Direction,
1146 'direction-type': DirType,
1147 'duration': Duration,
1150 'frame-note': Frame_Note,
1151 'figured-bass': FiguredBass,
1152 'glissando': Glissando,
1155 'identification': Identification,
1158 'notations': Notations,
1160 'octave-shift': Octave_shift,
1162 'part-group': Part_group,
1163 'part-list': Part_list,
1165 'per-minute': PerMinute,
1169 'score-part': Score_part,
1173 'syllabic': Syllabic,
1175 'time-modification': Time_modification,
1178 'unpitched': Unpitched,
1179 'wavy-line': Wavy_line,
1185 def name2class_name (name):
1186 name = name.replace ('-', '_')
1187 name = name.replace ('#', 'hash_')
1188 name = name[0].upper() + name[1:].lower()
1192 def get_class (name):
1193 classname = class_dict.get (name)
1197 class_name = name2class_name (name)
1198 klass = new.classobj (class_name, (Music_xml_node,) , {})
1199 class_dict[name] = klass
1202 def lxml_demarshal_node (node):
1205 # Ignore comment nodes, which are also returned by the etree parser!
1206 if name is None or node.__class__.__name__ == "_Comment":
1208 klass = get_class (name)
1211 py_node._original = node
1212 py_node._name = name
1213 py_node._data = node.text
1214 py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1215 py_node._children = filter (lambda x: x, py_node._children)
1217 for c in py_node._children:
1220 for (k, v) in node.items ():
1221 py_node.__dict__[k] = v
1222 py_node._attribute_dict[k] = v
1226 def minidom_demarshal_node (node):
1227 name = node.nodeName
1229 klass = get_class (name)
1231 py_node._name = name
1232 py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1233 for c in py_node._children:
1237 for (nm, value) in node.attributes.items ():
1238 py_node.__dict__[nm] = value
1239 py_node._attribute_dict[nm] = value
1241 py_node._data = None
1242 if node.nodeType == node.TEXT_NODE and node.data:
1243 py_node._data = node.data
1245 py_node._original = node
1249 if __name__ == '__main__':
1252 tree = lxml.etree.parse ('beethoven.xml')
1253 mxl_tree = lxml_demarshal_node (tree.getroot ())
1254 ks = class_dict.keys ()
1256 print '\n'.join (ks)