]> git.donarmstrong.com Git - lilypond.git/blob - python/musicxml.py
Merge branch 'master' of ssh://jneem@git.sv.gnu.org/srv/git/lilypond into tmp
[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
10 _ = ly._
11
12 def error (str):
13     ly.stderr_write ((_ ("error: %s") % str) + "\n")
14
15
16 def escape_ly_output_string (input_string):
17     return_string = input_string
18     needs_quotes = not re.match (u"^[a-zA-ZäöüÜÄÖßñ]*$", return_string);
19     if needs_quotes:
20         return_string = "\"" + string.replace (return_string, "\"", "\\\"") + "\""
21     return return_string
22
23
24 def musicxml_duration_to_log (dur):
25     return  {'256th': 8,
26              '128th': 7,
27              '64th': 6,
28              '32nd': 5,
29              '16th': 4,
30              'eighth': 3,
31              'quarter': 2,
32              'half': 1,
33              'whole': 0,
34              'breve': -1,
35              'longa': -2,
36              'long': -2}.get (dur, 0)
37
38 def interpret_alter_element (alter_elm):
39     alter = 0
40     if alter_elm:
41         val = eval(alter_elm.get_text ())
42         if type (val) in (int, float):
43             alter = val
44     return alter
45
46
47 class Xml_node:
48     def __init__ (self):
49         self._children = []
50         self._data = None
51         self._original = None
52         self._name = 'xml_node'
53         self._parent = None
54         self._attribute_dict = {}
55         
56     def get_parent (self):
57         return self._parent
58     
59     def is_first (self):
60         return self._parent.get_typed_children (self.__class__)[0] == self
61
62     def original (self):
63         return self._original 
64     def get_name (self):
65         return self._name
66
67     def get_text (self):
68         if self._data:
69             return self._data
70
71         if not self._children:
72             return ''
73
74         return ''.join ([c.get_text () for c in self._children])
75
76     def message (self, msg):
77         ly.stderr_write (msg+'\n')
78
79         p = self
80         while p:
81             sys.stderr.write ('  In: <%s %s>\n' % (p._name, ' '.join (['%s=%s' % item for item in p._attribute_dict.items ()])))
82             p = p.get_parent ()
83         
84     def dump (self, indent = ''):
85         sys.stderr.write ('%s<%s%s>' % (indent, self._name, ''.join ([' %s=%s' % item for item in self._attribute_dict.items ()])))
86         non_text_children = [c for c in self._children if not isinstance (c, Hash_text)]
87         if non_text_children:
88             sys.stderr.write ('\n')
89         for c in self._children:
90             c.dump (indent + "    ")
91         if non_text_children:
92             sys.stderr.write (indent)
93         sys.stderr.write ('</%s>\n' % self._name)
94
95         
96     def get_typed_children (self, klass):
97         if not klass:
98             return []
99         else:
100             return [c for c in self._children if isinstance(c, klass)]
101
102     def get_named_children (self, nm):
103         return self.get_typed_children (get_class (nm))
104
105     def get_named_child (self, nm):
106         return self.get_maybe_exist_named_child (nm)
107
108     def get_children (self, predicate):
109         return [c for c in self._children if predicate(c)]
110
111     def get_all_children (self):
112         return self._children
113
114     def get_maybe_exist_named_child (self, name):
115         return self.get_maybe_exist_typed_child (get_class (name))
116
117     def get_maybe_exist_typed_child (self, klass):
118         cn = self.get_typed_children (klass)
119         if len (cn)==0:
120             return None
121         elif len (cn) == 1:
122             return cn[0]
123         else:
124             raise "More than 1 child", klass
125
126     def get_unique_typed_child (self, klass):
127         cn = self.get_typed_children(klass)
128         if len (cn) <> 1:
129             sys.stderr.write (self.__dict__ + '\n')
130             raise 'Child is not unique for', (klass, 'found', cn)
131
132         return cn[0]
133
134     def get_named_child_value_number (self, name, default):
135         n = self.get_maybe_exist_named_child (name)
136         if n:
137             return string.atoi (n.get_text())
138         else:
139             return default
140
141
142 class Music_xml_node (Xml_node):
143     def __init__ (self):
144         Xml_node.__init__ (self)
145         self.duration = Rational (0)
146         self.start = Rational (0)
147
148 class Work (Xml_node):
149     def get_work_information (self, tag):
150         wt = self.get_maybe_exist_named_child (tag)
151         if wt:
152             return wt.get_text ()
153         else:
154             return ''
155       
156     def get_work_title (self):
157         return self.get_work_information ('work-title')
158     def get_work_number (self):
159         return self.get_work_information ('work-number')
160     def get_opus (self):
161         return self.get_work_information ('opus')
162
163 class Identification (Xml_node):
164     def get_rights (self):
165         rights = self.get_named_children ('rights')
166         ret = []
167         for r in rights:
168           ret.append (r.get_text ())
169         return string.join (ret, "\n")
170
171     def get_creator (self, type):
172         creators = self.get_named_children ('creator')
173         # return the first creator tag that has the particular type
174         for i in creators:
175             if hasattr (i, 'type') and i.type == type:
176                 return i.get_text ()
177         return None
178
179     def get_composer (self):
180         c = self.get_creator ('composer')
181         if c:
182             return c
183         creators = self.get_named_children ('creator')
184         # return the first creator tag that has no type at all
185         for i in creators:
186             if not hasattr (i, 'type'):
187                 return i.get_text ()
188         return None
189     def get_arranger (self):
190         return self.get_creator ('arranger')
191     def get_editor (self):
192         return self.get_creator ('editor')
193     def get_poet (self):
194         v = self.get_creator ('lyricist')
195         if v:
196             return v
197         v = self.get_creator ('poet')
198         return v
199     
200     def get_encoding_information (self, type):
201         enc = self.get_named_children ('encoding')
202         if enc:
203             children = enc[0].get_named_children (type)
204             if children:
205                 return children[0].get_text ()
206         else:
207             return None
208       
209     def get_encoding_software (self):
210         return self.get_encoding_information ('software')
211     def get_encoding_date (self):
212         return self.get_encoding_information ('encoding-date')
213     def get_encoding_person (self):
214         return self.get_encoding_information ('encoder')
215     def get_encoding_description (self):
216         return self.get_encoding_information ('encoding-description')
217     
218     def get_encoding_software_list (self):
219         enc = self.get_named_children ('encoding')
220         software = []
221         for e in enc:
222             softwares = e.get_named_children ('software')
223             for s in softwares:
224                 software.append (s.get_text ())
225         return software
226
227     def get_file_description (self):
228         misc = self.get_named_children ('miscellaneous')
229         for m in misc:
230             misc_fields = m.get_named_children ('miscellaneous-field')
231             for mf in misc_fields:
232                 if hasattr (mf, 'name') and mf.name == 'description':
233                     return mf.get_text () 
234         return None
235
236
237
238 class Duration (Music_xml_node):
239     def get_length (self):
240         dur = int (self.get_text ()) * Rational (1,4)
241         return dur
242
243 class Hash_comment (Music_xml_node):
244     pass
245 class Hash_text (Music_xml_node):
246     def dump (self, indent = ''):
247         sys.stderr.write ('%s' % string.strip (self._data))
248
249 class Pitch (Music_xml_node):
250     def get_step (self):
251         ch = self.get_unique_typed_child (get_class (u'step'))
252         step = ch.get_text ().strip ()
253         return step
254     def get_octave (self):
255         ch = self.get_unique_typed_child (get_class (u'octave'))
256
257         step = ch.get_text ().strip ()
258         return int (step)
259
260     def get_alteration (self):
261         ch = self.get_maybe_exist_typed_child (get_class (u'alter'))
262         return interpret_alter_element (ch)
263
264 class Unpitched (Music_xml_node):
265     def get_step (self):
266         ch = self.get_unique_typed_child (get_class (u'display-step'))
267         step = ch.get_text ().strip ()
268         return step
269
270     def get_octave (self):
271         ch = self.get_unique_typed_child (get_class (u'display-octave'))
272
273         if ch:
274             octave = ch.get_text ().strip ()
275             return int (octave)
276         else:
277             return None
278
279 class Measure_element (Music_xml_node):
280     def get_voice_id (self):
281         voice_id = self.get_maybe_exist_named_child ('voice')
282         if voice_id:
283             return voice_id.get_text ()
284         else:
285             return None
286
287     def is_first (self):
288         # Look at all measure elements (previously we had self.__class__, which
289         # only looked at objects of the same type!
290         cn = self._parent.get_typed_children (Measure_element)
291         # But only look at the correct voice; But include Attributes, too, which
292         # are not tied to any particular voice
293         cn = [c for c in cn if (c.get_voice_id () == self.get_voice_id ()) or isinstance (c, Attributes)]
294         return cn[0] == self
295
296 class Attributes (Measure_element):
297     def __init__ (self):
298         Measure_element.__init__ (self)
299         self._dict = {}
300         self._original_tag = None
301         self._time_signature_cache = None
302
303     def is_first (self):
304         cn = self._parent.get_typed_children (self.__class__)
305         if self._original_tag:
306             return cn[0] == self._original_tag
307         else:
308             return cn[0] == self
309     
310     def set_attributes_from_previous (self, dict):
311         self._dict.update (dict)
312         
313     def read_self (self):
314         for c in self.get_all_children ():
315             self._dict[c.get_name()] = c
316
317     def get_named_attribute (self, name):
318         return self._dict.get (name)
319         
320     def single_time_sig_to_fraction (self, sig):
321         if len (sig) < 2:
322             return 0
323         n = 0
324         for i in sig[0:-1]:
325           n += i
326         return Rational (n, sig[-1])
327
328     def get_measure_length (self):
329         sig = self.get_time_signature ()
330         if not sig or len (sig) == 0:
331             return 1
332         if isinstance (sig[0], list):
333             # Complex compound time signature
334             l = 0
335             for i in sig:
336                 l += self.single_time_sig_to_fraction (i)
337             return l
338         else:
339            # Simple (maybe compound) time signature of the form (beat, ..., type)
340             return self.single_time_sig_to_fraction (sig)
341         return 0
342         
343     def get_time_signature (self):
344         "Return time sig as a (beat, beat-type) tuple. For compound signatures,"
345         "return either (beat, beat,..., beat-type) or ((beat,..., type), "
346         "(beat,..., type), ...)."
347         if self._time_signature_cache:
348             return self._time_signature_cache
349
350         try:
351             mxl = self.get_named_attribute ('time')
352             if not mxl:
353                 return None
354
355             if mxl.get_maybe_exist_named_child ('senza-misura'):
356                 # TODO: Handle pieces without a time signature!
357                 error (_ ("Senza-misura time signatures are not yet supported!"))
358                 return (4, 4)
359             else:
360                 signature = []
361                 current_sig = []
362                 for i in mxl.get_all_children ():
363                     if isinstance (i, Beats):
364                         beats = string.split (i.get_text ().strip (), "+")
365                         current_sig = [int (j) for j in beats]
366                     elif isinstance (i, BeatType):
367                         current_sig.append (int (i.get_text ()))
368                         signature.append (current_sig)
369                         current_sig = []
370                 if isinstance (signature[0], list) and len (signature) == 1:
371                     signature = signature[0]
372                 self._time_signature_cache = signature
373                 return signature
374         except (KeyError, ValueError):
375             self.message (_ ("Unable to interpret time signature! Falling back to 4/4."))
376             return (4, 4)
377
378     # returns clef information in the form ("cleftype", position, octave-shift)
379     def get_clef_information (self):
380         clefinfo = ['G', 2, 0]
381         mxl = self.get_named_attribute ('clef')
382         if not mxl:
383             return clefinfo
384         sign = mxl.get_maybe_exist_named_child ('sign')
385         if sign:
386             clefinfo[0] = sign.get_text()
387         line = mxl.get_maybe_exist_named_child ('line')
388         if line:
389             clefinfo[1] = string.atoi (line.get_text ())
390         octave = mxl.get_maybe_exist_named_child ('clef-octave-change')
391         if octave:
392             clefinfo[2] = string.atoi (octave.get_text ())
393         return clefinfo
394
395     def get_key_signature (self):
396         "return (fifths, mode) tuple if the key signatures is given as "
397         "major/minor in the Circle of fifths. Otherwise return an alterations"
398         "list of the form [[step,alter<,octave>], [step,alter<,octave>], ...], "
399         "where the octave values are optional."
400
401         key = self.get_named_attribute ('key')
402         if not key:
403             return None
404         fifths_elm = key.get_maybe_exist_named_child ('fifths')
405         if fifths_elm:
406             mode_node = key.get_maybe_exist_named_child ('mode')
407             mode = None
408             if mode_node:
409                 mode = mode_node.get_text ()
410             if not mode or mode == '':
411                 mode = 'major'
412             fifths = int (fifths_elm.get_text ())
413             # TODO: Shall we try to convert the key-octave and the cancel, too?
414             return (fifths, mode)
415         else:
416             alterations = []
417             current_step = 0
418             for i in key.get_all_children ():
419                 if isinstance (i, KeyStep):
420                     current_step = int (i.get_text ())
421                 elif isinstance (i, KeyAlter):
422                     alterations.append ([current_step, interpret_alter_element (i)])
423                 elif isinstance (i, KeyOctave):
424                     nr = -1
425                     if hasattr (i, 'number'):
426                         nr = int (i.number)
427                     if (nr > 0) and (nr <= len (alterations)):
428                         # MusicXML Octave 4 is middle C -> shift to 0
429                         alterations[nr-1].append (int (i.get_text ())-4)
430                     else:
431                         i.message (_ ("Key alteration octave given for a "
432                             "non-existing alteration nr. %s, available numbers: %s!") % (nr, len(alterations)))
433             return alterations
434
435     def get_transposition (self):
436         return self.get_named_attribute ('transpose')
437
438 class KeyAlter (Music_xml_node):
439     pass
440 class KeyStep (Music_xml_node):
441     pass
442 class KeyOctave (Music_xml_node):
443     pass
444
445
446 class Barline (Measure_element):
447     pass
448 class BarStyle (Music_xml_node):
449     pass
450 class Partial (Measure_element):
451     def __init__ (self, partial):
452         Measure_element.__init__ (self)
453         self.partial = partial
454
455 class Note (Measure_element):
456     def __init__ (self):
457         Measure_element.__init__ (self)
458         self.instrument_name = ''
459         self._after_grace = False
460     def is_grace (self):
461         return self.get_maybe_exist_named_child (u'grace')
462     def is_after_grace (self):
463         if not self.is_grace():
464             return False;
465         gr = self.get_maybe_exist_typed_child (Grace)
466         return self._after_grace or hasattr (gr, 'steal-time-previous');
467
468     def get_duration_log (self):
469         ch = self.get_maybe_exist_named_child (u'type')
470
471         if ch:
472             log = ch.get_text ().strip()
473             return musicxml_duration_to_log (log)
474         elif self.get_maybe_exist_named_child (u'grace'):
475             # FIXME: is it ok to default to eight note for grace notes?
476             return 3
477         else:
478             return None
479     
480     def get_duration_info (self):
481         log = self.get_duration_log ()
482         if log != None:
483             dots = len (self.get_typed_children (Dot))
484             return (log, dots)
485         else:
486             return None
487
488     def get_factor (self):
489         return 1
490
491     def get_pitches (self):
492         return self.get_typed_children (get_class (u'pitch'))
493
494 class Part_list (Music_xml_node):
495     def __init__ (self):
496         Music_xml_node.__init__ (self)
497         self._id_instrument_name_dict = {}
498         
499     def generate_id_instrument_dict (self):
500
501         ## not empty to make sure this happens only once.
502         mapping = {1: 1}
503         for score_part in self.get_named_children ('score-part'):
504             for instr in score_part.get_named_children ('score-instrument'):
505                 id = instr.id
506                 name = instr.get_named_child ("instrument-name")
507                 mapping[id] = name.get_text ()
508
509         self._id_instrument_name_dict = mapping
510
511     def get_instrument (self, id):
512         if not self._id_instrument_name_dict:
513             self.generate_id_instrument_dict()
514
515         instrument_name = self._id_instrument_name_dict.get (id)
516         if instrument_name:
517             return instrument_name
518         else:
519             ly.stderr_write (_ ("Unable to find instrument for ID=%s\n") % id)
520             return "Grand Piano"
521
522 class Part_group (Music_xml_node):
523     pass
524 class Score_part (Music_xml_node):
525     pass
526         
527 class Measure (Music_xml_node):
528     def __init__ (self):
529         Music_xml_node.__init__ (self)
530         self.partial = 0
531     def is_implicit (self):
532         return hasattr (self, 'implicit') and self.implicit == 'yes'
533     def get_notes (self):
534         return self.get_typed_children (get_class (u'note'))
535
536 class Syllabic (Music_xml_node):
537     def continued (self):
538         text = self.get_text()
539         return (text == "begin") or (text == "middle")
540 class Elision (Music_xml_node):
541     pass
542 class Extend (Music_xml_node):
543     pass
544 class Text (Music_xml_node):
545     pass
546
547 class Lyric (Music_xml_node):
548     def get_number (self):
549         if hasattr (self, 'number'):
550             return self.number
551         else:
552             return -1
553
554 class Musicxml_voice:
555     def __init__ (self):
556         self._elements = []
557         self._staves = {}
558         self._start_staff = None
559         self._lyrics = []
560         self._has_lyrics = False
561
562     def add_element (self, e):
563         self._elements.append (e)
564         if (isinstance (e, Note)
565             and e.get_maybe_exist_typed_child (Staff)):
566             name = e.get_maybe_exist_typed_child (Staff).get_text ()
567
568             if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
569                 self._start_staff = name
570             self._staves[name] = True
571
572         lyrics = e.get_typed_children (Lyric)
573         if not self._has_lyrics:
574           self.has_lyrics = len (lyrics) > 0
575
576         for l in lyrics:
577             nr = l.get_number()
578             if (nr > 0) and not (nr in self._lyrics):
579                 self._lyrics.append (nr)
580
581     def insert (self, idx, e):
582         self._elements.insert (idx, e)
583
584     def get_lyrics_numbers (self):
585         if (len (self._lyrics) == 0) and self._has_lyrics:
586             #only happens if none of the <lyric> tags has a number attribute
587             return [1]
588         else:
589             return self._lyrics
590
591
592 def graces_to_aftergraces (pending_graces):
593     for gr in pending_graces:
594         gr._when = gr._prev_when
595         gr._measure_position = gr._prev_measure_position
596         gr._after_grace = True
597
598
599 class Part (Music_xml_node):
600     def __init__ (self):
601         Music_xml_node.__init__ (self)
602         self._voices = {}
603         self._staff_attributes_dict = {}
604
605     def get_part_list (self):
606         n = self
607         while n and n.get_name() != 'score-partwise':
608             n = n._parent
609
610         return n.get_named_child ('part-list')
611        
612     def interpret (self):
613         """Set durations and starting points."""
614         """The starting point of the very first note is 0!"""
615         
616         part_list = self.get_part_list ()
617         
618         now = Rational (0)
619         factor = Rational (1)
620         attributes_dict = {}
621         attributes_object = None
622         measures = self.get_typed_children (Measure)
623         last_moment = Rational (-1)
624         last_measure_position = Rational (-1)
625         measure_position = Rational (0)
626         measure_start_moment = now
627         is_first_measure = True
628         previous_measure = None
629         # Graces at the end of a measure need to have their position set to the
630         # previous number!
631         pending_graces = []
632         for m in measures:
633             # implicit measures are used for artificial measures, e.g. when
634             # a repeat bar line splits a bar into two halves. In this case,
635             # don't reset the measure position to 0. They are also used for
636             # upbeats (initial value of 0 fits these, too).
637             # Also, don't reset the measure position at the end of the loop,
638             # but rather when starting the next measure (since only then do we
639             # know if the next measure is implicit and continues that measure)
640             if not m.is_implicit ():
641                 # Warn about possibly overfull measures and reset the position
642                 if attributes_object and previous_measure and previous_measure.partial == 0:
643                     length = attributes_object.get_measure_length ()
644                     new_now = measure_start_moment + length
645                     if now <> new_now:
646                         problem = 'incomplete'
647                         if now > new_now:
648                             problem = 'overfull'
649                         ## only for verbose operation.
650                         if problem <> 'incomplete' and previous_measure:
651                             previous_measure.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
652                     now = new_now
653                 measure_start_moment = now
654                 measure_position = Rational (0)
655
656             for n in m.get_all_children ():
657                 # figured bass has a duration, but applies to the next note
658                 # and should not change the current measure position!
659                 if isinstance (n, FiguredBass):
660                     n._divisions = factor.denominator ()
661                     n._when = now
662                     n._measure_position = measure_position
663                     continue
664
665                 if isinstance (n, Hash_text):
666                     continue
667                 dur = Rational (0)
668
669                 if n.__class__ == Attributes:
670                     n.set_attributes_from_previous (attributes_dict)
671                     n.read_self ()
672                     attributes_dict = n._dict.copy ()
673                     attributes_object = n
674                     
675                     factor = Rational (1,
676                                        int (attributes_dict.get ('divisions').get_text ()))
677
678                 
679                 if (n.get_maybe_exist_typed_child (Duration)):
680                     mxl_dur = n.get_maybe_exist_typed_child (Duration)
681                     dur = mxl_dur.get_length () * factor
682                     
683                     if n.get_name() == 'backup':
684                         dur = - dur
685                         # reset all graces before the backup to after-graces:
686                         graces_to_aftergraces (pending_graces)
687                         pending_graces = []
688                     if n.get_maybe_exist_typed_child (Grace):
689                         dur = Rational (0)
690
691                     rest = n.get_maybe_exist_typed_child (Rest)
692                     if (rest
693                         and attributes_object
694                         and attributes_object.get_measure_length () == dur):
695
696                         rest._is_whole_measure = True
697
698                 if (dur > Rational (0)
699                     and n.get_maybe_exist_typed_child (Chord)):
700                     now = last_moment
701                     measure_position = last_measure_position
702
703                 n._when = now
704                 n._measure_position = measure_position
705
706                 # For all grace notes, store the previous note,  in case need
707                 # to turn the grace note into an after-grace later on!
708                 if isinstance(n, Note) and n.is_grace ():
709                     n._prev_when = last_moment
710                     n._prev_measure_position = last_measure_position
711                 # After-graces are placed at the same position as the previous note
712                 if isinstance(n, Note) and  n.is_after_grace ():
713                     # TODO: We should do the same for grace notes at the end of 
714                     # a measure with no following note!!!
715                     n._when = last_moment
716                     n._measure_position = last_measure_position
717                 elif isinstance(n, Note) and n.is_grace ():
718                     pending_graces.append (n)
719                 elif (dur > Rational (0)):
720                     pending_graces = [];
721
722                 n._duration = dur
723                 if dur > Rational (0):
724                     last_moment = now
725                     last_measure_position = measure_position
726                     now += dur
727                     measure_position += dur
728                 elif dur < Rational (0):
729                     # backup element, reset measure position
730                     now += dur
731                     measure_position += dur
732                     if measure_position < 0:
733                         # backup went beyond the measure start => reset to 0
734                         now -= measure_position
735                         measure_position = 0
736                     last_moment = now
737                     last_measure_position = measure_position
738                 if n._name == 'note':
739                     instrument = n.get_maybe_exist_named_child ('instrument')
740                     if instrument:
741                         n.instrument_name = part_list.get_instrument (instrument.id)
742
743             # reset all graces at the end of the measure to after-graces:
744             graces_to_aftergraces (pending_graces)
745             pending_graces = []
746             # Incomplete first measures are not padded, but registered as partial
747             if is_first_measure:
748                 is_first_measure = False
749                 # upbeats are marked as implicit measures
750                 if attributes_object and m.is_implicit ():
751                     length = attributes_object.get_measure_length ()
752                     measure_end = measure_start_moment + length
753                     if measure_end <> now:
754                         m.partial = now
755             previous_measure = m
756
757     # modify attributes so that only those applying to the given staff remain
758     def extract_attributes_for_staff (part, attr, staff):
759         attributes = copy.copy (attr)
760         attributes._children = [];
761         attributes._dict = attr._dict.copy ()
762         attributes._original_tag = attr
763         # copy only the relevant children over for the given staff
764         for c in attr._children:
765             if (not (hasattr (c, 'number') and (c.number != staff)) and
766                 not (isinstance (c, Hash_text))):
767                 attributes._children.append (c)
768         if not attributes._children:
769             return None
770         else:
771             return attributes
772
773     def extract_voices (part):
774         voices = {}
775         measures = part.get_typed_children (Measure)
776         elements = []
777         for m in measures:
778             if m.partial > 0:
779                 elements.append (Partial (m.partial))
780             elements.extend (m.get_all_children ())
781         # make sure we know all voices already so that dynamics, clefs, etc.
782         # can be assigned to the correct voices
783         voice_to_staff_dict = {}
784         for n in elements:
785             voice_id = n.get_maybe_exist_named_child (u'voice')
786             vid = None
787             if voice_id:
788                 vid = voice_id.get_text ()
789             elif isinstance (n, Note):
790                 vid = "None"
791
792             staff_id = n.get_maybe_exist_named_child (u'staff')
793             sid = None
794             if staff_id:
795                 sid = staff_id.get_text ()
796             else:
797                 sid = "None"
798             if vid and not voices.has_key (vid):
799                 voices[vid] = Musicxml_voice()
800             if vid and sid and not n.get_maybe_exist_typed_child (Grace):
801                 if not voice_to_staff_dict.has_key (vid):
802                     voice_to_staff_dict[vid] = sid
803         # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
804         # need to assign staff-assigned objects like clefs, times, etc. to
805         # all the correct voices. This will never work entirely correct due
806         # to staff-switches, but that's the best we can do!
807         staff_to_voice_dict = {}
808         for (v,s) in voice_to_staff_dict.items ():
809             if not staff_to_voice_dict.has_key (s):
810                 staff_to_voice_dict[s] = [v]
811             else:
812                 staff_to_voice_dict[s].append (v)
813
814
815         start_attr = None
816         assign_to_next_note = []
817         id = None
818         for n in elements:
819             voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
820             if voice_id:
821                 id = voice_id.get_text ()
822             else:
823                 id = "None"
824
825             # We don't need backup/forward any more, since we have already 
826             # assigned the correct onset times. 
827             # TODO: Let Grouping through. Also: link, print, bokmark sound
828             if not (isinstance (n, Note) or isinstance (n, Attributes) or
829                     isinstance (n, Direction) or isinstance (n, Partial) or
830                     isinstance (n, Barline) or isinstance (n, Harmony) or
831                     isinstance (n, FiguredBass) ):
832                 continue
833
834             if isinstance (n, Attributes) and not start_attr:
835                 start_attr = n
836                 continue
837
838             if isinstance (n, Attributes):
839                 # assign these only to the voices they really belongs to!
840                 for (s, vids) in staff_to_voice_dict.items ():
841                     staff_attributes = part.extract_attributes_for_staff (n, s)
842                     if staff_attributes:
843                         for v in vids:
844                             voices[v].add_element (staff_attributes)
845                 continue
846
847             if isinstance (n, Partial) or isinstance (n, Barline):
848                 for v in voices.keys ():
849                     voices[v].add_element (n)
850                 continue
851
852             if isinstance (n, Direction):
853                 staff_id = n.get_maybe_exist_named_child (u'staff')
854                 if staff_id:
855                     staff_id = staff_id.get_text ()
856                 if staff_id:
857                     dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
858                 else:
859                     dir_voices = voices.keys ()
860                 for v in dir_voices:
861                     voices[v].add_element (n)
862                 continue
863
864             if isinstance (n, Harmony) or isinstance (n, FiguredBass):
865                 # store the harmony or figured bass element until we encounter 
866                 # the next note and assign it only to that one voice.
867                 assign_to_next_note.append (n)
868                 continue
869
870             if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
871                 #Skip this note. 
872                 pass
873             else:
874                 for i in assign_to_next_note:
875                     voices[id].add_element (i)
876                 assign_to_next_note = []
877                 voices[id].add_element (n)
878
879         # Assign all remaining elements from assign_to_next_note to the voice
880         # of the previous note:
881         for i in assign_to_next_note:
882             voices[id].add_element (i)
883         assign_to_next_note = []
884
885         if start_attr:
886             for (s, vids) in staff_to_voice_dict.items ():
887                 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
888                 staff_attributes.read_self ()
889                 part._staff_attributes_dict[s] = staff_attributes
890                 for v in vids:
891                     voices[v].insert (0, staff_attributes)
892                     voices[v]._elements[0].read_self()
893
894         part._voices = voices
895
896     def get_voices (self):
897         return self._voices
898     def get_staff_attributes (self):
899         return self._staff_attributes_dict
900
901 class Notations (Music_xml_node):
902     def get_tie (self):
903         ts = self.get_named_children ('tied')
904         starts = [t for t in ts if t.type == 'start']
905         if starts:
906             return starts[0]
907         else:
908             return None
909
910     def get_tuplets (self):
911         return self.get_typed_children (Tuplet)
912
913 class Time_modification(Music_xml_node):
914     def get_fraction (self):
915         b = self.get_maybe_exist_named_child ('actual-notes')
916         a = self.get_maybe_exist_named_child ('normal-notes')
917         return (int(a.get_text ()), int (b.get_text ()))
918
919     def get_normal_type (self):
920         tuplet_type = self.get_maybe_exist_named_child ('normal-type')
921         if tuplet_type:
922             dots = self.get_named_children ('normal-dot')
923             log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
924             return (log , len (dots))
925         else:
926             return None
927
928
929 class Accidental (Music_xml_node):
930     def __init__ (self):
931         Music_xml_node.__init__ (self)
932         self.editorial = False
933         self.cautionary = False
934
935 class Music_xml_spanner (Music_xml_node):
936     def get_type (self):
937         if hasattr (self, 'type'):
938             return self.type
939         else:
940             return 0
941     def get_size (self):
942         if hasattr (self, 'size'):
943             return string.atoi (self.size)
944         else:
945             return 0
946
947 class Wedge (Music_xml_spanner):
948     pass
949
950 class Tuplet (Music_xml_spanner):
951     def duration_info_from_tuplet_note (self, tuplet_note):
952         tuplet_type = tuplet_note.get_maybe_exist_named_child ('tuplet-type')
953         if tuplet_type:
954             dots = tuplet_note.get_named_children ('tuplet-dot')
955             log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
956             return (log, len (dots))
957         else:
958             return None
959
960     # Return tuplet note type as (log, dots)
961     def get_normal_type (self):
962         tuplet = self.get_maybe_exist_named_child ('tuplet-normal')
963         if tuplet:
964             return self.duration_info_from_tuplet_note (tuplet)
965         else:
966             return None
967
968     def get_actual_type (self):
969         tuplet = self.get_maybe_exist_named_child ('tuplet-actual')
970         if tuplet:
971             return self.duration_info_from_tuplet_note (tuplet)
972         else:
973             return None
974
975     def get_tuplet_note_count (self, tuplet_note):
976         if tuplet_note:
977             tuplet_nr = tuplet_note.get_maybe_exist_named_child ('tuplet-number')
978             if tuplet_nr: 
979                 return int (tuplet_nr.get_text ())
980         return None
981     def get_normal_nr (self):
982         return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-normal'))
983     def get_actual_nr (self):
984         return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-actual'))
985
986 class Bracket (Music_xml_spanner):
987     pass
988
989 class Dashes (Music_xml_spanner):
990     pass
991
992 class Slur (Music_xml_spanner):
993     def get_type (self):
994         return self.type
995
996 class Beam (Music_xml_spanner):
997     def get_type (self):
998         return self.get_text ()
999     def is_primary (self):
1000         return self.number == "1"
1001
1002 class Wavy_line (Music_xml_spanner):
1003     pass
1004     
1005 class Pedal (Music_xml_spanner):
1006     pass
1007
1008 class Glissando (Music_xml_spanner):
1009     pass
1010
1011 class Slide (Music_xml_spanner):
1012     pass
1013
1014 class Octave_shift (Music_xml_spanner):
1015     # default is 8 for the octave-shift!
1016     def get_size (self):
1017         if hasattr (self, 'size'):
1018             return string.atoi (self.size)
1019         else:
1020             return 8
1021
1022 class Chord (Music_xml_node):
1023     pass
1024
1025 class Dot (Music_xml_node):
1026     pass
1027
1028 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
1029 # for the inner <rest> element, not the whole rest block.
1030 class Rest (Music_xml_node):
1031     def __init__ (self):
1032         Music_xml_node.__init__ (self)
1033         self._is_whole_measure = False
1034     def is_whole_measure (self):
1035         return self._is_whole_measure
1036     def get_step (self):
1037         ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
1038         if ch:
1039             step = ch.get_text ().strip ()
1040             return step
1041         else:
1042             return None
1043     def get_octave (self):
1044         ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
1045         if ch:
1046             step = ch.get_text ().strip ()
1047             return int (step)
1048         else:
1049             return None
1050
1051 class Type (Music_xml_node):
1052     pass
1053 class Grace (Music_xml_node):
1054     pass
1055 class Staff (Music_xml_node):
1056     pass
1057
1058 class Direction (Music_xml_node):
1059     pass
1060 class DirType (Music_xml_node):
1061     pass
1062
1063 class Bend (Music_xml_node):
1064     def bend_alter (self):
1065         alter = self.get_maybe_exist_named_child ('bend-alter')
1066         return interpret_alter_element (alter)
1067
1068 class Words (Music_xml_node):
1069     pass
1070
1071 class Harmony (Music_xml_node):
1072     pass
1073
1074 class ChordPitch (Music_xml_node):
1075     def step_class_name (self):
1076         return u'root-step'
1077     def alter_class_name (self):
1078         return u'root-alter'
1079     def get_step (self):
1080         ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
1081         return ch.get_text ().strip ()
1082     def get_alteration (self):
1083         ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
1084         return interpret_alter_element (ch)
1085
1086 class Root (ChordPitch):
1087     pass
1088
1089 class Bass (ChordPitch):
1090     def step_class_name (self):
1091         return u'bass-step'
1092     def alter_class_name (self):
1093         return u'bass-alter'
1094
1095 class ChordModification (Music_xml_node):
1096     def get_type (self):
1097         ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
1098         return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
1099     def get_value (self):
1100         ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
1101         value = 0
1102         if ch:
1103             value = int (ch.get_text ().strip ())
1104         return value
1105     def get_alter (self):
1106         ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
1107         return interpret_alter_element (ch)
1108
1109
1110 class Frame (Music_xml_node):
1111     def get_frets (self):
1112         return self.get_named_child_value_number ('frame-frets', 4)
1113     def get_strings (self):
1114         return self.get_named_child_value_number ('frame-strings', 6)
1115     def get_first_fret (self):
1116         return self.get_named_child_value_number ('first-fret', 1)
1117
1118 class Frame_Note (Music_xml_node):
1119     def get_string (self):
1120         return self.get_named_child_value_number ('string', 1)
1121     def get_fret (self):
1122         return self.get_named_child_value_number ('fret', 0)
1123     def get_fingering (self):
1124         return self.get_named_child_value_number ('fingering', -1)
1125     def get_barre (self):
1126         n = self.get_maybe_exist_named_child ('barre')
1127         if n:
1128             return getattr (n, 'type', '')
1129         else:
1130             return ''
1131
1132 class FiguredBass (Music_xml_node):
1133     pass
1134
1135 class Beats (Music_xml_node):
1136     pass
1137
1138 class BeatType (Music_xml_node):
1139     pass
1140
1141 class BeatUnit (Music_xml_node):
1142     pass
1143
1144 class BeatUnitDot (Music_xml_node):
1145     pass
1146
1147 class PerMinute (Music_xml_node):
1148     pass
1149
1150
1151
1152 ## need this, not all classes are instantiated
1153 ## for every input file. Only add those classes, that are either directly
1154 ## used by class name or extend Music_xml_node in some way!
1155 class_dict = {
1156         '#comment': Hash_comment,
1157         '#text': Hash_text,
1158         'accidental': Accidental,
1159         'attributes': Attributes,
1160         'barline': Barline,
1161         'bar-style': BarStyle,
1162         'bass': Bass,
1163         'beam' : Beam,
1164         'beats': Beats,
1165         'beat-type': BeatType,
1166         'beat-unit': BeatUnit,
1167         'beat-unit-dot': BeatUnitDot,
1168         'bend' : Bend,
1169         'bracket' : Bracket,
1170         'chord': Chord,
1171         'dashes' : Dashes,
1172         'degree' : ChordModification,
1173         'dot': Dot,
1174         'direction': Direction,
1175         'direction-type': DirType,
1176         'duration': Duration,
1177         'elision': Elision,
1178         'extend': Extend,
1179         'frame': Frame,
1180         'frame-note': Frame_Note,
1181         'figured-bass': FiguredBass,
1182         'glissando': Glissando,
1183         'grace': Grace,
1184         'harmony': Harmony,
1185         'identification': Identification,
1186         'key-alter': KeyAlter,
1187         'key-octave': KeyOctave,
1188         'key-step': KeyStep,
1189         'lyric': Lyric,
1190         'measure': Measure,
1191         'notations': Notations,
1192         'note': Note,
1193         'octave-shift': Octave_shift,
1194         'part': Part,
1195     'part-group': Part_group,
1196         'part-list': Part_list,
1197         'pedal': Pedal,
1198         'per-minute': PerMinute,
1199         'pitch': Pitch,
1200         'rest': Rest,
1201         'root': Root,
1202         'score-part': Score_part,
1203         'slide': Slide,
1204         'slur': Slur,
1205         'staff': Staff,
1206         'syllabic': Syllabic,
1207         'text': Text,
1208         'time-modification': Time_modification,
1209         'tuplet': Tuplet,
1210         'type': Type,
1211         'unpitched': Unpitched,
1212         'wavy-line': Wavy_line,
1213         'wedge': Wedge,
1214         'words': Words,
1215         'work': Work,
1216 }
1217
1218 def name2class_name (name):
1219     name = name.replace ('-', '_')
1220     name = name.replace ('#', 'hash_')
1221     name = name[0].upper() + name[1:].lower()
1222
1223     return str (name)
1224
1225 def get_class (name):
1226     classname = class_dict.get (name)
1227     if classname:
1228         return classname
1229     else:
1230         class_name = name2class_name (name)
1231         klass = new.classobj (class_name, (Music_xml_node,) , {})
1232         class_dict[name] = klass
1233         return klass
1234         
1235 def lxml_demarshal_node (node):
1236     name = node.tag
1237
1238     # Ignore comment nodes, which are also returned by the etree parser!
1239     if name is None or node.__class__.__name__ == "_Comment":
1240         return None
1241     klass = get_class (name)
1242     py_node = klass()
1243     
1244     py_node._original = node
1245     py_node._name = name
1246     py_node._data = node.text
1247     py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1248     py_node._children = filter (lambda x: x, py_node._children)
1249     
1250     for c in py_node._children:
1251         c._parent = py_node
1252
1253     for (k, v) in node.items ():
1254         py_node.__dict__[k] = v
1255         py_node._attribute_dict[k] = v
1256
1257     return py_node
1258
1259 def minidom_demarshal_node (node):
1260     name = node.nodeName
1261
1262     klass = get_class (name)
1263     py_node = klass ()
1264     py_node._name = name
1265     py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1266     for c in py_node._children:
1267         c._parent = py_node
1268
1269     if node.attributes:
1270         for (nm, value) in node.attributes.items ():
1271             py_node.__dict__[nm] = value
1272             py_node._attribute_dict[nm] = value
1273             
1274     py_node._data = None
1275     if node.nodeType == node.TEXT_NODE and node.data:
1276         py_node._data = node.data 
1277
1278     py_node._original = node
1279     return py_node
1280
1281
1282 if __name__  == '__main__':
1283     import lxml.etree
1284         
1285     tree = lxml.etree.parse ('beethoven.xml')
1286     mxl_tree = lxml_demarshal_node (tree.getroot ())
1287     ks = class_dict.keys ()
1288     ks.sort ()
1289     print '\n'.join (ks)