]> git.donarmstrong.com Git - lilypond.git/blob - python/musicxml.py
MusicXML: If staff number is given in an element, make it work even if other elements...
[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         if staff == "None":
766             staff = "1"
767         for c in attr._children:
768             if (not (hasattr (c, 'number') and (c.number != staff)) and
769                 not (isinstance (c, Hash_text))):
770                 attributes._children.append (c)
771         if not attributes._children:
772             return None
773         else:
774             return attributes
775
776     def extract_voices (part):
777         voices = {}
778         measures = part.get_typed_children (Measure)
779         elements = []
780         for m in measures:
781             if m.partial > 0:
782                 elements.append (Partial (m.partial))
783             elements.extend (m.get_all_children ())
784         # make sure we know all voices already so that dynamics, clefs, etc.
785         # can be assigned to the correct voices
786         voice_to_staff_dict = {}
787         for n in elements:
788             voice_id = n.get_maybe_exist_named_child (u'voice')
789             vid = None
790             if voice_id:
791                 vid = voice_id.get_text ()
792             elif isinstance (n, Note):
793                 # TODO: Check whether we shall really use "None" here, or
794                 #       rather use "1" as the default?
795                 vid = "None"
796
797             staff_id = n.get_maybe_exist_named_child (u'staff')
798             sid = None
799             if staff_id:
800                 sid = staff_id.get_text ()
801             else:
802                 # TODO: Check whether we shall really use "None" here, or
803                 #       rather use "1" as the default?
804                 #       If this is changed, need to change the corresponding
805                 #       check in extract_attributes_for_staff, too.
806                 sid = "None"
807             if vid and not voices.has_key (vid):
808                 voices[vid] = Musicxml_voice()
809             if vid and sid and not n.get_maybe_exist_typed_child (Grace):
810                 if not voice_to_staff_dict.has_key (vid):
811                     voice_to_staff_dict[vid] = sid
812         # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
813         # need to assign staff-assigned objects like clefs, times, etc. to
814         # all the correct voices. This will never work entirely correct due
815         # to staff-switches, but that's the best we can do!
816         staff_to_voice_dict = {}
817         for (v,s) in voice_to_staff_dict.items ():
818             if not staff_to_voice_dict.has_key (s):
819                 staff_to_voice_dict[s] = [v]
820             else:
821                 staff_to_voice_dict[s].append (v)
822
823
824         start_attr = None
825         assign_to_next_note = []
826         id = None
827         for n in elements:
828             voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
829             if voice_id:
830                 id = voice_id.get_text ()
831             else:
832                 id = "None"
833
834             # We don't need backup/forward any more, since we have already 
835             # assigned the correct onset times. 
836             # TODO: Let Grouping through. Also: link, print, bokmark sound
837             if not (isinstance (n, Note) or isinstance (n, Attributes) or
838                     isinstance (n, Direction) or isinstance (n, Partial) or
839                     isinstance (n, Barline) or isinstance (n, Harmony) or
840                     isinstance (n, FiguredBass) or isinstance (n, Print)):
841                 continue
842
843             if isinstance (n, Attributes) and not start_attr:
844                 start_attr = n
845                 continue
846
847             if isinstance (n, Attributes):
848                 # assign these only to the voices they really belong to!
849                 for (s, vids) in staff_to_voice_dict.items ():
850                     staff_attributes = part.extract_attributes_for_staff (n, s)
851                     if staff_attributes:
852                         for v in vids:
853                             voices[v].add_element (staff_attributes)
854                 continue
855
856             if isinstance (n, Partial) or isinstance (n, Barline) or isinstance (n, Print):
857                 for v in voices.keys ():
858                     voices[v].add_element (n)
859                 continue
860
861             if isinstance (n, Direction):
862                 staff_id = n.get_maybe_exist_named_child (u'staff')
863                 if staff_id:
864                     staff_id = staff_id.get_text ()
865                 if staff_id:
866                     dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
867                 else:
868                     dir_voices = voices.keys ()
869                 for v in dir_voices:
870                     voices[v].add_element (n)
871                 continue
872
873             if isinstance (n, Harmony) or isinstance (n, FiguredBass):
874                 # store the harmony or figured bass element until we encounter 
875                 # the next note and assign it only to that one voice.
876                 assign_to_next_note.append (n)
877                 continue
878
879             if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
880                 #Skip this note. 
881                 pass
882             else:
883                 for i in assign_to_next_note:
884                     voices[id].add_element (i)
885                 assign_to_next_note = []
886                 voices[id].add_element (n)
887
888         # Assign all remaining elements from assign_to_next_note to the voice
889         # of the previous note:
890         for i in assign_to_next_note:
891             voices[id].add_element (i)
892         assign_to_next_note = []
893
894         if start_attr:
895             for (s, vids) in staff_to_voice_dict.items ():
896                 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
897                 staff_attributes.read_self ()
898                 part._staff_attributes_dict[s] = staff_attributes
899                 for v in vids:
900                     voices[v].insert (0, staff_attributes)
901                     voices[v]._elements[0].read_self()
902
903         part._voices = voices
904
905     def get_voices (self):
906         return self._voices
907     def get_staff_attributes (self):
908         return self._staff_attributes_dict
909
910 class Notations (Music_xml_node):
911     def get_tie (self):
912         ts = self.get_named_children ('tied')
913         starts = [t for t in ts if t.type == 'start']
914         if starts:
915             return starts[0]
916         else:
917             return None
918
919     def get_tuplets (self):
920         return self.get_typed_children (Tuplet)
921
922 class Time_modification(Music_xml_node):
923     def get_fraction (self):
924         b = self.get_maybe_exist_named_child ('actual-notes')
925         a = self.get_maybe_exist_named_child ('normal-notes')
926         return (int(a.get_text ()), int (b.get_text ()))
927
928     def get_normal_type (self):
929         tuplet_type = self.get_maybe_exist_named_child ('normal-type')
930         if tuplet_type:
931             dots = self.get_named_children ('normal-dot')
932             log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
933             return (log , len (dots))
934         else:
935             return None
936
937
938 class Accidental (Music_xml_node):
939     def __init__ (self):
940         Music_xml_node.__init__ (self)
941         self.editorial = False
942         self.cautionary = False
943
944 class Music_xml_spanner (Music_xml_node):
945     def get_type (self):
946         if hasattr (self, 'type'):
947             return self.type
948         else:
949             return 0
950     def get_size (self):
951         if hasattr (self, 'size'):
952             return string.atoi (self.size)
953         else:
954             return 0
955
956 class Wedge (Music_xml_spanner):
957     pass
958
959 class Tuplet (Music_xml_spanner):
960     def duration_info_from_tuplet_note (self, tuplet_note):
961         tuplet_type = tuplet_note.get_maybe_exist_named_child ('tuplet-type')
962         if tuplet_type:
963             dots = tuplet_note.get_named_children ('tuplet-dot')
964             log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
965             return (log, len (dots))
966         else:
967             return None
968
969     # Return tuplet note type as (log, dots)
970     def get_normal_type (self):
971         tuplet = self.get_maybe_exist_named_child ('tuplet-normal')
972         if tuplet:
973             return self.duration_info_from_tuplet_note (tuplet)
974         else:
975             return None
976
977     def get_actual_type (self):
978         tuplet = self.get_maybe_exist_named_child ('tuplet-actual')
979         if tuplet:
980             return self.duration_info_from_tuplet_note (tuplet)
981         else:
982             return None
983
984     def get_tuplet_note_count (self, tuplet_note):
985         if tuplet_note:
986             tuplet_nr = tuplet_note.get_maybe_exist_named_child ('tuplet-number')
987             if tuplet_nr: 
988                 return int (tuplet_nr.get_text ())
989         return None
990     def get_normal_nr (self):
991         return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-normal'))
992     def get_actual_nr (self):
993         return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-actual'))
994
995 class Bracket (Music_xml_spanner):
996     pass
997
998 class Dashes (Music_xml_spanner):
999     pass
1000
1001 class Slur (Music_xml_spanner):
1002     def get_type (self):
1003         return self.type
1004
1005 class Beam (Music_xml_spanner):
1006     def get_type (self):
1007         return self.get_text ()
1008     def is_primary (self):
1009         if hasattr (self, 'number'):
1010             return self.number == "1"
1011         else:
1012             return True
1013
1014 class Wavy_line (Music_xml_spanner):
1015     pass
1016     
1017 class Pedal (Music_xml_spanner):
1018     pass
1019
1020 class Glissando (Music_xml_spanner):
1021     pass
1022
1023 class Slide (Music_xml_spanner):
1024     pass
1025
1026 class Octave_shift (Music_xml_spanner):
1027     # default is 8 for the octave-shift!
1028     def get_size (self):
1029         if hasattr (self, 'size'):
1030             return string.atoi (self.size)
1031         else:
1032             return 8
1033
1034 class Chord (Music_xml_node):
1035     pass
1036
1037 class Dot (Music_xml_node):
1038     pass
1039
1040 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
1041 # for the inner <rest> element, not the whole rest block.
1042 class Rest (Music_xml_node):
1043     def __init__ (self):
1044         Music_xml_node.__init__ (self)
1045         self._is_whole_measure = False
1046     def is_whole_measure (self):
1047         return self._is_whole_measure
1048     def get_step (self):
1049         ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
1050         if ch:
1051             return ch.get_text ().strip ()
1052         else:
1053             return None
1054     def get_octave (self):
1055         ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
1056         if ch:
1057             oct = ch.get_text ().strip ()
1058             return int (oct)
1059         else:
1060             return None
1061
1062 class Type (Music_xml_node):
1063     pass
1064 class Grace (Music_xml_node):
1065     pass
1066 class Staff (Music_xml_node):
1067     pass
1068
1069 class Direction (Music_xml_node):
1070     pass
1071 class DirType (Music_xml_node):
1072     pass
1073
1074 class Bend (Music_xml_node):
1075     def bend_alter (self):
1076         alter = self.get_maybe_exist_named_child ('bend-alter')
1077         return interpret_alter_element (alter)
1078
1079 class Words (Music_xml_node):
1080     pass
1081
1082 class Harmony (Music_xml_node):
1083     pass
1084
1085 class ChordPitch (Music_xml_node):
1086     def step_class_name (self):
1087         return u'root-step'
1088     def alter_class_name (self):
1089         return u'root-alter'
1090     def get_step (self):
1091         ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
1092         return ch.get_text ().strip ()
1093     def get_alteration (self):
1094         ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
1095         return interpret_alter_element (ch)
1096
1097 class Root (ChordPitch):
1098     pass
1099
1100 class Bass (ChordPitch):
1101     def step_class_name (self):
1102         return u'bass-step'
1103     def alter_class_name (self):
1104         return u'bass-alter'
1105
1106 class ChordModification (Music_xml_node):
1107     def get_type (self):
1108         ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
1109         return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
1110     def get_value (self):
1111         ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
1112         value = 0
1113         if ch:
1114             value = int (ch.get_text ().strip ())
1115         return value
1116     def get_alter (self):
1117         ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
1118         return interpret_alter_element (ch)
1119
1120
1121 class Frame (Music_xml_node):
1122     def get_frets (self):
1123         return self.get_named_child_value_number ('frame-frets', 4)
1124     def get_strings (self):
1125         return self.get_named_child_value_number ('frame-strings', 6)
1126     def get_first_fret (self):
1127         return self.get_named_child_value_number ('first-fret', 1)
1128
1129 class Frame_Note (Music_xml_node):
1130     def get_string (self):
1131         return self.get_named_child_value_number ('string', 1)
1132     def get_fret (self):
1133         return self.get_named_child_value_number ('fret', 0)
1134     def get_fingering (self):
1135         return self.get_named_child_value_number ('fingering', -1)
1136     def get_barre (self):
1137         n = self.get_maybe_exist_named_child ('barre')
1138         if n:
1139             return getattr (n, 'type', '')
1140         else:
1141             return ''
1142
1143 class FiguredBass (Music_xml_node):
1144     pass
1145
1146 class Beats (Music_xml_node):
1147     pass
1148
1149 class BeatType (Music_xml_node):
1150     pass
1151
1152 class BeatUnit (Music_xml_node):
1153     pass
1154
1155 class BeatUnitDot (Music_xml_node):
1156     pass
1157
1158 class PerMinute (Music_xml_node):
1159     pass
1160
1161 class Print (Music_xml_node):
1162     pass
1163
1164
1165
1166 ## need this, not all classes are instantiated
1167 ## for every input file. Only add those classes, that are either directly
1168 ## used by class name or extend Music_xml_node in some way!
1169 class_dict = {
1170         '#comment': Hash_comment,
1171         '#text': Hash_text,
1172         'accidental': Accidental,
1173         'attributes': Attributes,
1174         'barline': Barline,
1175         'bar-style': BarStyle,
1176         'bass': Bass,
1177         'beam' : Beam,
1178         'beats': Beats,
1179         'beat-type': BeatType,
1180         'beat-unit': BeatUnit,
1181         'beat-unit-dot': BeatUnitDot,
1182         'bend' : Bend,
1183         'bracket' : Bracket,
1184         'chord': Chord,
1185         'dashes' : Dashes,
1186         'degree' : ChordModification,
1187         'dot': Dot,
1188         'direction': Direction,
1189         'direction-type': DirType,
1190         'duration': Duration,
1191         'elision': Elision,
1192         'extend': Extend,
1193         'frame': Frame,
1194         'frame-note': Frame_Note,
1195         'figured-bass': FiguredBass,
1196         'glissando': Glissando,
1197         'grace': Grace,
1198         'harmony': Harmony,
1199         'identification': Identification,
1200         'key-alter': KeyAlter,
1201         'key-octave': KeyOctave,
1202         'key-step': KeyStep,
1203         'lyric': Lyric,
1204         'measure': Measure,
1205         'notations': Notations,
1206         'note': Note,
1207         'octave-shift': Octave_shift,
1208         'part': Part,
1209     'part-group': Part_group,
1210         'part-list': Part_list,
1211         'pedal': Pedal,
1212         'per-minute': PerMinute,
1213         'pitch': Pitch,
1214         'print': Print,
1215         'rest': Rest,
1216         'root': Root,
1217         'score-part': Score_part,
1218         'slide': Slide,
1219         'slur': Slur,
1220         'staff': Staff,
1221         'syllabic': Syllabic,
1222         'text': Text,
1223         'time-modification': Time_modification,
1224         'tuplet': Tuplet,
1225         'type': Type,
1226         'unpitched': Unpitched,
1227         'wavy-line': Wavy_line,
1228         'wedge': Wedge,
1229         'words': Words,
1230         'work': Work,
1231 }
1232
1233 def name2class_name (name):
1234     name = name.replace ('-', '_')
1235     name = name.replace ('#', 'hash_')
1236     name = name[0].upper() + name[1:].lower()
1237
1238     return str (name)
1239
1240 def get_class (name):
1241     classname = class_dict.get (name)
1242     if classname:
1243         return classname
1244     else:
1245         class_name = name2class_name (name)
1246         klass = new.classobj (class_name, (Music_xml_node,) , {})
1247         class_dict[name] = klass
1248         return klass
1249         
1250 def lxml_demarshal_node (node):
1251     name = node.tag
1252
1253     # Ignore comment nodes, which are also returned by the etree parser!
1254     if name is None or node.__class__.__name__ == "_Comment":
1255         return None
1256     klass = get_class (name)
1257     py_node = klass()
1258     
1259     py_node._original = node
1260     py_node._name = name
1261     py_node._data = node.text
1262     py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1263     py_node._children = filter (lambda x: x, py_node._children)
1264     
1265     for c in py_node._children:
1266         c._parent = py_node
1267
1268     for (k, v) in node.items ():
1269         py_node.__dict__[k] = v
1270         py_node._attribute_dict[k] = v
1271
1272     return py_node
1273
1274 def minidom_demarshal_node (node):
1275     name = node.nodeName
1276
1277     klass = get_class (name)
1278     py_node = klass ()
1279     py_node._name = name
1280     py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1281     for c in py_node._children:
1282         c._parent = py_node
1283
1284     if node.attributes:
1285         for (nm, value) in node.attributes.items ():
1286             py_node.__dict__[nm] = value
1287             py_node._attribute_dict[nm] = value
1288             
1289     py_node._data = None
1290     if node.nodeType == node.TEXT_NODE and node.data:
1291         py_node._data = node.data
1292
1293     py_node._original = node
1294     return py_node
1295
1296
1297 if __name__  == '__main__':
1298     import lxml.etree
1299         
1300     tree = lxml.etree.parse ('beethoven.xml')
1301     mxl_tree = lxml_demarshal_node (tree.getroot ())
1302     ks = class_dict.keys ()
1303     ks.sort ()
1304     print '\n'.join (ks)