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