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)
38 def interpret_alter_element (alter_elm):
41 val = eval(alter_elm.get_text ())
42 if type (val) in (int, float):
52 self._name = 'xml_node'
54 self._attribute_dict = {}
56 def get_parent (self):
60 return self._parent.get_typed_children (self.__class__)[0] == self
71 if not self._children:
74 return ''.join ([c.get_text () for c in self._children])
76 def message (self, msg):
77 ly.stderr_write (msg+'\n')
81 sys.stderr.write (' In: <%s %s>\n' % (p._name, ' '.join (['%s=%s' % item for item in p._attribute_dict.items ()])))
84 def dump (self, indent = ''):
85 sys.stderr.write ('%s<%s%s>' % (indent, self._name, ''.join ([' %s=%s' % item for item in self._attribute_dict.items ()])))
86 non_text_children = [c for c in self._children if not isinstance (c, Hash_text)]
88 sys.stderr.write ('\n')
89 for c in self._children:
92 sys.stderr.write (indent)
93 sys.stderr.write ('</%s>\n' % self._name)
96 def get_typed_children (self, klass):
100 return [c for c in self._children if isinstance(c, klass)]
102 def get_named_children (self, nm):
103 return self.get_typed_children (get_class (nm))
105 def get_named_child (self, nm):
106 return self.get_maybe_exist_named_child (nm)
108 def get_children (self, predicate):
109 return [c for c in self._children if predicate(c)]
111 def get_all_children (self):
112 return self._children
114 def get_maybe_exist_named_child (self, name):
115 return self.get_maybe_exist_typed_child (get_class (name))
117 def get_maybe_exist_typed_child (self, klass):
118 cn = self.get_typed_children (klass)
124 raise "More than 1 child", klass
126 def get_unique_typed_child (self, klass):
127 cn = self.get_typed_children(klass)
129 sys.stderr.write (self.__dict__ + '\n')
130 raise 'Child is not unique for', (klass, 'found', cn)
134 def get_named_child_value_number (self, name, default):
135 n = self.get_maybe_exist_named_child (name)
137 return string.atoi (n.get_text())
142 class Music_xml_node (Xml_node):
144 Xml_node.__init__ (self)
145 self.duration = Rational (0)
146 self.start = Rational (0)
148 class Work (Xml_node):
149 def get_work_information (self, tag):
150 wt = self.get_maybe_exist_named_child (tag)
152 return wt.get_text ()
156 def get_work_title (self):
157 return self.get_work_information ('work-title')
158 def get_work_number (self):
159 return self.get_work_information ('work-number')
161 return self.get_work_information ('opus')
163 class Identification (Xml_node):
164 def get_rights (self):
165 rights = self.get_named_children ('rights')
168 ret.append (r.get_text ())
169 return string.join (ret, "\n")
171 def get_creator (self, type):
172 creators = self.get_named_children ('creator')
173 # return the first creator tag that has the particular type
175 if hasattr (i, 'type') and i.type == type:
179 def get_composer (self):
180 c = self.get_creator ('composer')
183 creators = self.get_named_children ('creator')
184 # return the first creator tag that has no type at all
186 if not hasattr (i, 'type'):
189 def get_arranger (self):
190 return self.get_creator ('arranger')
191 def get_editor (self):
192 return self.get_creator ('editor')
194 v = self.get_creator ('lyricist')
197 v = self.get_creator ('poet')
200 def get_encoding_information (self, type):
201 enc = self.get_named_children ('encoding')
203 children = enc[0].get_named_children (type)
205 return children[0].get_text ()
209 def get_encoding_software (self):
210 return self.get_encoding_information ('software')
211 def get_encoding_date (self):
212 return self.get_encoding_information ('encoding-date')
213 def get_encoding_person (self):
214 return self.get_encoding_information ('encoder')
215 def get_encoding_description (self):
216 return self.get_encoding_information ('encoding-description')
218 def get_encoding_software_list (self):
219 enc = self.get_named_children ('encoding')
222 softwares = e.get_named_children ('software')
224 software.append (s.get_text ())
227 def get_file_description (self):
228 misc = self.get_named_children ('miscellaneous')
230 misc_fields = m.get_named_children ('miscellaneous-field')
231 for mf in misc_fields:
232 if hasattr (mf, 'name') and mf.name == 'description':
233 return mf.get_text ()
238 class Duration (Music_xml_node):
239 def get_length (self):
240 dur = int (self.get_text ()) * Rational (1,4)
243 class Hash_comment (Music_xml_node):
245 class Hash_text (Music_xml_node):
246 def dump (self, indent = ''):
247 sys.stderr.write ('%s' % string.strip (self._data))
249 class Pitch (Music_xml_node):
251 ch = self.get_unique_typed_child (get_class (u'step'))
252 step = ch.get_text ().strip ()
254 def get_octave (self):
255 ch = self.get_unique_typed_child (get_class (u'octave'))
257 step = ch.get_text ().strip ()
260 def get_alteration (self):
261 ch = self.get_maybe_exist_typed_child (get_class (u'alter'))
262 return interpret_alter_element (ch)
264 class Unpitched (Music_xml_node):
266 ch = self.get_unique_typed_child (get_class (u'display-step'))
267 step = ch.get_text ().strip ()
270 def get_octave (self):
271 ch = self.get_unique_typed_child (get_class (u'display-octave'))
274 octave = ch.get_text ().strip ()
279 class Measure_element (Music_xml_node):
280 def get_voice_id (self):
281 voice_id = self.get_maybe_exist_named_child ('voice')
283 return voice_id.get_text ()
288 # Look at all measure elements (previously we had self.__class__, which
289 # only looked at objects of the same type!
290 cn = self._parent.get_typed_children (Measure_element)
291 # But only look at the correct voice; But include Attributes, too, which
292 # are not tied to any particular voice
293 cn = [c for c in cn if (c.get_voice_id () == self.get_voice_id ()) or isinstance (c, Attributes)]
296 class Attributes (Measure_element):
298 Measure_element.__init__ (self)
300 self._original_tag = None
301 self._time_signature_cache = None
304 cn = self._parent.get_typed_children (self.__class__)
305 if self._original_tag:
306 return cn[0] == self._original_tag
310 def set_attributes_from_previous (self, dict):
311 self._dict.update (dict)
313 def read_self (self):
314 for c in self.get_all_children ():
315 self._dict[c.get_name()] = c
317 def get_named_attribute (self, name):
318 return self._dict.get (name)
320 def single_time_sig_to_fraction (self, sig):
326 return Rational (n, sig[-1])
328 def get_measure_length (self):
329 sig = self.get_time_signature ()
330 if not sig or len (sig) == 0:
332 if isinstance (sig[0], list):
333 # Complex compound time signature
336 l += self.single_time_sig_to_fraction (i)
339 # Simple (maybe compound) time signature of the form (beat, ..., type)
340 return self.single_time_sig_to_fraction (sig)
343 def get_time_signature (self):
344 "Return time sig as a (beat, beat-type) tuple. For compound signatures,"
345 "return either (beat, beat,..., beat-type) or ((beat,..., type), "
346 "(beat,..., type), ...)."
347 if self._time_signature_cache:
348 return self._time_signature_cache
351 mxl = self.get_named_attribute ('time')
355 if mxl.get_maybe_exist_named_child ('senza-misura'):
356 # TODO: Handle pieces without a time signature!
357 error (_ ("Senza-misura time signatures are not yet supported!"))
362 for i in mxl.get_all_children ():
363 if isinstance (i, Beats):
364 beats = string.split (i.get_text ().strip (), "+")
365 current_sig = [int (j) for j in beats]
366 elif isinstance (i, BeatType):
367 current_sig.append (int (i.get_text ()))
368 signature.append (current_sig)
370 if isinstance (signature[0], list) and len (signature) == 1:
371 signature = signature[0]
372 self._time_signature_cache = signature
374 except (KeyError, ValueError):
375 self.message (_ ("Unable to interpret time signature! Falling back to 4/4."))
378 # returns clef information in the form ("cleftype", position, octave-shift)
379 def get_clef_information (self):
380 clefinfo = ['G', 2, 0]
381 mxl = self.get_named_attribute ('clef')
384 sign = mxl.get_maybe_exist_named_child ('sign')
386 clefinfo[0] = sign.get_text()
387 line = mxl.get_maybe_exist_named_child ('line')
389 clefinfo[1] = string.atoi (line.get_text ())
390 octave = mxl.get_maybe_exist_named_child ('clef-octave-change')
392 clefinfo[2] = string.atoi (octave.get_text ())
395 def get_key_signature (self):
396 "return (fifths, mode) tuple if the key signatures is given as "
397 "major/minor in the Circle of fifths. Otherwise return an alterations"
398 "list of the form [[step,alter<,octave>], [step,alter<,octave>], ...], "
399 "where the octave values are optional."
401 key = self.get_named_attribute ('key')
404 fifths_elm = key.get_maybe_exist_named_child ('fifths')
406 mode_node = key.get_maybe_exist_named_child ('mode')
409 mode = mode_node.get_text ()
410 if not mode or mode == '':
412 fifths = int (fifths_elm.get_text ())
413 # TODO: Shall we try to convert the key-octave and the cancel, too?
414 return (fifths, mode)
418 for i in key.get_all_children ():
419 if isinstance (i, KeyStep):
420 current_step = int (i.get_text ())
421 elif isinstance (i, KeyAlter):
422 alterations.append ([current_step, interpret_alter_element (i)])
423 elif isinstance (i, KeyOctave):
425 if hasattr (i, 'number'):
427 if (nr > 0) and (nr <= len (alterations)):
428 # MusicXML Octave 4 is middle C -> shift to 0
429 alterations[nr-1].append (int (i.get_text ())-4)
431 i.message (_ ("Key alteration octave given for a "
432 "non-existing alteration nr. %s, available numbers: %s!") % (nr, len(alterations)))
435 def get_transposition (self):
436 return self.get_named_attribute ('transpose')
438 class KeyAlter (Music_xml_node):
440 class KeyStep (Music_xml_node):
442 class KeyOctave (Music_xml_node):
446 class Barline (Measure_element):
448 class BarStyle (Music_xml_node):
450 class Partial (Measure_element):
451 def __init__ (self, partial):
452 Measure_element.__init__ (self)
453 self.partial = partial
455 class Note (Measure_element):
457 Measure_element.__init__ (self)
458 self.instrument_name = ''
459 self._after_grace = False
461 return self.get_maybe_exist_named_child (u'grace')
462 def is_after_grace (self):
463 if not self.is_grace():
465 gr = self.get_maybe_exist_typed_child (Grace)
466 return self._after_grace or hasattr (gr, 'steal-time-previous');
468 def get_duration_log (self):
469 ch = self.get_maybe_exist_named_child (u'type')
472 log = ch.get_text ().strip()
473 return musicxml_duration_to_log (log)
474 elif self.get_maybe_exist_named_child (u'grace'):
475 # FIXME: is it ok to default to eight note for grace notes?
480 def get_duration_info (self):
481 log = self.get_duration_log ()
483 dots = len (self.get_typed_children (Dot))
488 def get_factor (self):
491 def get_pitches (self):
492 return self.get_typed_children (get_class (u'pitch'))
494 class Part_list (Music_xml_node):
496 Music_xml_node.__init__ (self)
497 self._id_instrument_name_dict = {}
499 def generate_id_instrument_dict (self):
501 ## not empty to make sure this happens only once.
503 for score_part in self.get_named_children ('score-part'):
504 for instr in score_part.get_named_children ('score-instrument'):
506 name = instr.get_named_child ("instrument-name")
507 mapping[id] = name.get_text ()
509 self._id_instrument_name_dict = mapping
511 def get_instrument (self, id):
512 if not self._id_instrument_name_dict:
513 self.generate_id_instrument_dict()
515 instrument_name = self._id_instrument_name_dict.get (id)
517 return instrument_name
519 ly.stderr_write (_ ("Unable to find instrument for ID=%s\n") % id)
522 class Part_group (Music_xml_node):
524 class Score_part (Music_xml_node):
527 class Measure (Music_xml_node):
529 Music_xml_node.__init__ (self)
531 def is_implicit (self):
532 return hasattr (self, 'implicit') and self.implicit == 'yes'
533 def get_notes (self):
534 return self.get_typed_children (get_class (u'note'))
536 class Syllabic (Music_xml_node):
537 def continued (self):
538 text = self.get_text()
539 return (text == "begin") or (text == "middle")
540 class Elision (Music_xml_node):
542 class Text (Music_xml_node):
545 class Lyric (Music_xml_node):
546 def get_number (self):
547 if hasattr (self, 'number'):
552 class Musicxml_voice:
556 self._start_staff = None
558 self._has_lyrics = False
560 def add_element (self, e):
561 self._elements.append (e)
562 if (isinstance (e, Note)
563 and e.get_maybe_exist_typed_child (Staff)):
564 name = e.get_maybe_exist_typed_child (Staff).get_text ()
566 if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
567 self._start_staff = name
568 self._staves[name] = True
570 lyrics = e.get_typed_children (Lyric)
571 if not self._has_lyrics:
572 self.has_lyrics = len (lyrics) > 0
576 if (nr > 0) and not (nr in self._lyrics):
577 self._lyrics.append (nr)
579 def insert (self, idx, e):
580 self._elements.insert (idx, e)
582 def get_lyrics_numbers (self):
583 if (len (self._lyrics) == 0) and self._has_lyrics:
584 #only happens if none of the <lyric> tags has a number attribute
590 def graces_to_aftergraces (pending_graces):
591 for gr in pending_graces:
592 gr._when = gr._prev_when
593 gr._measure_position = gr._prev_measure_position
594 gr._after_grace = True
597 class Part (Music_xml_node):
599 Music_xml_node.__init__ (self)
601 self._staff_attributes_dict = {}
603 def get_part_list (self):
605 while n and n.get_name() != 'score-partwise':
608 return n.get_named_child ('part-list')
610 def interpret (self):
611 """Set durations and starting points."""
612 """The starting point of the very first note is 0!"""
614 part_list = self.get_part_list ()
617 factor = Rational (1)
619 attributes_object = None
620 measures = self.get_typed_children (Measure)
621 last_moment = Rational (-1)
622 last_measure_position = Rational (-1)
623 measure_position = Rational (0)
624 measure_start_moment = now
625 is_first_measure = True
626 previous_measure = None
627 # Graces at the end of a measure need to have their position set to the
631 # implicit measures are used for artificial measures, e.g. when
632 # a repeat bar line splits a bar into two halves. In this case,
633 # don't reset the measure position to 0. They are also used for
634 # upbeats (initial value of 0 fits these, too).
635 # Also, don't reset the measure position at the end of the loop,
636 # but rather when starting the next measure (since only then do we
637 # know if the next measure is implicit and continues that measure)
638 if not m.is_implicit ():
639 # Warn about possibly overfull measures and reset the position
640 if attributes_object and previous_measure and previous_measure.partial == 0:
641 length = attributes_object.get_measure_length ()
642 new_now = measure_start_moment + length
644 problem = 'incomplete'
647 ## only for verbose operation.
648 if problem <> 'incomplete' and previous_measure:
649 previous_measure.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
651 measure_start_moment = now
652 measure_position = Rational (0)
654 for n in m.get_all_children ():
655 # figured bass has a duration, but applies to the next note
656 # and should not change the current measure position!
657 if isinstance (n, FiguredBass):
658 n._divisions = factor.denominator ()
660 n._measure_position = measure_position
663 if isinstance (n, Hash_text):
667 if n.__class__ == Attributes:
668 n.set_attributes_from_previous (attributes_dict)
670 attributes_dict = n._dict.copy ()
671 attributes_object = n
673 factor = Rational (1,
674 int (attributes_dict.get ('divisions').get_text ()))
677 if (n.get_maybe_exist_typed_child (Duration)):
678 mxl_dur = n.get_maybe_exist_typed_child (Duration)
679 dur = mxl_dur.get_length () * factor
681 if n.get_name() == 'backup':
683 # reset all graces before the backup to after-graces:
684 graces_to_aftergraces (pending_graces)
686 if n.get_maybe_exist_typed_child (Grace):
689 rest = n.get_maybe_exist_typed_child (Rest)
691 and attributes_object
692 and attributes_object.get_measure_length () == dur):
694 rest._is_whole_measure = True
696 if (dur > Rational (0)
697 and n.get_maybe_exist_typed_child (Chord)):
699 measure_position = last_measure_position
702 n._measure_position = measure_position
704 # For all grace notes, store the previous note, in case need
705 # to turn the grace note into an after-grace later on!
706 if isinstance(n, Note) and n.is_grace ():
707 n._prev_when = last_moment
708 n._prev_measure_position = last_measure_position
709 # After-graces are placed at the same position as the previous note
710 if isinstance(n, Note) and n.is_after_grace ():
711 # TODO: We should do the same for grace notes at the end of
712 # a measure with no following note!!!
713 n._when = last_moment
714 n._measure_position = last_measure_position
715 elif isinstance(n, Note) and n.is_grace ():
716 pending_graces.append (n)
717 elif (dur > Rational (0)):
721 if dur > Rational (0):
723 last_measure_position = measure_position
725 measure_position += dur
726 elif dur < Rational (0):
727 # backup element, reset measure position
729 measure_position += dur
730 if measure_position < 0:
731 # backup went beyond the measure start => reset to 0
732 now -= measure_position
735 last_measure_position = measure_position
736 if n._name == 'note':
737 instrument = n.get_maybe_exist_named_child ('instrument')
739 n.instrument_name = part_list.get_instrument (instrument.id)
741 # reset all graces at the end of the measure to after-graces:
742 graces_to_aftergraces (pending_graces)
744 # Incomplete first measures are not padded, but registered as partial
746 is_first_measure = False
747 # upbeats are marked as implicit measures
748 if attributes_object and m.is_implicit ():
749 length = attributes_object.get_measure_length ()
750 measure_end = measure_start_moment + length
751 if measure_end <> now:
755 # modify attributes so that only those applying to the given staff remain
756 def extract_attributes_for_staff (part, attr, staff):
757 attributes = copy.copy (attr)
758 attributes._children = [];
759 attributes._dict = attr._dict.copy ()
760 attributes._original_tag = attr
761 # copy only the relevant children over for the given staff
762 for c in attr._children:
763 if (not (hasattr (c, 'number') and (c.number != staff)) and
764 not (isinstance (c, Hash_text))):
765 attributes._children.append (c)
766 if not attributes._children:
771 def extract_voices (part):
773 measures = part.get_typed_children (Measure)
777 elements.append (Partial (m.partial))
778 elements.extend (m.get_all_children ())
779 # make sure we know all voices already so that dynamics, clefs, etc.
780 # can be assigned to the correct voices
781 voice_to_staff_dict = {}
783 voice_id = n.get_maybe_exist_named_child (u'voice')
786 vid = voice_id.get_text ()
787 elif isinstance (n, Note):
790 staff_id = n.get_maybe_exist_named_child (u'staff')
793 sid = staff_id.get_text ()
796 if vid and not voices.has_key (vid):
797 voices[vid] = Musicxml_voice()
798 if vid and sid and not n.get_maybe_exist_typed_child (Grace):
799 if not voice_to_staff_dict.has_key (vid):
800 voice_to_staff_dict[vid] = sid
801 # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
802 # need to assign staff-assigned objects like clefs, times, etc. to
803 # all the correct voices. This will never work entirely correct due
804 # to staff-switches, but that's the best we can do!
805 staff_to_voice_dict = {}
806 for (v,s) in voice_to_staff_dict.items ():
807 if not staff_to_voice_dict.has_key (s):
808 staff_to_voice_dict[s] = [v]
810 staff_to_voice_dict[s].append (v)
814 assign_to_next_note = []
817 voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
819 id = voice_id.get_text ()
823 # We don't need backup/forward any more, since we have already
824 # assigned the correct onset times.
825 # TODO: Let Grouping through. Also: link, print, bokmark sound
826 if not (isinstance (n, Note) or isinstance (n, Attributes) or
827 isinstance (n, Direction) or isinstance (n, Partial) or
828 isinstance (n, Barline) or isinstance (n, Harmony) or
829 isinstance (n, FiguredBass) ):
832 if isinstance (n, Attributes) and not start_attr:
836 if isinstance (n, Attributes):
837 # assign these only to the voices they really belongs to!
838 for (s, vids) in staff_to_voice_dict.items ():
839 staff_attributes = part.extract_attributes_for_staff (n, s)
842 voices[v].add_element (staff_attributes)
845 if isinstance (n, Partial) or isinstance (n, Barline):
846 for v in voices.keys ():
847 voices[v].add_element (n)
850 if isinstance (n, Direction):
851 staff_id = n.get_maybe_exist_named_child (u'staff')
853 staff_id = staff_id.get_text ()
855 dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
857 dir_voices = voices.keys ()
859 voices[v].add_element (n)
862 if isinstance (n, Harmony) or isinstance (n, FiguredBass):
863 # store the harmony or figured bass element until we encounter
864 # the next note and assign it only to that one voice.
865 assign_to_next_note.append (n)
868 if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
872 for i in assign_to_next_note:
873 voices[id].add_element (i)
874 assign_to_next_note = []
875 voices[id].add_element (n)
877 # Assign all remaining elements from assign_to_next_note to the voice
878 # of the previous note:
879 for i in assign_to_next_note:
880 voices[id].add_element (i)
881 assign_to_next_note = []
884 for (s, vids) in staff_to_voice_dict.items ():
885 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
886 staff_attributes.read_self ()
887 part._staff_attributes_dict[s] = staff_attributes
889 voices[v].insert (0, staff_attributes)
890 voices[v]._elements[0].read_self()
892 part._voices = voices
894 def get_voices (self):
896 def get_staff_attributes (self):
897 return self._staff_attributes_dict
899 class Notations (Music_xml_node):
901 ts = self.get_named_children ('tied')
902 starts = [t for t in ts if t.type == 'start']
908 def get_tuplets (self):
909 return self.get_typed_children (Tuplet)
911 class Time_modification(Music_xml_node):
912 def get_fraction (self):
913 b = self.get_maybe_exist_named_child ('actual-notes')
914 a = self.get_maybe_exist_named_child ('normal-notes')
915 return (int(a.get_text ()), int (b.get_text ()))
917 def get_normal_type (self):
918 tuplet_type = self.get_maybe_exist_named_child ('normal-type')
920 dots = self.get_named_children ('normal-dot')
921 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
922 return (log , len (dots))
927 class Accidental (Music_xml_node):
929 Music_xml_node.__init__ (self)
930 self.editorial = False
931 self.cautionary = False
933 class Music_xml_spanner (Music_xml_node):
935 if hasattr (self, 'type'):
940 if hasattr (self, 'size'):
941 return string.atoi (self.size)
945 class Wedge (Music_xml_spanner):
948 class Tuplet (Music_xml_spanner):
949 def duration_info_from_tuplet_note (self, tuplet_note):
950 tuplet_type = tuplet_note.get_maybe_exist_named_child ('tuplet-type')
952 dots = tuplet_note.get_named_children ('tuplet-dot')
953 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
954 return (log, len (dots))
958 # Return tuplet note type as (log, dots)
959 def get_normal_type (self):
960 tuplet = self.get_maybe_exist_named_child ('tuplet-normal')
962 return self.duration_info_from_tuplet_note (tuplet)
966 def get_actual_type (self):
967 tuplet = self.get_maybe_exist_named_child ('tuplet-actual')
969 return self.duration_info_from_tuplet_note (tuplet)
973 def get_tuplet_note_count (self, tuplet_note):
975 tuplet_nr = tuplet_note.get_maybe_exist_named_child ('tuplet-number')
977 return int (tuplet_nr.get_text ())
979 def get_normal_nr (self):
980 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-normal'))
981 def get_actual_nr (self):
982 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-actual'))
984 class Bracket (Music_xml_spanner):
987 class Dashes (Music_xml_spanner):
990 class Slur (Music_xml_spanner):
994 class Beam (Music_xml_spanner):
996 return self.get_text ()
997 def is_primary (self):
998 return self.number == "1"
1000 class Wavy_line (Music_xml_spanner):
1003 class Pedal (Music_xml_spanner):
1006 class Glissando (Music_xml_spanner):
1009 class Slide (Music_xml_spanner):
1012 class Octave_shift (Music_xml_spanner):
1013 # default is 8 for the octave-shift!
1014 def get_size (self):
1015 if hasattr (self, 'size'):
1016 return string.atoi (self.size)
1020 class Chord (Music_xml_node):
1023 class Dot (Music_xml_node):
1026 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
1027 # for the inner <rest> element, not the whole rest block.
1028 class Rest (Music_xml_node):
1029 def __init__ (self):
1030 Music_xml_node.__init__ (self)
1031 self._is_whole_measure = False
1032 def is_whole_measure (self):
1033 return self._is_whole_measure
1034 def get_step (self):
1035 ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
1037 step = ch.get_text ().strip ()
1041 def get_octave (self):
1042 ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
1044 step = ch.get_text ().strip ()
1049 class Type (Music_xml_node):
1051 class Grace (Music_xml_node):
1053 class Staff (Music_xml_node):
1056 class Direction (Music_xml_node):
1058 class DirType (Music_xml_node):
1061 class Bend (Music_xml_node):
1062 def bend_alter (self):
1063 alter = self.get_maybe_exist_named_child ('bend-alter')
1064 return interpret_alter_element (alter)
1066 class Words (Music_xml_node):
1069 class Harmony (Music_xml_node):
1072 class ChordPitch (Music_xml_node):
1073 def step_class_name (self):
1075 def alter_class_name (self):
1076 return u'root-alter'
1077 def get_step (self):
1078 ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
1079 return ch.get_text ().strip ()
1080 def get_alteration (self):
1081 ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
1082 return interpret_alter_element (ch)
1084 class Root (ChordPitch):
1087 class Bass (ChordPitch):
1088 def step_class_name (self):
1090 def alter_class_name (self):
1091 return u'bass-alter'
1093 class ChordModification (Music_xml_node):
1094 def get_type (self):
1095 ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
1096 return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
1097 def get_value (self):
1098 ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
1101 value = int (ch.get_text ().strip ())
1103 def get_alter (self):
1104 ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
1105 return interpret_alter_element (ch)
1108 class Frame (Music_xml_node):
1109 def get_frets (self):
1110 return self.get_named_child_value_number ('frame-frets', 4)
1111 def get_strings (self):
1112 return self.get_named_child_value_number ('frame-strings', 6)
1113 def get_first_fret (self):
1114 return self.get_named_child_value_number ('first-fret', 1)
1116 class Frame_Note (Music_xml_node):
1117 def get_string (self):
1118 return self.get_named_child_value_number ('string', 1)
1119 def get_fret (self):
1120 return self.get_named_child_value_number ('fret', 0)
1121 def get_fingering (self):
1122 return self.get_named_child_value_number ('fingering', -1)
1123 def get_barre (self):
1124 n = self.get_maybe_exist_named_child ('barre')
1126 return getattr (n, 'type', '')
1130 class FiguredBass (Music_xml_node):
1133 class Beats (Music_xml_node):
1136 class BeatType (Music_xml_node):
1139 class BeatUnit (Music_xml_node):
1142 class BeatUnitDot (Music_xml_node):
1145 class PerMinute (Music_xml_node):
1150 ## need this, not all classes are instantiated
1151 ## for every input file. Only add those classes, that are either directly
1152 ## used by class name or extend Music_xml_node in some way!
1154 '#comment': Hash_comment,
1156 'accidental': Accidental,
1157 'attributes': Attributes,
1159 'bar-style': BarStyle,
1163 'beat-type': BeatType,
1164 'beat-unit': BeatUnit,
1165 'beat-unit-dot': BeatUnitDot,
1167 'bracket' : Bracket,
1170 'degree' : ChordModification,
1172 'direction': Direction,
1173 'direction-type': DirType,
1174 'duration': Duration,
1177 'frame-note': Frame_Note,
1178 'figured-bass': FiguredBass,
1179 'glissando': Glissando,
1182 'identification': Identification,
1183 'key-alter': KeyAlter,
1184 'key-octave': KeyOctave,
1185 'key-step': KeyStep,
1188 'notations': Notations,
1190 'octave-shift': Octave_shift,
1192 'part-group': Part_group,
1193 'part-list': Part_list,
1195 'per-minute': PerMinute,
1199 'score-part': Score_part,
1203 'syllabic': Syllabic,
1205 'time-modification': Time_modification,
1208 'unpitched': Unpitched,
1209 'wavy-line': Wavy_line,
1215 def name2class_name (name):
1216 name = name.replace ('-', '_')
1217 name = name.replace ('#', 'hash_')
1218 name = name[0].upper() + name[1:].lower()
1222 def get_class (name):
1223 classname = class_dict.get (name)
1227 class_name = name2class_name (name)
1228 klass = new.classobj (class_name, (Music_xml_node,) , {})
1229 class_dict[name] = klass
1232 def lxml_demarshal_node (node):
1235 # Ignore comment nodes, which are also returned by the etree parser!
1236 if name is None or node.__class__.__name__ == "_Comment":
1238 klass = get_class (name)
1241 py_node._original = node
1242 py_node._name = name
1243 py_node._data = node.text
1244 py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1245 py_node._children = filter (lambda x: x, py_node._children)
1247 for c in py_node._children:
1250 for (k, v) in node.items ():
1251 py_node.__dict__[k] = v
1252 py_node._attribute_dict[k] = v
1256 def minidom_demarshal_node (node):
1257 name = node.nodeName
1259 klass = get_class (name)
1261 py_node._name = name
1262 py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1263 for c in py_node._children:
1267 for (nm, value) in node.attributes.items ():
1268 py_node.__dict__[nm] = value
1269 py_node._attribute_dict[nm] = value
1271 py_node._data = None
1272 if node.nodeType == node.TEXT_NODE and node.data:
1273 py_node._data = node.data
1275 py_node._original = node
1279 if __name__ == '__main__':
1282 tree = lxml.etree.parse ('beethoven.xml')
1283 mxl_tree = lxml_demarshal_node (tree.getroot ())
1284 ks = class_dict.keys ()
1286 print '\n'.join (ks)