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
129 class Music_xml_spanner(Music_xml_node):
132 if hasattr(self, 'type'):
138 if hasattr(self, 'size'):
139 return int(self.size)
144 class Measure_element(Music_xml_node):
146 def get_voice_id(self):
147 voice_id = self.get_maybe_exist_named_child('voice')
149 return voice_id.get_text()
154 # Look at all measure elements(previously we had self.__class__, which
155 # only looked at objects of the same type!
156 cn = self._parent.get_typed_children(Measure_element)
157 # But only look at the correct voice; But include Attributes, too, which
158 # are not tied to any particular voice
159 cn = [c for c in cn if(c.get_voice_id() == self.get_voice_id()) or isinstance(c, Attributes)]
163 class Work(Xml_node):
165 def get_work_information(self, tag):
166 wt = self.get_maybe_exist_named_child(tag)
172 def get_work_title(self):
173 return self.get_work_information('work-title')
175 def get_work_number(self):
176 return self.get_work_information('work-number')
178 # def get_opus(self):
179 # return self.get_work_information('opus')
182 class Identification(Xml_node):
184 def get_rights(self):
185 rights = self.get_named_children('rights')
189 # if this Xml_node has an attribute, such as 'type="words"',
190 # include it in the header. Otherwise, it is assumed that
191 # the text contents of this node looks something like this:
192 # 'Copyright: X.Y.' and thus already contains the relevant
194 if hasattr(r, 'type'):
195 rights_type = r.type.title() # capitalize first letter
196 result = ''.join([rights_type, ': ', text])
200 return string.join(ret, "\n")
202 # 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.)
203 def get_source(self):
204 source = self.get_named_children('source')
207 ret.append(r.get_text())
208 return string.join(ret, "\n")
210 def get_creator(self, type):
211 creators = self.get_named_children('creator')
212 # return the first creator tag that has the particular type
214 if hasattr(i, 'type') and i.type == type:
218 def get_composer(self):
219 c = self.get_creator('composer')
222 creators = self.get_named_children('creator')
223 # return the first creator tag that has no type at all
225 if not hasattr(i, 'type'):
229 def get_arranger(self):
230 return self.get_creator('arranger')
232 def get_editor(self):
233 return self.get_creator('editor')
236 v = self.get_creator('lyricist')
239 v = self.get_creator('poet')
242 def get_encoding_information(self, type):
243 enc = self.get_named_children('encoding')
245 children = enc[0].get_named_children(type)
247 return children[0].get_text()
251 def get_encoding_software(self):
252 return self.get_encoding_information('software')
254 def get_encoding_date(self):
255 return self.get_encoding_information('encoding-date')
257 def get_encoding_person(self):
258 return self.get_encoding_information('encoder')
260 def get_encoding_description(self):
261 return self.get_encoding_information('encoding-description')
263 def get_encoding_software_list(self):
264 enc = self.get_named_children('encoding')
267 softwares = e.get_named_children('software')
269 software.append(s.get_text())
272 def get_file_description(self):
273 misc = self.get_named_children('miscellaneous')
275 misc_fields = m.get_named_children('miscellaneous-field')
276 for mf in misc_fields:
277 if hasattr(mf, 'name') and mf.name == 'description':
282 class Credit(Xml_node):
285 type = self.get_maybe_exist_named_child('credit-type')
287 return type.get_text()
291 def find_type(self, credits):
292 sizes = self.get_font_sizes(credits)
293 sizes.sort(reverse=True)
294 ys = self.get_default_ys(credits)
295 ys.sort(reverse=True)
296 xs = self.get_default_xs(credits)
297 xs.sort(reverse=True)
299 # Words child of the self credit-element
300 words = self.get_maybe_exist_named_child('credit-words')
308 if hasattr(words, 'font-size'):
309 size = utilities.string_to_integer(getattr(words, 'font-size'))
310 if hasattr(words, 'default-x'):
311 x = round(float(getattr(words, 'default-x')))
312 if hasattr(words, 'default-y'):
313 y = round(float(getattr(words, 'default-y')))
314 if hasattr(words, 'halign'):
315 halign = getattr(words, 'halign')
316 if hasattr(words, 'valign'):
317 valign = getattr(words, 'valign')
318 if hasattr(words, 'justify'):
319 justify = getattr(words, 'justify')
320 if(size and size == max(sizes) and y and y == max(ys) and(justify or halign) and(justify == 'center' or halign == 'center')):
322 elif((y and y > min(ys) and y < max(ys)) and((justify or halign) and(justify == 'center' or halign == 'center'))):
324 elif((justify or halign) and(justify == 'left' or halign == 'left') and(not(x) or x == min(xs))):
326 elif((justify or halign) and(justify == 'right' or halign == 'right') and(not(x) or x == max(xs))):
328 elif(size and size == min(sizes) and y == min(ys)):
330 # Special cases for Finale NotePad
331 elif((valign and(valign == 'top')) and(y and y == ys[1])):
333 elif((valign and(valign == 'top')) and(x and x == min(xs))):
335 elif((valign and(valign == 'top')) and(y and y == min(ys))):
337 # Other special cases
338 elif((valign and(valign == 'bottom'))):
340 elif(len([item for item in range(len(ys)) if ys[item] == y]) == 2):
341 # The first one is the composer, the second one is the lyricist
344 return None # no type recognized
346 def get_font_sizes(self, credits):
349 words = cred.get_maybe_exist_named_child('credit-words')
350 if((words != None) and hasattr(words, 'font-size')):
351 sizes.append(getattr(words, 'font-size'))
352 return map(utilities.string_to_integer, sizes)
354 def get_default_xs(self, credits):
357 words = cred.get_maybe_exist_named_child('credit-words')
358 if((words != None) and hasattr(words, 'default-x')):
359 default_xs.append(getattr(words, 'default-x'))
360 return map(round, map(float, default_xs))
362 def get_default_ys(self, credits):
365 words = cred.get_maybe_exist_named_child('credit-words')
366 if((words != None) and hasattr(words, 'default-y')):
367 default_ys.append(getattr(words, 'default-y'))
368 return map(round, map(float, default_ys))
371 words = self.get_maybe_exist_named_child('credit-words')
373 return words.get_text()
378 class Duration(Music_xml_node):
380 def get_length(self):
381 dur = int(self.get_text()) * Rational(1, 4)
385 class Hash_text(Music_xml_node):
387 def dump(self, indent=''):
388 ly.debug_output('%s' % string.strip(self._data))
391 class Pitch(Music_xml_node):
394 ch = self.get_unique_typed_child(get_class(u'step'))
395 step = ch.get_text().strip()
398 def get_octave(self):
399 ch = self.get_unique_typed_child(get_class(u'octave'))
400 octave = ch.get_text().strip()
403 def get_alteration(self):
404 ch = self.get_maybe_exist_typed_child(get_class(u'alter'))
405 return utilities.interpret_alter_element(ch)
407 def to_lily_object(self):
409 p.alteration = self.get_alteration()
410 p.step = musicxml2ly_conversion.musicxml_step_to_lily(self.get_step())
411 p.octave = self.get_octave() - 4
415 class Unpitched(Music_xml_node):
418 ch = self.get_unique_typed_child(get_class(u'display-step'))
419 step = ch.get_text().strip()
422 def get_octave(self):
423 ch = self.get_unique_typed_child(get_class(u'display-octave'))
426 octave = ch.get_text().strip()
431 def to_lily_object(self):
433 step = self.get_step()
436 p.step = musicxml2ly_conversion.musicxml_step_to_lily(step)
437 octave = self.get_octave()
439 p.octave = octave - 4
443 class Attributes(Measure_element):
446 Measure_element.__init__(self)
448 self._original_tag = None
449 self._time_signature_cache = None
452 cn = self._parent.get_typed_children(self.__class__)
453 if self._original_tag:
454 return cn[0] == self._original_tag
458 def set_attributes_from_previous(self, dict):
459 self._dict.update(dict)
462 for c in self.get_all_children():
463 self._dict[c.get_name()] = c
465 def get_named_attribute(self, name):
466 return self._dict.get(name)
468 def single_time_sig_to_fraction(self, sig):
474 return Rational(n, sig[-1])
476 def get_measure_length(self):
477 sig = self.get_time_signature()
478 if not sig or len(sig) == 0:
480 if isinstance(sig[0], list):
481 # Complex compound time signature
484 l += self.single_time_sig_to_fraction(i)
487 # Simple(maybe compound) time signature of the form(beat, ..., type)
488 return self.single_time_sig_to_fraction(sig)
491 def get_time_signature(self):
492 "Return time sig as a(beat, beat-type) tuple. For compound signatures,"
493 "return either(beat, beat,..., beat-type) or((beat,..., type), "
494 "(beat,..., type), ...)."
495 if self._time_signature_cache:
496 return self._time_signature_cache
499 mxl = self.get_named_attribute('time')
503 if mxl.get_maybe_exist_named_child('senza-misura'):
504 # TODO: Handle pieces without a time signature!
505 ly.warning(_("Senza-misura time signatures are not yet supported!"))
510 for i in mxl.get_all_children():
511 if isinstance(i, Beats):
512 beats = string.split(i.get_text().strip(), "+")
513 current_sig = [int(j) for j in beats]
514 elif isinstance(i, BeatType):
515 current_sig.append(int(i.get_text()))
516 signature.append(current_sig)
518 if isinstance(signature[0], list) and len(signature) == 1:
519 signature = signature[0]
520 self._time_signature_cache = signature
522 except(KeyError, ValueError):
523 self.message(_("Unable to interpret time signature! Falling back to 4/4."))
526 # returns clef information in the form("cleftype", position, octave-shift)
527 def get_clef_information(self):
528 clefinfo = ['G', 2, 0]
529 mxl = self.get_named_attribute('clef')
532 sign = mxl.get_maybe_exist_named_child('sign')
534 clefinfo[0] = sign.get_text()
535 line = mxl.get_maybe_exist_named_child('line')
537 clefinfo[1] = int(line.get_text())
538 octave = mxl.get_maybe_exist_named_child('clef-octave-change')
540 clefinfo[2] = int(octave.get_text())
543 def get_key_signature(self):
544 "return(fifths, mode) tuple if the key signatures is given as "
545 "major/minor in the Circle of fifths. Otherwise return an alterations"
546 "list of the form [[step,alter<,octave>], [step,alter<,octave>], ...], "
547 "where the octave values are optional."
549 key = self.get_named_attribute('key')
552 fifths_elm = key.get_maybe_exist_named_child('fifths')
554 mode_node = key.get_maybe_exist_named_child('mode')
557 mode = mode_node.get_text()
558 if not mode or mode == '':
560 fifths = int(fifths_elm.get_text())
561 # TODO: Shall we try to convert the key-octave and the cancel, too?
566 for i in key.get_all_children():
567 if isinstance(i, KeyStep):
568 current_step = i.get_text().strip()
569 elif isinstance(i, KeyAlter):
570 alterations.append([current_step, utilities.interpret_alter_element(i)])
571 elif isinstance(i, KeyOctave):
573 if hasattr(i, 'number'):
575 if(nr > 0) and(nr <= len(alterations)):
576 # MusicXML Octave 4 is middle C -> shift to 0
577 alterations[nr - 1].append(int(i.get_text()) - 4)
579 i.message(_("Key alteration octave given for a "
580 "non-existing alteration nr. %s, available numbers: %s!") %(nr, len(alterations)))
583 def get_transposition(self):
584 return self.get_named_attribute('transpose')
587 class Barline(Measure_element):
589 def to_lily_object(self):
590 # retval contains all possible markers in the order:
591 # 0..bw_ending, 1..bw_repeat, 2..barline, 3..fw_repeat, 4..fw_ending
593 bartype_element = self.get_maybe_exist_named_child("bar-style")
594 repeat_element = self.get_maybe_exist_named_child("repeat")
595 ending_element = self.get_maybe_exist_named_child("ending")
599 bartype = bartype_element.get_text()
601 if repeat_element and hasattr(repeat_element, 'direction'):
602 repeat = musicxml2ly_conversion.RepeatMarker()
603 repeat.direction = {"forward":-1, "backward": 1}.get(
604 repeat_element.direction, 0)
606 if((repeat_element.direction == "forward" and bartype == "heavy-light") or
607 (repeat_element.direction == "backward" and bartype == "light-heavy")):
609 if hasattr(repeat_element, 'times'):
611 repeat.times = int(repeat_element.times)
615 if repeat.direction == -1:
620 if ending_element and hasattr(ending_element, 'type'):
621 ending = musicxml2ly_conversion.EndingMarker()
622 ending.direction = {"start":-1, "stop": 1, "discontinue": 1}.get(
623 ending_element.type, 0)
625 if ending.direction == -1:
629 # TODO. ending number=""
632 b = musicexp.BarLine()
636 return retval.values()
639 class Partial(Measure_element):
640 def __init__(self, partial):
641 Measure_element.__init__(self)
642 self.partial = partial
645 class Stem(Music_xml_node):
650 'double': None, # TODO: Implement
651 'none': 'stemNeutral'
654 def to_stem_event(self):
656 value = self.stem_value_dict.get(self.get_text(), None)
657 stem_value = musicexp.StemEvent()
659 stem_value.value = value
660 values.append(stem_value)
663 def to_stem_style_event(self):
665 style_elm = musicexp.StemstyleEvent()
666 if hasattr(self, 'color'):
667 style_elm.color = utilities.hex_to_color(getattr(self, 'color'))
668 if(style_elm.color != None):
669 styles.append(style_elm)
673 class Notehead(Music_xml_node):
675 notehead_styles_dict = {
677 'triangle': '\'triangle',
678 'diamond': '\'diamond',
679 'square': '\'la', # TODO: Proper squared note head
680 'cross': None, # TODO: + shaped note head
682 'circle-x': '\'xcircle',
683 'inverted triangle': None, # TODO: Implement
684 'arrow down': None, # TODO: Implement
685 'arrow up': None, # TODO: Implement
686 'slashed': None, # TODO: Implement
687 'back slashed': None, # TODO: Implement
689 'cluster': None, # TODO: Implement
700 def to_lily_object(self): #function changed: additionally processcolor attribute
704 key = self.get_text().strip()
705 style = self.notehead_styles_dict.get(key, None)
706 event = musicexp.NotestyleEvent()
709 if hasattr(self, 'filled'):
710 event.filled =(getattr(self, 'filled') == "yes")
711 if hasattr(self, 'color'):
712 event.color = utilities.hex_to_color(getattr(self, 'color'))
713 if event.style or(event.filled != None) or(event.color != None):
716 if hasattr(self, 'parentheses') and(self.parentheses == "yes"):
717 styles.append(musicexp.ParenthesizeEvent())
722 class Note(Measure_element):
725 'Acoustic Snare Drum': 'acousticsnare',
726 'Side Stick': 'sidestick',
727 'Open Triangle': 'opentriangle',
728 'Mute Triangle': 'mutetriangle',
729 'Tambourine': 'tambourine',
730 'Bass Drum': 'bassdrum',
734 Measure_element.__init__(self)
735 self.instrument_name = ''
736 self._after_grace = False
740 return self.get_maybe_exist_named_child(u'grace')
742 def is_after_grace(self):
743 if not self.is_grace():
745 gr = self.get_maybe_exist_typed_child(Grace)
746 return self._after_grace or hasattr(gr, 'steal-time-previous');
748 def get_duration_log(self):
749 ch = self.get_maybe_exist_named_child(u'type')
752 log = ch.get_text().strip()
753 return utilities.musicxml_duration_to_log(log)
754 elif self.get_maybe_exist_named_child(u'grace'):
755 # FIXME: is it ok to default to eight note for grace notes?
760 def get_duration_info(self):
761 log = self.get_duration_log()
763 dots = len(self.get_typed_children(Dot))
768 def get_factor(self):
771 def get_pitches(self):
772 return self.get_typed_children(get_class(u'pitch'))
774 def set_notehead_style(self, event):
775 noteheads = self.get_named_children('notehead')
777 styles = nh.to_lily_object()
779 event.add_associated_event(style)
781 def set_stem_directions(self, event):
782 stems = self.get_named_children('stem')
784 values = stem.to_stem_event()
786 event.add_associated_event(v)
788 def set_stem_style(self, event):
789 stems = self.get_named_children('stem')
791 styles = stem.to_stem_style_event()
793 event.add_associated_event(style)
795 def initialize_duration(self):
796 from musicxml2ly_conversion import rational_to_lily_duration
797 from musicexp import Duration
798 # if the note has no Type child, then that method returns None. In that case,
799 # use the <duration> tag instead. If that doesn't exist, either -> Error
800 dur = self.get_duration_info()
803 d.duration_log = dur[0]
805 # Grace notes by specification have duration 0, so no time modification
806 # factor is possible. It even messes up the output with *0/1
807 if not self.get_maybe_exist_typed_child(Grace):
808 d.factor = self._duration / d.get_length()
811 if self._duration > 0:
812 return rational_to_lily_duration(self._duration)
815 _("Encountered note at %s without type and duration(=%s)") \
816 %(mxl_note.start, mxl_note._duration))
819 def initialize_pitched_event(self):
820 mxl_pitch = self.get_maybe_exist_typed_child(Pitch)
821 pitch = mxl_pitch.to_lily_object()
822 event = musicexp.NoteEvent()
824 acc = self.get_maybe_exist_named_child('accidental')
826 # let's not force accs everywhere.
827 event.cautionary = acc.cautionary
828 # TODO: Handle editorial accidentals
829 # TODO: Handle the level-display setting for displaying brackets/parentheses
832 def initialize_unpitched_event(self):
833 # Unpitched elements have display-step and can also have
835 unpitched = self.get_maybe_exist_typed_child(Unpitched)
836 event = musicexp.NoteEvent()
837 event.pitch = unpitched.to_lily_object()
840 def initialize_rest_event(self, convert_rest_positions=True):
841 # rests can have display-octave and display-step, which are
842 # treated like an ordinary note pitch
843 rest = self.get_maybe_exist_typed_child(Rest)
844 event = musicexp.RestEvent()
845 if convert_rest_positions:
846 pitch = rest.to_lily_object()
850 def initialize_drum_event(self):
851 event = musicexp.NoteEvent()
852 drum_type = self.drumtype_dict.get(self.instrument_name)
854 event.drum_type = drum_type
857 _("drum %s type unknown, please add to instrument_drumtype_dict")
859 event.drum_type = 'acousticsnare'
862 def to_lily_object(self,
863 convert_stem_directions=True,
864 convert_rest_positions=True):
869 if self.get_maybe_exist_typed_child(Pitch):
870 event = self.initialize_pitched_event()
871 elif self.get_maybe_exist_typed_child(Unpitched):
872 event = self.initialize_unpitched_event()
873 elif self.get_maybe_exist_typed_child(Rest):
874 event = self.initialize_rest_event(convert_rest_positions)
875 elif self.instrument_name:
876 event = self.initialize_drum_event()
878 self.message(_("cannot find suitable event"))
881 event.duration = self.initialize_duration()
883 self.set_notehead_style(event)
884 self.set_stem_style(event)
885 if convert_stem_directions:
886 self.set_stem_directions(event)
891 class Part_list(Music_xml_node):
894 Music_xml_node.__init__(self)
895 self._id_instrument_name_dict = {}
897 def generate_id_instrument_dict(self):
899 ## not empty to make sure this happens only once.
901 for score_part in self.get_named_children('score-part'):
902 for instr in score_part.get_named_children('score-instrument'):
904 name = instr.get_named_child("instrument-name")
905 mapping[id] = name.get_text()
907 self._id_instrument_name_dict = mapping
909 def get_instrument(self, id):
910 if not self._id_instrument_name_dict:
911 self.generate_id_instrument_dict()
913 instrument_name = self._id_instrument_name_dict.get(id)
915 return instrument_name
917 ly.warning(_("Unable to find instrument for ID=%s\n") % id)
921 class Measure(Music_xml_node):
924 Music_xml_node.__init__(self)
927 def is_implicit(self):
928 return hasattr(self, 'implicit') and self.implicit == 'yes'
931 return self.get_typed_children(get_class(u'note'))
934 class Syllabic(Music_xml_node):
937 text = self.get_text()
938 return(text == "begin") or(text == "middle")
941 return(text == "begin")
944 return(text == "middle")
947 return(text == "end")
950 class Lyric(Music_xml_node):
954 Return the number attribute(if it exists) of the lyric element.
957 @return: The value of the number attribute
959 if hasattr(self, 'number'):
965 class Sound(Music_xml_node):
969 Return the tempo attribute(if it exists) of the sound element.
970 This attribute can be used by musicxml2ly for the midi output(see L{musicexp.Score}).
973 @return: The value of the tempo attribute
975 if hasattr(self, 'tempo'):
981 class Notations(Music_xml_node):
984 ts = self.get_named_children('tied')
985 starts = [t for t in ts if t.type == 'start']
991 def get_tuplets(self):
992 return self.get_typed_children(Tuplet)
995 class Time_modification(Music_xml_node):
997 def get_fraction(self):
998 b = self.get_maybe_exist_named_child('actual-notes')
999 a = self.get_maybe_exist_named_child('normal-notes')
1000 return(int(a.get_text()), int(b.get_text()))
1002 def get_normal_type(self):
1003 tuplet_type = self.get_maybe_exist_named_child('normal-type')
1005 dots = self.get_named_children('normal-dot')
1006 log = utilities.musicxml_duration_to_log(tuplet_type.get_text().strip())
1007 return(log , len(dots))
1012 class Accidental(Music_xml_node):
1015 Music_xml_node.__init__(self)
1016 self.editorial = False
1017 self.cautionary = False
1020 class Tuplet(Music_xml_spanner):
1022 def duration_info_from_tuplet_note(self, tuplet_note):
1023 tuplet_type = tuplet_note.get_maybe_exist_named_child('tuplet-type')
1025 dots = tuplet_note.get_named_children('tuplet-dot')
1026 log = utilities.musicxml_duration_to_log(tuplet_type.get_text().strip())
1027 return(log, len(dots))
1031 # Return tuplet note type as(log, dots)
1032 def get_normal_type(self):
1033 tuplet = self.get_maybe_exist_named_child('tuplet-normal')
1035 return self.duration_info_from_tuplet_note(tuplet)
1039 def get_actual_type(self):
1040 tuplet = self.get_maybe_exist_named_child('tuplet-actual')
1042 return self.duration_info_from_tuplet_note(tuplet)
1046 def get_tuplet_note_count(self, tuplet_note):
1048 tuplet_nr = tuplet_note.get_maybe_exist_named_child('tuplet-number')
1050 return int(tuplet_nr.get_text())
1053 def get_normal_nr(self):
1054 return self.get_tuplet_note_count(self.get_maybe_exist_named_child('tuplet-normal'))
1056 def get_actual_nr(self):
1057 return self.get_tuplet_note_count(self.get_maybe_exist_named_child('tuplet-actual'))
1060 class Slur(Music_xml_spanner):
1066 class Tied(Music_xml_spanner):
1072 class Beam(Music_xml_spanner):
1074 return self.get_text()
1075 def is_primary(self):
1076 if hasattr(self, 'number'):
1077 return self.number == "1"
1081 class Octave_shift(Music_xml_spanner):
1082 # default is 8 for the octave-shift!
1084 if hasattr(self, 'size'):
1085 return int(self.size)
1090 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
1091 # for the inner <rest> element, not the whole rest block.
1092 class Rest(Music_xml_node):
1095 Music_xml_node.__init__(self)
1096 self._is_whole_measure = False
1098 def is_whole_measure(self):
1099 return self._is_whole_measure
1102 ch = self.get_maybe_exist_typed_child(get_class(u'display-step'))
1104 return ch.get_text().strip()
1108 def get_octave(self):
1109 ch = self.get_maybe_exist_typed_child(get_class(u'display-octave'))
1111 oct = ch.get_text().strip()
1116 def to_lily_object(self):
1118 step = self.get_step()
1120 p = musicexp.Pitch()
1121 p.step = musicxml2ly_conversion.musicxml_step_to_lily(step)
1122 octave = self.get_octave()
1124 p.octave = octave - 4
1128 class Bend(Music_xml_node):
1130 def bend_alter(self):
1131 alter = self.get_maybe_exist_named_child('bend-alter')
1132 return utilities.interpret_alter_element(alter)
1135 class ChordPitch(Music_xml_node):
1137 def step_class_name(self):
1140 def alter_class_name(self):
1141 return u'root-alter'
1144 ch = self.get_unique_typed_child(get_class(self.step_class_name()))
1145 return ch.get_text().strip()
1147 def get_alteration(self):
1148 ch = self.get_maybe_exist_typed_child(get_class(self.alter_class_name()))
1149 return utilities.interpret_alter_element(ch)
1152 class Bass(ChordPitch):
1154 def step_class_name(self):
1157 def alter_class_name(self):
1158 return u'bass-alter'
1161 class ChordModification(Music_xml_node):
1164 ch = self.get_maybe_exist_typed_child(get_class(u'degree-type'))
1165 return {'add': 1, 'alter': 1, 'subtract':-1}.get(ch.get_text().strip(), 0)
1167 def get_value(self):
1168 ch = self.get_maybe_exist_typed_child(get_class(u'degree-value'))
1171 value = int(ch.get_text().strip())
1174 def get_alter(self):
1175 ch = self.get_maybe_exist_typed_child(get_class(u'degree-alter'))
1176 return utilities.interpret_alter_element(ch)
1179 class Frame(Music_xml_node):
1181 def get_frets(self):
1182 return self.get_named_child_value_number('frame-frets', 4)
1184 def get_strings(self):
1185 return self.get_named_child_value_number('frame-strings', 6)
1187 def get_first_fret(self):
1188 return self.get_named_child_value_number('first-fret', 1)
1191 class Frame_Note(Music_xml_node):
1193 def get_string(self):
1194 return self.get_named_child_value_number('string', 1)
1197 return self.get_named_child_value_number('fret', 0)
1199 def get_fingering(self):
1200 return self.get_named_child_value_number('fingering', -1)
1202 def get_barre(self):
1203 n = self.get_maybe_exist_named_child('barre')
1205 return getattr(n, 'type', '')
1210 class Musicxml_voice:
1215 self._start_staff = None
1217 self._has_lyrics = False
1219 def add_element(self, e):
1220 self._elements.append(e)
1221 if(isinstance(e, Note)
1222 and e.get_maybe_exist_typed_child(Staff)):
1223 name = e.get_maybe_exist_typed_child(Staff).get_text()
1225 if not self._start_staff and not e.get_maybe_exist_typed_child(Grace):
1226 self._start_staff = name
1227 self._staves[name] = True
1229 lyrics = e.get_typed_children(Lyric)
1230 if not self._has_lyrics:
1231 self.has_lyrics = len(lyrics) > 0
1235 if(nr > 0) and not(nr in self._lyrics):
1236 self._lyrics.append(nr)
1238 def insert(self, idx, e):
1239 self._elements.insert(idx, e)
1241 def get_lyrics_numbers(self):
1242 if(len(self._lyrics) == 0) and self._has_lyrics:
1243 #only happens if none of the <lyric> tags has a number attribute
1249 class Part(Music_xml_node):
1252 Music_xml_node.__init__(self)
1254 self._staff_attributes_dict = {}
1256 def get_part_list(self):
1258 while n and n.get_name() != 'score-partwise':
1261 return n.get_named_child('part-list')
1263 def graces_to_aftergraces(self, pending_graces):
1264 for gr in pending_graces:
1265 gr._when = gr._prev_when
1266 gr._measure_position = gr._prev_measure_position
1267 gr._after_grace = True
1269 def interpret(self):
1270 """Set durations and starting points."""
1271 """The starting point of the very first note is 0!"""
1273 part_list = self.get_part_list()
1276 factor = Rational(1)
1277 attributes_dict = {}
1278 attributes_object = None
1279 measures = self.get_typed_children(Measure)
1280 last_moment = Rational(-1)
1281 last_measure_position = Rational(-1)
1282 measure_position = Rational(0)
1283 measure_start_moment = now
1284 is_first_measure = True
1285 previous_measure = None
1286 # Graces at the end of a measure need to have their position set to the
1290 # implicit measures are used for artificial measures, e.g. when
1291 # a repeat bar line splits a bar into two halves. In this case,
1292 # don't reset the measure position to 0. They are also used for
1293 # upbeats(initial value of 0 fits these, too).
1294 # Also, don't reset the measure position at the end of the loop,
1295 # but rather when starting the next measure(since only then do we
1296 # know if the next measure is implicit and continues that measure)
1297 if not m.is_implicit():
1298 # Warn about possibly overfull measures and reset the position
1299 if attributes_object and previous_measure and previous_measure.partial == 0:
1300 length = attributes_object.get_measure_length()
1301 new_now = measure_start_moment + length
1303 problem = 'incomplete'
1305 problem = 'overfull'
1306 ## only for verbose operation.
1307 if problem <> 'incomplete' and previous_measure:
1308 previous_measure.message('%s measure? Expected: %s, Difference: %s' %(problem, now, new_now - now))
1310 measure_start_moment = now
1311 measure_position = Rational(0)
1313 for n in m.get_all_children():
1314 # figured bass has a duration, but applies to the next note
1315 # and should not change the current measure position!
1316 if isinstance(n, FiguredBass):
1317 n._divisions = factor.denominator()
1319 n._measure_position = measure_position
1322 if isinstance(n, Hash_text):
1326 if n.__class__ == Attributes:
1327 n.set_attributes_from_previous(attributes_dict)
1329 attributes_dict = n._dict.copy()
1330 attributes_object = n
1332 factor = Rational(1,
1333 int(attributes_dict.get('divisions').get_text()))
1336 if(n.get_maybe_exist_typed_child(Duration)):
1337 mxl_dur = n.get_maybe_exist_typed_child(Duration)
1338 dur = mxl_dur.get_length() * factor
1340 if n.get_name() == 'backup':
1342 # reset all graces before the backup to after-graces:
1343 self.graces_to_aftergraces(pending_graces)
1345 if n.get_maybe_exist_typed_child(Grace):
1348 rest = n.get_maybe_exist_typed_child(Rest)
1350 and attributes_object
1351 and attributes_object.get_measure_length() == dur):
1353 rest._is_whole_measure = True
1355 if(dur > Rational(0)
1356 and n.get_maybe_exist_typed_child(Chord)):
1358 measure_position = last_measure_position
1361 n._measure_position = measure_position
1363 # For all grace notes, store the previous note, in case need
1364 # to turn the grace note into an after-grace later on!
1365 if isinstance(n, Note) and n.is_grace():
1366 n._prev_when = last_moment
1367 n._prev_measure_position = last_measure_position
1368 # After-graces are placed at the same position as the previous note
1369 if isinstance(n, Note) and n.is_after_grace():
1370 # TODO: We should do the same for grace notes at the end of
1371 # a measure with no following note!!!
1372 n._when = last_moment
1373 n._measure_position = last_measure_position
1374 elif isinstance(n, Note) and n.is_grace():
1375 pending_graces.append(n)
1376 elif(dur > Rational(0)):
1377 pending_graces = [];
1380 if dur > Rational(0):
1382 last_measure_position = measure_position
1384 measure_position += dur
1385 elif dur < Rational(0):
1386 # backup element, reset measure position
1388 measure_position += dur
1389 if measure_position < 0:
1390 # backup went beyond the measure start => reset to 0
1391 now -= measure_position
1392 measure_position = 0
1394 last_measure_position = measure_position
1395 if n._name == 'note':
1396 instrument = n.get_maybe_exist_named_child('instrument')
1398 n.instrument_name = part_list.get_instrument(instrument.id)
1400 # reset all graces at the end of the measure to after-graces:
1401 self.graces_to_aftergraces(pending_graces)
1403 # Incomplete first measures are not padded, but registered as partial
1404 if is_first_measure:
1405 is_first_measure = False
1406 # upbeats are marked as implicit measures
1407 if attributes_object and m.is_implicit():
1408 length = attributes_object.get_measure_length()
1409 measure_end = measure_start_moment + length
1410 if measure_end <> now:
1412 previous_measure = m
1414 # modify attributes so that only those applying to the given staff remain
1415 def extract_attributes_for_staff(part, attr, staff):
1416 attributes = copy.copy(attr)
1417 attributes._children = [];
1418 attributes._dict = attr._dict.copy()
1419 attributes._original_tag = attr
1420 # copy only the relevant children over for the given staff
1423 for c in attr._children:
1424 if(not(hasattr(c, 'number') and(c.number != staff)) and
1425 not(isinstance(c, Hash_text))):
1426 attributes._children.append(c)
1427 if not attributes._children:
1432 def extract_voices(part):
1433 # The last indentified voice
1437 measures = part.get_typed_children(Measure)
1441 elements.append(Partial(m.partial))
1442 elements.extend(m.get_all_children())
1443 # make sure we know all voices already so that dynamics, clefs, etc.
1444 # can be assigned to the correct voices
1445 voice_to_staff_dict = {}
1447 voice_id = n.get_maybe_exist_named_child(u'voice')
1450 vid = voice_id.get_text()
1451 elif isinstance(n, Note):
1452 # TODO: Check whether we shall really use "None" here, or
1453 # rather use "1" as the default?
1454 if n.get_maybe_exist_named_child(u'chord'):
1462 staff_id = n.get_maybe_exist_named_child(u'staff')
1465 sid = staff_id.get_text()
1467 # TODO: Check whether we shall really use "None" here, or
1468 # rather use "1" as the default?
1469 # If this is changed, need to change the corresponding
1470 # check in extract_attributes_for_staff, too.
1472 if vid and not voices.has_key(vid):
1473 voices[vid] = Musicxml_voice()
1474 if vid and sid and not n.get_maybe_exist_typed_child(Grace):
1475 if not voice_to_staff_dict.has_key(vid):
1476 voice_to_staff_dict[vid] = sid
1478 # invert the voice_to_staff_dict into a staff_to_voice_dict(since we
1479 # need to assign staff-assigned objects like clefs, times, etc. to
1480 # all the correct voices. This will never work entirely correct due
1481 # to staff-switches, but that's the best we can do!
1482 staff_to_voice_dict = {}
1483 for(v, s) in voice_to_staff_dict.items():
1484 if not staff_to_voice_dict.has_key(s):
1485 staff_to_voice_dict[s] = [v]
1487 staff_to_voice_dict[s].append(v)
1490 assign_to_next_note = []
1493 voice_id = n.get_maybe_exist_typed_child(get_class('voice'))
1495 id = voice_id.get_text()
1497 if n.get_maybe_exist_typed_child(get_class('chord')):
1505 # We don't need backup/forward any more, since we have already
1506 # assigned the correct onset times.
1507 # TODO: Let Grouping through. Also: link, print, bokmark sound
1508 if not(isinstance(n, Note) or isinstance(n, Attributes) or
1509 isinstance(n, Direction) or isinstance(n, Partial) or
1510 isinstance(n, Barline) or isinstance(n, Harmony) or
1511 isinstance(n, FiguredBass) or isinstance(n, Print)):
1514 if isinstance(n, Attributes) and not start_attr:
1518 if isinstance(n, Attributes):
1519 # assign these only to the voices they really belong to!
1520 for(s, vids) in staff_to_voice_dict.items():
1521 staff_attributes = part.extract_attributes_for_staff(n, s)
1522 if staff_attributes:
1524 voices[v].add_element(staff_attributes)
1527 if isinstance(n, Partial) or isinstance(n, Barline) or isinstance(n, Print):
1528 for v in voices.keys():
1529 voices[v].add_element(n)
1532 if isinstance(n, Direction):
1533 staff_id = n.get_maybe_exist_named_child(u'staff')
1535 staff_id = staff_id.get_text()
1537 dir_voices = staff_to_voice_dict.get(staff_id, voices.keys())
1539 dir_voices = voices.keys()
1540 for v in dir_voices:
1541 voices[v].add_element(n)
1544 if isinstance(n, Harmony) or isinstance(n, FiguredBass):
1545 # store the harmony or figured bass element until we encounter
1546 # the next note and assign it only to that one voice.
1547 assign_to_next_note.append(n)
1550 if hasattr(n, 'print-object') and getattr(n, 'print-object') == "no":
1554 for i in assign_to_next_note:
1555 voices[id].add_element(i)
1556 assign_to_next_note = []
1557 voices[id].add_element(n)
1559 # Assign all remaining elements from assign_to_next_note to the voice
1560 # of the previous note:
1561 for i in assign_to_next_note:
1562 voices[id].add_element(i)
1563 assign_to_next_note = []
1566 for(s, vids) in staff_to_voice_dict.items():
1567 staff_attributes = part.extract_attributes_for_staff(start_attr, s)
1568 staff_attributes.read_self()
1569 part._staff_attributes_dict[s] = staff_attributes
1571 voices[v].insert(0, staff_attributes)
1572 voices[v]._elements[0].read_self()
1574 part._voices = voices
1576 def get_voices(self):
1579 def get_staff_attributes(self):
1580 return self._staff_attributes_dict
1583 class BarStyle(Music_xml_node):
1586 class BeatType(Music_xml_node):
1589 class BeatUnit(Music_xml_node):
1592 class BeatUnitDot(Music_xml_node):
1595 class Beats(Music_xml_node):
1598 class Bracket(Music_xml_spanner):
1601 class Chord(Music_xml_node):
1604 class Dashes(Music_xml_spanner):
1607 class DirType(Music_xml_node):
1610 class Direction(Music_xml_node):
1613 class Dot(Music_xml_node):
1616 class Elision(Music_xml_node):
1619 class Extend(Music_xml_node):
1622 class FiguredBass(Music_xml_node):
1625 class Glissando(Music_xml_spanner):
1628 class Grace(Music_xml_node):
1631 class Harmony(Music_xml_node):
1634 class Hash_comment(Music_xml_node):
1637 class KeyAlter(Music_xml_node):
1640 class KeyOctave(Music_xml_node):
1643 class KeyStep(Music_xml_node):
1646 class Part_group(Music_xml_node):
1649 class Pedal(Music_xml_spanner):
1652 class PerMinute(Music_xml_node):
1655 class Print(Music_xml_node):
1658 class Root(ChordPitch):
1661 class Score_part(Music_xml_node):
1664 class Slide(Music_xml_spanner):
1667 class Staff(Music_xml_node):
1670 class Text(Music_xml_node):
1673 class Type(Music_xml_node):
1676 class Wavy_line(Music_xml_spanner):
1679 class Wedge(Music_xml_spanner):
1682 class Words(Music_xml_node):
1686 ## need this, not all classes are instantiated
1687 ## for every input file. Only add those classes, that are either directly
1688 ## used by class name or extend Music_xml_node in some way!
1690 '#comment': Hash_comment,
1692 'accidental': Accidental,
1693 'attributes': Attributes,
1695 'bar-style': BarStyle,
1699 'beat-type': BeatType,
1700 'beat-unit': BeatUnit,
1701 'beat-unit-dot': BeatUnitDot,
1703 'bracket' : Bracket,
1707 'degree' : ChordModification,
1709 'direction': Direction,
1710 'direction-type': DirType,
1711 'duration': Duration,
1715 'frame-note': Frame_Note,
1716 'figured-bass': FiguredBass,
1717 'glissando': Glissando,
1720 'identification': Identification,
1721 'key-alter': KeyAlter,
1722 'key-octave': KeyOctave,
1723 'key-step': KeyStep,
1726 'notations': Notations,
1728 'notehead': Notehead,
1729 'octave-shift': Octave_shift,
1731 'part-group': Part_group,
1732 'part-list': Part_list,
1734 'per-minute': PerMinute,
1739 'score-part': Score_part,
1745 'syllabic': Syllabic,
1747 'time-modification': Time_modification,
1751 'unpitched': Unpitched,
1752 'wavy-line': Wavy_line,
1758 def name2class_name(name):
1759 name = name.replace('-', '_')
1760 name = name.replace('#', 'hash_')
1761 name = name[0].upper() + name[1:].lower()
1765 def get_class(name):
1766 classname = class_dict.get(name)
1770 class_name = name2class_name(name)
1771 klass = new.classobj(class_name,(Music_xml_node,) , {})
1772 class_dict[name] = klass
1775 def lxml_demarshal_node(node):
1778 # Ignore comment nodes, which are also returned by the etree parser!
1779 if name is None or node.__class__.__name__ == "_Comment":
1781 klass = get_class(name)
1784 py_node._original = node
1785 py_node._name = name
1786 py_node._data = node.text
1787 py_node._children = [lxml_demarshal_node(cn) for cn in node.getchildren()]
1788 py_node._children = filter(lambda x: x, py_node._children)
1790 for c in py_node._children:
1793 for(k, v) in node.items():
1794 py_node.__dict__[k] = v
1795 py_node._attribute_dict[k] = v
1799 def minidom_demarshal_node(node):
1800 name = node.nodeName
1802 klass = get_class(name)
1804 py_node._name = name
1805 py_node._children = [minidom_demarshal_node(cn) for cn in node.childNodes]
1806 for c in py_node._children:
1810 for(nm, value) in node.attributes.items():
1811 py_node.__dict__[nm] = value
1812 py_node._attribute_dict[nm] = value
1814 py_node._data = None
1815 if node.nodeType == node.TEXT_NODE and node.data:
1816 py_node._data = node.data
1818 py_node._original = node
1822 if __name__ == '__main__':
1825 tree = lxml.etree.parse('beethoven.xml')
1826 mxl_tree = lxml_demarshal_node(tree.getroot())
1827 ks = class_dict.keys()