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