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)
40 def interpret_alter_element (alter_elm):
43 val = eval(alter_elm.get_text ())
44 if type (val) in (int, float):
54 self._name = 'xml_node'
56 self._attribute_dict = {}
58 def get_parent (self):
62 return self._parent.get_typed_children (self.__class__)[0] == self
73 if not self._children:
76 return ''.join ([c.get_text () for c in self._children])
78 def message (self, msg):
79 ly.stderr_write (msg+'\n')
83 sys.stderr.write (' In: <%s %s>\n' % (p._name, ' '.join (['%s=%s' % item for item in p._attribute_dict.items ()])))
86 def dump (self, indent = ''):
87 sys.stderr.write ('%s<%s%s>' % (indent, self._name, ''.join ([' %s=%s' % item for item in self._attribute_dict.items ()])))
88 non_text_children = [c for c in self._children if not isinstance (c, Hash_text)]
90 sys.stderr.write ('\n')
91 for c in self._children:
94 sys.stderr.write (indent)
95 sys.stderr.write ('</%s>\n' % self._name)
98 def get_typed_children (self, klass):
102 return [c for c in self._children if isinstance(c, klass)]
104 def get_named_children (self, nm):
105 return self.get_typed_children (get_class (nm))
107 def get_named_child (self, nm):
108 return self.get_maybe_exist_named_child (nm)
110 def get_children (self, predicate):
111 return [c for c in self._children if predicate(c)]
113 def get_all_children (self):
114 return self._children
116 def get_maybe_exist_named_child (self, name):
117 return self.get_maybe_exist_typed_child (get_class (name))
119 def get_maybe_exist_typed_child (self, klass):
120 cn = self.get_typed_children (klass)
126 raise "More than 1 child", klass
128 def get_unique_typed_child (self, klass):
129 cn = self.get_typed_children(klass)
131 sys.stderr.write (self.__dict__ + '\n')
132 raise 'Child is not unique for', (klass, 'found', cn)
136 def get_named_child_value_number (self, name, default):
137 n = self.get_maybe_exist_named_child (name)
139 return string.atoi (n.get_text())
144 class Music_xml_node (Xml_node):
146 Xml_node.__init__ (self)
147 self.duration = Rational (0)
148 self.start = Rational (0)
150 class Work (Xml_node):
151 def get_work_information (self, tag):
152 wt = self.get_maybe_exist_named_child (tag)
154 return wt.get_text ()
158 def get_work_title (self):
159 return self.get_work_information ('work-title')
160 def get_work_number (self):
161 return self.get_work_information ('work-number')
163 return self.get_work_information ('opus')
165 class Identification (Xml_node):
166 def get_rights (self):
167 rights = self.get_named_children ('rights')
170 ret.append (r.get_text ())
171 return string.join (ret, "\n")
173 def get_creator (self, type):
174 creators = self.get_named_children ('creator')
175 # return the first creator tag that has the particular type
177 if hasattr (i, 'type') and i.type == type:
181 def get_composer (self):
182 c = self.get_creator ('composer')
185 creators = self.get_named_children ('creator')
186 # return the first creator tag that has no type at all
188 if not hasattr (i, 'type'):
191 def get_arranger (self):
192 return self.get_creator ('arranger')
193 def get_editor (self):
194 return self.get_creator ('editor')
196 v = self.get_creator ('lyricist')
199 v = self.get_creator ('poet')
202 def get_encoding_information (self, type):
203 enc = self.get_named_children ('encoding')
205 children = enc[0].get_named_children (type)
207 return children[0].get_text ()
211 def get_encoding_software (self):
212 return self.get_encoding_information ('software')
213 def get_encoding_date (self):
214 return self.get_encoding_information ('encoding-date')
215 def get_encoding_person (self):
216 return self.get_encoding_information ('encoder')
217 def get_encoding_description (self):
218 return self.get_encoding_information ('encoding-description')
220 def get_encoding_software_list (self):
221 enc = self.get_named_children ('encoding')
224 softwares = e.get_named_children ('software')
226 software.append (s.get_text ())
229 def get_file_description (self):
230 misc = self.get_named_children ('miscellaneous')
232 misc_fields = m.get_named_children ('miscellaneous-field')
233 for mf in misc_fields:
234 if hasattr (mf, 'name') and mf.name == 'description':
235 return mf.get_text ()
240 class Duration (Music_xml_node):
241 def get_length (self):
242 dur = int (self.get_text ()) * Rational (1,4)
245 class Hash_comment (Music_xml_node):
247 class Hash_text (Music_xml_node):
248 def dump (self, indent = ''):
249 sys.stderr.write ('%s' % string.strip (self._data))
251 class Pitch (Music_xml_node):
253 ch = self.get_unique_typed_child (get_class (u'step'))
254 step = ch.get_text ().strip ()
256 def get_octave (self):
257 ch = self.get_unique_typed_child (get_class (u'octave'))
258 octave = ch.get_text ().strip ()
261 def get_alteration (self):
262 ch = self.get_maybe_exist_typed_child (get_class (u'alter'))
263 return interpret_alter_element (ch)
265 class Unpitched (Music_xml_node):
267 ch = self.get_unique_typed_child (get_class (u'display-step'))
268 step = ch.get_text ().strip ()
271 def get_octave (self):
272 ch = self.get_unique_typed_child (get_class (u'display-octave'))
275 octave = ch.get_text ().strip ()
280 class Measure_element (Music_xml_node):
281 def get_voice_id (self):
282 voice_id = self.get_maybe_exist_named_child ('voice')
284 return voice_id.get_text ()
289 # Look at all measure elements (previously we had self.__class__, which
290 # only looked at objects of the same type!
291 cn = self._parent.get_typed_children (Measure_element)
292 # But only look at the correct voice; But include Attributes, too, which
293 # are not tied to any particular voice
294 cn = [c for c in cn if (c.get_voice_id () == self.get_voice_id ()) or isinstance (c, Attributes)]
297 class Attributes (Measure_element):
299 Measure_element.__init__ (self)
301 self._original_tag = None
302 self._time_signature_cache = None
305 cn = self._parent.get_typed_children (self.__class__)
306 if self._original_tag:
307 return cn[0] == self._original_tag
311 def set_attributes_from_previous (self, dict):
312 self._dict.update (dict)
314 def read_self (self):
315 for c in self.get_all_children ():
316 self._dict[c.get_name()] = c
318 def get_named_attribute (self, name):
319 return self._dict.get (name)
321 def single_time_sig_to_fraction (self, sig):
327 return Rational (n, sig[-1])
329 def get_measure_length (self):
330 sig = self.get_time_signature ()
331 if not sig or len (sig) == 0:
333 if isinstance (sig[0], list):
334 # Complex compound time signature
337 l += self.single_time_sig_to_fraction (i)
340 # Simple (maybe compound) time signature of the form (beat, ..., type)
341 return self.single_time_sig_to_fraction (sig)
344 def get_time_signature (self):
345 "Return time sig as a (beat, beat-type) tuple. For compound signatures,"
346 "return either (beat, beat,..., beat-type) or ((beat,..., type), "
347 "(beat,..., type), ...)."
348 if self._time_signature_cache:
349 return self._time_signature_cache
352 mxl = self.get_named_attribute ('time')
356 if mxl.get_maybe_exist_named_child ('senza-misura'):
357 # TODO: Handle pieces without a time signature!
358 error (_ ("Senza-misura time signatures are not yet supported!"))
363 for i in mxl.get_all_children ():
364 if isinstance (i, Beats):
365 beats = string.split (i.get_text ().strip (), "+")
366 current_sig = [int (j) for j in beats]
367 elif isinstance (i, BeatType):
368 current_sig.append (int (i.get_text ()))
369 signature.append (current_sig)
371 if isinstance (signature[0], list) and len (signature) == 1:
372 signature = signature[0]
373 self._time_signature_cache = signature
375 except (KeyError, ValueError):
376 self.message (_ ("Unable to interpret time signature! Falling back to 4/4."))
379 # returns clef information in the form ("cleftype", position, octave-shift)
380 def get_clef_information (self):
381 clefinfo = ['G', 2, 0]
382 mxl = self.get_named_attribute ('clef')
385 sign = mxl.get_maybe_exist_named_child ('sign')
387 clefinfo[0] = sign.get_text()
388 line = mxl.get_maybe_exist_named_child ('line')
390 clefinfo[1] = string.atoi (line.get_text ())
391 octave = mxl.get_maybe_exist_named_child ('clef-octave-change')
393 clefinfo[2] = string.atoi (octave.get_text ())
396 def get_key_signature (self):
397 "return (fifths, mode) tuple if the key signatures is given as "
398 "major/minor in the Circle of fifths. Otherwise return an alterations"
399 "list of the form [[step,alter<,octave>], [step,alter<,octave>], ...], "
400 "where the octave values are optional."
402 key = self.get_named_attribute ('key')
405 fifths_elm = key.get_maybe_exist_named_child ('fifths')
407 mode_node = key.get_maybe_exist_named_child ('mode')
410 mode = mode_node.get_text ()
411 if not mode or mode == '':
413 fifths = int (fifths_elm.get_text ())
414 # TODO: Shall we try to convert the key-octave and the cancel, too?
415 return (fifths, mode)
419 for i in key.get_all_children ():
420 if isinstance (i, KeyStep):
421 current_step = i.get_text ().strip ()
422 elif isinstance (i, KeyAlter):
423 alterations.append ([current_step, interpret_alter_element (i)])
424 elif isinstance (i, KeyOctave):
426 if hasattr (i, 'number'):
428 if (nr > 0) and (nr <= len (alterations)):
429 # MusicXML Octave 4 is middle C -> shift to 0
430 alterations[nr-1].append (int (i.get_text ())-4)
432 i.message (_ ("Key alteration octave given for a "
433 "non-existing alteration nr. %s, available numbers: %s!") % (nr, len(alterations)))
436 def get_transposition (self):
437 return self.get_named_attribute ('transpose')
439 class KeyAlter (Music_xml_node):
441 class KeyStep (Music_xml_node):
443 class KeyOctave (Music_xml_node):
447 class Barline (Measure_element):
449 class BarStyle (Music_xml_node):
451 class Partial (Measure_element):
452 def __init__ (self, partial):
453 Measure_element.__init__ (self)
454 self.partial = partial
456 class Note (Measure_element):
458 Measure_element.__init__ (self)
459 self.instrument_name = ''
460 self._after_grace = False
462 return self.get_maybe_exist_named_child (u'grace')
463 def is_after_grace (self):
464 if not self.is_grace():
466 gr = self.get_maybe_exist_typed_child (Grace)
467 return self._after_grace or hasattr (gr, 'steal-time-previous');
469 def get_duration_log (self):
470 ch = self.get_maybe_exist_named_child (u'type')
473 log = ch.get_text ().strip()
474 return musicxml_duration_to_log (log)
475 elif self.get_maybe_exist_named_child (u'grace'):
476 # FIXME: is it ok to default to eight note for grace notes?
481 def get_duration_info (self):
482 log = self.get_duration_log ()
484 dots = len (self.get_typed_children (Dot))
489 def get_factor (self):
492 def get_pitches (self):
493 return self.get_typed_children (get_class (u'pitch'))
495 class Part_list (Music_xml_node):
497 Music_xml_node.__init__ (self)
498 self._id_instrument_name_dict = {}
500 def generate_id_instrument_dict (self):
502 ## not empty to make sure this happens only once.
504 for score_part in self.get_named_children ('score-part'):
505 for instr in score_part.get_named_children ('score-instrument'):
507 name = instr.get_named_child ("instrument-name")
508 mapping[id] = name.get_text ()
510 self._id_instrument_name_dict = mapping
512 def get_instrument (self, id):
513 if not self._id_instrument_name_dict:
514 self.generate_id_instrument_dict()
516 instrument_name = self._id_instrument_name_dict.get (id)
518 return instrument_name
520 ly.stderr_write (_ ("Unable to find instrument for ID=%s\n") % id)
523 class Part_group (Music_xml_node):
525 class Score_part (Music_xml_node):
528 class Measure (Music_xml_node):
530 Music_xml_node.__init__ (self)
532 def is_implicit (self):
533 return hasattr (self, 'implicit') and self.implicit == 'yes'
534 def get_notes (self):
535 return self.get_typed_children (get_class (u'note'))
537 class Syllabic (Music_xml_node):
538 def continued (self):
539 text = self.get_text()
540 return (text == "begin") or (text == "middle")
541 class Elision (Music_xml_node):
543 class Extend (Music_xml_node):
545 class Text (Music_xml_node):
548 class Lyric (Music_xml_node):
549 def get_number (self):
550 if hasattr (self, 'number'):
555 class Musicxml_voice:
559 self._start_staff = None
561 self._has_lyrics = False
563 def add_element (self, e):
564 self._elements.append (e)
565 if (isinstance (e, Note)
566 and e.get_maybe_exist_typed_child (Staff)):
567 name = e.get_maybe_exist_typed_child (Staff).get_text ()
569 if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
570 self._start_staff = name
571 self._staves[name] = True
573 lyrics = e.get_typed_children (Lyric)
574 if not self._has_lyrics:
575 self.has_lyrics = len (lyrics) > 0
579 if (nr > 0) and not (nr in self._lyrics):
580 self._lyrics.append (nr)
582 def insert (self, idx, e):
583 self._elements.insert (idx, e)
585 def get_lyrics_numbers (self):
586 if (len (self._lyrics) == 0) and self._has_lyrics:
587 #only happens if none of the <lyric> tags has a number attribute
593 def graces_to_aftergraces (pending_graces):
594 for gr in pending_graces:
595 gr._when = gr._prev_when
596 gr._measure_position = gr._prev_measure_position
597 gr._after_grace = True
600 class Part (Music_xml_node):
602 Music_xml_node.__init__ (self)
604 self._staff_attributes_dict = {}
606 def get_part_list (self):
608 while n and n.get_name() != 'score-partwise':
611 return n.get_named_child ('part-list')
613 def interpret (self):
614 """Set durations and starting points."""
615 """The starting point of the very first note is 0!"""
617 part_list = self.get_part_list ()
620 factor = Rational (1)
622 attributes_object = None
623 measures = self.get_typed_children (Measure)
624 last_moment = Rational (-1)
625 last_measure_position = Rational (-1)
626 measure_position = Rational (0)
627 measure_start_moment = now
628 is_first_measure = True
629 previous_measure = None
630 # Graces at the end of a measure need to have their position set to the
634 # implicit measures are used for artificial measures, e.g. when
635 # a repeat bar line splits a bar into two halves. In this case,
636 # don't reset the measure position to 0. They are also used for
637 # upbeats (initial value of 0 fits these, too).
638 # Also, don't reset the measure position at the end of the loop,
639 # but rather when starting the next measure (since only then do we
640 # know if the next measure is implicit and continues that measure)
641 if not m.is_implicit ():
642 # Warn about possibly overfull measures and reset the position
643 if attributes_object and previous_measure and previous_measure.partial == 0:
644 length = attributes_object.get_measure_length ()
645 new_now = measure_start_moment + length
647 problem = 'incomplete'
650 ## only for verbose operation.
651 if problem <> 'incomplete' and previous_measure:
652 previous_measure.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
654 measure_start_moment = now
655 measure_position = Rational (0)
657 for n in m.get_all_children ():
658 # figured bass has a duration, but applies to the next note
659 # and should not change the current measure position!
660 if isinstance (n, FiguredBass):
661 n._divisions = factor.denominator ()
663 n._measure_position = measure_position
666 if isinstance (n, Hash_text):
670 if n.__class__ == Attributes:
671 n.set_attributes_from_previous (attributes_dict)
673 attributes_dict = n._dict.copy ()
674 attributes_object = n
676 factor = Rational (1,
677 int (attributes_dict.get ('divisions').get_text ()))
680 if (n.get_maybe_exist_typed_child (Duration)):
681 mxl_dur = n.get_maybe_exist_typed_child (Duration)
682 dur = mxl_dur.get_length () * factor
684 if n.get_name() == 'backup':
686 # reset all graces before the backup to after-graces:
687 graces_to_aftergraces (pending_graces)
689 if n.get_maybe_exist_typed_child (Grace):
692 rest = n.get_maybe_exist_typed_child (Rest)
694 and attributes_object
695 and attributes_object.get_measure_length () == dur):
697 rest._is_whole_measure = True
699 if (dur > Rational (0)
700 and n.get_maybe_exist_typed_child (Chord)):
702 measure_position = last_measure_position
705 n._measure_position = measure_position
707 # For all grace notes, store the previous note, in case need
708 # to turn the grace note into an after-grace later on!
709 if isinstance(n, Note) and n.is_grace ():
710 n._prev_when = last_moment
711 n._prev_measure_position = last_measure_position
712 # After-graces are placed at the same position as the previous note
713 if isinstance(n, Note) and n.is_after_grace ():
714 # TODO: We should do the same for grace notes at the end of
715 # a measure with no following note!!!
716 n._when = last_moment
717 n._measure_position = last_measure_position
718 elif isinstance(n, Note) and n.is_grace ():
719 pending_graces.append (n)
720 elif (dur > Rational (0)):
724 if dur > Rational (0):
726 last_measure_position = measure_position
728 measure_position += dur
729 elif dur < Rational (0):
730 # backup element, reset measure position
732 measure_position += dur
733 if measure_position < 0:
734 # backup went beyond the measure start => reset to 0
735 now -= measure_position
738 last_measure_position = measure_position
739 if n._name == 'note':
740 instrument = n.get_maybe_exist_named_child ('instrument')
742 n.instrument_name = part_list.get_instrument (instrument.id)
744 # reset all graces at the end of the measure to after-graces:
745 graces_to_aftergraces (pending_graces)
747 # Incomplete first measures are not padded, but registered as partial
749 is_first_measure = False
750 # upbeats are marked as implicit measures
751 if attributes_object and m.is_implicit ():
752 length = attributes_object.get_measure_length ()
753 measure_end = measure_start_moment + length
754 if measure_end <> now:
758 # modify attributes so that only those applying to the given staff remain
759 def extract_attributes_for_staff (part, attr, staff):
760 attributes = copy.copy (attr)
761 attributes._children = [];
762 attributes._dict = attr._dict.copy ()
763 attributes._original_tag = attr
764 # copy only the relevant children over for the given staff
767 for c in attr._children:
768 if (not (hasattr (c, 'number') and (c.number != staff)) and
769 not (isinstance (c, Hash_text))):
770 attributes._children.append (c)
771 if not attributes._children:
776 def extract_voices (part):
778 measures = part.get_typed_children (Measure)
782 elements.append (Partial (m.partial))
783 elements.extend (m.get_all_children ())
784 # make sure we know all voices already so that dynamics, clefs, etc.
785 # can be assigned to the correct voices
786 voice_to_staff_dict = {}
788 voice_id = n.get_maybe_exist_named_child (u'voice')
791 vid = voice_id.get_text ()
792 elif isinstance (n, Note):
793 # TODO: Check whether we shall really use "None" here, or
794 # rather use "1" as the default?
797 staff_id = n.get_maybe_exist_named_child (u'staff')
800 sid = staff_id.get_text ()
802 # TODO: Check whether we shall really use "None" here, or
803 # rather use "1" as the default?
804 # If this is changed, need to change the corresponding
805 # check in extract_attributes_for_staff, too.
807 if vid and not voices.has_key (vid):
808 voices[vid] = Musicxml_voice()
809 if vid and sid and not n.get_maybe_exist_typed_child (Grace):
810 if not voice_to_staff_dict.has_key (vid):
811 voice_to_staff_dict[vid] = sid
812 # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
813 # need to assign staff-assigned objects like clefs, times, etc. to
814 # all the correct voices. This will never work entirely correct due
815 # to staff-switches, but that's the best we can do!
816 staff_to_voice_dict = {}
817 for (v,s) in voice_to_staff_dict.items ():
818 if not staff_to_voice_dict.has_key (s):
819 staff_to_voice_dict[s] = [v]
821 staff_to_voice_dict[s].append (v)
825 assign_to_next_note = []
828 voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
830 id = voice_id.get_text ()
834 # We don't need backup/forward any more, since we have already
835 # assigned the correct onset times.
836 # TODO: Let Grouping through. Also: link, print, bokmark sound
837 if not (isinstance (n, Note) or isinstance (n, Attributes) or
838 isinstance (n, Direction) or isinstance (n, Partial) or
839 isinstance (n, Barline) or isinstance (n, Harmony) or
840 isinstance (n, FiguredBass) or isinstance (n, Print)):
843 if isinstance (n, Attributes) and not start_attr:
847 if isinstance (n, Attributes):
848 # assign these only to the voices they really belong to!
849 for (s, vids) in staff_to_voice_dict.items ():
850 staff_attributes = part.extract_attributes_for_staff (n, s)
853 voices[v].add_element (staff_attributes)
856 if isinstance (n, Partial) or isinstance (n, Barline) or isinstance (n, Print):
857 for v in voices.keys ():
858 voices[v].add_element (n)
861 if isinstance (n, Direction):
862 staff_id = n.get_maybe_exist_named_child (u'staff')
864 staff_id = staff_id.get_text ()
866 dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
868 dir_voices = voices.keys ()
870 voices[v].add_element (n)
873 if isinstance (n, Harmony) or isinstance (n, FiguredBass):
874 # store the harmony or figured bass element until we encounter
875 # the next note and assign it only to that one voice.
876 assign_to_next_note.append (n)
879 if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
883 for i in assign_to_next_note:
884 voices[id].add_element (i)
885 assign_to_next_note = []
886 voices[id].add_element (n)
888 # Assign all remaining elements from assign_to_next_note to the voice
889 # of the previous note:
890 for i in assign_to_next_note:
891 voices[id].add_element (i)
892 assign_to_next_note = []
895 for (s, vids) in staff_to_voice_dict.items ():
896 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
897 staff_attributes.read_self ()
898 part._staff_attributes_dict[s] = staff_attributes
900 voices[v].insert (0, staff_attributes)
901 voices[v]._elements[0].read_self()
903 part._voices = voices
905 def get_voices (self):
907 def get_staff_attributes (self):
908 return self._staff_attributes_dict
910 class Notations (Music_xml_node):
912 ts = self.get_named_children ('tied')
913 starts = [t for t in ts if t.type == 'start']
919 def get_tuplets (self):
920 return self.get_typed_children (Tuplet)
922 class Time_modification(Music_xml_node):
923 def get_fraction (self):
924 b = self.get_maybe_exist_named_child ('actual-notes')
925 a = self.get_maybe_exist_named_child ('normal-notes')
926 return (int(a.get_text ()), int (b.get_text ()))
928 def get_normal_type (self):
929 tuplet_type = self.get_maybe_exist_named_child ('normal-type')
931 dots = self.get_named_children ('normal-dot')
932 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
933 return (log , len (dots))
938 class Accidental (Music_xml_node):
940 Music_xml_node.__init__ (self)
941 self.editorial = False
942 self.cautionary = False
944 class Music_xml_spanner (Music_xml_node):
946 if hasattr (self, 'type'):
951 if hasattr (self, 'size'):
952 return string.atoi (self.size)
956 class Wedge (Music_xml_spanner):
959 class Tuplet (Music_xml_spanner):
960 def duration_info_from_tuplet_note (self, tuplet_note):
961 tuplet_type = tuplet_note.get_maybe_exist_named_child ('tuplet-type')
963 dots = tuplet_note.get_named_children ('tuplet-dot')
964 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
965 return (log, len (dots))
969 # Return tuplet note type as (log, dots)
970 def get_normal_type (self):
971 tuplet = self.get_maybe_exist_named_child ('tuplet-normal')
973 return self.duration_info_from_tuplet_note (tuplet)
977 def get_actual_type (self):
978 tuplet = self.get_maybe_exist_named_child ('tuplet-actual')
980 return self.duration_info_from_tuplet_note (tuplet)
984 def get_tuplet_note_count (self, tuplet_note):
986 tuplet_nr = tuplet_note.get_maybe_exist_named_child ('tuplet-number')
988 return int (tuplet_nr.get_text ())
990 def get_normal_nr (self):
991 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-normal'))
992 def get_actual_nr (self):
993 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-actual'))
995 class Bracket (Music_xml_spanner):
998 class Dashes (Music_xml_spanner):
1001 class Slur (Music_xml_spanner):
1002 def get_type (self):
1005 class Beam (Music_xml_spanner):
1006 def get_type (self):
1007 return self.get_text ()
1008 def is_primary (self):
1009 if hasattr (self, 'number'):
1010 return self.number == "1"
1014 class Wavy_line (Music_xml_spanner):
1017 class Pedal (Music_xml_spanner):
1020 class Glissando (Music_xml_spanner):
1023 class Slide (Music_xml_spanner):
1026 class Octave_shift (Music_xml_spanner):
1027 # default is 8 for the octave-shift!
1028 def get_size (self):
1029 if hasattr (self, 'size'):
1030 return string.atoi (self.size)
1034 class Chord (Music_xml_node):
1037 class Dot (Music_xml_node):
1040 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
1041 # for the inner <rest> element, not the whole rest block.
1042 class Rest (Music_xml_node):
1043 def __init__ (self):
1044 Music_xml_node.__init__ (self)
1045 self._is_whole_measure = False
1046 def is_whole_measure (self):
1047 return self._is_whole_measure
1048 def get_step (self):
1049 ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
1051 return ch.get_text ().strip ()
1054 def get_octave (self):
1055 ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
1057 oct = ch.get_text ().strip ()
1062 class Type (Music_xml_node):
1064 class Grace (Music_xml_node):
1066 class Staff (Music_xml_node):
1069 class Direction (Music_xml_node):
1071 class DirType (Music_xml_node):
1074 class Bend (Music_xml_node):
1075 def bend_alter (self):
1076 alter = self.get_maybe_exist_named_child ('bend-alter')
1077 return interpret_alter_element (alter)
1079 class Words (Music_xml_node):
1082 class Harmony (Music_xml_node):
1085 class ChordPitch (Music_xml_node):
1086 def step_class_name (self):
1088 def alter_class_name (self):
1089 return u'root-alter'
1090 def get_step (self):
1091 ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
1092 return ch.get_text ().strip ()
1093 def get_alteration (self):
1094 ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
1095 return interpret_alter_element (ch)
1097 class Root (ChordPitch):
1100 class Bass (ChordPitch):
1101 def step_class_name (self):
1103 def alter_class_name (self):
1104 return u'bass-alter'
1106 class ChordModification (Music_xml_node):
1107 def get_type (self):
1108 ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
1109 return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
1110 def get_value (self):
1111 ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
1114 value = int (ch.get_text ().strip ())
1116 def get_alter (self):
1117 ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
1118 return interpret_alter_element (ch)
1121 class Frame (Music_xml_node):
1122 def get_frets (self):
1123 return self.get_named_child_value_number ('frame-frets', 4)
1124 def get_strings (self):
1125 return self.get_named_child_value_number ('frame-strings', 6)
1126 def get_first_fret (self):
1127 return self.get_named_child_value_number ('first-fret', 1)
1129 class Frame_Note (Music_xml_node):
1130 def get_string (self):
1131 return self.get_named_child_value_number ('string', 1)
1132 def get_fret (self):
1133 return self.get_named_child_value_number ('fret', 0)
1134 def get_fingering (self):
1135 return self.get_named_child_value_number ('fingering', -1)
1136 def get_barre (self):
1137 n = self.get_maybe_exist_named_child ('barre')
1139 return getattr (n, 'type', '')
1143 class FiguredBass (Music_xml_node):
1146 class Beats (Music_xml_node):
1149 class BeatType (Music_xml_node):
1152 class BeatUnit (Music_xml_node):
1155 class BeatUnitDot (Music_xml_node):
1158 class PerMinute (Music_xml_node):
1161 class Print (Music_xml_node):
1166 ## need this, not all classes are instantiated
1167 ## for every input file. Only add those classes, that are either directly
1168 ## used by class name or extend Music_xml_node in some way!
1170 '#comment': Hash_comment,
1172 'accidental': Accidental,
1173 'attributes': Attributes,
1175 'bar-style': BarStyle,
1179 'beat-type': BeatType,
1180 'beat-unit': BeatUnit,
1181 'beat-unit-dot': BeatUnitDot,
1183 'bracket' : Bracket,
1186 'degree' : ChordModification,
1188 'direction': Direction,
1189 'direction-type': DirType,
1190 'duration': Duration,
1194 'frame-note': Frame_Note,
1195 'figured-bass': FiguredBass,
1196 'glissando': Glissando,
1199 'identification': Identification,
1200 'key-alter': KeyAlter,
1201 'key-octave': KeyOctave,
1202 'key-step': KeyStep,
1205 'notations': Notations,
1207 'octave-shift': Octave_shift,
1209 'part-group': Part_group,
1210 'part-list': Part_list,
1212 'per-minute': PerMinute,
1217 'score-part': Score_part,
1221 'syllabic': Syllabic,
1223 'time-modification': Time_modification,
1226 'unpitched': Unpitched,
1227 'wavy-line': Wavy_line,
1233 def name2class_name (name):
1234 name = name.replace ('-', '_')
1235 name = name.replace ('#', 'hash_')
1236 name = name[0].upper() + name[1:].lower()
1240 def get_class (name):
1241 classname = class_dict.get (name)
1245 class_name = name2class_name (name)
1246 klass = new.classobj (class_name, (Music_xml_node,) , {})
1247 class_dict[name] = klass
1250 def lxml_demarshal_node (node):
1253 # Ignore comment nodes, which are also returned by the etree parser!
1254 if name is None or node.__class__.__name__ == "_Comment":
1256 klass = get_class (name)
1259 py_node._original = node
1260 py_node._name = name
1261 py_node._data = node.text
1262 py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1263 py_node._children = filter (lambda x: x, py_node._children)
1265 for c in py_node._children:
1268 for (k, v) in node.items ():
1269 py_node.__dict__[k] = v
1270 py_node._attribute_dict[k] = v
1274 def minidom_demarshal_node (node):
1275 name = node.nodeName
1277 klass = get_class (name)
1279 py_node._name = name
1280 py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1281 for c in py_node._children:
1285 for (nm, value) in node.attributes.items ():
1286 py_node.__dict__[nm] = value
1287 py_node._attribute_dict[nm] = value
1289 py_node._data = None
1290 if node.nodeType == node.TEXT_NODE and node.data:
1291 py_node._data = node.data
1293 py_node._original = node
1297 if __name__ == '__main__':
1300 tree = lxml.etree.parse ('beethoven.xml')
1301 mxl_tree = lxml_demarshal_node (tree.getroot ())
1302 ks = class_dict.keys ()
1304 print '\n'.join (ks)