1 # -*- coding: utf-8 -*-
14 import musicxml2ly_conversion
24 self._name = 'xml_node'
26 self._attribute_dict = {}
32 return self._parent.get_typed_children(self.__class__)[0] == self
43 if not self._children:
46 return ''.join([c.get_text() for c in self._children])
48 def message(self, msg):
53 ly.progress(' In: <%s %s>\n' %(p._name, ' '.join(['%s=%s' % item for item in p._attribute_dict.items()])))
56 def dump(self, indent=''):
57 ly.debug_output('%s<%s%s>' %(indent, self._name, ''.join([' %s=%s' % item for item in self._attribute_dict.items()])))
58 non_text_children = [c for c in self._children if not isinstance(c, Hash_text)]
61 for c in self._children:
64 ly.debug_output(indent)
65 ly.debug_output('</%s>\n' % self._name)
68 def get_typed_children(self, klass):
72 return [c for c in self._children if isinstance(c, klass)]
74 def get_named_children(self, nm):
75 return self.get_typed_children(get_class(nm))
77 def get_named_child(self, nm):
78 return self.get_maybe_exist_named_child(nm)
80 def get_children(self, predicate):
81 return [c for c in self._children if predicate(c)]
83 def get_all_children(self):
86 def get_maybe_exist_named_child(self, name):
87 return self.get_maybe_exist_typed_child(get_class(name))
89 def get_maybe_exist_typed_child(self, klass):
90 cn = self.get_typed_children(klass)
99 ['more than one child of class ',
101 '...all but the first will be ignored!'])
105 def get_unique_typed_child(self, klass):
106 cn = self.get_typed_children(klass)
108 ly.error(self.__dict__)
109 raise 'Child is not unique for',(klass, 'found', cn)
113 def get_named_child_value_number(self, name, default):
114 n = self.get_maybe_exist_named_child(name)
116 return int(n.get_text())
121 class Music_xml_node(Xml_node):
123 Xml_node.__init__(self)
124 self.duration = Rational(0)
125 self.start = Rational(0)
126 self.converted = False
127 self.voice_id = None;
130 class Music_xml_spanner(Music_xml_node):
133 if hasattr(self, 'type'):
139 if hasattr(self, 'size'):
140 return int(self.size)
145 class Measure_element(Music_xml_node):
147 def get_voice_id(self):
148 voice = self.get_maybe_exist_named_child('voice')
150 return voice.get_text()
155 # Look at all measure elements(previously we had self.__class__, which
156 # only looked at objects of the same type!
157 cn = self._parent.get_typed_children(Measure_element)
158 # But only look at the correct voice; But include Attributes, too, which
159 # are not tied to any particular voice
160 cn = [c for c in cn if(c.get_voice_id() == self.get_voice_id()) or isinstance(c, Attributes)]
164 class Work(Xml_node):
166 def get_work_information(self, tag):
167 wt = self.get_maybe_exist_named_child(tag)
173 def get_work_title(self):
174 return self.get_work_information('work-title')
176 def get_work_number(self):
177 return self.get_work_information('work-number')
179 # def get_opus(self):
180 # return self.get_work_information('opus')
183 class Identification(Xml_node):
185 def get_rights(self):
186 rights = self.get_named_children('rights')
190 # if this Xml_node has an attribute, such as 'type="words"',
191 # include it in the header. Otherwise, it is assumed that
192 # the text contents of this node looks something like this:
193 # 'Copyright: X.Y.' and thus already contains the relevant
195 if hasattr(r, 'type'):
196 rights_type = r.type.title() # capitalize first letter
197 result = ''.join([rights_type, ': ', text])
201 return string.join(ret, "\n")
203 # 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.)
204 def get_source(self):
205 source = self.get_named_children('source')
208 ret.append(r.get_text())
209 return string.join(ret, "\n")
211 def get_creator(self, type):
212 creators = self.get_named_children('creator')
213 # return the first creator tag that has the particular type
215 if hasattr(i, 'type') and i.type == type:
219 def get_composer(self):
220 c = self.get_creator('composer')
223 creators = self.get_named_children('creator')
224 # return the first creator tag that has no type at all
226 if not hasattr(i, 'type'):
230 def get_arranger(self):
231 return self.get_creator('arranger')
233 def get_editor(self):
234 return self.get_creator('editor')
237 v = self.get_creator('lyricist')
240 v = self.get_creator('poet')
243 def get_encoding_information(self, type):
244 enc = self.get_named_children('encoding')
246 children = enc[0].get_named_children(type)
248 return children[0].get_text()
252 def get_encoding_software(self):
253 return self.get_encoding_information('software')
255 def get_encoding_date(self):
256 return self.get_encoding_information('encoding-date')
258 def get_encoding_person(self):
259 return self.get_encoding_information('encoder')
261 def get_encoding_description(self):
262 return self.get_encoding_information('encoding-description')
264 def get_encoding_software_list(self):
265 enc = self.get_named_children('encoding')
268 softwares = e.get_named_children('software')
270 software.append(s.get_text())
273 def get_file_description(self):
274 misc = self.get_named_children('miscellaneous')
276 misc_fields = m.get_named_children('miscellaneous-field')
277 for mf in misc_fields:
278 if hasattr(mf, 'name') and mf.name == 'description':
283 class Credit(Xml_node):
286 type = self.get_maybe_exist_named_child('credit-type')
288 return type.get_text()
292 def find_type(self, credits):
293 sizes = self.get_font_sizes(credits)
294 sizes.sort(reverse=True)
295 ys = self.get_default_ys(credits)
296 ys.sort(reverse=True)
297 xs = self.get_default_xs(credits)
298 xs.sort(reverse=True)
300 # Words child of the self credit-element
301 words = self.get_maybe_exist_named_child('credit-words')
309 if hasattr(words, 'font-size'):
310 size = utilities.string_to_integer(getattr(words, 'font-size'))
311 if hasattr(words, 'default-x'):
312 x = round(float(getattr(words, 'default-x')))
313 if hasattr(words, 'default-y'):
314 y = round(float(getattr(words, 'default-y')))
315 if hasattr(words, 'halign'):
316 halign = getattr(words, 'halign')
317 if hasattr(words, 'valign'):
318 valign = getattr(words, 'valign')
319 if hasattr(words, 'justify'):
320 justify = getattr(words, 'justify')
321 if(size and size == max(sizes) and y and y == max(ys) and(justify or halign) and(justify == 'center' or halign == 'center')):
323 elif((y and y > min(ys) and y < max(ys)) and((justify or halign) and(justify == 'center' or halign == 'center'))):
325 elif((justify or halign) and(justify == 'left' or halign == 'left') and(not(x) or x == min(xs))):
327 elif((justify or halign) and(justify == 'right' or halign == 'right') and(not(x) or x == max(xs))):
329 elif(size and size == min(sizes) and y == min(ys)):
331 # Special cases for Finale NotePad
332 elif((valign and(valign == 'top')) and(y and y == ys[1])):
334 elif((valign and(valign == 'top')) and(x and x == min(xs))):
336 elif((valign and(valign == 'top')) and(y and y == min(ys))):
338 # Other special cases
339 elif((valign and(valign == 'bottom'))):
341 elif(len([item for item in range(len(ys)) if ys[item] == y]) == 2):
342 # The first one is the composer, the second one is the lyricist
345 return None # no type recognized
347 def get_font_sizes(self, credits):
350 words = cred.get_maybe_exist_named_child('credit-words')
351 if((words != None) and hasattr(words, 'font-size')):
352 sizes.append(getattr(words, 'font-size'))
353 return map(utilities.string_to_integer, sizes)
355 def get_default_xs(self, credits):
358 words = cred.get_maybe_exist_named_child('credit-words')
359 if((words != None) and hasattr(words, 'default-x')):
360 default_xs.append(getattr(words, 'default-x'))
361 return map(round, map(float, default_xs))
363 def get_default_ys(self, credits):
366 words = cred.get_maybe_exist_named_child('credit-words')
367 if((words != None) and hasattr(words, 'default-y')):
368 default_ys.append(getattr(words, 'default-y'))
369 return map(round, map(float, default_ys))
372 words = self.get_maybe_exist_named_child('credit-words')
374 return words.get_text()
379 class Duration(Music_xml_node):
381 def get_length(self):
382 dur = int(self.get_text()) * Rational(1, 4)
386 class Hash_text(Music_xml_node):
388 def dump(self, indent=''):
389 ly.debug_output('%s' % string.strip(self._data))
392 class Pitch(Music_xml_node):
395 ch = self.get_unique_typed_child(get_class(u'step'))
396 step = ch.get_text().strip()
399 def get_octave(self):
400 ch = self.get_unique_typed_child(get_class(u'octave'))
401 octave = ch.get_text().strip()
404 def get_alteration(self):
405 ch = self.get_maybe_exist_typed_child(get_class(u'alter'))
406 return utilities.interpret_alter_element(ch)
408 def to_lily_object(self):
410 p.alteration = self.get_alteration()
411 p.step = musicxml2ly_conversion.musicxml_step_to_lily(self.get_step())
412 p.octave = self.get_octave() - 4
416 class Unpitched(Music_xml_node):
419 ch = self.get_unique_typed_child(get_class(u'display-step'))
420 step = ch.get_text().strip()
423 def get_octave(self):
424 ch = self.get_unique_typed_child(get_class(u'display-octave'))
427 octave = ch.get_text().strip()
432 def to_lily_object(self):
434 step = self.get_step()
437 p.step = musicxml2ly_conversion.musicxml_step_to_lily(step)
438 octave = self.get_octave()
440 p.octave = octave - 4
444 class Measure_element (Music_xml_node):
445 def get_voice_id (self):
446 voice = self.get_maybe_exist_named_child ('voice')
448 return voice.get_text ()
450 return self.voice_id;
453 class Attributes(Measure_element):
456 Measure_element.__init__(self)
458 self._original_tag = None
459 self._time_signature_cache = None
462 cn = self._parent.get_typed_children(self.__class__)
463 if self._original_tag:
464 return cn[0] == self._original_tag
468 def set_attributes_from_previous(self, dict):
469 self._dict.update(dict)
472 for c in self.get_all_children():
473 self._dict[c.get_name()] = c
475 def get_named_attribute(self, name):
476 return self._dict.get(name)
478 def single_time_sig_to_fraction(self, sig):
484 return Rational(n, sig[-1])
486 def get_measure_length(self):
487 sig = self.get_time_signature()
488 if not sig or len(sig) == 0:
490 if isinstance(sig[0], list):
491 # Complex compound time signature
494 l += self.single_time_sig_to_fraction(i)
497 # Simple(maybe compound) time signature of the form(beat, ..., type)
498 return self.single_time_sig_to_fraction(sig)
501 def get_time_signature(self):
502 "Return time sig as a(beat, beat-type) tuple. For compound signatures,"
503 "return either(beat, beat,..., beat-type) or((beat,..., type), "
504 "(beat,..., type), ...)."
505 if self._time_signature_cache:
506 return self._time_signature_cache
509 mxl = self.get_named_attribute('time')
513 if mxl.get_maybe_exist_named_child('senza-misura'):
514 # TODO: Handle pieces without a time signature!
515 ly.warning(_("Senza-misura time signatures are not yet supported!"))
520 for i in mxl.get_all_children():
521 if isinstance(i, Beats):
522 beats = string.split(i.get_text().strip(), "+")
523 current_sig = [int(j) for j in beats]
524 elif isinstance(i, BeatType):
525 current_sig.append(int(i.get_text()))
526 signature.append(current_sig)
528 if isinstance(signature[0], list) and len(signature) == 1:
529 signature = signature[0]
530 self._time_signature_cache = signature
532 except(KeyError, ValueError):
533 self.message(_("Unable to interpret time signature! Falling back to 4/4."))
536 # returns clef information in the form("cleftype", position, octave-shift)
537 def get_clef_information(self):
538 clefinfo = ['G', 2, 0]
539 mxl = self.get_named_attribute('clef')
542 sign = mxl.get_maybe_exist_named_child('sign')
544 clefinfo[0] = sign.get_text()
545 line = mxl.get_maybe_exist_named_child('line')
547 clefinfo[1] = int(line.get_text())
548 octave = mxl.get_maybe_exist_named_child('clef-octave-change')
550 clefinfo[2] = int(octave.get_text())
553 def get_key_signature(self):
554 "return(fifths, mode) tuple if the key signatures is given as "
555 "major/minor in the Circle of fifths. Otherwise return an alterations"
556 "list of the form [[step,alter<,octave>], [step,alter<,octave>], ...], "
557 "where the octave values are optional."
559 key = self.get_named_attribute('key')
562 fifths_elm = key.get_maybe_exist_named_child('fifths')
564 mode_node = key.get_maybe_exist_named_child('mode')
567 mode = mode_node.get_text()
568 if not mode or mode == '':
570 fifths = int(fifths_elm.get_text())
571 # TODO: Shall we try to convert the key-octave and the cancel, too?
576 for i in key.get_all_children():
577 if isinstance(i, KeyStep):
578 current_step = i.get_text().strip()
579 elif isinstance(i, KeyAlter):
580 alterations.append([current_step, utilities.interpret_alter_element(i)])
581 elif isinstance(i, KeyOctave):
583 if hasattr(i, 'number'):
585 if(nr > 0) and(nr <= len(alterations)):
586 # MusicXML Octave 4 is middle C -> shift to 0
587 alterations[nr - 1].append(int(i.get_text()) - 4)
589 i.message(_("Key alteration octave given for a "
590 "non-existing alteration nr. %s, available numbers: %s!") %(nr, len(alterations)))
593 def get_transposition(self):
594 return self.get_named_attribute('transpose')
597 class Barline(Measure_element):
599 def to_lily_object(self):
600 # retval contains all possible markers in the order:
601 # 0..bw_ending, 1..bw_repeat, 2..barline, 3..fw_repeat, 4..fw_ending
603 bartype_element = self.get_maybe_exist_named_child("bar-style")
604 repeat_element = self.get_maybe_exist_named_child("repeat")
605 ending_element = self.get_maybe_exist_named_child("ending")
609 bartype = bartype_element.get_text()
611 if repeat_element and hasattr(repeat_element, 'direction'):
612 repeat = musicxml2ly_conversion.RepeatMarker()
613 repeat.direction = {"forward":-1, "backward": 1}.get(
614 repeat_element.direction, 0)
616 if((repeat_element.direction == "forward" and bartype == "heavy-light") or
617 (repeat_element.direction == "backward" and bartype == "light-heavy")):
619 if hasattr(repeat_element, 'times'):
621 repeat.times = int(repeat_element.times)
625 if repeat.direction == -1:
630 if ending_element and hasattr(ending_element, 'type'):
631 ending = musicxml2ly_conversion.EndingMarker()
632 ending.direction = {"start":-1, "stop": 1, "discontinue": 1}.get(
633 ending_element.type, 0)
635 if ending.direction == -1:
639 # TODO. ending number=""
642 b = musicexp.BarLine()
646 return retval.values()
649 class Partial(Measure_element):
650 def __init__(self, partial):
651 Measure_element.__init__(self)
652 self.partial = partial
655 class Stem(Music_xml_node):
660 'double': None, # TODO: Implement
661 'none': 'stemNeutral'
664 def to_stem_event(self):
666 value = self.stem_value_dict.get(self.get_text(), None)
667 stem_value = musicexp.StemEvent()
669 stem_value.value = value
670 values.append(stem_value)
673 def to_stem_style_event(self):
675 style_elm = musicexp.StemstyleEvent()
676 if hasattr(self, 'color'):
677 style_elm.color = utilities.hex_to_color(getattr(self, 'color'))
678 if(style_elm.color != None):
679 styles.append(style_elm)
683 class Notehead(Music_xml_node):
685 notehead_styles_dict = {
687 'triangle': '\'triangle',
688 'diamond': '\'diamond',
689 'square': '\'la', # TODO: Proper squared note head
690 'cross': None, # TODO: + shaped note head
692 'circle-x': '\'xcircle',
693 'inverted triangle': None, # TODO: Implement
694 'arrow down': None, # TODO: Implement
695 'arrow up': None, # TODO: Implement
696 'slashed': None, # TODO: Implement
697 'back slashed': None, # TODO: Implement
699 'cluster': None, # TODO: Implement
710 def to_lily_object(self): #function changed: additionally processcolor attribute
714 key = self.get_text().strip()
715 style = self.notehead_styles_dict.get(key, None)
716 event = musicexp.NotestyleEvent()
719 if hasattr(self, 'filled'):
720 event.filled =(getattr(self, 'filled') == "yes")
721 if hasattr(self, 'color'):
722 event.color = utilities.hex_to_color(getattr(self, 'color'))
723 if event.style or(event.filled != None) or(event.color != None):
726 if hasattr(self, 'parentheses') and(self.parentheses == "yes"):
727 styles.append(musicexp.ParenthesizeEvent())
732 class Note(Measure_element):
735 'Acoustic Snare Drum': 'acousticsnare',
736 'Side Stick': 'sidestick',
737 'Open Triangle': 'opentriangle',
738 'Mute Triangle': 'mutetriangle',
739 'Tambourine': 'tambourine',
740 'Bass Drum': 'bassdrum',
744 Measure_element.__init__(self)
745 self.instrument_name = ''
746 self._after_grace = False
750 return self.get_maybe_exist_named_child(u'grace')
752 def is_after_grace(self):
753 if not self.is_grace():
755 gr = self.get_maybe_exist_typed_child(Grace)
756 return self._after_grace or hasattr(gr, 'steal-time-previous');
758 def get_duration_log(self):
759 ch = self.get_maybe_exist_named_child(u'type')
762 log = ch.get_text().strip()
763 return utilities.musicxml_duration_to_log(log)
764 elif self.get_maybe_exist_named_child(u'grace'):
765 # FIXME: is it ok to default to eight note for grace notes?
770 def get_duration_info(self):
771 log = self.get_duration_log()
773 dots = len(self.get_typed_children(Dot))
778 def get_factor(self):
781 def get_pitches(self):
782 return self.get_typed_children(get_class(u'pitch'))
784 def set_notehead_style(self, event):
785 noteheads = self.get_named_children('notehead')
787 styles = nh.to_lily_object()
789 event.add_associated_event(style)
791 def set_stem_directions(self, event):
792 stems = self.get_named_children('stem')
794 values = stem.to_stem_event()
796 event.add_associated_event(v)
798 def set_stem_style(self, event):
799 stems = self.get_named_children('stem')
801 styles = stem.to_stem_style_event()
803 event.add_associated_event(style)
805 def initialize_duration(self):
806 from musicxml2ly_conversion import rational_to_lily_duration
807 from musicexp import Duration
808 # if the note has no Type child, then that method returns None. In that case,
809 # use the <duration> tag instead. If that doesn't exist, either -> Error
810 dur = self.get_duration_info()
813 d.duration_log = dur[0]
815 # Grace notes by specification have duration 0, so no time modification
816 # factor is possible. It even messes up the output with *0/1
817 if not self.get_maybe_exist_typed_child(Grace):
818 d.factor = self._duration / d.get_length()
821 if self._duration > 0:
822 return rational_to_lily_duration(self._duration)
825 _("Encountered note at %s without type and duration(=%s)") \
826 %(mxl_note.start, mxl_note._duration))
829 def initialize_pitched_event(self):
830 mxl_pitch = self.get_maybe_exist_typed_child(Pitch)
831 pitch = mxl_pitch.to_lily_object()
832 event = musicexp.NoteEvent()
834 acc = self.get_maybe_exist_named_child('accidental')
836 # let's not force accs everywhere.
837 event.cautionary = acc.cautionary
838 # TODO: Handle editorial accidentals
839 # TODO: Handle the level-display setting for displaying brackets/parentheses
842 def initialize_unpitched_event(self):
843 # Unpitched elements have display-step and can also have
845 unpitched = self.get_maybe_exist_typed_child(Unpitched)
846 event = musicexp.NoteEvent()
847 event.pitch = unpitched.to_lily_object()
850 def initialize_rest_event(self, convert_rest_positions=True):
851 # rests can have display-octave and display-step, which are
852 # treated like an ordinary note pitch
853 rest = self.get_maybe_exist_typed_child(Rest)
854 event = musicexp.RestEvent()
855 if convert_rest_positions:
856 pitch = rest.to_lily_object()
860 def initialize_drum_event(self):
861 event = musicexp.NoteEvent()
862 drum_type = self.drumtype_dict.get(self.instrument_name)
864 event.drum_type = drum_type
867 _("drum %s type unknown, please add to instrument_drumtype_dict")
869 event.drum_type = 'acousticsnare'
872 def to_lily_object(self,
873 convert_stem_directions=True,
874 convert_rest_positions=True):
879 if self.get_maybe_exist_typed_child(Pitch):
880 event = self.initialize_pitched_event()
881 elif self.get_maybe_exist_typed_child(Unpitched):
882 event = self.initialize_unpitched_event()
883 elif self.get_maybe_exist_typed_child(Rest):
884 event = self.initialize_rest_event(convert_rest_positions)
885 elif self.instrument_name:
886 event = self.initialize_drum_event()
888 self.message(_("cannot find suitable event"))
891 event.duration = self.initialize_duration()
893 self.set_notehead_style(event)
894 self.set_stem_style(event)
895 if convert_stem_directions:
896 self.set_stem_directions(event)
901 class Part_list(Music_xml_node):
904 Music_xml_node.__init__(self)
905 self._id_instrument_name_dict = {}
907 def generate_id_instrument_dict(self):
909 ## not empty to make sure this happens only once.
911 for score_part in self.get_named_children('score-part'):
912 for instr in score_part.get_named_children('score-instrument'):
914 name = instr.get_named_child("instrument-name")
915 mapping[id] = name.get_text()
917 self._id_instrument_name_dict = mapping
919 def get_instrument(self, id):
920 if not self._id_instrument_name_dict:
921 self.generate_id_instrument_dict()
923 instrument_name = self._id_instrument_name_dict.get(id)
925 return instrument_name
927 ly.warning(_("Unable to find instrument for ID=%s\n") % id)
931 class Measure(Music_xml_node):
934 Music_xml_node.__init__(self)
937 def is_implicit(self):
938 return hasattr(self, 'implicit') and self.implicit == 'yes'
941 return self.get_typed_children(get_class(u'note'))
944 class Syllabic(Music_xml_node):
947 text = self.get_text()
948 return(text == "begin") or(text == "middle")
951 return(text == "begin")
954 return(text == "middle")
957 return(text == "end")
960 class Lyric(Music_xml_node):
964 Return the number attribute(if it exists) of the lyric element.
967 @return: The value of the number attribute
969 if hasattr(self, 'number'):
975 class Sound(Music_xml_node):
979 Return the tempo attribute(if it exists) of the sound element.
980 This attribute can be used by musicxml2ly for the midi output(see L{musicexp.Score}).
983 @return: The value of the tempo attribute
985 if hasattr(self, 'tempo'):
991 class Notations(Music_xml_node):
994 ts = self.get_named_children('tied')
995 starts = [t for t in ts if t.type == 'start']
1001 def get_tuplets(self):
1002 return self.get_typed_children(Tuplet)
1005 class Time_modification(Music_xml_node):
1007 def get_fraction(self):
1008 b = self.get_maybe_exist_named_child('actual-notes')
1009 a = self.get_maybe_exist_named_child('normal-notes')
1010 return(int(a.get_text()), int(b.get_text()))
1012 def get_normal_type(self):
1013 tuplet_type = self.get_maybe_exist_named_child('normal-type')
1015 dots = self.get_named_children('normal-dot')
1016 log = utilities.musicxml_duration_to_log(tuplet_type.get_text().strip())
1017 return(log , len(dots))
1022 class Accidental(Music_xml_node):
1025 Music_xml_node.__init__(self)
1026 self.editorial = False
1027 self.cautionary = False
1030 class Tuplet(Music_xml_spanner):
1032 def duration_info_from_tuplet_note(self, tuplet_note):
1033 tuplet_type = tuplet_note.get_maybe_exist_named_child('tuplet-type')
1035 dots = tuplet_note.get_named_children('tuplet-dot')
1036 log = utilities.musicxml_duration_to_log(tuplet_type.get_text().strip())
1037 return(log, len(dots))
1041 # Return tuplet note type as(log, dots)
1042 def get_normal_type(self):
1043 tuplet = self.get_maybe_exist_named_child('tuplet-normal')
1045 return self.duration_info_from_tuplet_note(tuplet)
1049 def get_actual_type(self):
1050 tuplet = self.get_maybe_exist_named_child('tuplet-actual')
1052 return self.duration_info_from_tuplet_note(tuplet)
1056 def get_tuplet_note_count(self, tuplet_note):
1058 tuplet_nr = tuplet_note.get_maybe_exist_named_child('tuplet-number')
1060 return int(tuplet_nr.get_text())
1063 def get_normal_nr(self):
1064 return self.get_tuplet_note_count(self.get_maybe_exist_named_child('tuplet-normal'))
1066 def get_actual_nr(self):
1067 return self.get_tuplet_note_count(self.get_maybe_exist_named_child('tuplet-actual'))
1070 class Slur(Music_xml_spanner):
1076 class Tied(Music_xml_spanner):
1082 class Beam(Music_xml_spanner):
1084 return self.get_text()
1085 def is_primary(self):
1086 if hasattr(self, 'number'):
1087 return self.number == "1"
1091 class Octave_shift(Music_xml_spanner):
1092 # default is 8 for the octave-shift!
1094 if hasattr(self, 'size'):
1095 return int(self.size)
1100 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
1101 # for the inner <rest> element, not the whole rest block.
1102 class Rest(Music_xml_node):
1105 Music_xml_node.__init__(self)
1106 self._is_whole_measure = False
1108 def is_whole_measure(self):
1109 return self._is_whole_measure
1112 ch = self.get_maybe_exist_typed_child(get_class(u'display-step'))
1114 return ch.get_text().strip()
1118 def get_octave(self):
1119 ch = self.get_maybe_exist_typed_child(get_class(u'display-octave'))
1121 oct = ch.get_text().strip()
1126 def to_lily_object(self):
1128 step = self.get_step()
1130 p = musicexp.Pitch()
1131 p.step = musicxml2ly_conversion.musicxml_step_to_lily(step)
1132 octave = self.get_octave()
1134 p.octave = octave - 4
1138 class Bend(Music_xml_node):
1140 def bend_alter(self):
1141 alter = self.get_maybe_exist_named_child('bend-alter')
1142 return utilities.interpret_alter_element(alter)
1145 class ChordPitch(Music_xml_node):
1147 def step_class_name(self):
1150 def alter_class_name(self):
1151 return u'root-alter'
1154 ch = self.get_unique_typed_child(get_class(self.step_class_name()))
1155 return ch.get_text().strip()
1157 def get_alteration(self):
1158 ch = self.get_maybe_exist_typed_child(get_class(self.alter_class_name()))
1159 return utilities.interpret_alter_element(ch)
1162 class Bass(ChordPitch):
1164 def step_class_name(self):
1167 def alter_class_name(self):
1168 return u'bass-alter'
1171 class ChordModification(Music_xml_node):
1174 ch = self.get_maybe_exist_typed_child(get_class(u'degree-type'))
1175 return {'add': 1, 'alter': 1, 'subtract':-1}.get(ch.get_text().strip(), 0)
1177 def get_value(self):
1178 ch = self.get_maybe_exist_typed_child(get_class(u'degree-value'))
1181 value = int(ch.get_text().strip())
1184 def get_alter(self):
1185 ch = self.get_maybe_exist_typed_child(get_class(u'degree-alter'))
1186 return utilities.interpret_alter_element(ch)
1189 class Frame(Music_xml_node):
1191 def get_frets(self):
1192 return self.get_named_child_value_number('frame-frets', 4)
1194 def get_strings(self):
1195 return self.get_named_child_value_number('frame-strings', 6)
1197 def get_first_fret(self):
1198 return self.get_named_child_value_number('first-fret', 1)
1201 class Frame_Note(Music_xml_node):
1203 def get_string(self):
1204 return self.get_named_child_value_number('string', 1)
1207 return self.get_named_child_value_number('fret', 0)
1209 def get_fingering(self):
1210 return self.get_named_child_value_number('fingering', -1)
1212 def get_barre(self):
1213 n = self.get_maybe_exist_named_child('barre')
1215 return getattr(n, 'type', '')
1220 class Musicxml_voice:
1225 self._start_staff = None
1227 self._has_lyrics = False
1229 def add_element(self, e):
1230 self._elements.append(e)
1231 if(isinstance(e, Note)
1232 and e.get_maybe_exist_typed_child(Staff)):
1233 name = e.get_maybe_exist_typed_child(Staff).get_text()
1235 if not self._start_staff and not e.get_maybe_exist_typed_child(Grace):
1236 self._start_staff = name
1237 self._staves[name] = True
1239 lyrics = e.get_typed_children(Lyric)
1240 if not self._has_lyrics:
1241 self.has_lyrics = len(lyrics) > 0
1245 if(nr > 0) and not(nr in self._lyrics):
1246 self._lyrics.append(nr)
1248 def insert(self, idx, e):
1249 self._elements.insert(idx, e)
1251 def get_lyrics_numbers(self):
1252 if(len(self._lyrics) == 0) and self._has_lyrics:
1253 #only happens if none of the <lyric> tags has a number attribute
1259 class Part(Music_xml_node):
1262 Music_xml_node.__init__(self)
1264 self._staff_attributes_dict = {}
1266 def get_part_list(self):
1268 while n and n.get_name() != 'score-partwise':
1271 return n.get_named_child('part-list')
1273 def graces_to_aftergraces(self, pending_graces):
1274 for gr in pending_graces:
1275 gr._when = gr._prev_when
1276 gr._measure_position = gr._prev_measure_position
1277 gr._after_grace = True
1279 def interpret(self):
1280 """Set durations and starting points."""
1281 """The starting point of the very first note is 0!"""
1283 part_list = self.get_part_list()
1286 factor = Rational(1)
1287 attributes_dict = {}
1288 attributes_object = None
1289 measures = self.get_typed_children(Measure)
1290 last_moment = Rational(-1)
1291 last_measure_position = Rational(-1)
1292 measure_position = Rational(0)
1293 measure_start_moment = now
1294 is_first_measure = True
1295 previous_measure = None
1296 # Graces at the end of a measure need to have their position set to the
1300 # implicit measures are used for artificial measures, e.g. when
1301 # a repeat bar line splits a bar into two halves. In this case,
1302 # don't reset the measure position to 0. They are also used for
1303 # upbeats(initial value of 0 fits these, too).
1304 # Also, don't reset the measure position at the end of the loop,
1305 # but rather when starting the next measure(since only then do we
1306 # know if the next measure is implicit and continues that measure)
1307 if not m.is_implicit():
1308 # Warn about possibly overfull measures and reset the position
1309 if attributes_object and previous_measure and previous_measure.partial == 0:
1310 length = attributes_object.get_measure_length()
1311 new_now = measure_start_moment + length
1313 problem = 'incomplete'
1315 problem = 'overfull'
1316 ## only for verbose operation.
1317 if problem <> 'incomplete' and previous_measure:
1318 previous_measure.message('%s measure? Expected: %s, Difference: %s' %(problem, now, new_now - now))
1320 measure_start_moment = now
1321 measure_position = Rational(0)
1324 assign_to_next_voice = []
1325 for n in m.get_all_children ():
1326 # assign a voice to all measure elements
1327 if (n.get_name() == 'backup'):
1330 if isinstance(n, Measure_element):
1331 if n.get_voice_id ():
1332 voice_id = n.get_voice_id ()
1333 for i in assign_to_next_voice:
1334 i.voice_id = voice_id
1335 assign_to_next_voice = []
1338 n.voice_id = voice_id
1340 assign_to_next_voice.append (n)
1342 # figured bass has a duration, but applies to the next note
1343 # and should not change the current measure position!
1344 if isinstance(n, FiguredBass):
1345 n._divisions = factor.denominator()
1347 n._measure_position = measure_position
1350 if isinstance(n, Hash_text):
1354 if n.__class__ == Attributes:
1355 n.set_attributes_from_previous(attributes_dict)
1357 attributes_dict = n._dict.copy()
1358 attributes_object = n
1360 factor = Rational(1,
1361 int(attributes_dict.get('divisions').get_text()))
1364 if(n.get_maybe_exist_typed_child(Duration)):
1365 mxl_dur = n.get_maybe_exist_typed_child(Duration)
1366 dur = mxl_dur.get_length() * factor
1368 if n.get_name() == 'backup':
1370 # reset all graces before the backup to after-graces:
1371 self.graces_to_aftergraces(pending_graces)
1373 if n.get_maybe_exist_typed_child(Grace):
1376 rest = n.get_maybe_exist_typed_child(Rest)
1378 and attributes_object
1379 and attributes_object.get_measure_length() == dur):
1381 rest._is_whole_measure = True
1383 if(dur > Rational(0)
1384 and n.get_maybe_exist_typed_child(Chord)):
1386 measure_position = last_measure_position
1389 n._measure_position = measure_position
1391 # For all grace notes, store the previous note, in case need
1392 # to turn the grace note into an after-grace later on!
1393 if isinstance(n, Note) and n.is_grace():
1394 n._prev_when = last_moment
1395 n._prev_measure_position = last_measure_position
1396 # After-graces are placed at the same position as the previous note
1397 if isinstance(n, Note) and n.is_after_grace():
1398 # TODO: We should do the same for grace notes at the end of
1399 # a measure with no following note!!!
1400 n._when = last_moment
1401 n._measure_position = last_measure_position
1402 elif isinstance(n, Note) and n.is_grace():
1403 pending_graces.append(n)
1404 elif(dur > Rational(0)):
1405 pending_graces = [];
1408 if dur > Rational(0):
1410 last_measure_position = measure_position
1412 measure_position += dur
1413 elif dur < Rational(0):
1414 # backup element, reset measure position
1416 measure_position += dur
1417 if measure_position < 0:
1418 # backup went beyond the measure start => reset to 0
1419 now -= measure_position
1420 measure_position = 0
1422 last_measure_position = measure_position
1423 if n._name == 'note':
1424 instrument = n.get_maybe_exist_named_child('instrument')
1426 n.instrument_name = part_list.get_instrument(instrument.id)
1428 # reset all graces at the end of the measure to after-graces:
1429 self.graces_to_aftergraces(pending_graces)
1431 # Incomplete first measures are not padded, but registered as partial
1432 if is_first_measure:
1433 is_first_measure = False
1434 # upbeats are marked as implicit measures
1435 if attributes_object and m.is_implicit():
1436 length = attributes_object.get_measure_length()
1437 measure_end = measure_start_moment + length
1438 if measure_end <> now:
1440 previous_measure = m
1442 # modify attributes so that only those applying to the given staff remain
1443 def extract_attributes_for_staff(part, attr, staff):
1444 attributes = copy.copy(attr)
1445 attributes._children = [];
1446 attributes._dict = attr._dict.copy()
1447 attributes._original_tag = attr
1448 # copy only the relevant children over for the given staff
1451 for c in attr._children:
1452 if(not(hasattr(c, 'number') and(c.number != staff)) and
1453 not(isinstance(c, Hash_text))):
1454 attributes._children.append(c)
1455 if not attributes._children:
1460 def extract_voices(part):
1461 # The last indentified voice
1465 measures = part.get_typed_children(Measure)
1469 elements.append(Partial(m.partial))
1470 elements.extend(m.get_all_children())
1471 # make sure we know all voices already so that dynamics, clefs, etc.
1472 # can be assigned to the correct voices
1473 voice_to_staff_dict = {}
1475 voice_id = n.get_maybe_exist_named_child(u'voice')
1478 vid = voice_id.get_text()
1479 elif isinstance(n, Note):
1480 # TODO: Check whether we shall really use "None" here, or
1481 # rather use "1" as the default?
1482 if n.get_maybe_exist_named_child(u'chord'):
1490 staff_id = n.get_maybe_exist_named_child(u'staff')
1493 sid = staff_id.get_text()
1495 # TODO: Check whether we shall really use "None" here, or
1496 # rather use "1" as the default?
1497 # If this is changed, need to change the corresponding
1498 # check in extract_attributes_for_staff, too.
1500 if vid and not voices.has_key(vid):
1501 voices[vid] = Musicxml_voice()
1502 if vid and sid and not n.get_maybe_exist_typed_child(Grace):
1503 if not voice_to_staff_dict.has_key(vid):
1504 voice_to_staff_dict[vid] = sid
1506 # invert the voice_to_staff_dict into a staff_to_voice_dict(since we
1507 # need to assign staff-assigned objects like clefs, times, etc. to
1508 # all the correct voices. This will never work entirely correct due
1509 # to staff-switches, but that's the best we can do!
1510 staff_to_voice_dict = {}
1511 for(v, s) in voice_to_staff_dict.items():
1512 if not staff_to_voice_dict.has_key(s):
1513 staff_to_voice_dict[s] = [v]
1515 staff_to_voice_dict[s].append(v)
1518 assign_to_next_note = []
1521 voice_id = n.get_maybe_exist_typed_child(get_class('voice'))
1523 id = voice_id.get_text()
1525 if n.get_maybe_exist_typed_child(get_class('chord')):
1533 # We don't need backup/forward any more, since we have already
1534 # assigned the correct onset times.
1535 # TODO: Let Grouping through. Also: link, print, bokmark sound
1536 if not(isinstance(n, Note) or isinstance(n, Attributes) or
1537 isinstance(n, Direction) or isinstance(n, Partial) or
1538 isinstance(n, Barline) or isinstance(n, Harmony) or
1539 isinstance(n, FiguredBass) or isinstance(n, Print)):
1542 if isinstance(n, Attributes) and not start_attr:
1546 if isinstance(n, Attributes):
1547 # assign these only to the voices they really belong to!
1548 for(s, vids) in staff_to_voice_dict.items():
1549 staff_attributes = part.extract_attributes_for_staff(n, s)
1550 if staff_attributes:
1552 voices[v].add_element(staff_attributes)
1555 if isinstance(n, Partial) or isinstance(n, Barline) or isinstance(n, Print):
1556 for v in voices.keys():
1557 voices[v].add_element(n)
1560 if isinstance(n, Direction):
1562 voices[n.voice_id].add_element (n)
1564 assign_to_next_note.append (n)
1567 if isinstance(n, Harmony) or isinstance(n, FiguredBass):
1568 # store the harmony or figured bass element until we encounter
1569 # the next note and assign it only to that one voice.
1570 assign_to_next_note.append(n)
1573 if hasattr(n, 'print-object') and getattr(n, 'print-object') == "no":
1577 for i in assign_to_next_note:
1578 voices[id].add_element(i)
1579 assign_to_next_note = []
1580 voices[id].add_element(n)
1582 # Assign all remaining elements from assign_to_next_note to the voice
1583 # of the previous note:
1584 for i in assign_to_next_note:
1585 voices[id].add_element(i)
1586 assign_to_next_note = []
1589 for(s, vids) in staff_to_voice_dict.items():
1590 staff_attributes = part.extract_attributes_for_staff(start_attr, s)
1591 staff_attributes.read_self()
1592 part._staff_attributes_dict[s] = staff_attributes
1594 voices[v].insert(0, staff_attributes)
1595 voices[v]._elements[0].read_self()
1597 part._voices = voices
1599 def get_voices(self):
1602 def get_staff_attributes(self):
1603 return self._staff_attributes_dict
1606 class BarStyle(Music_xml_node):
1609 class BeatType(Music_xml_node):
1612 class BeatUnit(Music_xml_node):
1615 class BeatUnitDot(Music_xml_node):
1618 class Beats(Music_xml_node):
1621 class Bracket(Music_xml_spanner):
1624 class Chord(Music_xml_node):
1627 class Dashes(Music_xml_spanner):
1630 class DirType(Music_xml_node):
1633 class Direction(Measure_element):
1636 class Dot(Music_xml_node):
1639 class Elision(Music_xml_node):
1642 class Extend(Music_xml_node):
1645 class FiguredBass(Music_xml_node):
1648 class Glissando(Music_xml_spanner):
1651 class Grace(Music_xml_node):
1654 class Harmony(Music_xml_node):
1657 class Hash_comment(Music_xml_node):
1660 class KeyAlter(Music_xml_node):
1663 class Direction (Measure_element):
1666 class KeyOctave(Music_xml_node):
1669 class KeyStep(Music_xml_node):
1672 class Part_group(Music_xml_node):
1675 class Pedal(Music_xml_spanner):
1678 class PerMinute(Music_xml_node):
1681 class Print(Music_xml_node):
1684 class Root(ChordPitch):
1687 class Score_part(Music_xml_node):
1690 class Slide(Music_xml_spanner):
1693 class Staff(Music_xml_node):
1696 class Text(Music_xml_node):
1699 class Type(Music_xml_node):
1702 class Wavy_line(Music_xml_spanner):
1705 class Wedge(Music_xml_spanner):
1708 class Words(Music_xml_node):
1712 ## need this, not all classes are instantiated
1713 ## for every input file. Only add those classes, that are either directly
1714 ## used by class name or extend Music_xml_node in some way!
1716 '#comment': Hash_comment,
1718 'accidental': Accidental,
1719 'attributes': Attributes,
1721 'bar-style': BarStyle,
1725 'beat-type': BeatType,
1726 'beat-unit': BeatUnit,
1727 'beat-unit-dot': BeatUnitDot,
1729 'bracket' : Bracket,
1733 'degree' : ChordModification,
1735 'direction': Direction,
1736 'direction-type': DirType,
1737 'duration': Duration,
1741 'frame-note': Frame_Note,
1742 'figured-bass': FiguredBass,
1743 'glissando': Glissando,
1746 'identification': Identification,
1747 'key-alter': KeyAlter,
1748 'key-octave': KeyOctave,
1749 'key-step': KeyStep,
1752 'notations': Notations,
1754 'notehead': Notehead,
1755 'octave-shift': Octave_shift,
1757 'part-group': Part_group,
1758 'part-list': Part_list,
1760 'per-minute': PerMinute,
1765 'score-part': Score_part,
1771 'syllabic': Syllabic,
1773 'time-modification': Time_modification,
1777 'unpitched': Unpitched,
1778 'wavy-line': Wavy_line,
1784 def name2class_name(name):
1785 name = name.replace('-', '_')
1786 name = name.replace('#', 'hash_')
1787 name = name[0].upper() + name[1:].lower()
1791 def get_class(name):
1792 classname = class_dict.get(name)
1796 class_name = name2class_name(name)
1797 klass = new.classobj(class_name,(Music_xml_node,) , {})
1798 class_dict[name] = klass
1801 def lxml_demarshal_node(node):
1804 # Ignore comment nodes, which are also returned by the etree parser!
1805 if name is None or node.__class__.__name__ == "_Comment":
1807 klass = get_class(name)
1810 py_node._original = node
1811 py_node._name = name
1812 py_node._data = node.text
1813 py_node._children = [lxml_demarshal_node(cn) for cn in node.getchildren()]
1814 py_node._children = filter(lambda x: x, py_node._children)
1816 for c in py_node._children:
1819 for(k, v) in node.items():
1820 py_node.__dict__[k] = v
1821 py_node._attribute_dict[k] = v
1825 def minidom_demarshal_node(node):
1826 name = node.nodeName
1828 klass = get_class(name)
1830 py_node._name = name
1831 py_node._children = [minidom_demarshal_node(cn) for cn in node.childNodes]
1832 for c in py_node._children:
1836 for(nm, value) in node.attributes.items():
1837 py_node.__dict__[nm] = value
1838 py_node._attribute_dict[nm] = value
1840 py_node._data = None
1841 if node.nodeType == node.TEXT_NODE and node.data:
1842 py_node._data = node.data
1844 py_node._original = node
1848 if __name__ == '__main__':
1851 tree = lxml.etree.parse('beethoven.xml')
1852 mxl_tree = lxml_demarshal_node(tree.getroot())
1853 ks = class_dict.keys()