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