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