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