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