1# musicxml.py 2# -*- coding: utf-8 -*- 3# 4# This file is part of LilyPond, the GNU music typesetter. 5# 6# Copyright (C) 2005--2020 Han-Wen Nienhuys <hanwen@xs4all.nl>, 7# 2007-2011 Reinhold Kainhofer <reinhold@kainhofer.com> 8# 9# LilyPond is free software: you can redistribute it and/or modify 10# it under the terms of the GNU General Public License as published by 11# the Free Software Foundation, either version 3 of the License, or 12# (at your option) any later version. 13# 14# LilyPond is distributed in the hope that it will be useful, 15# but WITHOUT ANY WARRANTY; without even the implied warranty of 16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17# GNU General Public License for more details. 18# 19# You should have received a copy of the GNU General Public License 20# along with LilyPond. If not, see <http://www.gnu.org/licenses/>. 21 22 23from collections import OrderedDict 24import copy 25from fractions import Fraction 26import re 27import sys 28import warnings 29 30import lilylib as ly 31import musicexp 32import musicxml2ly_conversion 33import utilities 34 35 36class Xml_node(object): 37 38 def __init__(self): 39 self._children = [] 40 self._data = None 41 self._original = None 42 self._name = 'xml_node' 43 self._parent = None 44 self._attribute_dict = {} 45 46 def get_parent(self): 47 return self._parent 48 49 def is_first(self): 50 return self._parent.get_typed_children(self.__class__)[0] == self 51 52 def original(self): 53 return self._original 54 55 def get_name(self): 56 return self._name 57 58 def get_text(self): 59 if self._data: 60 return self._data 61 62 if not self._children: 63 return '' 64 65 return ''.join([c.get_text() for c in self._children]) 66 67 def message(self, msg): 68 ly.warning(msg) 69 70 p = self 71 while p: 72 ly.progress(' In: <%s %s>\n' % (p._name, ' '.join( 73 ['%s=%s' % item for item in list(p._attribute_dict.items())]))) 74 p = p.get_parent() 75 76 def dump(self, indent=''): 77 ly.debug_output('%s<%s%s>' % (indent, self._name, ''.join( 78 [' %s=%s' % item for item in list(self._attribute_dict.items())]))) 79 non_text_children = [ 80 c for c in self._children if not isinstance(c, Hash_text)] 81 if non_text_children: 82 ly.debug_output('\n') 83 for c in self._children: 84 c.dump(indent + " ") 85 if non_text_children: 86 ly.debug_output(indent) 87 ly.debug_output('</%s>\n' % self._name) 88 89 def get_typed_children(self, klass): 90 if not klass: 91 return [] 92 else: 93 return [c for c in self._children if isinstance(c, klass)] 94 95 def get_named_children(self, nm): 96 return self.get_typed_children(get_class(nm)) 97 98 def get_named_child(self, nm): 99 return self.get_maybe_exist_named_child(nm) 100 101 def get_children(self, predicate): 102 return [c for c in self._children if predicate(c)] 103 104 def get_all_children(self): 105 return self._children 106 107 def get_maybe_exist_named_child(self, name): 108 return self.get_maybe_exist_typed_child(get_class(name)) 109 110 def get_maybe_exist_typed_child(self, klass): 111 cn = self.get_typed_children(klass) 112 if len(cn) == 0: 113 return None 114 else: 115 if len(cn) > 1: 116 warnings.warn(_('more than one child of class %s, all but' 117 'the first will be ignored') % klass.__name__) 118 return cn[0] 119 120 def get_unique_typed_child(self, klass): 121 cn = self.get_typed_children(klass) 122 if len(cn) != 1: 123 ly.error(self.__dict__) 124 raise RuntimeError( 125 'Child is not unique for %s found %d' % (klass, cn)) 126 127 return cn[0] 128 129 def get_named_child_value_number(self, name, default): 130 n = self.get_maybe_exist_named_child(name) 131 if n: 132 return int(n.get_text()) 133 else: 134 return default 135 136 137class Music_xml_node(Xml_node): 138 def __init__(self): 139 Xml_node.__init__(self) 140 self.duration = Fraction(0) 141 self.start = Fraction(0) 142 self.converted = False 143 self.voice_id = None 144 145 146class Music_xml_spanner(Music_xml_node): 147 148 def get_type(self): 149 if hasattr(self, 'type'): 150 return self.type 151 else: 152 return 0 153 154 def get_size(self): 155 if hasattr(self, 'size'): 156 return int(self.size) 157 else: 158 return 0 159 160 161class Measure_element(Music_xml_node): 162 163 def get_voice_id(self): 164 voice = self.get_maybe_exist_named_child('voice') 165 if voice: 166 return voice.get_text() 167 else: 168 return self.voice_id 169 170 def is_first(self): 171 # Look at all measure elements(previously we had self.__class__, which 172 # only looked at objects of the same type! 173 cn = self._parent.get_typed_children(Measure_element) 174 # But only look at the correct voice; But include Attributes, too, which 175 # are not tied to any particular voice 176 cn = [c for c in cn if( 177 c.get_voice_id() == self.get_voice_id()) or isinstance(c, Attributes)] 178 return cn[0] == self 179 180 181class Work(Xml_node): 182 183 def get_work_information(self, tag): 184 wt = self.get_maybe_exist_named_child(tag) 185 if wt: 186 return wt.get_text() 187 else: 188 return '' 189 190 def get_work_title(self): 191 return self.get_work_information('work-title') 192 193 def get_work_number(self): 194 return self.get_work_information('work-number') 195 196 # def get_opus(self): 197 # return self.get_work_information('opus') 198 199 200class Identification(Xml_node): 201 202 def get_rights(self): 203 rights = self.get_named_children('rights') 204 ret = [] 205 for r in rights: 206 text = r.get_text() 207 # if this Xml_node has an attribute, such as 'type="words"', 208 # include it in the header. Otherwise, it is assumed that 209 # the text contents of this node looks something like this: 210 # 'Copyright: X.Y.' and thus already contains the relevant 211 # information. 212 if hasattr(r, 'type'): 213 rights_type = r.type.title() # capitalize first letter 214 result = ''.join([rights_type, ': ', text]) 215 ret.append(result) 216 else: 217 ret.append(text) 218 return "\n".join(ret) 219 220 # 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.) 221 def get_source(self): 222 source = self.get_named_children('source') 223 ret = [] 224 for r in source: 225 ret.append(r.get_text()) 226 return "\n".join(ret) 227 228 def get_creator(self, type): 229 creators = self.get_named_children('creator') 230 # return the first creator tag that has the particular type 231 for i in creators: 232 if hasattr(i, 'type') and i.type == type: 233 return i.get_text() 234 return None 235 236 def get_composer(self): 237 c = self.get_creator('composer') 238 if c: 239 return c 240 creators = self.get_named_children('creator') 241 # return the first creator tag that has no type at all 242 for i in creators: 243 if not hasattr(i, 'type'): 244 return i.get_text() 245 return None 246 247 def get_arranger(self): 248 return self.get_creator('arranger') 249 250 def get_editor(self): 251 return self.get_creator('editor') 252 253 def get_poet(self): 254 v = self.get_creator('lyricist') 255 if v: 256 return v 257 v = self.get_creator('poet') 258 return v 259 260 def get_encoding_information(self, type): 261 enc = self.get_named_children('encoding') 262 if enc: 263 children = enc[0].get_named_children(type) 264 if children: 265 return children[0].get_text() 266 else: 267 return None 268 269 def get_encoding_software(self): 270 return self.get_encoding_information('software') 271 272 def get_encoding_date(self): 273 return self.get_encoding_information('encoding-date') 274 275 def get_encoding_person(self): 276 return self.get_encoding_information('encoder') 277 278 def get_encoding_description(self): 279 return self.get_encoding_information('encoding-description') 280 281 def get_encoding_software_list(self): 282 enc = self.get_named_children('encoding') 283 software = [] 284 for e in enc: 285 softwares = e.get_named_children('software') 286 for s in softwares: 287 software.append(s.get_text()) 288 return software 289 290 def get_file_description(self): 291 misc = self.get_named_children('miscellaneous') 292 for m in misc: 293 misc_fields = m.get_named_children('miscellaneous-field') 294 for mf in misc_fields: 295 if hasattr(mf, 'name') and mf.name == 'description': 296 return mf.get_text() 297 return None 298 299 300class Credit(Xml_node): 301 302 def get_type(self): 303 type = self.get_maybe_exist_named_child('credit-type') 304 if type is not None: 305 return type.get_text() 306 else: 307 return None 308 309 def find_type(self, credits): 310 sizes = self.get_font_sizes(credits) 311 sizes.sort(reverse=True) 312 ys = self.get_default_ys(credits) 313 ys.sort(reverse=True) 314 xs = self.get_default_xs(credits) 315 xs.sort(reverse=True) 316 317 # Words child of the self credit-element 318 words = self.get_maybe_exist_named_child('credit-words') 319 size = None 320 x = None 321 y = None 322 halign = None 323 valign = None 324 justify = None 325 if words is not None: 326 if hasattr(words, 'font-size'): 327 size = utilities.string_to_integer(getattr(words, 'font-size')) 328 if hasattr(words, 'default-x'): 329 x = round(float(getattr(words, 'default-x'))) 330 if hasattr(words, 'default-y'): 331 y = round(float(getattr(words, 'default-y'))) 332 if hasattr(words, 'halign'): 333 halign = getattr(words, 'halign') 334 if hasattr(words, 'valign'): 335 valign = getattr(words, 'valign') 336 if hasattr(words, 'justify'): 337 justify = getattr(words, 'justify') 338 if (size and size == max(sizes) and y and y == max(ys) and 339 (justify or halign) and (justify == 'center' or halign == 'center')): 340 return 'title' 341 elif (y and y > min(ys) and y < max(ys) and (justify or halign) and 342 (justify == 'center' or halign == 'center')): 343 return 'subtitle' 344 elif ((justify or halign) and (justify == 'left' or halign == 'left') and 345 (not x or x == min(xs))): 346 return 'lyricist' 347 elif ((justify or halign) and (justify == 'right' or halign == 'right') 348 and (not x or x == max(xs))): 349 return 'composer' 350 elif size and size == min(sizes) and y == min(ys): 351 return 'rights' 352 # Special cases for Finale NotePad 353 elif valign and valign == 'top' and y and y == ys[1]: 354 return 'subtitle' 355 elif valign and valign == 'top' and x and x == min(xs): 356 return 'lyricist' 357 elif valign and valign == 'top' and y and y == min(ys): 358 return 'rights' 359 # Other special cases 360 elif valign and valign == 'bottom': 361 return 'rights' 362 elif len([i for i, item in enumerate(ys) if item == y]) == 2: 363 # The first one is the composer, the second one is the lyricist 364 return 'composer' 365 366 return None # no type recognized 367 368 def get_font_sizes(self, credits): 369 sizes = [] 370 for cred in credits: 371 words = cred.get_maybe_exist_named_child('credit-words') 372 if((words is not None) and hasattr(words, 'font-size')): 373 sizes.append(getattr(words, 'font-size')) 374 return list(map(utilities.string_to_integer, sizes)) 375 376 def get_default_xs(self, credits): 377 default_xs = [] 378 for cred in credits: 379 words = cred.get_maybe_exist_named_child('credit-words') 380 if((words is not None) and hasattr(words, 'default-x')): 381 default_xs.append(getattr(words, 'default-x')) 382 return list(map(round, list(map(float, default_xs)))) 383 384 def get_default_ys(self, credits): 385 default_ys = [] 386 for cred in credits: 387 words = cred.get_maybe_exist_named_child('credit-words') 388 if words is not None and hasattr(words, 'default-y'): 389 default_ys.append(getattr(words, 'default-y')) 390 return list(map(round, list(map(float, default_ys)))) 391 392 def get_text(self): 393 words = self.get_maybe_exist_named_child('credit-words') 394 if words is not None: 395 return words.get_text() 396 else: 397 return '' 398 399 400class Duration(Music_xml_node): 401 402 def get_length(self): 403 dur = int(self.get_text()) * Fraction(1, 4) 404 return dur 405 406 407class Hash_text(Music_xml_node): 408 409 def dump(self, indent=''): 410 ly.debug_output(self._data.strip()) 411 412 413class Pitch(Music_xml_node): 414 415 def get_step(self): 416 ch = self.get_unique_typed_child(get_class('step')) 417 step = ch.get_text().strip() 418 return step 419 420 def get_octave(self): 421 ch = self.get_unique_typed_child(get_class('octave')) 422 octave = ch.get_text().strip() 423 return int(octave) 424 425 def get_alteration(self): 426 ch = self.get_maybe_exist_typed_child(get_class('alter')) 427 return utilities.interpret_alter_element(ch) 428 429 def to_lily_object(self): 430 p = musicexp.Pitch() 431 p.alteration = self.get_alteration() 432 p.step = musicxml2ly_conversion.musicxml_step_to_lily(self.get_step()) 433 p.octave = self.get_octave() - 4 434 return p 435 436 437class Unpitched(Music_xml_node): 438 439 def get_step(self): 440 ch = self.get_unique_typed_child(get_class('display-step')) 441 step = ch.get_text().strip() 442 return step 443 444 def get_octave(self): 445 ch = self.get_unique_typed_child(get_class('display-octave')) 446 447 if ch: 448 octave = ch.get_text().strip() 449 return int(octave) 450 else: 451 return None 452 453 def to_lily_object(self): 454 p = None 455 step = self.get_step() 456 if step: 457 p = musicexp.Pitch() 458 p.step = musicxml2ly_conversion.musicxml_step_to_lily(step) 459 octave = self.get_octave() 460 if octave and p: 461 p.octave = octave - 4 462 return p 463 464 465class Measure_element (Music_xml_node): 466 def get_voice_id(self): 467 voice = self.get_maybe_exist_named_child('voice') 468 if voice: 469 return voice.get_text() 470 else: 471 return self.voice_id 472 473 474class Attributes(Measure_element): 475 476 def __init__(self): 477 Measure_element.__init__(self) 478 self._dict = {} 479 self._original_tag = None 480 self._time_signature_cache = None 481 482 def is_first(self): 483 cn = self._parent.get_typed_children(self.__class__) 484 if self._original_tag: 485 return cn[0] == self._original_tag 486 else: 487 return cn[0] == self 488 489 def set_attributes_from_previous(self, dict): 490 self._dict.update(dict) 491 492 def read_self(self): 493 for c in self.get_all_children(): 494 self._dict[c.get_name()] = c 495 496 def get_named_attribute(self, name): 497 return self._dict.get(name) 498 499 def single_time_sig_to_fraction(self, sig): 500 if len(sig) < 2: 501 return 0 502 n = 0 503 for i in sig[0:-1]: 504 n += i 505 return Fraction(n, sig[-1]) 506 507 def get_measure_length(self): 508 sig = self.get_time_signature() 509 if not sig or len(sig) == 0: 510 return 1 511 if isinstance(sig[0], list): 512 # Complex compound time signature 513 l = 0 514 for i in sig: 515 l += self.single_time_sig_to_fraction(i) 516 return l 517 else: 518 # Simple(maybe compound) time signature of the form(beat, ..., type) 519 return self.single_time_sig_to_fraction(sig) 520 return 0 521 522 def get_time_signature(self): 523 "Return time sig as a(beat, beat-type) tuple. For compound signatures," 524 "return either(beat, beat,..., beat-type) or((beat,..., type), " 525 "(beat,..., type), ...)." 526 if self._time_signature_cache: 527 return self._time_signature_cache 528 529 try: 530 mxl = self.get_named_attribute('time') 531 if not mxl: 532 return None 533 534 if mxl.get_maybe_exist_named_child('senza-misura'): 535 # TODO: Handle pieces without a time signature! 536 ly.warning( 537 _("Senza-misura time signatures are not yet supported!")) 538 return(4, 4) 539 else: 540 signature = [] 541 current_sig = [] 542 for i in mxl.get_all_children(): 543 if isinstance(i, Beats): 544 beats = i.get_text().strip().split("+") 545 current_sig = [int(j) for j in beats] 546 elif isinstance(i, BeatType): 547 current_sig.append(int(i.get_text())) 548 signature.append(current_sig) 549 current_sig = [] 550 if isinstance(signature[0], list) and len(signature) == 1: 551 signature = signature[0] 552 self._time_signature_cache = signature 553 return signature 554 except(KeyError, ValueError): 555 self.message( 556 _("Unable to interpret time signature! Falling back to 4/4.")) 557 return(4, 4) 558 559 # returns clef information in the form("cleftype", position, octave-shift) 560 def get_clef_information(self): 561 clefinfo = ['G', 2, 0] 562 mxl = self.get_named_attribute('clef') 563 if not mxl: 564 return clefinfo 565 sign = mxl.get_maybe_exist_named_child('sign') 566 if sign: 567 clefinfo[0] = sign.get_text() 568 line = mxl.get_maybe_exist_named_child('line') 569 if line: 570 clefinfo[1] = int(line.get_text()) 571 octave = mxl.get_maybe_exist_named_child('clef-octave-change') 572 if octave: 573 clefinfo[2] = int(octave.get_text()) 574 return clefinfo 575 576 def get_key_signature(self): 577 "return(fifths, mode) tuple if the key signatures is given as " 578 "major/minor in the Circle of fifths. Otherwise return an alterations" 579 "list of the form [[step,alter<,octave>], [step,alter<,octave>], ...], " 580 "where the octave values are optional." 581 582 key = self.get_named_attribute('key') 583 if not key: 584 return None 585 fifths_elm = key.get_maybe_exist_named_child('fifths') 586 if fifths_elm: 587 mode_node = key.get_maybe_exist_named_child('mode') 588 mode = None 589 if mode_node: 590 mode = mode_node.get_text() 591 if not mode or mode == '': 592 mode = 'major' 593 fifths = int(fifths_elm.get_text()) 594 # TODO: Shall we try to convert the key-octave and the cancel, too? 595 return(fifths, mode) 596 else: 597 alterations = [] 598 current_step = 0 599 for i in key.get_all_children(): 600 if isinstance(i, KeyStep): 601 current_step = i.get_text().strip() 602 elif isinstance(i, KeyAlter): 603 alterations.append( 604 [current_step, utilities.interpret_alter_element(i)]) 605 elif isinstance(i, KeyOctave): 606 nr = -1 607 if hasattr(i, 'number'): 608 nr = int(i.number) 609 if(nr > 0) and (nr <= len(alterations)): 610 # MusicXML Octave 4 is middle C -> shift to 0 611 alterations[nr - 1].append(int(i.get_text()) - 4) 612 else: 613 i.message(_("Key alteration octave given for a " 614 "non-existing alteration nr. %s, available numbers: %s!") % (nr, len(alterations))) 615 return alterations 616 617 def get_transposition(self): 618 return self.get_named_attribute('transpose') 619 620 621class Barline(Measure_element): 622 623 def to_lily_object(self): 624 # retval contains all possible markers in the order: 625 # 0..bw_ending, 1..bw_repeat, 2..barline, 3..fw_repeat, 4..fw_ending 626 retval = {} 627 bartype_element = self.get_maybe_exist_named_child("bar-style") 628 repeat_element = self.get_maybe_exist_named_child("repeat") 629 ending_element = self.get_maybe_exist_named_child("ending") 630 631 bartype = None 632 if bartype_element: 633 bartype = bartype_element.get_text() 634 635 if repeat_element and hasattr(repeat_element, 'direction'): 636 repeat = musicxml2ly_conversion.RepeatMarker() 637 repeat.direction = {"forward": -1, "backward": 1}.get( 638 repeat_element.direction, 0) 639 640 if((repeat_element.direction == "forward" and bartype == "heavy-light") or 641 (repeat_element.direction == "backward" and bartype == "light-heavy")): 642 bartype = None 643 if hasattr(repeat_element, 'times'): 644 try: 645 repeat.times = int(repeat_element.times) 646 except ValueError: 647 repeat.times = 2 648 repeat.event = self 649 if repeat.direction == -1: 650 retval[3] = repeat 651 else: 652 retval[1] = repeat 653 654 if ending_element and hasattr(ending_element, 'type'): 655 ending = musicxml2ly_conversion.EndingMarker() 656 ending.direction = {"start": -1, "stop": 1, "discontinue": 1}.get( 657 ending_element.type, 0) 658 ending.event = self 659 if ending.direction == -1: 660 retval[4] = ending 661 else: 662 retval[0] = ending 663 # TODO. ending number="" 664 665 if bartype: 666 b = musicexp.BarLine() 667 b.type = bartype 668 retval[2] = b 669 670 return list(retval.values()) 671 672 673class Partial(Measure_element): 674 def __init__(self, partial): 675 Measure_element.__init__(self) 676 self.partial = partial 677 678 679class Stem(Music_xml_node): 680 681 stem_value_dict = { 682 'down': 'stemDown', 683 'up': 'stemUp', 684 'double': None, # TODO: Implement 685 'none': 'stemNeutral' 686 } 687 688 def to_stem_event(self): 689 values = [] 690 value = self.stem_value_dict.get(self.get_text(), None) 691 stem_value = musicexp.StemEvent() 692 if value: 693 stem_value.value = value 694 values.append(stem_value) 695 return values 696 697 def to_stem_style_event(self): 698 styles = [] 699 style_elm = musicexp.StemstyleEvent() 700 if hasattr(self, 'color'): 701 style_elm.color = utilities.hex_to_color(getattr(self, 'color')) 702 if style_elm.color is not None: 703 styles.append(style_elm) 704 return styles 705 706 707class Notehead(Music_xml_node): 708 709 notehead_styles_dict = { 710 'slash': '\'slash', 711 'triangle': '\'triangle', 712 'diamond': '\'diamond', 713 'square': '\'la', # TODO: Proper squared note head 714 'cross': None, # TODO: + shaped note head 715 'x': '\'cross', 716 'circle-x': '\'xcircle', 717 'inverted triangle': None, # TODO: Implement 718 'arrow down': None, # TODO: Implement 719 'arrow up': None, # TODO: Implement 720 'slashed': None, # TODO: Implement 721 'back slashed': None, # TODO: Implement 722 'normal': None, 723 'cluster': None, # TODO: Implement 724 'none': '#f', 725 'do': '\'do', 726 're': '\'re', 727 'mi': '\'mi', 728 'fa': '\'fa', 729 'so': None, 730 'la': '\'la', 731 'ti': '\'ti', 732 } 733 734 def to_lily_object(self): # function changed: additionally processcolor attribute 735 styles = [] 736 737 # Notehead style 738 key = self.get_text().strip() 739 style = self.notehead_styles_dict.get(key, None) 740 event = musicexp.NotestyleEvent() 741 if style: 742 event.style = style 743 if hasattr(self, 'filled'): 744 event.filled = (getattr(self, 'filled') == "yes") 745 if hasattr(self, 'color'): 746 event.color = utilities.hex_to_color(getattr(self, 'color')) 747 if event.style or (event.filled is not None) or (event.color is not None): 748 styles.append(event) 749 # parentheses 750 if hasattr(self, 'parentheses') and (self.parentheses == "yes"): 751 styles.append(musicexp.ParenthesizeEvent()) 752 753 return styles 754 755 756class Note(Measure_element): 757 758 def __init__(self): 759 Measure_element.__init__(self) 760 self.instrument_name = '' 761 self._after_grace = False 762 self._duration = 1 763 764 def is_grace(self): 765 return self.get_maybe_exist_named_child('grace') 766 767 def is_after_grace(self): 768 if not self.is_grace(): 769 return False 770 gr = self.get_maybe_exist_typed_child(Grace) 771 return self._after_grace or hasattr(gr, 'steal-time-previous') 772 773 def get_duration_log(self): 774 ch = self.get_maybe_exist_named_child('type') 775 776 if ch: 777 log = ch.get_text().strip() 778 return utilities.musicxml_duration_to_log(log) 779 elif self.get_maybe_exist_named_child('grace'): 780 # FIXME: is it ok to default to eight note for grace notes? 781 return 3 782 else: 783 return None 784 785 def get_duration_info(self): 786 log = self.get_duration_log() 787 if log is not None: 788 dots = len(self.get_typed_children(Dot)) 789 return(log, dots) 790 else: 791 return None 792 793 def get_factor(self): 794 return 1 795 796 def get_pitches(self): 797 return self.get_typed_children(get_class('pitch')) 798 799 def set_notehead_style(self, event): 800 noteheads = self.get_named_children('notehead') 801 for nh in noteheads: 802 styles = nh.to_lily_object() 803 for style in styles: 804 event.add_associated_event(style) 805 806 def set_stem_directions(self, event): 807 stems = self.get_named_children('stem') 808 for stem in stems: 809 values = stem.to_stem_event() 810 for v in values: 811 event.add_associated_event(v) 812 813 def set_stem_style(self, event): 814 stems = self.get_named_children('stem') 815 for stem in stems: 816 styles = stem.to_stem_style_event() 817 for style in styles: 818 event.add_associated_event(style) 819 820 def initialize_duration(self): 821 from musicxml2ly_conversion import rational_to_lily_duration 822 from musicexp import Duration 823 # if the note has no Type child, then that method returns None. In that case, 824 # use the <duration> tag instead. If that doesn't exist, either -> Error 825 dur = self.get_duration_info() 826 if dur: 827 d = Duration() 828 d.duration_log = dur[0] 829 d.dots = dur[1] 830 # Grace notes by specification have duration 0, so no time modification 831 # factor is possible. It even messes up the output with *0/1 832 if not self.get_maybe_exist_typed_child(Grace): 833 d.factor = self._duration / d.get_length() 834 return d 835 else: 836 if self._duration > 0: 837 return rational_to_lily_duration(self._duration) 838 else: 839 self.message( 840 _("Encountered note at %s without type and duration(=%s)") 841 % (mxl_note.start, mxl_note._duration)) 842 return None 843 844 def initialize_pitched_event(self): 845 mxl_pitch = self.get_maybe_exist_typed_child(Pitch) 846 pitch = mxl_pitch.to_lily_object() 847 event = musicexp.NoteEvent() 848 event.pitch = pitch 849 acc = self.get_maybe_exist_named_child('accidental') 850 if acc: 851 # let's not force accs everywhere. 852 event.cautionary = acc.cautionary 853 # TODO: Handle editorial accidentals 854 # TODO: Handle the level-display setting for displaying brackets/parentheses 855 return event 856 857 def initialize_unpitched_event(self): 858 # Unpitched elements have display-step and can also have 859 # display-octave. 860 unpitched = self.get_maybe_exist_typed_child(Unpitched) 861 event = musicexp.NoteEvent() 862 event.pitch = unpitched.to_lily_object() 863 return event 864 865 def initialize_rest_event(self, convert_rest_positions=True): 866 # rests can have display-octave and display-step, which are 867 # treated like an ordinary note pitch 868 rest = self.get_maybe_exist_typed_child(Rest) 869 event = musicexp.RestEvent() 870 if convert_rest_positions: 871 pitch = rest.to_lily_object() 872 event.pitch = pitch 873 return event 874 875 def to_lily_object(self, 876 convert_stem_directions=True, 877 convert_rest_positions=True): 878 pitch = None 879 duration = None 880 event = None 881 882 if self.get_maybe_exist_typed_child(Pitch): 883 event = self.initialize_pitched_event() 884 elif self.get_maybe_exist_typed_child(Unpitched): 885 event = self.initialize_unpitched_event() 886 elif self.get_maybe_exist_typed_child(Rest): 887 event = self.initialize_rest_event(convert_rest_positions) 888 else: 889 self.message(_("cannot find suitable event")) 890 891 if event: 892 event.duration = self.initialize_duration() 893 894 self.set_notehead_style(event) 895 self.set_stem_style(event) 896 if convert_stem_directions: 897 self.set_stem_directions(event) 898 899 return event 900 901 902class Part_list(Music_xml_node): 903 904 def __init__(self): 905 Music_xml_node.__init__(self) 906 self._id_instrument_name_dict = {} 907 908 def generate_id_instrument_dict(self): 909 910 # not empty to make sure this happens only once. 911 mapping = {1: 1} 912 for score_part in self.get_named_children('score-part'): 913 for instr in score_part.get_named_children('score-instrument'): 914 id = instr.id 915 name = instr.get_named_child("instrument-name") 916 mapping[id] = name.get_text() 917 918 self._id_instrument_name_dict = mapping 919 920 def get_instrument(self, id): 921 if not self._id_instrument_name_dict: 922 self.generate_id_instrument_dict() 923 924 instrument_name = self._id_instrument_name_dict.get(id) 925 if instrument_name: 926 return instrument_name 927 else: 928 ly.warning(_("Unable to find instrument for ID=%s\n") % id) 929 return "Grand Piano" 930 931 932class Measure(Music_xml_node): 933 934 def __init__(self): 935 Music_xml_node.__init__(self) 936 self.partial = 0 937 938 def is_implicit(self): 939 return hasattr(self, 'implicit') and self.implicit == 'yes' 940 941 def get_notes(self): 942 return self.get_typed_children(get_class('note')) 943 944 945class Syllabic(Music_xml_node): 946 947 def continued(self): 948 text = self.get_text() 949 return text == "begin" or text == "middle" 950 951 def begin(self): 952 return text == "begin" 953 954 def middle(self): 955 return text == "middle" 956 957 def end(self): 958 return text == "end" 959 960 961class Lyric(Music_xml_node): 962 963 def get_number(self): 964 """ 965 Return the number attribute(if it exists) of the lyric element. 966 967 @rtype: number 968 @return: The value of the number attribute 969 """ 970 if hasattr(self, 'number'): 971 return int(self.number) 972 else: 973 return -1 974 975 976class Sound(Music_xml_node): 977 978 def get_tempo(self): 979 """ 980 Return the tempo attribute(if it exists) of the sound element. 981 This attribute can be used by musicxml2ly for the midi output(see L{musicexp.Score}). 982 983 @rtype: string 984 @return: The value of the tempo attribute 985 """ 986 if hasattr(self, 'tempo'): 987 return self.tempo 988 else: 989 return None 990 991 992class Notations(Music_xml_node): 993 994 def get_tie(self): 995 ts = self.get_named_children('tied') 996 starts = [t for t in ts if t.type == 'start'] 997 if starts: 998 return starts[0] 999 else: 1000 return None 1001 1002 def get_tuplets(self): 1003 return self.get_typed_children(Tuplet) 1004 1005 1006class Time_modification(Music_xml_node): 1007 1008 def get_fraction(self): 1009 b = self.get_maybe_exist_named_child('actual-notes') 1010 a = self.get_maybe_exist_named_child('normal-notes') 1011 return(int(a.get_text()), int(b.get_text())) 1012 1013 def get_normal_type(self): 1014 tuplet_type = self.get_maybe_exist_named_child('normal-type') 1015 if tuplet_type: 1016 dots = self.get_named_children('normal-dot') 1017 log = utilities.musicxml_duration_to_log( 1018 tuplet_type.get_text().strip()) 1019 return(log, len(dots)) 1020 else: 1021 return None 1022 1023 1024class Accidental(Music_xml_node): 1025 1026 def __init__(self): 1027 Music_xml_node.__init__(self) 1028 self.editorial = False 1029 self.cautionary = False 1030 1031 1032class Tuplet(Music_xml_spanner): 1033 1034 def duration_info_from_tuplet_note(self, tuplet_note): 1035 tuplet_type = tuplet_note.get_maybe_exist_named_child('tuplet-type') 1036 if tuplet_type: 1037 dots = tuplet_note.get_named_children('tuplet-dot') 1038 log = utilities.musicxml_duration_to_log( 1039 tuplet_type.get_text().strip()) 1040 return(log, len(dots)) 1041 else: 1042 return None 1043 1044 # Return tuplet note type as(log, dots) 1045 def get_normal_type(self): 1046 tuplet = self.get_maybe_exist_named_child('tuplet-normal') 1047 if tuplet: 1048 return self.duration_info_from_tuplet_note(tuplet) 1049 else: 1050 return None 1051 1052 def get_actual_type(self): 1053 tuplet = self.get_maybe_exist_named_child('tuplet-actual') 1054 if tuplet: 1055 return self.duration_info_from_tuplet_note(tuplet) 1056 else: 1057 return None 1058 1059 def get_tuplet_note_count(self, tuplet_note): 1060 if tuplet_note: 1061 tuplet_nr = tuplet_note.get_maybe_exist_named_child( 1062 'tuplet-number') 1063 if tuplet_nr: 1064 return int(tuplet_nr.get_text()) 1065 return None 1066 1067 def get_normal_nr(self): 1068 return self.get_tuplet_note_count(self.get_maybe_exist_named_child('tuplet-normal')) 1069 1070 def get_actual_nr(self): 1071 return self.get_tuplet_note_count(self.get_maybe_exist_named_child('tuplet-actual')) 1072 1073 1074class Slur(Music_xml_spanner): 1075 1076 def get_type(self): 1077 return self.type 1078 1079 1080class Tied(Music_xml_spanner): 1081 1082 def get_type(self): 1083 return self.type 1084 1085 1086class Beam(Music_xml_spanner): 1087 def get_type(self): 1088 return self.get_text() 1089 1090 def is_primary(self): 1091 if hasattr(self, 'number'): 1092 return self.number == "1" 1093 else: 1094 return True 1095 1096 1097class Octave_shift(Music_xml_spanner): 1098 # default is 8 for the octave-shift! 1099 def get_size(self): 1100 if hasattr(self, 'size'): 1101 return int(self.size) 1102 else: 1103 return 8 1104 1105 1106# Rests in MusicXML are <note> blocks with a <rest> inside. This class is only 1107# for the inner <rest> element, not the whole rest block. 1108class Rest(Music_xml_node): 1109 1110 def __init__(self): 1111 Music_xml_node.__init__(self) 1112 self._is_whole_measure = False 1113 1114 def is_whole_measure(self): 1115 return self._is_whole_measure 1116 1117 def get_step(self): 1118 ch = self.get_maybe_exist_typed_child(get_class('display-step')) 1119 if ch: 1120 return ch.get_text().strip() 1121 else: 1122 return None 1123 1124 def get_octave(self): 1125 ch = self.get_maybe_exist_typed_child(get_class('display-octave')) 1126 if ch: 1127 oct = ch.get_text().strip() 1128 return int(oct) 1129 else: 1130 return None 1131 1132 def to_lily_object(self): 1133 p = None 1134 step = self.get_step() 1135 if step: 1136 p = musicexp.Pitch() 1137 p.step = musicxml2ly_conversion.musicxml_step_to_lily(step) 1138 octave = self.get_octave() 1139 if octave and p: 1140 p.octave = octave - 4 1141 return p 1142 1143 1144class Bend(Music_xml_node): 1145 1146 def bend_alter(self): 1147 alter = self.get_maybe_exist_named_child('bend-alter') 1148 return utilities.interpret_alter_element(alter) 1149 1150 1151class ChordPitch(Music_xml_node): 1152 1153 def step_class_name(self): 1154 return 'root-step' 1155 1156 def alter_class_name(self): 1157 return 'root-alter' 1158 1159 def get_step(self): 1160 ch = self.get_unique_typed_child(get_class(self.step_class_name())) 1161 return ch.get_text().strip() 1162 1163 def get_alteration(self): 1164 ch = self.get_maybe_exist_typed_child( 1165 get_class(self.alter_class_name())) 1166 return utilities.interpret_alter_element(ch) 1167 1168 1169class Bass(ChordPitch): 1170 1171 def step_class_name(self): 1172 return 'bass-step' 1173 1174 def alter_class_name(self): 1175 return 'bass-alter' 1176 1177 1178class ChordModification(Music_xml_node): 1179 1180 def get_type(self): 1181 ch = self.get_maybe_exist_typed_child(get_class('degree-type')) 1182 return {'add': 1, 'alter': 1, 'subtract': -1}.get(ch.get_text().strip(), 0) 1183 1184 def get_value(self): 1185 ch = self.get_maybe_exist_typed_child(get_class('degree-value')) 1186 value = 0 1187 if ch: 1188 value = int(ch.get_text().strip()) 1189 return value 1190 1191 def get_alter(self): 1192 ch = self.get_maybe_exist_typed_child(get_class('degree-alter')) 1193 return utilities.interpret_alter_element(ch) 1194 1195 1196class Frame(Music_xml_node): 1197 1198 def get_frets(self): 1199 return self.get_named_child_value_number('frame-frets', 4) 1200 1201 def get_strings(self): 1202 return self.get_named_child_value_number('frame-strings', 6) 1203 1204 def get_first_fret(self): 1205 return self.get_named_child_value_number('first-fret', 1) 1206 1207 1208class Frame_Note(Music_xml_node): 1209 1210 def get_string(self): 1211 return self.get_named_child_value_number('string', 1) 1212 1213 def get_fret(self): 1214 return self.get_named_child_value_number('fret', 0) 1215 1216 def get_fingering(self): 1217 return self.get_named_child_value_number('fingering', -1) 1218 1219 def get_barre(self): 1220 n = self.get_maybe_exist_named_child('barre') 1221 if n: 1222 return getattr(n, 'type', '') 1223 else: 1224 return '' 1225 1226 1227class Musicxml_voice: 1228 1229 def __init__(self): 1230 self._elements = [] 1231 self._staves = {} 1232 self._start_staff = None 1233 self._lyrics = [] 1234 self._has_lyrics = False 1235 1236 def add_element(self, e): 1237 self._elements.append(e) 1238 if(isinstance(e, Note) 1239 and e.get_maybe_exist_typed_child(Staff)): 1240 name = e.get_maybe_exist_typed_child(Staff).get_text() 1241 1242 if not self._start_staff and not e.get_maybe_exist_typed_child(Grace): 1243 self._start_staff = name 1244 self._staves[name] = True 1245 1246 lyrics = e.get_typed_children(Lyric) 1247 if not self._has_lyrics: 1248 self.has_lyrics = len(lyrics) > 0 1249 1250 for l in lyrics: 1251 nr = l.get_number() 1252 if nr > 0 and nr not in self._lyrics: 1253 self._lyrics.append(nr) 1254 1255 def insert(self, idx, e): 1256 self._elements.insert(idx, e) 1257 1258 def get_lyrics_numbers(self): 1259 if(len(self._lyrics) == 0) and self._has_lyrics: 1260 # only happens if none of the <lyric> tags has a number attribute 1261 return [1] 1262 else: 1263 return self._lyrics 1264 1265 1266class Part(Music_xml_node): 1267 1268 def __init__(self): 1269 Music_xml_node.__init__(self) 1270 self._voices = {} 1271 self._staff_attributes_dict = {} 1272 1273 def get_part_list(self): 1274 n = self 1275 while n and n.get_name() != 'score-partwise': 1276 n = n._parent 1277 1278 return n.get_named_child('part-list') 1279 1280 def graces_to_aftergraces(self, pending_graces): 1281 for gr in pending_graces: 1282 gr._when = gr._prev_when 1283 gr._measure_position = gr._prev_measure_position 1284 gr._after_grace = True 1285 1286 def interpret(self): 1287 """Set durations and starting points.""" 1288 """The starting point of the very first note is 0!""" 1289 1290 part_list = self.get_part_list() 1291 1292 now = Fraction(0) 1293 factor = Fraction(1) 1294 attributes_dict = {} 1295 attributes_object = None 1296 measures = self.get_typed_children(Measure) 1297 last_moment = Fraction(-1) 1298 last_measure_position = Fraction(-1) 1299 measure_position = Fraction(0) 1300 measure_start_moment = now 1301 is_first_measure = True 1302 previous_measure = None 1303 # Graces at the end of a measure need to have their position set to the 1304 # previous number! 1305 pending_graces = [] 1306 for m in measures: 1307 # implicit measures are used for artificial measures, e.g. when 1308 # a repeat bar line splits a bar into two halves. In this case, 1309 # don't reset the measure position to 0. They are also used for 1310 # upbeats(initial value of 0 fits these, too). 1311 # Also, don't reset the measure position at the end of the loop, 1312 # but rather when starting the next measure(since only then do we 1313 # know if the next measure is implicit and continues that measure) 1314 if not m.is_implicit(): 1315 # Warn about possibly overfull measures and reset the position 1316 if attributes_object and previous_measure and previous_measure.partial == 0: 1317 length = attributes_object.get_measure_length() 1318 new_now = measure_start_moment + length 1319 if now != new_now: 1320 problem = 'incomplete' 1321 if now > new_now: 1322 problem = 'overfull' 1323 # only for verbose operation. 1324 if problem != 'incomplete' and previous_measure: 1325 previous_measure.message( 1326 '%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now)) 1327 now = new_now 1328 measure_start_moment = now 1329 measure_position = Fraction(0) 1330 1331 voice_id = None 1332 assign_to_next_voice = [] 1333 for n in m.get_all_children(): 1334 # assign a voice to all measure elements 1335 if n.get_name() == 'backup': 1336 voice_id = None 1337 1338 if isinstance(n, Measure_element): 1339 if n.get_voice_id(): 1340 voice_id = n.get_voice_id() 1341 for i in assign_to_next_voice: 1342 i.voice_id = voice_id 1343 assign_to_next_voice = [] 1344 else: 1345 if voice_id: 1346 n.voice_id = voice_id 1347 else: 1348 assign_to_next_voice.append(n) 1349 1350 # figured bass has a duration, but applies to the next note 1351 # and should not change the current measure position! 1352 if isinstance(n, FiguredBass): 1353 n._divisions = factor.denominator 1354 n._when = now 1355 n._measure_position = measure_position 1356 continue 1357 1358 if isinstance(n, Hash_text): 1359 continue 1360 dur = Fraction(0) 1361 1362 if n.__class__ == Attributes: 1363 n.set_attributes_from_previous(attributes_dict) 1364 n.read_self() 1365 attributes_dict = n._dict.copy() 1366 attributes_object = n 1367 1368 factor = Fraction(1, 1369 int(attributes_dict.get('divisions').get_text())) 1370 1371 if n.get_maybe_exist_typed_child(Duration): 1372 mxl_dur = n.get_maybe_exist_typed_child(Duration) 1373 dur = mxl_dur.get_length() * factor 1374 1375 if n.get_name() == 'backup': 1376 dur = -dur 1377 # reset all graces before the backup to after-graces: 1378 self.graces_to_aftergraces(pending_graces) 1379 pending_graces = [] 1380 if n.get_maybe_exist_typed_child(Grace): 1381 dur = Fraction(0) 1382 1383 rest = n.get_maybe_exist_typed_child(Rest) 1384 if(rest 1385 and attributes_object 1386 and attributes_object.get_measure_length() == dur): 1387 1388 rest._is_whole_measure = True 1389 1390 if(dur > Fraction(0) 1391 and n.get_maybe_exist_typed_child(Chord)): 1392 now = last_moment 1393 measure_position = last_measure_position 1394 1395 n._when = now 1396 n._measure_position = measure_position 1397 1398 # For all grace notes, store the previous note, in case need 1399 # to turn the grace note into an after-grace later on! 1400 if isinstance(n, Note) and n.is_grace(): 1401 n._prev_when = last_moment 1402 n._prev_measure_position = last_measure_position 1403 # After-graces are placed at the same position as the previous note 1404 if isinstance(n, Note) and n.is_after_grace(): 1405 # TODO: We should do the same for grace notes at the end of 1406 # a measure with no following note!!! 1407 n._when = last_moment 1408 n._measure_position = last_measure_position 1409 elif isinstance(n, Note) and n.is_grace(): 1410 pending_graces.append(n) 1411 elif dur > Fraction(0): 1412 pending_graces = [] 1413 1414 n._duration = dur 1415 if dur > Fraction(0): 1416 last_moment = now 1417 last_measure_position = measure_position 1418 now += dur 1419 measure_position += dur 1420 elif dur < Fraction(0): 1421 # backup element, reset measure position 1422 now += dur 1423 measure_position += dur 1424 if measure_position < 0: 1425 # backup went beyond the measure start => reset to 0 1426 now -= measure_position 1427 measure_position = 0 1428 last_moment = now 1429 last_measure_position = measure_position 1430 if n._name == 'note': 1431 instrument = n.get_maybe_exist_named_child('instrument') 1432 if instrument: 1433 n.instrument_name = part_list.get_instrument( 1434 instrument.id) 1435 1436 # reset all graces at the end of the measure to after-graces: 1437 self.graces_to_aftergraces(pending_graces) 1438 pending_graces = [] 1439 # Incomplete first measures are not padded, but registered as partial 1440 if is_first_measure: 1441 is_first_measure = False 1442 # upbeats are marked as implicit measures 1443 if attributes_object and m.is_implicit(): 1444 length = attributes_object.get_measure_length() 1445 measure_end = measure_start_moment + length 1446 if measure_end != now: 1447 m.partial = now 1448 previous_measure = m 1449 1450 # modify attributes so that only those applying to the given staff remain 1451 def extract_attributes_for_staff(part, attr, staff): 1452 attributes = copy.copy(attr) 1453 attributes._children = [] 1454 attributes._dict = attr._dict.copy() 1455 attributes._original_tag = attr 1456 # copy only the relevant children over for the given staff 1457 if staff == "None": 1458 staff = "1" 1459 for c in attr._children: 1460 if ((not hasattr(c, 'number') or c.number == staff) and 1461 not isinstance(c, Hash_text)): 1462 attributes._children.append(c) 1463 if not attributes._children: 1464 return None 1465 else: 1466 return attributes 1467 1468 def extract_voices(part): 1469 # The last indentified voice 1470 last_voice = None 1471 1472 voices = OrderedDict() 1473 measures = part.get_typed_children(Measure) 1474 elements = [] 1475 for m in measures: 1476 if m.partial > 0: 1477 elements.append(Partial(m.partial)) 1478 elements.extend(m.get_all_children()) 1479 # make sure we know all voices already so that dynamics, clefs, etc. 1480 # can be assigned to the correct voices 1481 voice_to_staff_dict = {} 1482 for n in elements: 1483 voice_id = n.get_maybe_exist_named_child('voice') 1484 vid = None 1485 if voice_id: 1486 vid = voice_id.get_text() 1487 elif isinstance(n, Note): 1488 # TODO: Check whether we shall really use "None" here, or 1489 # rather use "1" as the default? 1490 if n.get_maybe_exist_named_child('chord'): 1491 vid = last_voice 1492 else: 1493 vid = "1" 1494 1495 if vid is not None: 1496 last_voice = vid 1497 1498 staff_id = n.get_maybe_exist_named_child('staff') 1499 sid = None 1500 if staff_id: 1501 sid = staff_id.get_text() 1502 else: 1503 # TODO: Check whether we shall really use "None" here, or 1504 # rather use "1" as the default? 1505 # If this is changed, need to change the corresponding 1506 # check in extract_attributes_for_staff, too. 1507 sid = "None" 1508 if vid and vid not in voices: 1509 voices[vid] = Musicxml_voice() 1510 if vid and sid and not n.get_maybe_exist_typed_child(Grace): 1511 if vid not in voice_to_staff_dict: 1512 voice_to_staff_dict[vid] = sid 1513 1514 # invert the voice_to_staff_dict into a staff_to_voice_dict(since we 1515 # need to assign staff-assigned objects like clefs, times, etc. to 1516 # all the correct voices. This will never work entirely correct due 1517 # to staff-switches, but that's the best we can do! 1518 staff_to_voice_dict = {} 1519 for(v, s) in list(voice_to_staff_dict.items()): 1520 if s not in staff_to_voice_dict: 1521 staff_to_voice_dict[s] = [v] 1522 else: 1523 staff_to_voice_dict[s].append(v) 1524 1525 start_attr = None 1526 assign_to_next_note = [] 1527 id = None 1528 for n in elements: 1529 voice_id = n.get_maybe_exist_typed_child(get_class('voice')) 1530 if voice_id: 1531 id = voice_id.get_text() 1532 else: 1533 if n.get_maybe_exist_typed_child(get_class('chord')): 1534 id = last_voice 1535 else: 1536 id = "1" 1537 1538 if id != "None": 1539 last_voice = id 1540 1541 # We don't need backup/forward any more, since we have already 1542 # assigned the correct onset times. 1543 # TODO: Let Grouping through. Also: link, print, bokmark sound 1544 if not(isinstance(n, Note) or isinstance(n, Attributes) or 1545 isinstance(n, Direction) or isinstance(n, Partial) or 1546 isinstance(n, Barline) or isinstance(n, Harmony) or 1547 isinstance(n, FiguredBass) or isinstance(n, Print)): 1548 continue 1549 1550 if isinstance(n, Attributes) and not start_attr: 1551 start_attr = n 1552 continue 1553 1554 if isinstance(n, Attributes): 1555 # assign these only to the voices they really belong to! 1556 for(s, vids) in list(staff_to_voice_dict.items()): 1557 staff_attributes = part.extract_attributes_for_staff(n, s) 1558 if staff_attributes: 1559 for v in vids: 1560 voices[v].add_element(staff_attributes) 1561 continue 1562 1563 if isinstance(n, Partial) or isinstance(n, Barline) or isinstance(n, Print): 1564 for v in list(voices.keys()): 1565 voices[v].add_element(n) 1566 continue 1567 1568 if isinstance(n, Direction): 1569 if n.voice_id: 1570 voices[n.voice_id].add_element(n) 1571 else: 1572 assign_to_next_note.append(n) 1573 continue 1574 1575 if isinstance(n, Harmony) or isinstance(n, FiguredBass): 1576 # store the harmony or figured bass element until we encounter 1577 # the next note and assign it only to that one voice. 1578 assign_to_next_note.append(n) 1579 continue 1580 1581 if hasattr(n, 'print-object') and getattr(n, 'print-object') == "no": 1582 # Skip this note. 1583 pass 1584 else: 1585 for i in assign_to_next_note: 1586 voices[id].add_element(i) 1587 assign_to_next_note = [] 1588 voices[id].add_element(n) 1589 1590 # Assign all remaining elements from assign_to_next_note to the voice 1591 # of the previous note: 1592 for i in assign_to_next_note: 1593 voices[id].add_element(i) 1594 assign_to_next_note = [] 1595 1596 if start_attr: 1597 for(s, vids) in list(staff_to_voice_dict.items()): 1598 staff_attributes = part.extract_attributes_for_staff( 1599 start_attr, s) 1600 staff_attributes.read_self() 1601 part._staff_attributes_dict[s] = staff_attributes 1602 for v in vids: 1603 voices[v].insert(0, staff_attributes) 1604 voices[v]._elements[0].read_self() 1605 1606 part._voices = voices 1607 1608 def get_voices(self): 1609 return self._voices 1610 1611 def get_staff_attributes(self): 1612 return self._staff_attributes_dict 1613 1614 1615class BarStyle(Music_xml_node): 1616 pass 1617 1618 1619class BeatType(Music_xml_node): 1620 pass 1621 1622 1623class BeatUnit(Music_xml_node): 1624 pass 1625 1626 1627class BeatUnitDot(Music_xml_node): 1628 pass 1629 1630 1631class Beats(Music_xml_node): 1632 pass 1633 1634 1635class Bracket(Music_xml_spanner): 1636 pass 1637 1638 1639class Chord(Music_xml_node): 1640 pass 1641 1642 1643class Dashes(Music_xml_spanner): 1644 pass 1645 1646 1647class DirType(Music_xml_node): 1648 pass 1649 1650 1651class Direction(Measure_element): 1652 pass 1653 1654 1655class Dot(Music_xml_node): 1656 pass 1657 1658 1659class Elision(Music_xml_node): 1660 pass 1661 1662 1663class Extend(Music_xml_node): 1664 pass 1665 1666 1667class FiguredBass(Music_xml_node): 1668 pass 1669 1670 1671class Glissando(Music_xml_spanner): 1672 pass 1673 1674 1675class Grace(Music_xml_node): 1676 pass 1677 1678 1679class Harmony(Music_xml_node): 1680 pass 1681 1682 1683class Hash_comment(Music_xml_node): 1684 pass 1685 1686 1687class KeyAlter(Music_xml_node): 1688 pass 1689 1690 1691class Direction (Measure_element): 1692 pass 1693 1694 1695class KeyOctave(Music_xml_node): 1696 pass 1697 1698 1699class KeyStep(Music_xml_node): 1700 pass 1701 1702 1703class Part_group(Music_xml_node): 1704 pass 1705 1706 1707class Pedal(Music_xml_spanner): 1708 pass 1709 1710 1711class PerMinute(Music_xml_node): 1712 pass 1713 1714 1715class Print(Music_xml_node): 1716 pass 1717 1718 1719class Root(ChordPitch): 1720 pass 1721 1722 1723class Score_part(Music_xml_node): 1724 pass 1725 1726 1727class Slide(Music_xml_spanner): 1728 pass 1729 1730 1731class Staff(Music_xml_node): 1732 pass 1733 1734 1735class Text(Music_xml_node): 1736 pass 1737 1738 1739class Type(Music_xml_node): 1740 pass 1741 1742 1743class Wavy_line(Music_xml_spanner): 1744 pass 1745 1746 1747class Wedge(Music_xml_spanner): 1748 pass 1749 1750 1751class Words(Music_xml_node): 1752 pass 1753 1754 1755# need this, not all classes are instantiated 1756# for every input file. Only add those classes, that are either directly 1757# used by class name or extend Music_xml_node in some way! 1758class_dict = { 1759 '#comment': Hash_comment, 1760 '#text': Hash_text, 1761 'accidental': Accidental, 1762 'attributes': Attributes, 1763 'barline': Barline, 1764 'bar-style': BarStyle, 1765 'bass': Bass, 1766 'beam': Beam, 1767 'beats': Beats, 1768 'beat-type': BeatType, 1769 'beat-unit': BeatUnit, 1770 'beat-unit-dot': BeatUnitDot, 1771 'bend': Bend, 1772 'bracket': Bracket, 1773 'chord': Chord, 1774 'credit': Credit, 1775 'dashes': Dashes, 1776 'degree': ChordModification, 1777 'dot': Dot, 1778 'direction': Direction, 1779 'direction-type': DirType, 1780 'duration': Duration, 1781 'elision': Elision, 1782 'extend': Extend, 1783 'frame': Frame, 1784 'frame-note': Frame_Note, 1785 'figured-bass': FiguredBass, 1786 'glissando': Glissando, 1787 'grace': Grace, 1788 'harmony': Harmony, 1789 'identification': Identification, 1790 'key-alter': KeyAlter, 1791 'key-octave': KeyOctave, 1792 'key-step': KeyStep, 1793 'lyric': Lyric, 1794 'measure': Measure, 1795 'notations': Notations, 1796 'note': Note, 1797 'notehead': Notehead, 1798 'octave-shift': Octave_shift, 1799 'part': Part, 1800 'part-group': Part_group, 1801 'part-list': Part_list, 1802 'pedal': Pedal, 1803 'per-minute': PerMinute, 1804 'pitch': Pitch, 1805 'print': Print, 1806 'rest': Rest, 1807 'root': Root, 1808 'score-part': Score_part, 1809 'slide': Slide, 1810 'slur': Slur, 1811 'sound': Sound, 1812 'staff': Staff, 1813 'stem': Stem, 1814 'syllabic': Syllabic, 1815 'text': Text, 1816 'time-modification': Time_modification, 1817 'tied': Tied, 1818 'tuplet': Tuplet, 1819 'type': Type, 1820 'unpitched': Unpitched, 1821 'wavy-line': Wavy_line, 1822 'wedge': Wedge, 1823 'words': Words, 1824 'work': Work, 1825} 1826 1827 1828def name2class_name(name): 1829 name = name.replace('-', '_') 1830 name = name.replace('#', 'hash_') 1831 name = name[0].upper() + name[1:].lower() 1832 1833 return str(name) 1834 1835 1836def get_class(name): 1837 classname = class_dict.get(name) 1838 if classname: 1839 return classname 1840 else: 1841 class_name = name2class_name(name) 1842 klass = type(class_name, (Music_xml_node,), {}) 1843 class_dict[name] = klass 1844 return klass 1845 1846 1847def lxml_demarshal_node(node): 1848 name = node.tag 1849 1850 # Ignore comment nodes, which are also returned by the etree parser! 1851 if name is None or node.__class__.__name__ == "_Comment": 1852 return None 1853 klass = get_class(name) 1854 py_node = klass() 1855 1856 py_node._original = node 1857 py_node._name = name 1858 py_node._data = node.text 1859 py_node._children = [lxml_demarshal_node(cn) for cn in node.getchildren()] 1860 py_node._children = [x for x in py_node._children if x] 1861 1862 for c in py_node._children: 1863 c._parent = py_node 1864 1865 for(k, v) in list(node.items()): 1866 py_node.__dict__[k] = v 1867 py_node._attribute_dict[k] = v 1868 1869 return py_node 1870 1871 1872def minidom_demarshal_node(node): 1873 name = node.nodeName 1874 1875 klass = get_class(name) 1876 py_node = klass() 1877 py_node._name = name 1878 py_node._children = [minidom_demarshal_node(cn) for cn in node.childNodes] 1879 for c in py_node._children: 1880 c._parent = py_node 1881 1882 if node.attributes: 1883 for(nm, value) in list(node.attributes.items()): 1884 py_node.__dict__[nm] = value 1885 py_node._attribute_dict[nm] = value 1886 1887 py_node._data = None 1888 if node.nodeType == node.TEXT_NODE and node.data: 1889 py_node._data = node.data 1890 1891 py_node._original = node 1892 return py_node 1893 1894 1895if __name__ == '__main__': 1896 import lxml.etree 1897 1898 tree = lxml.etree.parse('beethoven.xml') 1899 mxl_tree = lxml_demarshal_node(tree.getroot()) 1900 ks = sorted(class_dict.keys()) 1901 print('\n'.join(ks)) 1902