1 # -*- coding: utf-8 -*-
13 def escape_ly_output_string (input_string):
14 return_string = input_string
15 needs_quotes = not re.match (u"^[a-zA-ZäöüÜÄÖß,\.!:ñ]*$", return_string);
17 return_string = "\"" + string.replace (return_string, "\"", "\\\"") + "\""
21 def musicxml_duration_to_log (dur):
33 'long': -2}.get (dur, 0)
37 def interpret_alter_element (alter_elm):
40 val = eval(alter_elm.get_text ())
41 if type (val) in (int, float):
51 self._name = 'xml_node'
53 self._attribute_dict = {}
55 def get_parent (self):
59 return self._parent.get_typed_children (self.__class__)[0] == self
70 if not self._children:
73 return ''.join ([c.get_text () for c in self._children])
75 def message (self, msg):
80 ly.progress (' In: <%s %s>\n' % (p._name, ' '.join (['%s=%s' % item for item in p._attribute_dict.items ()])))
83 def dump (self, indent = ''):
84 ly.debug_output ('%s<%s%s>' % (indent, self._name, ''.join ([' %s=%s' % item for item in self._attribute_dict.items ()])))
85 non_text_children = [c for c in self._children if not isinstance (c, Hash_text)]
87 ly.debug_output ('\n')
88 for c in self._children:
91 ly.debug_output (indent)
92 ly.debug_output ('</%s>\n' % self._name)
95 def get_typed_children (self, klass):
99 return [c for c in self._children if isinstance(c, klass)]
101 def get_named_children (self, nm):
102 return self.get_typed_children (get_class (nm))
104 def get_named_child (self, nm):
105 return self.get_maybe_exist_named_child (nm)
107 def get_children (self, predicate):
108 return [c for c in self._children if predicate(c)]
110 def get_all_children (self):
111 return self._children
113 def get_maybe_exist_named_child (self, name):
114 return self.get_maybe_exist_typed_child (get_class (name))
116 def get_maybe_exist_typed_child (self, klass):
117 cn = self.get_typed_children (klass)
123 raise "More than 1 child", klass
125 def get_unique_typed_child (self, klass):
126 cn = self.get_typed_children(klass)
128 ly.error (self.__dict__)
129 raise 'Child is not unique for', (klass, 'found', cn)
133 def get_named_child_value_number (self, name, default):
134 n = self.get_maybe_exist_named_child (name)
136 return string.atoi (n.get_text())
141 class Music_xml_node (Xml_node):
143 Xml_node.__init__ (self)
144 self.duration = Rational (0)
145 self.start = Rational (0)
147 class Work (Xml_node):
148 def get_work_information (self, tag):
149 wt = self.get_maybe_exist_named_child (tag)
151 return wt.get_text ()
155 def get_work_title (self):
156 return self.get_work_information ('work-title')
157 def get_work_number (self):
158 return self.get_work_information ('work-number')
160 return self.get_work_information ('opus')
162 class Identification (Xml_node):
163 def get_rights (self):
164 rights = self.get_named_children ('rights')
167 ret.append (r.get_text ())
168 return string.join (ret, "\n")
170 def get_creator (self, type):
171 creators = self.get_named_children ('creator')
172 # return the first creator tag that has the particular type
174 if hasattr (i, 'type') and i.type == type:
178 def get_composer (self):
179 c = self.get_creator ('composer')
182 creators = self.get_named_children ('creator')
183 # return the first creator tag that has no type at all
185 if not hasattr (i, 'type'):
188 def get_arranger (self):
189 return self.get_creator ('arranger')
190 def get_editor (self):
191 return self.get_creator ('editor')
193 v = self.get_creator ('lyricist')
196 v = self.get_creator ('poet')
199 def get_encoding_information (self, type):
200 enc = self.get_named_children ('encoding')
202 children = enc[0].get_named_children (type)
204 return children[0].get_text ()
208 def get_encoding_software (self):
209 return self.get_encoding_information ('software')
210 def get_encoding_date (self):
211 return self.get_encoding_information ('encoding-date')
212 def get_encoding_person (self):
213 return self.get_encoding_information ('encoder')
214 def get_encoding_description (self):
215 return self.get_encoding_information ('encoding-description')
217 def get_encoding_software_list (self):
218 enc = self.get_named_children ('encoding')
221 softwares = e.get_named_children ('software')
223 software.append (s.get_text ())
226 def get_file_description (self):
227 misc = self.get_named_children ('miscellaneous')
229 misc_fields = m.get_named_children ('miscellaneous-field')
230 for mf in misc_fields:
231 if hasattr (mf, 'name') and mf.name == 'description':
232 return mf.get_text ()
237 class Duration (Music_xml_node):
238 def get_length (self):
239 dur = int (self.get_text ()) * Rational (1,4)
242 class Hash_comment (Music_xml_node):
244 class Hash_text (Music_xml_node):
245 def dump (self, indent = ''):
246 ly.debug_output ('%s' % string.strip (self._data))
248 class Pitch (Music_xml_node):
250 ch = self.get_unique_typed_child (get_class (u'step'))
251 step = ch.get_text ().strip ()
253 def get_octave (self):
254 ch = self.get_unique_typed_child (get_class (u'octave'))
255 octave = ch.get_text ().strip ()
258 def get_alteration (self):
259 ch = self.get_maybe_exist_typed_child (get_class (u'alter'))
260 return interpret_alter_element (ch)
262 class Unpitched (Music_xml_node):
264 ch = self.get_unique_typed_child (get_class (u'display-step'))
265 step = ch.get_text ().strip ()
268 def get_octave (self):
269 ch = self.get_unique_typed_child (get_class (u'display-octave'))
272 octave = ch.get_text ().strip ()
277 class Measure_element (Music_xml_node):
278 def get_voice_id (self):
279 voice_id = self.get_maybe_exist_named_child ('voice')
281 return voice_id.get_text ()
286 # Look at all measure elements (previously we had self.__class__, which
287 # only looked at objects of the same type!
288 cn = self._parent.get_typed_children (Measure_element)
289 # But only look at the correct voice; But include Attributes, too, which
290 # are not tied to any particular voice
291 cn = [c for c in cn if (c.get_voice_id () == self.get_voice_id ()) or isinstance (c, Attributes)]
294 class Attributes (Measure_element):
296 Measure_element.__init__ (self)
298 self._original_tag = None
299 self._time_signature_cache = None
302 cn = self._parent.get_typed_children (self.__class__)
303 if self._original_tag:
304 return cn[0] == self._original_tag
308 def set_attributes_from_previous (self, dict):
309 self._dict.update (dict)
311 def read_self (self):
312 for c in self.get_all_children ():
313 self._dict[c.get_name()] = c
315 def get_named_attribute (self, name):
316 return self._dict.get (name)
318 def single_time_sig_to_fraction (self, sig):
324 return Rational (n, sig[-1])
326 def get_measure_length (self):
327 sig = self.get_time_signature ()
328 if not sig or len (sig) == 0:
330 if isinstance (sig[0], list):
331 # Complex compound time signature
334 l += self.single_time_sig_to_fraction (i)
337 # Simple (maybe compound) time signature of the form (beat, ..., type)
338 return self.single_time_sig_to_fraction (sig)
341 def get_time_signature (self):
342 "Return time sig as a (beat, beat-type) tuple. For compound signatures,"
343 "return either (beat, beat,..., beat-type) or ((beat,..., type), "
344 "(beat,..., type), ...)."
345 if self._time_signature_cache:
346 return self._time_signature_cache
349 mxl = self.get_named_attribute ('time')
353 if mxl.get_maybe_exist_named_child ('senza-misura'):
354 # TODO: Handle pieces without a time signature!
355 ly.warning (_ ("Senza-misura time signatures are not yet supported!"))
360 for i in mxl.get_all_children ():
361 if isinstance (i, Beats):
362 beats = string.split (i.get_text ().strip (), "+")
363 current_sig = [int (j) for j in beats]
364 elif isinstance (i, BeatType):
365 current_sig.append (int (i.get_text ()))
366 signature.append (current_sig)
368 if isinstance (signature[0], list) and len (signature) == 1:
369 signature = signature[0]
370 self._time_signature_cache = signature
372 except (KeyError, ValueError):
373 self.message (_ ("Unable to interpret time signature! Falling back to 4/4."))
376 # returns clef information in the form ("cleftype", position, octave-shift)
377 def get_clef_information (self):
378 clefinfo = ['G', 2, 0]
379 mxl = self.get_named_attribute ('clef')
382 sign = mxl.get_maybe_exist_named_child ('sign')
384 clefinfo[0] = sign.get_text()
385 line = mxl.get_maybe_exist_named_child ('line')
387 clefinfo[1] = string.atoi (line.get_text ())
388 octave = mxl.get_maybe_exist_named_child ('clef-octave-change')
390 clefinfo[2] = string.atoi (octave.get_text ())
393 def get_key_signature (self):
394 "return (fifths, mode) tuple if the key signatures is given as "
395 "major/minor in the Circle of fifths. Otherwise return an alterations"
396 "list of the form [[step,alter<,octave>], [step,alter<,octave>], ...], "
397 "where the octave values are optional."
399 key = self.get_named_attribute ('key')
402 fifths_elm = key.get_maybe_exist_named_child ('fifths')
404 mode_node = key.get_maybe_exist_named_child ('mode')
407 mode = mode_node.get_text ()
408 if not mode or mode == '':
410 fifths = int (fifths_elm.get_text ())
411 # TODO: Shall we try to convert the key-octave and the cancel, too?
412 return (fifths, mode)
416 for i in key.get_all_children ():
417 if isinstance (i, KeyStep):
418 current_step = i.get_text ().strip ()
419 elif isinstance (i, KeyAlter):
420 alterations.append ([current_step, interpret_alter_element (i)])
421 elif isinstance (i, KeyOctave):
423 if hasattr (i, 'number'):
425 if (nr > 0) and (nr <= len (alterations)):
426 # MusicXML Octave 4 is middle C -> shift to 0
427 alterations[nr-1].append (int (i.get_text ())-4)
429 i.message (_ ("Key alteration octave given for a "
430 "non-existing alteration nr. %s, available numbers: %s!") % (nr, len(alterations)))
433 def get_transposition (self):
434 return self.get_named_attribute ('transpose')
436 class KeyAlter (Music_xml_node):
438 class KeyStep (Music_xml_node):
440 class KeyOctave (Music_xml_node):
444 class Barline (Measure_element):
446 class BarStyle (Music_xml_node):
448 class Partial (Measure_element):
449 def __init__ (self, partial):
450 Measure_element.__init__ (self)
451 self.partial = partial
453 class Note (Measure_element):
455 Measure_element.__init__ (self)
456 self.instrument_name = ''
457 self._after_grace = False
459 return self.get_maybe_exist_named_child (u'grace')
460 def is_after_grace (self):
461 if not self.is_grace():
463 gr = self.get_maybe_exist_typed_child (Grace)
464 return self._after_grace or hasattr (gr, 'steal-time-previous');
466 def get_duration_log (self):
467 ch = self.get_maybe_exist_named_child (u'type')
470 log = ch.get_text ().strip()
471 return musicxml_duration_to_log (log)
472 elif self.get_maybe_exist_named_child (u'grace'):
473 # FIXME: is it ok to default to eight note for grace notes?
478 def get_duration_info (self):
479 log = self.get_duration_log ()
481 dots = len (self.get_typed_children (Dot))
486 def get_factor (self):
489 def get_pitches (self):
490 return self.get_typed_children (get_class (u'pitch'))
492 class Part_list (Music_xml_node):
494 Music_xml_node.__init__ (self)
495 self._id_instrument_name_dict = {}
497 def generate_id_instrument_dict (self):
499 ## not empty to make sure this happens only once.
501 for score_part in self.get_named_children ('score-part'):
502 for instr in score_part.get_named_children ('score-instrument'):
504 name = instr.get_named_child ("instrument-name")
505 mapping[id] = name.get_text ()
507 self._id_instrument_name_dict = mapping
509 def get_instrument (self, id):
510 if not self._id_instrument_name_dict:
511 self.generate_id_instrument_dict()
513 instrument_name = self._id_instrument_name_dict.get (id)
515 return instrument_name
517 ly.warning (_ ("Unable to find instrument for ID=%s\n") % id)
520 class Part_group (Music_xml_node):
522 class Score_part (Music_xml_node):
525 class Measure (Music_xml_node):
527 Music_xml_node.__init__ (self)
529 def is_implicit (self):
530 return hasattr (self, 'implicit') and self.implicit == 'yes'
531 def get_notes (self):
532 return self.get_typed_children (get_class (u'note'))
534 class Syllabic (Music_xml_node):
535 def continued (self):
536 text = self.get_text()
537 return (text == "begin") or (text == "middle")
538 class Elision (Music_xml_node):
540 class Extend (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
764 for c in attr._children:
765 if (not (hasattr (c, 'number') and (c.number != staff)) and
766 not (isinstance (c, Hash_text))):
767 attributes._children.append (c)
768 if not attributes._children:
773 def extract_voices (part):
775 measures = part.get_typed_children (Measure)
779 elements.append (Partial (m.partial))
780 elements.extend (m.get_all_children ())
781 # make sure we know all voices already so that dynamics, clefs, etc.
782 # can be assigned to the correct voices
783 voice_to_staff_dict = {}
785 voice_id = n.get_maybe_exist_named_child (u'voice')
788 vid = voice_id.get_text ()
789 elif isinstance (n, Note):
790 # TODO: Check whether we shall really use "None" here, or
791 # rather use "1" as the default?
794 staff_id = n.get_maybe_exist_named_child (u'staff')
797 sid = staff_id.get_text ()
799 # TODO: Check whether we shall really use "None" here, or
800 # rather use "1" as the default?
801 # If this is changed, need to change the corresponding
802 # check in extract_attributes_for_staff, too.
804 if vid and not voices.has_key (vid):
805 voices[vid] = Musicxml_voice()
806 if vid and sid and not n.get_maybe_exist_typed_child (Grace):
807 if not voice_to_staff_dict.has_key (vid):
808 voice_to_staff_dict[vid] = sid
809 # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
810 # need to assign staff-assigned objects like clefs, times, etc. to
811 # all the correct voices. This will never work entirely correct due
812 # to staff-switches, but that's the best we can do!
813 staff_to_voice_dict = {}
814 for (v,s) in voice_to_staff_dict.items ():
815 if not staff_to_voice_dict.has_key (s):
816 staff_to_voice_dict[s] = [v]
818 staff_to_voice_dict[s].append (v)
822 assign_to_next_note = []
825 voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
827 id = voice_id.get_text ()
831 # We don't need backup/forward any more, since we have already
832 # assigned the correct onset times.
833 # TODO: Let Grouping through. Also: link, print, bokmark sound
834 if not (isinstance (n, Note) or isinstance (n, Attributes) or
835 isinstance (n, Direction) or isinstance (n, Partial) or
836 isinstance (n, Barline) or isinstance (n, Harmony) or
837 isinstance (n, FiguredBass) or isinstance (n, Print)):
840 if isinstance (n, Attributes) and not start_attr:
844 if isinstance (n, Attributes):
845 # assign these only to the voices they really belong to!
846 for (s, vids) in staff_to_voice_dict.items ():
847 staff_attributes = part.extract_attributes_for_staff (n, s)
850 voices[v].add_element (staff_attributes)
853 if isinstance (n, Partial) or isinstance (n, Barline) or isinstance (n, Print):
854 for v in voices.keys ():
855 voices[v].add_element (n)
858 if isinstance (n, Direction):
859 staff_id = n.get_maybe_exist_named_child (u'staff')
861 staff_id = staff_id.get_text ()
863 dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
865 dir_voices = voices.keys ()
867 voices[v].add_element (n)
870 if isinstance (n, Harmony) or isinstance (n, FiguredBass):
871 # store the harmony or figured bass element until we encounter
872 # the next note and assign it only to that one voice.
873 assign_to_next_note.append (n)
876 if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
880 for i in assign_to_next_note:
881 voices[id].add_element (i)
882 assign_to_next_note = []
883 voices[id].add_element (n)
885 # Assign all remaining elements from assign_to_next_note to the voice
886 # of the previous note:
887 for i in assign_to_next_note:
888 voices[id].add_element (i)
889 assign_to_next_note = []
892 for (s, vids) in staff_to_voice_dict.items ():
893 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
894 staff_attributes.read_self ()
895 part._staff_attributes_dict[s] = staff_attributes
897 voices[v].insert (0, staff_attributes)
898 voices[v]._elements[0].read_self()
900 part._voices = voices
902 def get_voices (self):
904 def get_staff_attributes (self):
905 return self._staff_attributes_dict
907 class Notations (Music_xml_node):
909 ts = self.get_named_children ('tied')
910 starts = [t for t in ts if t.type == 'start']
916 def get_tuplets (self):
917 return self.get_typed_children (Tuplet)
919 class Time_modification(Music_xml_node):
920 def get_fraction (self):
921 b = self.get_maybe_exist_named_child ('actual-notes')
922 a = self.get_maybe_exist_named_child ('normal-notes')
923 return (int(a.get_text ()), int (b.get_text ()))
925 def get_normal_type (self):
926 tuplet_type = self.get_maybe_exist_named_child ('normal-type')
928 dots = self.get_named_children ('normal-dot')
929 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
930 return (log , len (dots))
935 class Accidental (Music_xml_node):
937 Music_xml_node.__init__ (self)
938 self.editorial = False
939 self.cautionary = False
941 class Music_xml_spanner (Music_xml_node):
943 if hasattr (self, 'type'):
948 if hasattr (self, 'size'):
949 return string.atoi (self.size)
953 class Wedge (Music_xml_spanner):
956 class Tuplet (Music_xml_spanner):
957 def duration_info_from_tuplet_note (self, tuplet_note):
958 tuplet_type = tuplet_note.get_maybe_exist_named_child ('tuplet-type')
960 dots = tuplet_note.get_named_children ('tuplet-dot')
961 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
962 return (log, len (dots))
966 # Return tuplet note type as (log, dots)
967 def get_normal_type (self):
968 tuplet = self.get_maybe_exist_named_child ('tuplet-normal')
970 return self.duration_info_from_tuplet_note (tuplet)
974 def get_actual_type (self):
975 tuplet = self.get_maybe_exist_named_child ('tuplet-actual')
977 return self.duration_info_from_tuplet_note (tuplet)
981 def get_tuplet_note_count (self, tuplet_note):
983 tuplet_nr = tuplet_note.get_maybe_exist_named_child ('tuplet-number')
985 return int (tuplet_nr.get_text ())
987 def get_normal_nr (self):
988 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-normal'))
989 def get_actual_nr (self):
990 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-actual'))
992 class Bracket (Music_xml_spanner):
995 class Dashes (Music_xml_spanner):
998 class Slur (Music_xml_spanner):
1002 class Beam (Music_xml_spanner):
1003 def get_type (self):
1004 return self.get_text ()
1005 def is_primary (self):
1006 if hasattr (self, 'number'):
1007 return self.number == "1"
1011 class Wavy_line (Music_xml_spanner):
1014 class Pedal (Music_xml_spanner):
1017 class Glissando (Music_xml_spanner):
1020 class Slide (Music_xml_spanner):
1023 class Octave_shift (Music_xml_spanner):
1024 # default is 8 for the octave-shift!
1025 def get_size (self):
1026 if hasattr (self, 'size'):
1027 return string.atoi (self.size)
1031 class Chord (Music_xml_node):
1034 class Dot (Music_xml_node):
1037 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
1038 # for the inner <rest> element, not the whole rest block.
1039 class Rest (Music_xml_node):
1040 def __init__ (self):
1041 Music_xml_node.__init__ (self)
1042 self._is_whole_measure = False
1043 def is_whole_measure (self):
1044 return self._is_whole_measure
1045 def get_step (self):
1046 ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
1048 return ch.get_text ().strip ()
1051 def get_octave (self):
1052 ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
1054 oct = ch.get_text ().strip ()
1059 class Type (Music_xml_node):
1061 class Grace (Music_xml_node):
1063 class Staff (Music_xml_node):
1066 class Direction (Music_xml_node):
1068 class DirType (Music_xml_node):
1071 class Bend (Music_xml_node):
1072 def bend_alter (self):
1073 alter = self.get_maybe_exist_named_child ('bend-alter')
1074 return interpret_alter_element (alter)
1076 class Words (Music_xml_node):
1079 class Harmony (Music_xml_node):
1082 class ChordPitch (Music_xml_node):
1083 def step_class_name (self):
1085 def alter_class_name (self):
1086 return u'root-alter'
1087 def get_step (self):
1088 ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
1089 return ch.get_text ().strip ()
1090 def get_alteration (self):
1091 ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
1092 return interpret_alter_element (ch)
1094 class Root (ChordPitch):
1097 class Bass (ChordPitch):
1098 def step_class_name (self):
1100 def alter_class_name (self):
1101 return u'bass-alter'
1103 class ChordModification (Music_xml_node):
1104 def get_type (self):
1105 ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
1106 return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
1107 def get_value (self):
1108 ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
1111 value = int (ch.get_text ().strip ())
1113 def get_alter (self):
1114 ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
1115 return interpret_alter_element (ch)
1118 class Frame (Music_xml_node):
1119 def get_frets (self):
1120 return self.get_named_child_value_number ('frame-frets', 4)
1121 def get_strings (self):
1122 return self.get_named_child_value_number ('frame-strings', 6)
1123 def get_first_fret (self):
1124 return self.get_named_child_value_number ('first-fret', 1)
1126 class Frame_Note (Music_xml_node):
1127 def get_string (self):
1128 return self.get_named_child_value_number ('string', 1)
1129 def get_fret (self):
1130 return self.get_named_child_value_number ('fret', 0)
1131 def get_fingering (self):
1132 return self.get_named_child_value_number ('fingering', -1)
1133 def get_barre (self):
1134 n = self.get_maybe_exist_named_child ('barre')
1136 return getattr (n, 'type', '')
1140 class FiguredBass (Music_xml_node):
1143 class Beats (Music_xml_node):
1146 class BeatType (Music_xml_node):
1149 class BeatUnit (Music_xml_node):
1152 class BeatUnitDot (Music_xml_node):
1155 class PerMinute (Music_xml_node):
1158 class Print (Music_xml_node):
1163 ## need this, not all classes are instantiated
1164 ## for every input file. Only add those classes, that are either directly
1165 ## used by class name or extend Music_xml_node in some way!
1167 '#comment': Hash_comment,
1169 'accidental': Accidental,
1170 'attributes': Attributes,
1172 'bar-style': BarStyle,
1176 'beat-type': BeatType,
1177 'beat-unit': BeatUnit,
1178 'beat-unit-dot': BeatUnitDot,
1180 'bracket' : Bracket,
1183 'degree' : ChordModification,
1185 'direction': Direction,
1186 'direction-type': DirType,
1187 'duration': Duration,
1191 'frame-note': Frame_Note,
1192 'figured-bass': FiguredBass,
1193 'glissando': Glissando,
1196 'identification': Identification,
1197 'key-alter': KeyAlter,
1198 'key-octave': KeyOctave,
1199 'key-step': KeyStep,
1202 'notations': Notations,
1204 'octave-shift': Octave_shift,
1206 'part-group': Part_group,
1207 'part-list': Part_list,
1209 'per-minute': PerMinute,
1214 'score-part': Score_part,
1218 'syllabic': Syllabic,
1220 'time-modification': Time_modification,
1223 'unpitched': Unpitched,
1224 'wavy-line': Wavy_line,
1230 def name2class_name (name):
1231 name = name.replace ('-', '_')
1232 name = name.replace ('#', 'hash_')
1233 name = name[0].upper() + name[1:].lower()
1237 def get_class (name):
1238 classname = class_dict.get (name)
1242 class_name = name2class_name (name)
1243 klass = new.classobj (class_name, (Music_xml_node,) , {})
1244 class_dict[name] = klass
1247 def lxml_demarshal_node (node):
1250 # Ignore comment nodes, which are also returned by the etree parser!
1251 if name is None or node.__class__.__name__ == "_Comment":
1253 klass = get_class (name)
1256 py_node._original = node
1257 py_node._name = name
1258 py_node._data = node.text
1259 py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1260 py_node._children = filter (lambda x: x, py_node._children)
1262 for c in py_node._children:
1265 for (k, v) in node.items ():
1266 py_node.__dict__[k] = v
1267 py_node._attribute_dict[k] = v
1271 def minidom_demarshal_node (node):
1272 name = node.nodeName
1274 klass = get_class (name)
1276 py_node._name = name
1277 py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1278 for c in py_node._children:
1282 for (nm, value) in node.attributes.items ():
1283 py_node.__dict__[nm] = value
1284 py_node._attribute_dict[nm] = value
1286 py_node._data = None
1287 if node.nodeType == node.TEXT_NODE and node.data:
1288 py_node._data = node.data
1290 py_node._original = node
1294 if __name__ == '__main__':
1297 tree = lxml.etree.parse ('beethoven.xml')
1298 mxl_tree = lxml_demarshal_node (tree.getroot ())
1299 ks = class_dict.keys ()
1301 print '\n'.join (ks)