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