]> git.donarmstrong.com Git - lilypond.git/blob - python/musicxml.py
Merge branch 'lilypond/translation' of ssh://jomand@git.sv.gnu.org/srv/git/lilypond
[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 Text (Music_xml_node):
543     pass
544
545 class Lyric (Music_xml_node):
546     def get_number (self):
547         if hasattr (self, 'number'):
548             return self.number
549         else:
550             return -1
551
552 class Musicxml_voice:
553     def __init__ (self):
554         self._elements = []
555         self._staves = {}
556         self._start_staff = None
557         self._lyrics = []
558         self._has_lyrics = False
559
560     def add_element (self, e):
561         self._elements.append (e)
562         if (isinstance (e, Note)
563             and e.get_maybe_exist_typed_child (Staff)):
564             name = e.get_maybe_exist_typed_child (Staff).get_text ()
565
566             if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
567                 self._start_staff = name
568             self._staves[name] = True
569
570         lyrics = e.get_typed_children (Lyric)
571         if not self._has_lyrics:
572           self.has_lyrics = len (lyrics) > 0
573
574         for l in lyrics:
575             nr = l.get_number()
576             if (nr > 0) and not (nr in self._lyrics):
577                 self._lyrics.append (nr)
578
579     def insert (self, idx, e):
580         self._elements.insert (idx, e)
581
582     def get_lyrics_numbers (self):
583         if (len (self._lyrics) == 0) and self._has_lyrics:
584             #only happens if none of the <lyric> tags has a number attribute
585             return [1]
586         else:
587             return self._lyrics
588
589
590 def graces_to_aftergraces (pending_graces):
591     for gr in pending_graces:
592         gr._when = gr._prev_when
593         gr._measure_position = gr._prev_measure_position
594         gr._after_grace = True
595
596
597 class Part (Music_xml_node):
598     def __init__ (self):
599         Music_xml_node.__init__ (self)
600         self._voices = {}
601         self._staff_attributes_dict = {}
602
603     def get_part_list (self):
604         n = self
605         while n and n.get_name() != 'score-partwise':
606             n = n._parent
607
608         return n.get_named_child ('part-list')
609        
610     def interpret (self):
611         """Set durations and starting points."""
612         """The starting point of the very first note is 0!"""
613         
614         part_list = self.get_part_list ()
615         
616         now = Rational (0)
617         factor = Rational (1)
618         attributes_dict = {}
619         attributes_object = None
620         measures = self.get_typed_children (Measure)
621         last_moment = Rational (-1)
622         last_measure_position = Rational (-1)
623         measure_position = Rational (0)
624         measure_start_moment = now
625         is_first_measure = True
626         previous_measure = None
627         # Graces at the end of a measure need to have their position set to the
628         # previous number!
629         pending_graces = []
630         for m in measures:
631             # implicit measures are used for artificial measures, e.g. when
632             # a repeat bar line splits a bar into two halves. In this case,
633             # don't reset the measure position to 0. They are also used for
634             # upbeats (initial value of 0 fits these, too).
635             # Also, don't reset the measure position at the end of the loop,
636             # but rather when starting the next measure (since only then do we
637             # know if the next measure is implicit and continues that measure)
638             if not m.is_implicit ():
639                 # Warn about possibly overfull measures and reset the position
640                 if attributes_object and previous_measure and previous_measure.partial == 0:
641                     length = attributes_object.get_measure_length ()
642                     new_now = measure_start_moment + length
643                     if now <> new_now:
644                         problem = 'incomplete'
645                         if now > new_now:
646                             problem = 'overfull'
647                         ## only for verbose operation.
648                         if problem <> 'incomplete' and previous_measure:
649                             previous_measure.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
650                     now = new_now
651                 measure_start_moment = now
652                 measure_position = Rational (0)
653
654             for n in m.get_all_children ():
655                 # figured bass has a duration, but applies to the next note
656                 # and should not change the current measure position!
657                 if isinstance (n, FiguredBass):
658                     n._divisions = factor.denominator ()
659                     n._when = now
660                     n._measure_position = measure_position
661                     continue
662
663                 if isinstance (n, Hash_text):
664                     continue
665                 dur = Rational (0)
666
667                 if n.__class__ == Attributes:
668                     n.set_attributes_from_previous (attributes_dict)
669                     n.read_self ()
670                     attributes_dict = n._dict.copy ()
671                     attributes_object = n
672                     
673                     factor = Rational (1,
674                                        int (attributes_dict.get ('divisions').get_text ()))
675
676                 
677                 if (n.get_maybe_exist_typed_child (Duration)):
678                     mxl_dur = n.get_maybe_exist_typed_child (Duration)
679                     dur = mxl_dur.get_length () * factor
680                     
681                     if n.get_name() == 'backup':
682                         dur = - dur
683                         # reset all graces before the backup to after-graces:
684                         graces_to_aftergraces (pending_graces)
685                         pending_graces = []
686                     if n.get_maybe_exist_typed_child (Grace):
687                         dur = Rational (0)
688
689                     rest = n.get_maybe_exist_typed_child (Rest)
690                     if (rest
691                         and attributes_object
692                         and attributes_object.get_measure_length () == dur):
693
694                         rest._is_whole_measure = True
695
696                 if (dur > Rational (0)
697                     and n.get_maybe_exist_typed_child (Chord)):
698                     now = last_moment
699                     measure_position = last_measure_position
700
701                 n._when = now
702                 n._measure_position = measure_position
703
704                 # For all grace notes, store the previous note,  in case need
705                 # to turn the grace note into an after-grace later on!
706                 if isinstance(n, Note) and n.is_grace ():
707                     n._prev_when = last_moment
708                     n._prev_measure_position = last_measure_position
709                 # After-graces are placed at the same position as the previous note
710                 if isinstance(n, Note) and  n.is_after_grace ():
711                     # TODO: We should do the same for grace notes at the end of 
712                     # a measure with no following note!!!
713                     n._when = last_moment
714                     n._measure_position = last_measure_position
715                 elif isinstance(n, Note) and n.is_grace ():
716                     pending_graces.append (n)
717                 elif (dur > Rational (0)):
718                     pending_graces = [];
719
720                 n._duration = dur
721                 if dur > Rational (0):
722                     last_moment = now
723                     last_measure_position = measure_position
724                     now += dur
725                     measure_position += dur
726                 elif dur < Rational (0):
727                     # backup element, reset measure position
728                     now += dur
729                     measure_position += dur
730                     if measure_position < 0:
731                         # backup went beyond the measure start => reset to 0
732                         now -= measure_position
733                         measure_position = 0
734                     last_moment = now
735                     last_measure_position = measure_position
736                 if n._name == 'note':
737                     instrument = n.get_maybe_exist_named_child ('instrument')
738                     if instrument:
739                         n.instrument_name = part_list.get_instrument (instrument.id)
740
741             # reset all graces at the end of the measure to after-graces:
742             graces_to_aftergraces (pending_graces)
743             pending_graces = []
744             # Incomplete first measures are not padded, but registered as partial
745             if is_first_measure:
746                 is_first_measure = False
747                 # upbeats are marked as implicit measures
748                 if attributes_object and m.is_implicit ():
749                     length = attributes_object.get_measure_length ()
750                     measure_end = measure_start_moment + length
751                     if measure_end <> now:
752                         m.partial = now
753             previous_measure = m
754
755     # modify attributes so that only those applying to the given staff remain
756     def extract_attributes_for_staff (part, attr, staff):
757         attributes = copy.copy (attr)
758         attributes._children = [];
759         attributes._dict = attr._dict.copy ()
760         attributes._original_tag = attr
761         # copy only the relevant children over for the given staff
762         for c in attr._children:
763             if (not (hasattr (c, 'number') and (c.number != staff)) and
764                 not (isinstance (c, Hash_text))):
765                 attributes._children.append (c)
766         if not attributes._children:
767             return None
768         else:
769             return attributes
770
771     def extract_voices (part):
772         voices = {}
773         measures = part.get_typed_children (Measure)
774         elements = []
775         for m in measures:
776             if m.partial > 0:
777                 elements.append (Partial (m.partial))
778             elements.extend (m.get_all_children ())
779         # make sure we know all voices already so that dynamics, clefs, etc.
780         # can be assigned to the correct voices
781         voice_to_staff_dict = {}
782         for n in elements:
783             voice_id = n.get_maybe_exist_named_child (u'voice')
784             vid = None
785             if voice_id:
786                 vid = voice_id.get_text ()
787             elif isinstance (n, Note):
788                 vid = "None"
789
790             staff_id = n.get_maybe_exist_named_child (u'staff')
791             sid = None
792             if staff_id:
793                 sid = staff_id.get_text ()
794             else:
795                 sid = "None"
796             if vid and not voices.has_key (vid):
797                 voices[vid] = Musicxml_voice()
798             if vid and sid and not n.get_maybe_exist_typed_child (Grace):
799                 if not voice_to_staff_dict.has_key (vid):
800                     voice_to_staff_dict[vid] = sid
801         # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
802         # need to assign staff-assigned objects like clefs, times, etc. to
803         # all the correct voices. This will never work entirely correct due
804         # to staff-switches, but that's the best we can do!
805         staff_to_voice_dict = {}
806         for (v,s) in voice_to_staff_dict.items ():
807             if not staff_to_voice_dict.has_key (s):
808                 staff_to_voice_dict[s] = [v]
809             else:
810                 staff_to_voice_dict[s].append (v)
811
812
813         start_attr = None
814         assign_to_next_note = []
815         id = None
816         for n in elements:
817             voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
818             if voice_id:
819                 id = voice_id.get_text ()
820             else:
821                 id = "None"
822
823             # We don't need backup/forward any more, since we have already 
824             # assigned the correct onset times. 
825             # TODO: Let Grouping through. Also: link, print, bokmark sound
826             if not (isinstance (n, Note) or isinstance (n, Attributes) or
827                     isinstance (n, Direction) or isinstance (n, Partial) or
828                     isinstance (n, Barline) or isinstance (n, Harmony) or
829                     isinstance (n, FiguredBass) ):
830                 continue
831
832             if isinstance (n, Attributes) and not start_attr:
833                 start_attr = n
834                 continue
835
836             if isinstance (n, Attributes):
837                 # assign these only to the voices they really belongs to!
838                 for (s, vids) in staff_to_voice_dict.items ():
839                     staff_attributes = part.extract_attributes_for_staff (n, s)
840                     if staff_attributes:
841                         for v in vids:
842                             voices[v].add_element (staff_attributes)
843                 continue
844
845             if isinstance (n, Partial) or isinstance (n, Barline):
846                 for v in voices.keys ():
847                     voices[v].add_element (n)
848                 continue
849
850             if isinstance (n, Direction):
851                 staff_id = n.get_maybe_exist_named_child (u'staff')
852                 if staff_id:
853                     staff_id = staff_id.get_text ()
854                 if staff_id:
855                     dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
856                 else:
857                     dir_voices = voices.keys ()
858                 for v in dir_voices:
859                     voices[v].add_element (n)
860                 continue
861
862             if isinstance (n, Harmony) or isinstance (n, FiguredBass):
863                 # store the harmony or figured bass element until we encounter 
864                 # the next note and assign it only to that one voice.
865                 assign_to_next_note.append (n)
866                 continue
867
868             if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
869                 #Skip this note. 
870                 pass
871             else:
872                 for i in assign_to_next_note:
873                     voices[id].add_element (i)
874                 assign_to_next_note = []
875                 voices[id].add_element (n)
876
877         # Assign all remaining elements from assign_to_next_note to the voice
878         # of the previous note:
879         for i in assign_to_next_note:
880             voices[id].add_element (i)
881         assign_to_next_note = []
882
883         if start_attr:
884             for (s, vids) in staff_to_voice_dict.items ():
885                 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
886                 staff_attributes.read_self ()
887                 part._staff_attributes_dict[s] = staff_attributes
888                 for v in vids:
889                     voices[v].insert (0, staff_attributes)
890                     voices[v]._elements[0].read_self()
891
892         part._voices = voices
893
894     def get_voices (self):
895         return self._voices
896     def get_staff_attributes (self):
897         return self._staff_attributes_dict
898
899 class Notations (Music_xml_node):
900     def get_tie (self):
901         ts = self.get_named_children ('tied')
902         starts = [t for t in ts if t.type == 'start']
903         if starts:
904             return starts[0]
905         else:
906             return None
907
908     def get_tuplets (self):
909         return self.get_typed_children (Tuplet)
910
911 class Time_modification(Music_xml_node):
912     def get_fraction (self):
913         b = self.get_maybe_exist_named_child ('actual-notes')
914         a = self.get_maybe_exist_named_child ('normal-notes')
915         return (int(a.get_text ()), int (b.get_text ()))
916
917     def get_normal_type (self):
918         tuplet_type = self.get_maybe_exist_named_child ('normal-type')
919         if tuplet_type:
920             dots = self.get_named_children ('normal-dot')
921             log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
922             return (log , len (dots))
923         else:
924             return None
925
926
927 class Accidental (Music_xml_node):
928     def __init__ (self):
929         Music_xml_node.__init__ (self)
930         self.editorial = False
931         self.cautionary = False
932
933 class Music_xml_spanner (Music_xml_node):
934     def get_type (self):
935         if hasattr (self, 'type'):
936             return self.type
937         else:
938             return 0
939     def get_size (self):
940         if hasattr (self, 'size'):
941             return string.atoi (self.size)
942         else:
943             return 0
944
945 class Wedge (Music_xml_spanner):
946     pass
947
948 class Tuplet (Music_xml_spanner):
949     def duration_info_from_tuplet_note (self, tuplet_note):
950         tuplet_type = tuplet_note.get_maybe_exist_named_child ('tuplet-type')
951         if tuplet_type:
952             dots = tuplet_note.get_named_children ('tuplet-dot')
953             log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
954             return (log, len (dots))
955         else:
956             return None
957
958     # Return tuplet note type as (log, dots)
959     def get_normal_type (self):
960         tuplet = self.get_maybe_exist_named_child ('tuplet-normal')
961         if tuplet:
962             return self.duration_info_from_tuplet_note (tuplet)
963         else:
964             return None
965
966     def get_actual_type (self):
967         tuplet = self.get_maybe_exist_named_child ('tuplet-actual')
968         if tuplet:
969             return self.duration_info_from_tuplet_note (tuplet)
970         else:
971             return None
972
973     def get_tuplet_note_count (self, tuplet_note):
974         if tuplet_note:
975             tuplet_nr = tuplet_note.get_maybe_exist_named_child ('tuplet-number')
976             if tuplet_nr: 
977                 return int (tuplet_nr.get_text ())
978         return None
979     def get_normal_nr (self):
980         return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-normal'))
981     def get_actual_nr (self):
982         return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-actual'))
983
984 class Bracket (Music_xml_spanner):
985     pass
986
987 class Dashes (Music_xml_spanner):
988     pass
989
990 class Slur (Music_xml_spanner):
991     def get_type (self):
992         return self.type
993
994 class Beam (Music_xml_spanner):
995     def get_type (self):
996         return self.get_text ()
997     def is_primary (self):
998         return self.number == "1"
999
1000 class Wavy_line (Music_xml_spanner):
1001     pass
1002     
1003 class Pedal (Music_xml_spanner):
1004     pass
1005
1006 class Glissando (Music_xml_spanner):
1007     pass
1008
1009 class Slide (Music_xml_spanner):
1010     pass
1011
1012 class Octave_shift (Music_xml_spanner):
1013     # default is 8 for the octave-shift!
1014     def get_size (self):
1015         if hasattr (self, 'size'):
1016             return string.atoi (self.size)
1017         else:
1018             return 8
1019
1020 class Chord (Music_xml_node):
1021     pass
1022
1023 class Dot (Music_xml_node):
1024     pass
1025
1026 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
1027 # for the inner <rest> element, not the whole rest block.
1028 class Rest (Music_xml_node):
1029     def __init__ (self):
1030         Music_xml_node.__init__ (self)
1031         self._is_whole_measure = False
1032     def is_whole_measure (self):
1033         return self._is_whole_measure
1034     def get_step (self):
1035         ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
1036         if ch:
1037             step = ch.get_text ().strip ()
1038             return step
1039         else:
1040             return None
1041     def get_octave (self):
1042         ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
1043         if ch:
1044             step = ch.get_text ().strip ()
1045             return int (step)
1046         else:
1047             return None
1048
1049 class Type (Music_xml_node):
1050     pass
1051 class Grace (Music_xml_node):
1052     pass
1053 class Staff (Music_xml_node):
1054     pass
1055
1056 class Direction (Music_xml_node):
1057     pass
1058 class DirType (Music_xml_node):
1059     pass
1060
1061 class Bend (Music_xml_node):
1062     def bend_alter (self):
1063         alter = self.get_maybe_exist_named_child ('bend-alter')
1064         return interpret_alter_element (alter)
1065
1066 class Words (Music_xml_node):
1067     pass
1068
1069 class Harmony (Music_xml_node):
1070     pass
1071
1072 class ChordPitch (Music_xml_node):
1073     def step_class_name (self):
1074         return u'root-step'
1075     def alter_class_name (self):
1076         return u'root-alter'
1077     def get_step (self):
1078         ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
1079         return ch.get_text ().strip ()
1080     def get_alteration (self):
1081         ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
1082         return interpret_alter_element (ch)
1083
1084 class Root (ChordPitch):
1085     pass
1086
1087 class Bass (ChordPitch):
1088     def step_class_name (self):
1089         return u'bass-step'
1090     def alter_class_name (self):
1091         return u'bass-alter'
1092
1093 class ChordModification (Music_xml_node):
1094     def get_type (self):
1095         ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
1096         return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
1097     def get_value (self):
1098         ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
1099         value = 0
1100         if ch:
1101             value = int (ch.get_text ().strip ())
1102         return value
1103     def get_alter (self):
1104         ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
1105         return interpret_alter_element (ch)
1106
1107
1108 class Frame (Music_xml_node):
1109     def get_frets (self):
1110         return self.get_named_child_value_number ('frame-frets', 4)
1111     def get_strings (self):
1112         return self.get_named_child_value_number ('frame-strings', 6)
1113     def get_first_fret (self):
1114         return self.get_named_child_value_number ('first-fret', 1)
1115
1116 class Frame_Note (Music_xml_node):
1117     def get_string (self):
1118         return self.get_named_child_value_number ('string', 1)
1119     def get_fret (self):
1120         return self.get_named_child_value_number ('fret', 0)
1121     def get_fingering (self):
1122         return self.get_named_child_value_number ('fingering', -1)
1123     def get_barre (self):
1124         n = self.get_maybe_exist_named_child ('barre')
1125         if n:
1126             return getattr (n, 'type', '')
1127         else:
1128             return ''
1129
1130 class FiguredBass (Music_xml_node):
1131     pass
1132
1133 class Beats (Music_xml_node):
1134     pass
1135
1136 class BeatType (Music_xml_node):
1137     pass
1138
1139 class BeatUnit (Music_xml_node):
1140     pass
1141
1142 class BeatUnitDot (Music_xml_node):
1143     pass
1144
1145 class PerMinute (Music_xml_node):
1146     pass
1147
1148
1149
1150 ## need this, not all classes are instantiated
1151 ## for every input file. Only add those classes, that are either directly
1152 ## used by class name or extend Music_xml_node in some way!
1153 class_dict = {
1154         '#comment': Hash_comment,
1155         '#text': Hash_text,
1156         'accidental': Accidental,
1157         'attributes': Attributes,
1158         'barline': Barline,
1159         'bar-style': BarStyle,
1160         'bass': Bass,
1161         'beam' : Beam,
1162         'beats': Beats,
1163         'beat-type': BeatType,
1164         'beat-unit': BeatUnit,
1165         'beat-unit-dot': BeatUnitDot,
1166         'bend' : Bend,
1167         'bracket' : Bracket,
1168         'chord': Chord,
1169         'dashes' : Dashes,
1170         'degree' : ChordModification,
1171         'dot': Dot,
1172         'direction': Direction,
1173         'direction-type': DirType,
1174         'duration': Duration,
1175         'elision': Elision,
1176         'frame': Frame,
1177         'frame-note': Frame_Note,
1178         'figured-bass': FiguredBass,
1179         'glissando': Glissando,
1180         'grace': Grace,
1181         'harmony': Harmony,
1182         'identification': Identification,
1183         'key-alter': KeyAlter,
1184         'key-octave': KeyOctave,
1185         'key-step': KeyStep,
1186         'lyric': Lyric,
1187         'measure': Measure,
1188         'notations': Notations,
1189         'note': Note,
1190         'octave-shift': Octave_shift,
1191         'part': Part,
1192     'part-group': Part_group,
1193         'part-list': Part_list,
1194         'pedal': Pedal,
1195         'per-minute': PerMinute,
1196         'pitch': Pitch,
1197         'rest': Rest,
1198         'root': Root,
1199         'score-part': Score_part,
1200         'slide': Slide,
1201         'slur': Slur,
1202         'staff': Staff,
1203         'syllabic': Syllabic,
1204         'text': Text,
1205         'time-modification': Time_modification,
1206         'tuplet': Tuplet,
1207         'type': Type,
1208         'unpitched': Unpitched,
1209         'wavy-line': Wavy_line,
1210         'wedge': Wedge,
1211         'words': Words,
1212         'work': Work,
1213 }
1214
1215 def name2class_name (name):
1216     name = name.replace ('-', '_')
1217     name = name.replace ('#', 'hash_')
1218     name = name[0].upper() + name[1:].lower()
1219
1220     return str (name)
1221
1222 def get_class (name):
1223     classname = class_dict.get (name)
1224     if classname:
1225         return classname
1226     else:
1227         class_name = name2class_name (name)
1228         klass = new.classobj (class_name, (Music_xml_node,) , {})
1229         class_dict[name] = klass
1230         return klass
1231         
1232 def lxml_demarshal_node (node):
1233     name = node.tag
1234
1235     # Ignore comment nodes, which are also returned by the etree parser!
1236     if name is None or node.__class__.__name__ == "_Comment":
1237         return None
1238     klass = get_class (name)
1239     py_node = klass()
1240     
1241     py_node._original = node
1242     py_node._name = name
1243     py_node._data = node.text
1244     py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1245     py_node._children = filter (lambda x: x, py_node._children)
1246     
1247     for c in py_node._children:
1248         c._parent = py_node
1249
1250     for (k, v) in node.items ():
1251         py_node.__dict__[k] = v
1252         py_node._attribute_dict[k] = v
1253
1254     return py_node
1255
1256 def minidom_demarshal_node (node):
1257     name = node.nodeName
1258
1259     klass = get_class (name)
1260     py_node = klass ()
1261     py_node._name = name
1262     py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1263     for c in py_node._children:
1264         c._parent = py_node
1265
1266     if node.attributes:
1267         for (nm, value) in node.attributes.items ():
1268             py_node.__dict__[nm] = value
1269             py_node._attribute_dict[nm] = value
1270             
1271     py_node._data = None
1272     if node.nodeType == node.TEXT_NODE and node.data:
1273         py_node._data = node.data 
1274
1275     py_node._original = node
1276     return py_node
1277
1278
1279 if __name__  == '__main__':
1280     import lxml.etree
1281         
1282     tree = lxml.etree.parse ('beethoven.xml')
1283     mxl_tree = lxml_demarshal_node (tree.getroot ())
1284     ks = class_dict.keys ()
1285     ks.sort ()
1286     print '\n'.join (ks)