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)
146 self.voice_id = None;
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 # get contents of the source-element (usually used for publishing information). (These contents are saved in a custom variable named "source" in the header of the .ly file.)
172 def get_source (self):
173 source = self.get_named_children ('source')
176 ret.append (r.get_text ())
177 return string.join (ret, "\n")
179 def get_creator (self, type):
180 creators = self.get_named_children ('creator')
181 # return the first creator tag that has the particular type
183 if hasattr (i, 'type') and i.type == type:
187 def get_composer (self):
188 c = self.get_creator ('composer')
191 creators = self.get_named_children ('creator')
192 # return the first creator tag that has no type at all
194 if not hasattr (i, 'type'):
197 def get_arranger (self):
198 return self.get_creator ('arranger')
199 def get_editor (self):
200 return self.get_creator ('editor')
202 v = self.get_creator ('lyricist')
205 v = self.get_creator ('poet')
208 def get_encoding_information (self, type):
209 enc = self.get_named_children ('encoding')
211 children = enc[0].get_named_children (type)
213 return children[0].get_text ()
217 def get_encoding_software (self):
218 return self.get_encoding_information ('software')
219 def get_encoding_date (self):
220 return self.get_encoding_information ('encoding-date')
221 def get_encoding_person (self):
222 return self.get_encoding_information ('encoder')
223 def get_encoding_description (self):
224 return self.get_encoding_information ('encoding-description')
226 def get_encoding_software_list (self):
227 enc = self.get_named_children ('encoding')
230 softwares = e.get_named_children ('software')
232 software.append (s.get_text ())
235 def get_file_description (self):
236 misc = self.get_named_children ('miscellaneous')
238 misc_fields = m.get_named_children ('miscellaneous-field')
239 for mf in misc_fields:
240 if hasattr (mf, 'name') and mf.name == 'description':
241 return mf.get_text ()
244 class Duration (Music_xml_node):
245 def get_length (self):
246 dur = int (self.get_text ()) * Rational (1,4)
249 class Hash_comment (Music_xml_node):
251 class Hash_text (Music_xml_node):
252 def dump (self, indent = ''):
253 ly.debug_output ('%s' % string.strip (self._data))
255 class Pitch (Music_xml_node):
257 ch = self.get_unique_typed_child (get_class (u'step'))
258 step = ch.get_text ().strip ()
260 def get_octave (self):
261 ch = self.get_unique_typed_child (get_class (u'octave'))
262 octave = ch.get_text ().strip ()
265 def get_alteration (self):
266 ch = self.get_maybe_exist_typed_child (get_class (u'alter'))
267 return interpret_alter_element (ch)
269 class Unpitched (Music_xml_node):
271 ch = self.get_unique_typed_child (get_class (u'display-step'))
272 step = ch.get_text ().strip ()
275 def get_octave (self):
276 ch = self.get_unique_typed_child (get_class (u'display-octave'))
279 octave = ch.get_text ().strip ()
284 class Measure_element (Music_xml_node):
285 def get_voice_id (self):
286 voice = self.get_maybe_exist_named_child ('voice')
288 return voice.get_text ()
290 return self.voice_id;
293 # Look at all measure elements (previously we had self.__class__, which
294 # only looked at objects of the same type!
295 cn = self._parent.get_typed_children (Measure_element)
296 # But only look at the correct voice; But include Attributes, too, which
297 # are not tied to any particular voice
298 cn = [c for c in cn if (c.get_voice_id () == self.get_voice_id ()) or isinstance (c, Attributes)]
301 class Attributes (Measure_element):
303 Measure_element.__init__ (self)
305 self._original_tag = None
306 self._time_signature_cache = None
309 cn = self._parent.get_typed_children (self.__class__)
310 if self._original_tag:
311 return cn[0] == self._original_tag
315 def set_attributes_from_previous (self, dict):
316 self._dict.update (dict)
318 def read_self (self):
319 for c in self.get_all_children ():
320 self._dict[c.get_name()] = c
322 def get_named_attribute (self, name):
323 return self._dict.get (name)
325 def single_time_sig_to_fraction (self, sig):
331 return Rational (n, sig[-1])
333 def get_measure_length (self):
334 sig = self.get_time_signature ()
335 if not sig or len (sig) == 0:
337 if isinstance (sig[0], list):
338 # Complex compound time signature
341 l += self.single_time_sig_to_fraction (i)
344 # Simple (maybe compound) time signature of the form (beat, ..., type)
345 return self.single_time_sig_to_fraction (sig)
348 def get_time_signature (self):
349 "Return time sig as a (beat, beat-type) tuple. For compound signatures,"
350 "return either (beat, beat,..., beat-type) or ((beat,..., type), "
351 "(beat,..., type), ...)."
352 if self._time_signature_cache:
353 return self._time_signature_cache
356 mxl = self.get_named_attribute ('time')
360 if mxl.get_maybe_exist_named_child ('senza-misura'):
361 # TODO: Handle pieces without a time signature!
362 ly.warning (_ ("Senza-misura time signatures are not yet supported!"))
367 for i in mxl.get_all_children ():
368 if isinstance (i, Beats):
369 beats = string.split (i.get_text ().strip (), "+")
370 current_sig = [int (j) for j in beats]
371 elif isinstance (i, BeatType):
372 current_sig.append (int (i.get_text ()))
373 signature.append (current_sig)
375 if isinstance (signature[0], list) and len (signature) == 1:
376 signature = signature[0]
377 self._time_signature_cache = signature
379 except (KeyError, ValueError):
380 self.message (_ ("Unable to interpret time signature! Falling back to 4/4."))
383 # returns clef information in the form ("cleftype", position, octave-shift)
384 def get_clef_information (self):
385 clefinfo = ['G', 2, 0]
386 mxl = self.get_named_attribute ('clef')
389 sign = mxl.get_maybe_exist_named_child ('sign')
391 clefinfo[0] = sign.get_text()
392 line = mxl.get_maybe_exist_named_child ('line')
394 clefinfo[1] = string.atoi (line.get_text ())
395 octave = mxl.get_maybe_exist_named_child ('clef-octave-change')
397 clefinfo[2] = string.atoi (octave.get_text ())
400 def get_key_signature (self):
401 "return (fifths, mode) tuple if the key signatures is given as "
402 "major/minor in the Circle of fifths. Otherwise return an alterations"
403 "list of the form [[step,alter<,octave>], [step,alter<,octave>], ...], "
404 "where the octave values are optional."
406 key = self.get_named_attribute ('key')
409 fifths_elm = key.get_maybe_exist_named_child ('fifths')
411 mode_node = key.get_maybe_exist_named_child ('mode')
414 mode = mode_node.get_text ()
415 if not mode or mode == '':
417 fifths = int (fifths_elm.get_text ())
418 # TODO: Shall we try to convert the key-octave and the cancel, too?
419 return (fifths, mode)
423 for i in key.get_all_children ():
424 if isinstance (i, KeyStep):
425 current_step = i.get_text ().strip ()
426 elif isinstance (i, KeyAlter):
427 alterations.append ([current_step, interpret_alter_element (i)])
428 elif isinstance (i, KeyOctave):
430 if hasattr (i, 'number'):
432 if (nr > 0) and (nr <= len (alterations)):
433 # MusicXML Octave 4 is middle C -> shift to 0
434 alterations[nr-1].append (int (i.get_text ())-4)
436 i.message (_ ("Key alteration octave given for a "
437 "non-existing alteration nr. %s, available numbers: %s!") % (nr, len(alterations)))
440 def get_transposition (self):
441 return self.get_named_attribute ('transpose')
443 class KeyAlter (Music_xml_node):
445 class KeyStep (Music_xml_node):
447 class KeyOctave (Music_xml_node):
451 class Barline (Measure_element):
453 class BarStyle (Music_xml_node):
455 class Partial (Measure_element):
456 def __init__ (self, partial):
457 Measure_element.__init__ (self)
458 self.partial = partial
460 class Note (Measure_element):
462 Measure_element.__init__ (self)
463 self.instrument_name = ''
464 self._after_grace = False
466 return self.get_maybe_exist_named_child (u'grace')
467 def is_after_grace (self):
468 if not self.is_grace():
470 gr = self.get_maybe_exist_typed_child (Grace)
471 return self._after_grace or hasattr (gr, 'steal-time-previous');
473 def get_duration_log (self):
474 ch = self.get_maybe_exist_named_child (u'type')
477 log = ch.get_text ().strip()
478 return musicxml_duration_to_log (log)
479 elif self.get_maybe_exist_named_child (u'grace'):
480 # FIXME: is it ok to default to eight note for grace notes?
485 def get_duration_info (self):
486 log = self.get_duration_log ()
488 dots = len (self.get_typed_children (Dot))
493 def get_factor (self):
496 def get_pitches (self):
497 return self.get_typed_children (get_class (u'pitch'))
499 class Part_list (Music_xml_node):
501 Music_xml_node.__init__ (self)
502 self._id_instrument_name_dict = {}
504 def generate_id_instrument_dict (self):
506 ## not empty to make sure this happens only once.
508 for score_part in self.get_named_children ('score-part'):
509 for instr in score_part.get_named_children ('score-instrument'):
511 name = instr.get_named_child ("instrument-name")
512 mapping[id] = name.get_text ()
514 self._id_instrument_name_dict = mapping
516 def get_instrument (self, id):
517 if not self._id_instrument_name_dict:
518 self.generate_id_instrument_dict()
520 instrument_name = self._id_instrument_name_dict.get (id)
522 return instrument_name
524 ly.warning (_ ("Unable to find instrument for ID=%s\n") % id)
527 class Part_group (Music_xml_node):
529 class Score_part (Music_xml_node):
532 class Measure (Music_xml_node):
534 Music_xml_node.__init__ (self)
536 def is_implicit (self):
537 return hasattr (self, 'implicit') and self.implicit == 'yes'
538 def get_notes (self):
539 return self.get_typed_children (get_class (u'note'))
541 class Syllabic (Music_xml_node):
542 def continued (self):
543 text = self.get_text()
544 return (text == "begin") or (text == "middle")
545 class Elision (Music_xml_node):
547 class Extend (Music_xml_node):
549 class Text (Music_xml_node):
552 class Lyric (Music_xml_node):
553 def get_number (self):
554 if hasattr (self, 'number'):
559 class Musicxml_voice:
563 self._start_staff = None
565 self._has_lyrics = False
567 def add_element (self, e):
568 self._elements.append (e)
569 if (isinstance (e, Note)
570 and e.get_maybe_exist_typed_child (Staff)):
571 name = e.get_maybe_exist_typed_child (Staff).get_text ()
573 if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
574 self._start_staff = name
575 self._staves[name] = True
577 lyrics = e.get_typed_children (Lyric)
578 if not self._has_lyrics:
579 self.has_lyrics = len (lyrics) > 0
583 if (nr > 0) and not (nr in self._lyrics):
584 self._lyrics.append (nr)
586 def insert (self, idx, e):
587 self._elements.insert (idx, e)
589 def get_lyrics_numbers (self):
590 if (len (self._lyrics) == 0) and self._has_lyrics:
591 #only happens if none of the <lyric> tags has a number attribute
597 def graces_to_aftergraces (pending_graces):
598 for gr in pending_graces:
599 gr._when = gr._prev_when
600 gr._measure_position = gr._prev_measure_position
601 gr._after_grace = True
604 class Part (Music_xml_node):
606 Music_xml_node.__init__ (self)
608 self._staff_attributes_dict = {}
610 def get_part_list (self):
612 while n and n.get_name() != 'score-partwise':
615 return n.get_named_child ('part-list')
617 def interpret (self):
618 """Set durations and starting points."""
619 """The starting point of the very first note is 0!"""
621 part_list = self.get_part_list ()
624 factor = Rational (1)
626 attributes_object = None
627 measures = self.get_typed_children (Measure)
628 last_moment = Rational (-1)
629 last_measure_position = Rational (-1)
630 measure_position = Rational (0)
631 measure_start_moment = now
632 is_first_measure = True
633 previous_measure = None
634 # Graces at the end of a measure need to have their position set to the
638 # implicit measures are used for artificial measures, e.g. when
639 # a repeat bar line splits a bar into two halves. In this case,
640 # don't reset the measure position to 0. They are also used for
641 # upbeats (initial value of 0 fits these, too).
642 # Also, don't reset the measure position at the end of the loop,
643 # but rather when starting the next measure (since only then do we
644 # know if the next measure is implicit and continues that measure)
645 if not m.is_implicit ():
646 # Warn about possibly overfull measures and reset the position
647 if attributes_object and previous_measure and previous_measure.partial == 0:
648 length = attributes_object.get_measure_length ()
649 new_now = measure_start_moment + length
651 problem = 'incomplete'
654 ## only for verbose operation.
655 if problem <> 'incomplete' and previous_measure:
656 previous_measure.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
658 measure_start_moment = now
659 measure_position = Rational (0)
662 assign_to_next_voice = []
663 for n in m.get_all_children ():
664 # assign a voice to all measure elements
665 if (n.get_name() == 'backup'):
668 if isinstance(n, Measure_element):
669 if n.get_voice_id ():
670 voice_id = n.get_voice_id ()
671 for i in assign_to_next_voice:
672 i.voice_id = voice_id
673 assign_to_next_voice = []
676 n.voice_id = voice_id
678 assign_to_next_voice.append (n)
680 # figured bass has a duration, but applies to the next note
681 # and should not change the current measure position!
682 if isinstance (n, FiguredBass):
683 n._divisions = factor.denominator ()
685 n._measure_position = measure_position
688 if isinstance (n, Hash_text):
692 if n.__class__ == Attributes:
693 n.set_attributes_from_previous (attributes_dict)
695 attributes_dict = n._dict.copy ()
696 attributes_object = n
698 factor = Rational (1,
699 int (attributes_dict.get ('divisions').get_text ()))
702 if (n.get_maybe_exist_typed_child (Duration)):
703 mxl_dur = n.get_maybe_exist_typed_child (Duration)
704 dur = mxl_dur.get_length () * factor
706 if n.get_name() == 'backup':
708 # reset all graces before the backup to after-graces:
709 graces_to_aftergraces (pending_graces)
711 if n.get_maybe_exist_typed_child (Grace):
714 rest = n.get_maybe_exist_typed_child (Rest)
716 and attributes_object
717 and attributes_object.get_measure_length () == dur):
719 rest._is_whole_measure = True
721 if (dur > Rational (0)
722 and n.get_maybe_exist_typed_child (Chord)):
724 measure_position = last_measure_position
727 n._measure_position = measure_position
729 # For all grace notes, store the previous note, in case need
730 # to turn the grace note into an after-grace later on!
731 if isinstance(n, Note) and n.is_grace ():
732 n._prev_when = last_moment
733 n._prev_measure_position = last_measure_position
734 # After-graces are placed at the same position as the previous note
735 if isinstance(n, Note) and n.is_after_grace ():
736 # TODO: We should do the same for grace notes at the end of
737 # a measure with no following note!!!
738 n._when = last_moment
739 n._measure_position = last_measure_position
740 elif isinstance(n, Note) and n.is_grace ():
741 pending_graces.append (n)
742 elif (dur > Rational (0)):
746 if dur > Rational (0):
748 last_measure_position = measure_position
750 measure_position += dur
751 elif dur < Rational (0):
752 # backup element, reset measure position
754 measure_position += dur
755 if measure_position < 0:
756 # backup went beyond the measure start => reset to 0
757 now -= measure_position
760 last_measure_position = measure_position
761 if n._name == 'note':
762 instrument = n.get_maybe_exist_named_child ('instrument')
764 n.instrument_name = part_list.get_instrument (instrument.id)
766 # reset all graces at the end of the measure to after-graces:
767 graces_to_aftergraces (pending_graces)
769 # Incomplete first measures are not padded, but registered as partial
771 is_first_measure = False
772 # upbeats are marked as implicit measures
773 if attributes_object and m.is_implicit ():
774 length = attributes_object.get_measure_length ()
775 measure_end = measure_start_moment + length
776 if measure_end <> now:
780 # modify attributes so that only those applying to the given staff remain
781 def extract_attributes_for_staff (part, attr, staff):
782 attributes = copy.copy (attr)
783 attributes._children = [];
784 attributes._dict = attr._dict.copy ()
785 attributes._original_tag = attr
786 # copy only the relevant children over for the given staff
789 for c in attr._children:
790 if (not (hasattr (c, 'number') and (c.number != staff)) and
791 not (isinstance (c, Hash_text))):
792 attributes._children.append (c)
793 if not attributes._children:
798 def extract_voices (part):
800 measures = part.get_typed_children (Measure)
804 elements.append (Partial (m.partial))
805 elements.extend (m.get_all_children ())
806 # make sure we know all voices already so that dynamics, clefs, etc.
807 # can be assigned to the correct voices
808 voice_to_staff_dict = {}
810 voice_id = n.get_maybe_exist_named_child (u'voice')
813 vid = voice_id.get_text ()
814 elif isinstance (n, Note):
815 # TODO: Check whether we shall really use "None" here, or
816 # rather use "1" as the default?
819 staff_id = n.get_maybe_exist_named_child (u'staff')
822 sid = staff_id.get_text ()
824 # TODO: Check whether we shall really use "None" here, or
825 # rather use "1" as the default?
826 # If this is changed, need to change the corresponding
827 # check in extract_attributes_for_staff, too.
829 if vid and not voices.has_key (vid):
830 voices[vid] = Musicxml_voice()
831 if vid and sid and not n.get_maybe_exist_typed_child (Grace):
832 if not voice_to_staff_dict.has_key (vid):
833 voice_to_staff_dict[vid] = sid
834 # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
835 # need to assign staff-assigned objects like clefs, times, etc. to
836 # all the correct voices. This will never work entirely correct due
837 # to staff-switches, but that's the best we can do!
838 staff_to_voice_dict = {}
839 for (v,s) in voice_to_staff_dict.items ():
840 if not staff_to_voice_dict.has_key (s):
841 staff_to_voice_dict[s] = [v]
843 staff_to_voice_dict[s].append (v)
847 assign_to_next_note = []
850 voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
852 id = voice_id.get_text ()
856 # We don't need backup/forward any more, since we have already
857 # assigned the correct onset times.
858 # TODO: Let Grouping through. Also: link, print, bokmark sound
859 if not (isinstance (n, Note) or isinstance (n, Attributes) or
860 isinstance (n, Direction) or isinstance (n, Partial) or
861 isinstance (n, Barline) or isinstance (n, Harmony) or
862 isinstance (n, FiguredBass) or isinstance (n, Print)):
865 if isinstance (n, Attributes) and not start_attr:
869 if isinstance (n, Attributes):
870 # assign these only to the voices they really belong to!
871 for (s, vids) in staff_to_voice_dict.items ():
872 staff_attributes = part.extract_attributes_for_staff (n, s)
875 voices[v].add_element (staff_attributes)
878 if isinstance (n, Partial) or isinstance (n, Barline) or isinstance (n, Print):
879 for v in voices.keys ():
880 voices[v].add_element (n)
883 if isinstance (n, Direction):
885 voices[n.voice_id].add_element (n)
887 assign_to_next_note.append (n)
890 if isinstance (n, Harmony) or isinstance (n, FiguredBass):
891 # store the harmony or figured bass element until we encounter
892 # the next note and assign it only to that one voice.
893 assign_to_next_note.append (n)
896 if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
900 for i in assign_to_next_note:
901 voices[id].add_element (i)
902 assign_to_next_note = []
903 voices[id].add_element (n)
905 # Assign all remaining elements from assign_to_next_note to the voice
906 # of the previous note:
907 for i in assign_to_next_note:
908 voices[id].add_element (i)
909 assign_to_next_note = []
912 for (s, vids) in staff_to_voice_dict.items ():
913 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
914 staff_attributes.read_self ()
915 part._staff_attributes_dict[s] = staff_attributes
917 voices[v].insert (0, staff_attributes)
918 voices[v]._elements[0].read_self()
920 part._voices = voices
922 def get_voices (self):
924 def get_staff_attributes (self):
925 return self._staff_attributes_dict
927 class Notations (Music_xml_node):
929 ts = self.get_named_children ('tied')
930 starts = [t for t in ts if t.type == 'start']
936 def get_tuplets (self):
937 return self.get_typed_children (Tuplet)
939 class Time_modification(Music_xml_node):
940 def get_fraction (self):
941 b = self.get_maybe_exist_named_child ('actual-notes')
942 a = self.get_maybe_exist_named_child ('normal-notes')
943 return (int(a.get_text ()), int (b.get_text ()))
945 def get_normal_type (self):
946 tuplet_type = self.get_maybe_exist_named_child ('normal-type')
948 dots = self.get_named_children ('normal-dot')
949 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
950 return (log , len (dots))
955 class Accidental (Music_xml_node):
957 Music_xml_node.__init__ (self)
958 self.editorial = False
959 self.cautionary = False
961 class Music_xml_spanner (Music_xml_node):
963 if hasattr (self, 'type'):
968 if hasattr (self, 'size'):
969 return string.atoi (self.size)
973 class Wedge (Music_xml_spanner):
976 class Tuplet (Music_xml_spanner):
977 def duration_info_from_tuplet_note (self, tuplet_note):
978 tuplet_type = tuplet_note.get_maybe_exist_named_child ('tuplet-type')
980 dots = tuplet_note.get_named_children ('tuplet-dot')
981 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
982 return (log, len (dots))
986 # Return tuplet note type as (log, dots)
987 def get_normal_type (self):
988 tuplet = self.get_maybe_exist_named_child ('tuplet-normal')
990 return self.duration_info_from_tuplet_note (tuplet)
994 def get_actual_type (self):
995 tuplet = self.get_maybe_exist_named_child ('tuplet-actual')
997 return self.duration_info_from_tuplet_note (tuplet)
1001 def get_tuplet_note_count (self, tuplet_note):
1003 tuplet_nr = tuplet_note.get_maybe_exist_named_child ('tuplet-number')
1005 return int (tuplet_nr.get_text ())
1007 def get_normal_nr (self):
1008 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-normal'))
1009 def get_actual_nr (self):
1010 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-actual'))
1012 class Bracket (Music_xml_spanner):
1015 class Dashes (Music_xml_spanner):
1018 class Slur (Music_xml_spanner):
1019 def get_type (self):
1022 class Beam (Music_xml_spanner):
1023 def get_type (self):
1024 return self.get_text ()
1025 def is_primary (self):
1026 if hasattr (self, 'number'):
1027 return self.number == "1"
1031 class Wavy_line (Music_xml_spanner):
1034 class Pedal (Music_xml_spanner):
1037 class Glissando (Music_xml_spanner):
1040 class Slide (Music_xml_spanner):
1043 class Octave_shift (Music_xml_spanner):
1044 # default is 8 for the octave-shift!
1045 def get_size (self):
1046 if hasattr (self, 'size'):
1047 return string.atoi (self.size)
1051 class Chord (Music_xml_node):
1054 class Dot (Music_xml_node):
1057 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
1058 # for the inner <rest> element, not the whole rest block.
1059 class Rest (Music_xml_node):
1060 def __init__ (self):
1061 Music_xml_node.__init__ (self)
1062 self._is_whole_measure = False
1063 def is_whole_measure (self):
1064 return self._is_whole_measure
1065 def get_step (self):
1066 ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
1068 return ch.get_text ().strip ()
1071 def get_octave (self):
1072 ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
1074 oct = ch.get_text ().strip ()
1079 class Type (Music_xml_node):
1081 class Grace (Music_xml_node):
1083 class Staff (Music_xml_node):
1086 class Direction (Measure_element):
1088 class DirType (Music_xml_node):
1091 class Bend (Music_xml_node):
1092 def bend_alter (self):
1093 alter = self.get_maybe_exist_named_child ('bend-alter')
1094 return interpret_alter_element (alter)
1096 class Words (Music_xml_node):
1099 class Harmony (Music_xml_node):
1102 class ChordPitch (Music_xml_node):
1103 def step_class_name (self):
1105 def alter_class_name (self):
1106 return u'root-alter'
1107 def get_step (self):
1108 ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
1109 return ch.get_text ().strip ()
1110 def get_alteration (self):
1111 ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
1112 return interpret_alter_element (ch)
1114 class Root (ChordPitch):
1117 class Bass (ChordPitch):
1118 def step_class_name (self):
1120 def alter_class_name (self):
1121 return u'bass-alter'
1123 class ChordModification (Music_xml_node):
1124 def get_type (self):
1125 ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
1126 return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
1127 def get_value (self):
1128 ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
1131 value = int (ch.get_text ().strip ())
1133 def get_alter (self):
1134 ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
1135 return interpret_alter_element (ch)
1138 class Frame (Music_xml_node):
1139 def get_frets (self):
1140 return self.get_named_child_value_number ('frame-frets', 4)
1141 def get_strings (self):
1142 return self.get_named_child_value_number ('frame-strings', 6)
1143 def get_first_fret (self):
1144 return self.get_named_child_value_number ('first-fret', 1)
1146 class Frame_Note (Music_xml_node):
1147 def get_string (self):
1148 return self.get_named_child_value_number ('string', 1)
1149 def get_fret (self):
1150 return self.get_named_child_value_number ('fret', 0)
1151 def get_fingering (self):
1152 return self.get_named_child_value_number ('fingering', -1)
1153 def get_barre (self):
1154 n = self.get_maybe_exist_named_child ('barre')
1156 return getattr (n, 'type', '')
1160 class FiguredBass (Music_xml_node):
1163 class Beats (Music_xml_node):
1166 class BeatType (Music_xml_node):
1169 class BeatUnit (Music_xml_node):
1172 class BeatUnitDot (Music_xml_node):
1175 class PerMinute (Music_xml_node):
1178 class Print (Music_xml_node):
1183 ## need this, not all classes are instantiated
1184 ## for every input file. Only add those classes, that are either directly
1185 ## used by class name or extend Music_xml_node in some way!
1187 '#comment': Hash_comment,
1189 'accidental': Accidental,
1190 'attributes': Attributes,
1192 'bar-style': BarStyle,
1196 'beat-type': BeatType,
1197 'beat-unit': BeatUnit,
1198 'beat-unit-dot': BeatUnitDot,
1200 'bracket' : Bracket,
1203 'degree' : ChordModification,
1205 'direction': Direction,
1206 'direction-type': DirType,
1207 'duration': Duration,
1211 'frame-note': Frame_Note,
1212 'figured-bass': FiguredBass,
1213 'glissando': Glissando,
1216 'identification': Identification,
1217 'key-alter': KeyAlter,
1218 'key-octave': KeyOctave,
1219 'key-step': KeyStep,
1222 'notations': Notations,
1224 'octave-shift': Octave_shift,
1226 'part-group': Part_group,
1227 'part-list': Part_list,
1229 'per-minute': PerMinute,
1234 'score-part': Score_part,
1238 'syllabic': Syllabic,
1240 'time-modification': Time_modification,
1243 'unpitched': Unpitched,
1244 'wavy-line': Wavy_line,
1250 def name2class_name (name):
1251 name = name.replace ('-', '_')
1252 name = name.replace ('#', 'hash_')
1253 name = name[0].upper() + name[1:].lower()
1257 def get_class (name):
1258 classname = class_dict.get (name)
1262 class_name = name2class_name (name)
1263 klass = new.classobj (class_name, (Music_xml_node,) , {})
1264 class_dict[name] = klass
1267 def lxml_demarshal_node (node):
1270 # Ignore comment nodes, which are also returned by the etree parser!
1271 if name is None or node.__class__.__name__ == "_Comment":
1273 klass = get_class (name)
1276 py_node._original = node
1277 py_node._name = name
1278 py_node._data = node.text
1279 py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1280 py_node._children = filter (lambda x: x, py_node._children)
1282 for c in py_node._children:
1285 for (k, v) in node.items ():
1286 py_node.__dict__[k] = v
1287 py_node._attribute_dict[k] = v
1291 def minidom_demarshal_node (node):
1292 name = node.nodeName
1294 klass = get_class (name)
1296 py_node._name = name
1297 py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1298 for c in py_node._children:
1302 for (nm, value) in node.attributes.items ():
1303 py_node.__dict__[nm] = value
1304 py_node._attribute_dict[nm] = value
1306 py_node._data = None
1307 if node.nodeType == node.TEXT_NODE and node.data:
1308 py_node._data = node.data
1310 py_node._original = node
1314 if __name__ == '__main__':
1317 tree = lxml.etree.parse ('beethoven.xml')
1318 mxl_tree = lxml_demarshal_node (tree.getroot ())
1319 ks = class_dict.keys ()
1321 print '\n'.join (ks)