]> git.donarmstrong.com Git - lilypond.git/blob - python/musicxml.py
MusicXML: Implement after-grace notes
[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_factor (self):
406         return 1
407
408     def get_pitches (self):
409         return self.get_typed_children (get_class (u'pitch'))
410
411 class Part_list (Music_xml_node):
412     def __init__ (self):
413         Music_xml_node.__init__ (self)
414         self._id_instrument_name_dict = {}
415         
416     def generate_id_instrument_dict (self):
417
418         ## not empty to make sure this happens only once.
419         mapping = {1: 1}
420         for score_part in self.get_named_children ('score-part'):
421             for instr in score_part.get_named_children ('score-instrument'):
422                 id = instr.id
423                 name = instr.get_named_child ("instrument-name")
424                 mapping[id] = name.get_text ()
425
426         self._id_instrument_name_dict = mapping
427
428     def get_instrument (self, id):
429         if not self._id_instrument_name_dict:
430             self.generate_id_instrument_dict()
431
432         instrument_name = self._id_instrument_name_dict.get (id)
433         if instrument_name:
434             return instrument_name
435         else:
436             ly.stderr_write (_ ("Unable to find instrument for ID=%s\n") % id)
437             return "Grand Piano"
438
439 class Part_group (Music_xml_node):
440     pass
441 class Score_part (Music_xml_node):
442     pass
443         
444 class Measure (Music_xml_node):
445     def __init__ (self):
446         Music_xml_node.__init__ (self)
447         self.partial = 0
448     def is_implicit (self):
449         return hasattr (self, 'implicit') and self.implicit == 'yes'
450     def get_notes (self):
451         return self.get_typed_children (get_class (u'note'))
452
453 class Syllabic (Music_xml_node):
454     def continued (self):
455         text = self.get_text()
456         return (text == "begin") or (text == "middle")
457 class Text (Music_xml_node):
458     pass
459
460 class Lyric (Music_xml_node):
461     def get_number (self):
462         if hasattr (self, 'number'):
463             return self.number
464         else:
465             return -1
466
467     def lyric_to_text (self):
468         continued = False
469         syllabic = self.get_maybe_exist_typed_child (Syllabic)
470         if syllabic:
471             continued = syllabic.continued ()
472         text = self.get_maybe_exist_typed_child (Text)
473         
474         if text:
475             text = text.get_text()
476             # We need to convert soft hyphens to -, otherwise the ascii codec as well
477             # as lilypond will barf on that character
478             text = string.replace( text, u'\xad', '-' )
479         
480         if text == "-" and continued:
481             return "--"
482         elif text == "_" and continued:
483             return "__"
484         elif continued and text:
485             return escape_ly_output_string (text) + " --"
486         elif continued:
487             return "--"
488         elif text:
489             return escape_ly_output_string (text)
490         else:
491             return ""
492
493 class Musicxml_voice:
494     def __init__ (self):
495         self._elements = []
496         self._staves = {}
497         self._start_staff = None
498         self._lyrics = []
499         self._has_lyrics = False
500
501     def add_element (self, e):
502         self._elements.append (e)
503         if (isinstance (e, Note)
504             and e.get_maybe_exist_typed_child (Staff)):
505             name = e.get_maybe_exist_typed_child (Staff).get_text ()
506
507             if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
508                 self._start_staff = name
509             self._staves[name] = True
510
511         lyrics = e.get_typed_children (Lyric)
512         if not self._has_lyrics:
513           self.has_lyrics = len (lyrics) > 0
514
515         for l in lyrics:
516             nr = l.get_number()
517             if (nr > 0) and not (nr in self._lyrics):
518                 self._lyrics.append (nr)
519
520     def insert (self, idx, e):
521         self._elements.insert (idx, e)
522
523     def get_lyrics_numbers (self):
524         if (len (self._lyrics) == 0) and self._has_lyrics:
525             #only happens if none of the <lyric> tags has a number attribute
526             return [1]
527         else:
528             return self._lyrics
529
530
531 def graces_to_aftergraces (pending_graces):
532     for gr in pending_graces:
533         gr._when = gr._prev_when
534         gr._measure_position = gr._prev_measure_position
535         gr._after_grace = True
536
537
538 class Part (Music_xml_node):
539     def __init__ (self):
540         Music_xml_node.__init__ (self)
541         self._voices = {}
542         self._staff_attributes_dict = {}
543
544     def get_part_list (self):
545         n = self
546         while n and n.get_name() != 'score-partwise':
547             n = n._parent
548
549         return n.get_named_child ('part-list')
550        
551     def interpret (self):
552         """Set durations and starting points."""
553         """The starting point of the very first note is 0!"""
554         
555         part_list = self.get_part_list ()
556         
557         now = Rational (0)
558         factor = Rational (1)
559         attributes_dict = {}
560         attributes_object = None
561         measures = self.get_typed_children (Measure)
562         last_moment = Rational (-1)
563         last_measure_position = Rational (-1)
564         measure_position = Rational (0)
565         measure_start_moment = now
566         is_first_measure = True
567         previous_measure = None
568         # Graces at the end of a measure need to have their position set to the
569         # previous number!
570         pending_graces = []
571         for m in measures:
572             # implicit measures are used for artificial measures, e.g. when
573             # a repeat bar line splits a bar into two halves. In this case,
574             # don't reset the measure position to 0. They are also used for
575             # upbeats (initial value of 0 fits these, too).
576             # Also, don't reset the measure position at the end of the loop,
577             # but rather when starting the next measure (since only then do we
578             # know if the next measure is implicit and continues that measure)
579             if not m.is_implicit ():
580                 # Warn about possibly overfull measures and reset the position
581                 if attributes_object and previous_measure and previous_measure.partial == 0:
582                     length = attributes_object.get_measure_length ()
583                     new_now = measure_start_moment + length
584                     if now <> new_now:
585                         problem = 'incomplete'
586                         if now > new_now:
587                             problem = 'overfull'
588                         ## only for verbose operation.
589                         if problem <> 'incomplete' and previous_measure:
590                             previous_measure.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
591                     now = new_now
592                 measure_start_moment = now
593                 measure_position = Rational (0)
594
595             for n in m.get_all_children ():
596                 # figured bass has a duration, but applies to the next note
597                 # and should not change the current measure position!
598                 if isinstance (n, FiguredBass):
599                     n._divisions = factor.denominator ()
600                     n._when = now
601                     n._measure_position = measure_position
602                     continue
603
604                 if isinstance (n, Hash_text):
605                     continue
606                 dur = Rational (0)
607
608                 if n.__class__ == Attributes:
609                     n.set_attributes_from_previous (attributes_dict)
610                     n.read_self ()
611                     attributes_dict = n._dict.copy ()
612                     attributes_object = n
613                     
614                     factor = Rational (1,
615                                        int (attributes_dict.get ('divisions').get_text ()))
616
617                 
618                 if (n.get_maybe_exist_typed_child (Duration)):
619                     mxl_dur = n.get_maybe_exist_typed_child (Duration)
620                     dur = mxl_dur.get_length () * factor
621                     
622                     if n.get_name() == 'backup':
623                         dur = - dur
624                         # reset all graces before the backup to after-graces:
625                         graces_to_aftergraces (pending_graces)
626                         pending_graces = []
627                     if n.get_maybe_exist_typed_child (Grace):
628                         dur = Rational (0)
629
630                     rest = n.get_maybe_exist_typed_child (Rest)
631                     if (rest
632                         and attributes_object
633                         and attributes_object.get_measure_length () == dur):
634
635                         rest._is_whole_measure = True
636
637                 if (dur > Rational (0)
638                     and n.get_maybe_exist_typed_child (Chord)):
639                     now = last_moment
640                     measure_position = last_measure_position
641
642                 n._when = now
643                 n._measure_position = measure_position
644
645                 # For all grace notes, store the previous note,  in case need
646                 # to turn the grace note into an after-grace later on!
647                 if isinstance(n, Note) and n.is_grace ():
648                     n._prev_when = last_moment
649                     n._prev_measure_position = last_measure_position
650                 # After-graces are placed at the same position as the previous note
651                 if isinstance(n, Note) and  n.is_after_grace ():
652                     # TODO: We should do the same for grace notes at the end of 
653                     # a measure with no following note!!!
654                     n._when = last_moment
655                     n._measure_position = last_measure_position
656                 elif isinstance(n, Note) and n.is_grace ():
657                     pending_graces.append (n)
658                 elif (dur > Rational (0)):
659                     pending_graces = [];
660
661                 n._duration = dur
662                 if dur > Rational (0):
663                     last_moment = now
664                     last_measure_position = measure_position
665                     now += dur
666                     measure_position += dur
667                 elif dur < Rational (0):
668                     # backup element, reset measure position
669                     now += dur
670                     measure_position += dur
671                     if measure_position < 0:
672                         # backup went beyond the measure start => reset to 0
673                         now -= measure_position
674                         measure_position = 0
675                     last_moment = now
676                     last_measure_position = measure_position
677                 if n._name == 'note':
678                     instrument = n.get_maybe_exist_named_child ('instrument')
679                     if instrument:
680                         n.instrument_name = part_list.get_instrument (instrument.id)
681
682             # reset all graces at the end of the measure to after-graces:
683             graces_to_aftergraces (pending_graces)
684             pending_graces = []
685             # Incomplete first measures are not padded, but registered as partial
686             if is_first_measure:
687                 is_first_measure = False
688                 # upbeats are marked as implicit measures
689                 if attributes_object and m.is_implicit ():
690                     length = attributes_object.get_measure_length ()
691                     measure_end = measure_start_moment + length
692                     if measure_end <> now:
693                         m.partial = now
694             previous_measure = m
695
696     # modify attributes so that only those applying to the given staff remain
697     def extract_attributes_for_staff (part, attr, staff):
698         attributes = copy.copy (attr)
699         attributes._children = [];
700         attributes._dict = attr._dict.copy ()
701         attributes._original_tag = attr
702         # copy only the relevant children over for the given staff
703         for c in attr._children:
704             if (not (hasattr (c, 'number') and (c.number != staff)) and
705                 not (isinstance (c, Hash_text))):
706                 attributes._children.append (c)
707         if not attributes._children:
708             return None
709         else:
710             return attributes
711
712     def extract_voices (part):
713         voices = {}
714         measures = part.get_typed_children (Measure)
715         elements = []
716         for m in measures:
717             if m.partial > 0:
718                 elements.append (Partial (m.partial))
719             elements.extend (m.get_all_children ())
720         # make sure we know all voices already so that dynamics, clefs, etc.
721         # can be assigned to the correct voices
722         voice_to_staff_dict = {}
723         for n in elements:
724             voice_id = n.get_maybe_exist_named_child (u'voice')
725             vid = None
726             if voice_id:
727                 vid = voice_id.get_text ()
728             elif isinstance (n, Note):
729                 vid = "None"
730
731             staff_id = n.get_maybe_exist_named_child (u'staff')
732             sid = None
733             if staff_id:
734                 sid = staff_id.get_text ()
735             else:
736                 sid = "None"
737             if vid and not voices.has_key (vid):
738                 voices[vid] = Musicxml_voice()
739             if vid and sid and not n.get_maybe_exist_typed_child (Grace):
740                 if not voice_to_staff_dict.has_key (vid):
741                     voice_to_staff_dict[vid] = sid
742         # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
743         # need to assign staff-assigned objects like clefs, times, etc. to
744         # all the correct voices. This will never work entirely correct due
745         # to staff-switches, but that's the best we can do!
746         staff_to_voice_dict = {}
747         for (v,s) in voice_to_staff_dict.items ():
748             if not staff_to_voice_dict.has_key (s):
749                 staff_to_voice_dict[s] = [v]
750             else:
751                 staff_to_voice_dict[s].append (v)
752
753
754         start_attr = None
755         assign_to_next_note = []
756         id = None
757         for n in elements:
758             voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
759             if voice_id:
760                 id = voice_id.get_text ()
761             else:
762                 id = "None"
763
764             # We don't need backup/forward any more, since we have already 
765             # assigned the correct onset times. 
766             # TODO: Let Grouping through. Also: link, print, bokmark sound
767             if not (isinstance (n, Note) or isinstance (n, Attributes) or
768                     isinstance (n, Direction) or isinstance (n, Partial) or
769                     isinstance (n, Barline) or isinstance (n, Harmony) or
770                     isinstance (n, FiguredBass) ):
771                 continue
772
773             if isinstance (n, Attributes) and not start_attr:
774                 start_attr = n
775                 continue
776
777             if isinstance (n, Attributes):
778                 # assign these only to the voices they really belongs to!
779                 for (s, vids) in staff_to_voice_dict.items ():
780                     staff_attributes = part.extract_attributes_for_staff (n, s)
781                     if staff_attributes:
782                         for v in vids:
783                             voices[v].add_element (staff_attributes)
784                 continue
785
786             if isinstance (n, Partial) or isinstance (n, Barline):
787                 for v in voices.keys ():
788                     voices[v].add_element (n)
789                 continue
790
791             if isinstance (n, Direction):
792                 staff_id = n.get_maybe_exist_named_child (u'staff')
793                 if staff_id:
794                     staff_id = staff_id.get_text ()
795                 if staff_id:
796                     dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
797                 else:
798                     dir_voices = voices.keys ()
799                 for v in dir_voices:
800                     voices[v].add_element (n)
801                 continue
802
803             if isinstance (n, Harmony) or isinstance (n, FiguredBass):
804                 # store the harmony or figured bass element until we encounter 
805                 # the next note and assign it only to that one voice.
806                 assign_to_next_note.append (n)
807                 continue
808
809             if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
810                 #Skip this note. 
811                 pass
812             else:
813                 for i in assign_to_next_note:
814                     voices[id].add_element (i)
815                 assign_to_next_note = []
816                 voices[id].add_element (n)
817
818         # Assign all remaining elements from assign_to_next_note to the voice
819         # of the previous note:
820         for i in assign_to_next_note:
821             voices[id].add_element (i)
822         assign_to_next_note = []
823
824         if start_attr:
825             for (s, vids) in staff_to_voice_dict.items ():
826                 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
827                 staff_attributes.read_self ()
828                 part._staff_attributes_dict[s] = staff_attributes
829                 for v in vids:
830                     voices[v].insert (0, staff_attributes)
831                     voices[v]._elements[0].read_self()
832
833         part._voices = voices
834
835     def get_voices (self):
836         return self._voices
837     def get_staff_attributes (self):
838         return self._staff_attributes_dict
839
840 class Notations (Music_xml_node):
841     def get_tie (self):
842         ts = self.get_named_children ('tied')
843         starts = [t for t in ts if t.type == 'start']
844         if starts:
845             return starts[0]
846         else:
847             return None
848
849     def get_tuplets (self):
850         return self.get_typed_children (Tuplet)
851
852 class Time_modification(Music_xml_node):
853     def get_fraction (self):
854         b = self.get_maybe_exist_named_child ('actual-notes')
855         a = self.get_maybe_exist_named_child ('normal-notes')
856         return (int(a.get_text ()), int (b.get_text ()))
857
858 class Accidental (Music_xml_node):
859     def __init__ (self):
860         Music_xml_node.__init__ (self)
861         self.editorial = False
862         self.cautionary = False
863
864 class Music_xml_spanner (Music_xml_node):
865     def get_type (self):
866         if hasattr (self, 'type'):
867             return self.type
868         else:
869             return 0
870     def get_size (self):
871         if hasattr (self, 'size'):
872             return string.atoi (self.size)
873         else:
874             return 0
875
876 class Wedge (Music_xml_spanner):
877     pass
878
879 class Tuplet (Music_xml_spanner):
880     pass
881
882 class Bracket (Music_xml_spanner):
883     pass
884
885 class Dashes (Music_xml_spanner):
886     pass
887
888 class Slur (Music_xml_spanner):
889     def get_type (self):
890         return self.type
891
892 class Beam (Music_xml_spanner):
893     def get_type (self):
894         return self.get_text ()
895     def is_primary (self):
896         return self.number == "1"
897
898 class Wavy_line (Music_xml_spanner):
899     pass
900     
901 class Pedal (Music_xml_spanner):
902     pass
903
904 class Glissando (Music_xml_spanner):
905     pass
906
907 class Slide (Music_xml_spanner):
908     pass
909
910 class Octave_shift (Music_xml_spanner):
911     # default is 8 for the octave-shift!
912     def get_size (self):
913         if hasattr (self, 'size'):
914             return string.atoi (self.size)
915         else:
916             return 8
917
918 class Chord (Music_xml_node):
919     pass
920
921 class Dot (Music_xml_node):
922     pass
923
924 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
925 # for the inner <rest> element, not the whole rest block.
926 class Rest (Music_xml_node):
927     def __init__ (self):
928         Music_xml_node.__init__ (self)
929         self._is_whole_measure = False
930     def is_whole_measure (self):
931         return self._is_whole_measure
932     def get_step (self):
933         ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
934         if ch:
935             step = ch.get_text ().strip ()
936             return step
937         else:
938             return None
939     def get_octave (self):
940         ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
941         if ch:
942             step = ch.get_text ().strip ()
943             return int (step)
944         else:
945             return None
946
947 class Type (Music_xml_node):
948     pass
949 class Grace (Music_xml_node):
950     pass
951 class Staff (Music_xml_node):
952     pass
953
954 class Direction (Music_xml_node):
955     pass
956 class DirType (Music_xml_node):
957     pass
958
959 class Bend (Music_xml_node):
960     def bend_alter (self):
961         alter = self.get_maybe_exist_named_child ('bend-alter')
962         if alter:
963             return alter.get_text()
964         else:
965             return 0
966
967 class Words (Music_xml_node):
968     pass
969
970 class Harmony (Music_xml_node):
971     pass
972
973 class ChordPitch (Music_xml_node):
974     def step_class_name (self):
975         return u'root-step'
976     def alter_class_name (self):
977         return u'root-alter'
978     def get_step (self):
979         ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
980         return ch.get_text ().strip ()
981     def get_alteration (self):
982         ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
983         alter = 0
984         if ch:
985             alter = int (ch.get_text ().strip ())
986         return alter
987
988 class Root (ChordPitch):
989     pass
990
991 class Bass (ChordPitch):
992     def step_class_name (self):
993         return u'bass-step'
994     def alter_class_name (self):
995         return u'bass-alter'
996
997 class ChordModification (Music_xml_node):
998     def get_type (self):
999         ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
1000         return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
1001     def get_value (self):
1002         ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
1003         value = 0
1004         if ch:
1005             value = int (ch.get_text ().strip ())
1006         return value
1007     def get_alter (self):
1008         ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
1009         value = 0
1010         if ch:
1011             value = int (ch.get_text ().strip ())
1012         return value
1013
1014
1015 class Frame (Music_xml_node):
1016     def get_frets (self):
1017         return self.get_named_child_value_number ('frame-frets', 4)
1018     def get_strings (self):
1019         return self.get_named_child_value_number ('frame-strings', 6)
1020     def get_first_fret (self):
1021         return self.get_named_child_value_number ('first-fret', 1)
1022
1023 class Frame_Note (Music_xml_node):
1024     def get_string (self):
1025         return self.get_named_child_value_number ('string', 1)
1026     def get_fret (self):
1027         return self.get_named_child_value_number ('fret', 0)
1028     def get_fingering (self):
1029         return self.get_named_child_value_number ('fingering', -1)
1030     def get_barre (self):
1031         n = self.get_maybe_exist_named_child ('barre')
1032         if n:
1033             return getattr (n, 'type', '')
1034         else:
1035             return ''
1036
1037 class FiguredBass (Music_xml_node):
1038     pass
1039
1040 class BeatUnit (Music_xml_node):
1041     pass
1042
1043 class BeatUnitDot (Music_xml_node):
1044     pass
1045
1046 class PerMinute (Music_xml_node):
1047     pass
1048
1049
1050
1051 ## need this, not all classes are instantiated
1052 ## for every input file. Only add those classes, that are either directly
1053 ## used by class name or extend Music_xml_node in some way!
1054 class_dict = {
1055         '#comment': Hash_comment,
1056         '#text': Hash_text,
1057         'accidental': Accidental,
1058         'attributes': Attributes,
1059         'barline': Barline,
1060         'bar-style': BarStyle,
1061         'bass': Bass,
1062         'beam' : Beam,
1063         'beat-unit': BeatUnit,
1064         'beat-unit-dot': BeatUnitDot,
1065         'bend' : Bend,
1066         'bracket' : Bracket,
1067         'chord': Chord,
1068         'dashes' : Dashes,
1069         'degree' : ChordModification,
1070         'dot': Dot,
1071         'direction': Direction,
1072         'direction-type': DirType,
1073         'duration': Duration,
1074         'frame': Frame,
1075         'frame-note': Frame_Note,
1076         'figured-bass': FiguredBass,
1077         'glissando': Glissando,
1078         'grace': Grace,
1079         'harmony': Harmony,
1080         'identification': Identification,
1081         'lyric': Lyric,
1082         'measure': Measure,
1083         'notations': Notations,
1084         'note': Note,
1085         'octave-shift': Octave_shift,
1086         'part': Part,
1087     'part-group': Part_group,
1088         'part-list': Part_list,
1089         'pedal': Pedal,
1090         'per-minute': PerMinute,
1091         'pitch': Pitch,
1092         'rest': Rest,
1093         'root': Root,
1094         'score-part': Score_part,
1095         'slide': Slide,
1096         'slur': Slur,
1097         'staff': Staff,
1098         'syllabic': Syllabic,
1099         'text': Text,
1100         'time-modification': Time_modification,
1101         'tuplet': Tuplet,
1102         'type': Type,
1103         'unpitched': Unpitched,
1104         'wavy-line': Wavy_line,
1105         'wedge': Wedge,
1106         'words': Words,
1107         'work': Work,
1108 }
1109
1110 def name2class_name (name):
1111     name = name.replace ('-', '_')
1112     name = name.replace ('#', 'hash_')
1113     name = name[0].upper() + name[1:].lower()
1114
1115     return str (name)
1116
1117 def get_class (name):
1118     classname = class_dict.get (name)
1119     if classname:
1120         return classname
1121     else:
1122         class_name = name2class_name (name)
1123         klass = new.classobj (class_name, (Music_xml_node,) , {})
1124         class_dict[name] = klass
1125         return klass
1126         
1127 def lxml_demarshal_node (node):
1128     name = node.tag
1129
1130     # Ignore comment nodes, which are also returned by the etree parser!
1131     if name is None or node.__class__.__name__ == "_Comment":
1132         return None
1133     klass = get_class (name)
1134     py_node = klass()
1135     
1136     py_node._original = node
1137     py_node._name = name
1138     py_node._data = node.text
1139     py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1140     py_node._children = filter (lambda x: x, py_node._children)
1141     
1142     for c in py_node._children:
1143         c._parent = py_node
1144
1145     for (k, v) in node.items ():
1146         py_node.__dict__[k] = v
1147         py_node._attribute_dict[k] = v
1148
1149     return py_node
1150
1151 def minidom_demarshal_node (node):
1152     name = node.nodeName
1153
1154     klass = get_class (name)
1155     py_node = klass ()
1156     py_node._name = name
1157     py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1158     for c in py_node._children:
1159         c._parent = py_node
1160
1161     if node.attributes:
1162         for (nm, value) in node.attributes.items ():
1163             py_node.__dict__[nm] = value
1164             py_node._attribute_dict[nm] = value
1165             
1166     py_node._data = None
1167     if node.nodeType == node.TEXT_NODE and node.data:
1168         py_node._data = node.data 
1169
1170     py_node._original = node
1171     return py_node
1172
1173
1174 if __name__  == '__main__':
1175     import lxml.etree
1176         
1177     tree = lxml.etree.parse ('beethoven.xml')
1178     mxl_tree = lxml_demarshal_node (tree.getroot ())
1179     ks = class_dict.keys ()
1180     ks.sort ()
1181     print '\n'.join (ks)