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