1 # -*- coding: utf-8 -*-
13 ly.stderr_write ((_ ("error: %s") % str) + "\n")
16 def escape_ly_output_string (input_string):
17 return_string = input_string
18 needs_quotes = not re.match (u"^[a-zA-ZäöüÜÄÖßñ]*$", return_string);
20 return_string = "\"" + string.replace (return_string, "\"", "\\\"") + "\""
24 def musicxml_duration_to_log (dur):
36 'long': -2}.get (dur, 0)
45 self._name = 'xml_node'
47 self._attribute_dict = {}
49 def get_parent (self):
53 return self._parent.get_typed_children (self.__class__)[0] == self
64 if not self._children:
67 return ''.join ([c.get_text () for c in self._children])
69 def message (self, msg):
70 ly.stderr_write (msg+'\n')
74 sys.stderr.write (' In: <%s %s>\n' % (p._name, ' '.join (['%s=%s' % item for item in p._attribute_dict.items ()])))
77 def dump (self, indent = ''):
78 sys.stderr.write ('%s<%s%s>' % (indent, self._name, ''.join ([' %s=%s' % item for item in self._attribute_dict.items ()])))
79 non_text_children = [c for c in self._children if not isinstance (c, Hash_text)]
81 sys.stderr.write ('\n')
82 for c in self._children:
85 sys.stderr.write (indent)
86 sys.stderr.write ('</%s>\n' % self._name)
89 def get_typed_children (self, klass):
93 return [c for c in self._children if isinstance(c, klass)]
95 def get_named_children (self, nm):
96 return self.get_typed_children (get_class (nm))
98 def get_named_child (self, nm):
99 return self.get_maybe_exist_named_child (nm)
101 def get_children (self, predicate):
102 return [c for c in self._children if predicate(c)]
104 def get_all_children (self):
105 return self._children
107 def get_maybe_exist_named_child (self, name):
108 return self.get_maybe_exist_typed_child (get_class (name))
110 def get_maybe_exist_typed_child (self, klass):
111 cn = self.get_typed_children (klass)
117 raise "More than 1 child", klass
119 def get_unique_typed_child (self, klass):
120 cn = self.get_typed_children(klass)
122 sys.stderr.write (self.__dict__ + '\n')
123 raise 'Child is not unique for', (klass, 'found', cn)
127 def get_named_child_value_number (self, name, default):
128 n = self.get_maybe_exist_named_child (name)
130 return string.atoi (n.get_text())
135 class Music_xml_node (Xml_node):
137 Xml_node.__init__ (self)
138 self.duration = Rational (0)
139 self.start = Rational (0)
141 class Work (Xml_node):
142 def get_work_information (self, tag):
143 wt = self.get_maybe_exist_named_child (tag)
145 return wt.get_text ()
149 def get_work_title (self):
150 return self.get_work_information ('work-title')
151 def get_work_number (self):
152 return self.get_work_information ('work-number')
154 return self.get_work_information ('opus')
156 class Identification (Xml_node):
157 def get_rights (self):
158 rights = self.get_named_children ('rights')
161 ret.append (r.get_text ())
162 return string.join (ret, "\n")
164 def get_creator (self, type):
165 creators = self.get_named_children ('creator')
166 # return the first creator tag that has the particular type
168 if hasattr (i, 'type') and i.type == type:
172 def get_composer (self):
173 c = self.get_creator ('composer')
176 creators = self.get_named_children ('creator')
177 # return the first creator tag that has no type at all
179 if not hasattr (i, 'type'):
182 def get_arranger (self):
183 return self.get_creator ('arranger')
184 def get_editor (self):
185 return self.get_creator ('editor')
187 v = self.get_creator ('lyricist')
190 v = self.get_creator ('poet')
193 def get_encoding_information (self, type):
194 enc = self.get_named_children ('encoding')
196 children = enc[0].get_named_children (type)
198 return children[0].get_text ()
202 def get_encoding_software (self):
203 return self.get_encoding_information ('software')
204 def get_encoding_date (self):
205 return self.get_encoding_information ('encoding-date')
206 def get_encoding_person (self):
207 return self.get_encoding_information ('encoder')
208 def get_encoding_description (self):
209 return self.get_encoding_information ('encoding-description')
211 def get_encoding_software_list (self):
212 enc = self.get_named_children ('encoding')
215 softwares = e.get_named_children ('software')
217 software.append (s.get_text ())
220 def get_file_description (self):
221 misc = self.get_named_children ('miscellaneous')
223 misc_fields = m.get_named_children ('miscellaneous-field')
224 for mf in misc_fields:
225 if hasattr (mf, 'name') and mf.name == 'description':
226 return mf.get_text ()
231 class Duration (Music_xml_node):
232 def get_length (self):
233 dur = int (self.get_text ()) * Rational (1,4)
236 class Hash_comment (Music_xml_node):
238 class Hash_text (Music_xml_node):
239 def dump (self, indent = ''):
240 sys.stderr.write ('%s' % string.strip (self._data))
242 class Pitch (Music_xml_node):
244 ch = self.get_unique_typed_child (get_class (u'step'))
245 step = ch.get_text ().strip ()
247 def get_octave (self):
248 ch = self.get_unique_typed_child (get_class (u'octave'))
250 step = ch.get_text ().strip ()
253 def get_alteration (self):
254 ch = self.get_maybe_exist_typed_child (get_class (u'alter'))
257 alter = int (ch.get_text ().strip ())
260 class Unpitched (Music_xml_node):
262 ch = self.get_unique_typed_child (get_class (u'display-step'))
263 step = ch.get_text ().strip ()
266 def get_octave (self):
267 ch = self.get_unique_typed_child (get_class (u'display-octave'))
270 octave = ch.get_text ().strip ()
275 class Measure_element (Music_xml_node):
276 def get_voice_id (self):
277 voice_id = self.get_maybe_exist_named_child ('voice')
279 return voice_id.get_text ()
284 # Look at all measure elements (previously we had self.__class__, which
285 # only looked at objects of the same type!
286 cn = self._parent.get_typed_children (Measure_element)
287 # But only look at the correct voice; But include Attributes, too, which
288 # are not tied to any particular voice
289 cn = [c for c in cn if (c.get_voice_id () == self.get_voice_id ()) or isinstance (c, Attributes)]
292 class Attributes (Measure_element):
294 Measure_element.__init__ (self)
296 self._original_tag = None
297 self._time_signature_cache = None
300 cn = self._parent.get_typed_children (self.__class__)
301 if self._original_tag:
302 return cn[0] == self._original_tag
306 def set_attributes_from_previous (self, dict):
307 self._dict.update (dict)
309 def read_self (self):
310 for c in self.get_all_children ():
311 self._dict[c.get_name()] = c
313 def get_named_attribute (self, name):
314 return self._dict.get (name)
316 def single_time_sig_to_fraction (self, sig):
322 return Rational (n, sig[-1])
324 def get_measure_length (self):
325 sig = self.get_time_signature ()
326 if not sig or len (sig) == 0:
328 if isinstance (sig[0], list):
329 # Complex compound time signature
332 l += self.single_time_sig_to_fraction (i)
335 # Simple (maybe compound) time signature of the form (beat, ..., type)
336 return self.single_time_sig_to_fraction (sig)
339 def get_time_signature (self):
340 "Return time sig as a (beat, beat-type) tuple. For compound signatures,"
341 "return either (beat, beat,..., beat-type) or ((beat,..., type), "
342 "(beat,..., type), ...)."
343 if self._time_signature_cache:
344 return self._time_signature_cache
347 mxl = self.get_named_attribute ('time')
351 if mxl.get_maybe_exist_named_child ('senza-misura'):
352 # TODO: Handle pieces without a time signature!
353 error (_ ("Senza-misura time signatures are not yet supported!"))
358 for i in mxl.get_all_children ():
359 if isinstance (i, Beats):
360 beats = string.split (i.get_text ().strip (), "+")
361 current_sig = [int (j) for j in beats]
362 elif isinstance (i, BeatType):
363 current_sig.append (int (i.get_text ()))
364 signature.append (current_sig)
366 if isinstance (signature[0], list) and len (signature) == 1:
367 signature = signature[0]
368 self._time_signature_cache = signature
370 except (KeyError, ValueError):
371 self.message (_ ("Unable to interpret time signature! Falling back to 4/4."))
374 # returns clef information in the form ("cleftype", position, octave-shift)
375 def get_clef_information (self):
376 clefinfo = ['G', 2, 0]
377 mxl = self.get_named_attribute ('clef')
380 sign = mxl.get_maybe_exist_named_child ('sign')
382 clefinfo[0] = sign.get_text()
383 line = mxl.get_maybe_exist_named_child ('line')
385 clefinfo[1] = string.atoi (line.get_text ())
386 octave = mxl.get_maybe_exist_named_child ('clef-octave-change')
388 clefinfo[2] = string.atoi (octave.get_text ())
391 def get_key_signature (self):
392 "return (fifths, mode) tuple if the key signatures is given as "
393 "major/minor in the Circle of fifths. Otherwise return an alterations"
394 "list of the form [[step,alter<,octave>], [step,alter<,octave>], ...], "
395 "where the octave values are optional."
397 key = self.get_named_attribute ('key')
400 fifths_elm = key.get_maybe_exist_named_child ('fifths')
402 mode_node = key.get_maybe_exist_named_child ('mode')
405 mode = mode_node.get_text ()
406 if not mode or mode == '':
408 fifths = int (fifths_elm.get_text ())
409 # TODO: Shall we try to convert the key-octave and the cancel, too?
410 return (fifths, mode)
414 for i in key.get_all_children ():
415 if isinstance (i, KeyStep):
416 current_step = int (i.get_text ())
417 elif isinstance (i, KeyAlter):
418 alterations.append ([current_step, int (i.get_text ())])
419 elif isinstance (i, KeyOctave):
421 if hasattr (i, 'number'):
423 if (nr > 0) and (nr <= len (alterations)):
424 # MusicXML Octave 4 is middle C -> shift to 0
425 alterations[nr-1].append (int (i.get_text ())-4)
427 i.message (_ ("Key alteration octave given for a "
428 "non-existing alteration nr. %s, available numbers: %s!") % (nr, len(alterations)))
429 i.message ( "Non-standard key signature (after octave %s for alter nr %s): %s" % (i.get_text (), nr, alterations))
430 i.message ( "Non-standard key signature with alterations %s found!" % 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.stderr_write (_ ("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 Text (Music_xml_node):
543 class Lyric (Music_xml_node):
544 def get_number (self):
545 if hasattr (self, 'number'):
550 class Musicxml_voice:
554 self._start_staff = None
556 self._has_lyrics = False
558 def add_element (self, e):
559 self._elements.append (e)
560 if (isinstance (e, Note)
561 and e.get_maybe_exist_typed_child (Staff)):
562 name = e.get_maybe_exist_typed_child (Staff).get_text ()
564 if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
565 self._start_staff = name
566 self._staves[name] = True
568 lyrics = e.get_typed_children (Lyric)
569 if not self._has_lyrics:
570 self.has_lyrics = len (lyrics) > 0
574 if (nr > 0) and not (nr in self._lyrics):
575 self._lyrics.append (nr)
577 def insert (self, idx, e):
578 self._elements.insert (idx, e)
580 def get_lyrics_numbers (self):
581 if (len (self._lyrics) == 0) and self._has_lyrics:
582 #only happens if none of the <lyric> tags has a number attribute
588 def graces_to_aftergraces (pending_graces):
589 for gr in pending_graces:
590 gr._when = gr._prev_when
591 gr._measure_position = gr._prev_measure_position
592 gr._after_grace = True
595 class Part (Music_xml_node):
597 Music_xml_node.__init__ (self)
599 self._staff_attributes_dict = {}
601 def get_part_list (self):
603 while n and n.get_name() != 'score-partwise':
606 return n.get_named_child ('part-list')
608 def interpret (self):
609 """Set durations and starting points."""
610 """The starting point of the very first note is 0!"""
612 part_list = self.get_part_list ()
615 factor = Rational (1)
617 attributes_object = None
618 measures = self.get_typed_children (Measure)
619 last_moment = Rational (-1)
620 last_measure_position = Rational (-1)
621 measure_position = Rational (0)
622 measure_start_moment = now
623 is_first_measure = True
624 previous_measure = None
625 # Graces at the end of a measure need to have their position set to the
629 # implicit measures are used for artificial measures, e.g. when
630 # a repeat bar line splits a bar into two halves. In this case,
631 # don't reset the measure position to 0. They are also used for
632 # upbeats (initial value of 0 fits these, too).
633 # Also, don't reset the measure position at the end of the loop,
634 # but rather when starting the next measure (since only then do we
635 # know if the next measure is implicit and continues that measure)
636 if not m.is_implicit ():
637 # Warn about possibly overfull measures and reset the position
638 if attributes_object and previous_measure and previous_measure.partial == 0:
639 length = attributes_object.get_measure_length ()
640 new_now = measure_start_moment + length
642 problem = 'incomplete'
645 ## only for verbose operation.
646 if problem <> 'incomplete' and previous_measure:
647 previous_measure.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
649 measure_start_moment = now
650 measure_position = Rational (0)
652 for n in m.get_all_children ():
653 # figured bass has a duration, but applies to the next note
654 # and should not change the current measure position!
655 if isinstance (n, FiguredBass):
656 n._divisions = factor.denominator ()
658 n._measure_position = measure_position
661 if isinstance (n, Hash_text):
665 if n.__class__ == Attributes:
666 n.set_attributes_from_previous (attributes_dict)
668 attributes_dict = n._dict.copy ()
669 attributes_object = n
671 factor = Rational (1,
672 int (attributes_dict.get ('divisions').get_text ()))
675 if (n.get_maybe_exist_typed_child (Duration)):
676 mxl_dur = n.get_maybe_exist_typed_child (Duration)
677 dur = mxl_dur.get_length () * factor
679 if n.get_name() == 'backup':
681 # reset all graces before the backup to after-graces:
682 graces_to_aftergraces (pending_graces)
684 if n.get_maybe_exist_typed_child (Grace):
687 rest = n.get_maybe_exist_typed_child (Rest)
689 and attributes_object
690 and attributes_object.get_measure_length () == dur):
692 rest._is_whole_measure = True
694 if (dur > Rational (0)
695 and n.get_maybe_exist_typed_child (Chord)):
697 measure_position = last_measure_position
700 n._measure_position = measure_position
702 # For all grace notes, store the previous note, in case need
703 # to turn the grace note into an after-grace later on!
704 if isinstance(n, Note) and n.is_grace ():
705 n._prev_when = last_moment
706 n._prev_measure_position = last_measure_position
707 # After-graces are placed at the same position as the previous note
708 if isinstance(n, Note) and n.is_after_grace ():
709 # TODO: We should do the same for grace notes at the end of
710 # a measure with no following note!!!
711 n._when = last_moment
712 n._measure_position = last_measure_position
713 elif isinstance(n, Note) and n.is_grace ():
714 pending_graces.append (n)
715 elif (dur > Rational (0)):
719 if dur > Rational (0):
721 last_measure_position = measure_position
723 measure_position += dur
724 elif dur < Rational (0):
725 # backup element, reset measure position
727 measure_position += dur
728 if measure_position < 0:
729 # backup went beyond the measure start => reset to 0
730 now -= measure_position
733 last_measure_position = measure_position
734 if n._name == 'note':
735 instrument = n.get_maybe_exist_named_child ('instrument')
737 n.instrument_name = part_list.get_instrument (instrument.id)
739 # reset all graces at the end of the measure to after-graces:
740 graces_to_aftergraces (pending_graces)
742 # Incomplete first measures are not padded, but registered as partial
744 is_first_measure = False
745 # upbeats are marked as implicit measures
746 if attributes_object and m.is_implicit ():
747 length = attributes_object.get_measure_length ()
748 measure_end = measure_start_moment + length
749 if measure_end <> now:
753 # modify attributes so that only those applying to the given staff remain
754 def extract_attributes_for_staff (part, attr, staff):
755 attributes = copy.copy (attr)
756 attributes._children = [];
757 attributes._dict = attr._dict.copy ()
758 attributes._original_tag = attr
759 # copy only the relevant children over for the given staff
760 for c in attr._children:
761 if (not (hasattr (c, 'number') and (c.number != staff)) and
762 not (isinstance (c, Hash_text))):
763 attributes._children.append (c)
764 if not attributes._children:
769 def extract_voices (part):
771 measures = part.get_typed_children (Measure)
775 elements.append (Partial (m.partial))
776 elements.extend (m.get_all_children ())
777 # make sure we know all voices already so that dynamics, clefs, etc.
778 # can be assigned to the correct voices
779 voice_to_staff_dict = {}
781 voice_id = n.get_maybe_exist_named_child (u'voice')
784 vid = voice_id.get_text ()
785 elif isinstance (n, Note):
788 staff_id = n.get_maybe_exist_named_child (u'staff')
791 sid = staff_id.get_text ()
794 if vid and not voices.has_key (vid):
795 voices[vid] = Musicxml_voice()
796 if vid and sid and not n.get_maybe_exist_typed_child (Grace):
797 if not voice_to_staff_dict.has_key (vid):
798 voice_to_staff_dict[vid] = sid
799 # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
800 # need to assign staff-assigned objects like clefs, times, etc. to
801 # all the correct voices. This will never work entirely correct due
802 # to staff-switches, but that's the best we can do!
803 staff_to_voice_dict = {}
804 for (v,s) in voice_to_staff_dict.items ():
805 if not staff_to_voice_dict.has_key (s):
806 staff_to_voice_dict[s] = [v]
808 staff_to_voice_dict[s].append (v)
812 assign_to_next_note = []
815 voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
817 id = voice_id.get_text ()
821 # We don't need backup/forward any more, since we have already
822 # assigned the correct onset times.
823 # TODO: Let Grouping through. Also: link, print, bokmark sound
824 if not (isinstance (n, Note) or isinstance (n, Attributes) or
825 isinstance (n, Direction) or isinstance (n, Partial) or
826 isinstance (n, Barline) or isinstance (n, Harmony) or
827 isinstance (n, FiguredBass) ):
830 if isinstance (n, Attributes) and not start_attr:
834 if isinstance (n, Attributes):
835 # assign these only to the voices they really belongs to!
836 for (s, vids) in staff_to_voice_dict.items ():
837 staff_attributes = part.extract_attributes_for_staff (n, s)
840 voices[v].add_element (staff_attributes)
843 if isinstance (n, Partial) or isinstance (n, Barline):
844 for v in voices.keys ():
845 voices[v].add_element (n)
848 if isinstance (n, Direction):
849 staff_id = n.get_maybe_exist_named_child (u'staff')
851 staff_id = staff_id.get_text ()
853 dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
855 dir_voices = voices.keys ()
857 voices[v].add_element (n)
860 if isinstance (n, Harmony) or isinstance (n, FiguredBass):
861 # store the harmony or figured bass element until we encounter
862 # the next note and assign it only to that one voice.
863 assign_to_next_note.append (n)
866 if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
870 for i in assign_to_next_note:
871 voices[id].add_element (i)
872 assign_to_next_note = []
873 voices[id].add_element (n)
875 # Assign all remaining elements from assign_to_next_note to the voice
876 # of the previous note:
877 for i in assign_to_next_note:
878 voices[id].add_element (i)
879 assign_to_next_note = []
882 for (s, vids) in staff_to_voice_dict.items ():
883 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
884 staff_attributes.read_self ()
885 part._staff_attributes_dict[s] = staff_attributes
887 voices[v].insert (0, staff_attributes)
888 voices[v]._elements[0].read_self()
890 part._voices = voices
892 def get_voices (self):
894 def get_staff_attributes (self):
895 return self._staff_attributes_dict
897 class Notations (Music_xml_node):
899 ts = self.get_named_children ('tied')
900 starts = [t for t in ts if t.type == 'start']
906 def get_tuplets (self):
907 return self.get_typed_children (Tuplet)
909 class Time_modification(Music_xml_node):
910 def get_fraction (self):
911 b = self.get_maybe_exist_named_child ('actual-notes')
912 a = self.get_maybe_exist_named_child ('normal-notes')
913 return (int(a.get_text ()), int (b.get_text ()))
915 def get_normal_type (self):
916 tuplet_type = self.get_maybe_exist_named_child ('normal-type')
918 dots = self.get_named_children ('normal-dot')
919 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
920 return (log , len (dots))
925 class Accidental (Music_xml_node):
927 Music_xml_node.__init__ (self)
928 self.editorial = False
929 self.cautionary = False
931 class Music_xml_spanner (Music_xml_node):
933 if hasattr (self, 'type'):
938 if hasattr (self, 'size'):
939 return string.atoi (self.size)
943 class Wedge (Music_xml_spanner):
946 class Tuplet (Music_xml_spanner):
947 def duration_info_from_tuplet_note (self, tuplet_note):
948 tuplet_type = tuplet_note.get_maybe_exist_named_child ('tuplet-type')
950 dots = tuplet_note.get_named_children ('tuplet-dot')
951 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
952 return (log, len (dots))
956 # Return tuplet note type as (log, dots)
957 def get_normal_type (self):
958 tuplet = self.get_maybe_exist_named_child ('tuplet-normal')
960 return self.duration_info_from_tuplet_note (tuplet)
964 def get_actual_type (self):
965 tuplet = self.get_maybe_exist_named_child ('tuplet-actual')
967 return self.duration_info_from_tuplet_note (tuplet)
971 def get_tuplet_note_count (self, tuplet_note):
973 tuplet_nr = tuplet_note.get_maybe_exist_named_child ('tuplet-number')
975 return int (tuplet_nr.get_text ())
977 def get_normal_nr (self):
978 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-normal'))
979 def get_actual_nr (self):
980 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-actual'))
982 class Bracket (Music_xml_spanner):
985 class Dashes (Music_xml_spanner):
988 class Slur (Music_xml_spanner):
992 class Beam (Music_xml_spanner):
994 return self.get_text ()
995 def is_primary (self):
996 return self.number == "1"
998 class Wavy_line (Music_xml_spanner):
1001 class Pedal (Music_xml_spanner):
1004 class Glissando (Music_xml_spanner):
1007 class Slide (Music_xml_spanner):
1010 class Octave_shift (Music_xml_spanner):
1011 # default is 8 for the octave-shift!
1012 def get_size (self):
1013 if hasattr (self, 'size'):
1014 return string.atoi (self.size)
1018 class Chord (Music_xml_node):
1021 class Dot (Music_xml_node):
1024 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
1025 # for the inner <rest> element, not the whole rest block.
1026 class Rest (Music_xml_node):
1027 def __init__ (self):
1028 Music_xml_node.__init__ (self)
1029 self._is_whole_measure = False
1030 def is_whole_measure (self):
1031 return self._is_whole_measure
1032 def get_step (self):
1033 ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
1035 step = ch.get_text ().strip ()
1039 def get_octave (self):
1040 ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
1042 step = ch.get_text ().strip ()
1047 class Type (Music_xml_node):
1049 class Grace (Music_xml_node):
1051 class Staff (Music_xml_node):
1054 class Direction (Music_xml_node):
1056 class DirType (Music_xml_node):
1059 class Bend (Music_xml_node):
1060 def bend_alter (self):
1061 alter = self.get_maybe_exist_named_child ('bend-alter')
1063 return alter.get_text()
1067 class Words (Music_xml_node):
1070 class Harmony (Music_xml_node):
1073 class ChordPitch (Music_xml_node):
1074 def step_class_name (self):
1076 def alter_class_name (self):
1077 return u'root-alter'
1078 def get_step (self):
1079 ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
1080 return ch.get_text ().strip ()
1081 def get_alteration (self):
1082 ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
1085 alter = int (ch.get_text ().strip ())
1088 class Root (ChordPitch):
1091 class Bass (ChordPitch):
1092 def step_class_name (self):
1094 def alter_class_name (self):
1095 return u'bass-alter'
1097 class ChordModification (Music_xml_node):
1098 def get_type (self):
1099 ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
1100 return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
1101 def get_value (self):
1102 ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
1105 value = int (ch.get_text ().strip ())
1107 def get_alter (self):
1108 ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
1111 value = int (ch.get_text ().strip ())
1115 class Frame (Music_xml_node):
1116 def get_frets (self):
1117 return self.get_named_child_value_number ('frame-frets', 4)
1118 def get_strings (self):
1119 return self.get_named_child_value_number ('frame-strings', 6)
1120 def get_first_fret (self):
1121 return self.get_named_child_value_number ('first-fret', 1)
1123 class Frame_Note (Music_xml_node):
1124 def get_string (self):
1125 return self.get_named_child_value_number ('string', 1)
1126 def get_fret (self):
1127 return self.get_named_child_value_number ('fret', 0)
1128 def get_fingering (self):
1129 return self.get_named_child_value_number ('fingering', -1)
1130 def get_barre (self):
1131 n = self.get_maybe_exist_named_child ('barre')
1133 return getattr (n, 'type', '')
1137 class FiguredBass (Music_xml_node):
1140 class Beats (Music_xml_node):
1143 class BeatType (Music_xml_node):
1146 class BeatUnit (Music_xml_node):
1149 class BeatUnitDot (Music_xml_node):
1152 class PerMinute (Music_xml_node):
1157 ## need this, not all classes are instantiated
1158 ## for every input file. Only add those classes, that are either directly
1159 ## used by class name or extend Music_xml_node in some way!
1161 '#comment': Hash_comment,
1163 'accidental': Accidental,
1164 'attributes': Attributes,
1166 'bar-style': BarStyle,
1170 'beat-type': BeatType,
1171 'beat-unit': BeatUnit,
1172 'beat-unit-dot': BeatUnitDot,
1174 'bracket' : Bracket,
1177 'degree' : ChordModification,
1179 'direction': Direction,
1180 'direction-type': DirType,
1181 'duration': Duration,
1184 'frame-note': Frame_Note,
1185 'figured-bass': FiguredBass,
1186 'glissando': Glissando,
1189 'identification': Identification,
1190 'key-alter': KeyAlter,
1191 'key-octave': KeyOctave,
1192 'key-step': KeyStep,
1195 'notations': Notations,
1197 'octave-shift': Octave_shift,
1199 'part-group': Part_group,
1200 'part-list': Part_list,
1202 'per-minute': PerMinute,
1206 'score-part': Score_part,
1210 'syllabic': Syllabic,
1212 'time-modification': Time_modification,
1215 'unpitched': Unpitched,
1216 'wavy-line': Wavy_line,
1222 def name2class_name (name):
1223 name = name.replace ('-', '_')
1224 name = name.replace ('#', 'hash_')
1225 name = name[0].upper() + name[1:].lower()
1229 def get_class (name):
1230 classname = class_dict.get (name)
1234 class_name = name2class_name (name)
1235 klass = new.classobj (class_name, (Music_xml_node,) , {})
1236 class_dict[name] = klass
1239 def lxml_demarshal_node (node):
1242 # Ignore comment nodes, which are also returned by the etree parser!
1243 if name is None or node.__class__.__name__ == "_Comment":
1245 klass = get_class (name)
1248 py_node._original = node
1249 py_node._name = name
1250 py_node._data = node.text
1251 py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1252 py_node._children = filter (lambda x: x, py_node._children)
1254 for c in py_node._children:
1257 for (k, v) in node.items ():
1258 py_node.__dict__[k] = v
1259 py_node._attribute_dict[k] = v
1263 def minidom_demarshal_node (node):
1264 name = node.nodeName
1266 klass = get_class (name)
1268 py_node._name = name
1269 py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1270 for c in py_node._children:
1274 for (nm, value) in node.attributes.items ():
1275 py_node.__dict__[nm] = value
1276 py_node._attribute_dict[nm] = value
1278 py_node._data = None
1279 if node.nodeType == node.TEXT_NODE and node.data:
1280 py_node._data = node.data
1282 py_node._original = node
1286 if __name__ == '__main__':
1289 tree = lxml.etree.parse ('beethoven.xml')
1290 mxl_tree = lxml_demarshal_node (tree.getroot ())
1291 ks = class_dict.keys ()
1293 print '\n'.join (ks)