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