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