]> git.donarmstrong.com Git - lilypond.git/blobdiff - python/musicxml.py
Typo
[lilypond.git] / python / musicxml.py
index c71bf2d0be68ba9cecd8fe9ca88900a32b19187b..3a38efafdbca7c272565fef2b9ce0b8719869452 100644 (file)
@@ -15,7 +15,7 @@ def error (str):
 
 def escape_ly_output_string (input_string):
     return_string = input_string
 
 def escape_ly_output_string (input_string):
     return_string = input_string
-    needs_quotes = not re.match (u"^[a-zA-ZäöüÜÄÖßñ]*$", return_string);
+    needs_quotes = not re.match (u"^[a-zA-ZäöüÜÄÖß,\.!:ñ]*$", return_string);
     if needs_quotes:
         return_string = "\"" + string.replace (return_string, "\"", "\\\"") + "\""
     return return_string
     if needs_quotes:
         return_string = "\"" + string.replace (return_string, "\"", "\\\"") + "\""
     return return_string
@@ -37,43 +37,64 @@ def musicxml_duration_to_log (dur):
 
 
 
 
 
 
+def interpret_alter_element (alter_elm):
+    alter = 0
+    if alter_elm:
+        val = eval(alter_elm.get_text ())
+        if type (val) in (int, float):
+            alter = val
+    return alter
+
+
 class Xml_node:
     def __init__ (self):
 class Xml_node:
     def __init__ (self):
-       self._children = []
-       self._data = None
-       self._original = None
-       self._name = 'xml_node'
-       self._parent = None
+        self._children = []
+        self._data = None
+        self._original = None
+        self._name = 'xml_node'
+        self._parent = None
         self._attribute_dict = {}
         
     def get_parent (self):
         return self._parent
     
     def is_first (self):
         self._attribute_dict = {}
         
     def get_parent (self):
         return self._parent
     
     def is_first (self):
-       return self._parent.get_typed_children (self.__class__)[0] == self
+        return self._parent.get_typed_children (self.__class__)[0] == self
 
     def original (self):
 
     def original (self):
-       return self._original 
+        return self._original
     def get_name (self):
     def get_name (self):
-       return self._name
+        return self._name
 
     def get_text (self):
 
     def get_text (self):
-       if self._data:
-           return self._data
+        if self._data:
+            return self._data
 
 
-       if not self._children:
-           return ''
+        if not self._children:
+            return ''
 
 
-       return ''.join ([c.get_text () for c in self._children])
+        return ''.join ([c.get_text () for c in self._children])
 
     def message (self, msg):
         ly.stderr_write (msg+'\n')
 
         p = self
         while p:
 
     def message (self, msg):
         ly.stderr_write (msg+'\n')
 
         p = self
         while p:
-            sys.stderr.write ('  In: <%s %s>\n' % (p._name, ' '.join (['%s=%s' % item for item in p._attribute_dict.items()])))
+            sys.stderr.write ('  In: <%s %s>\n' % (p._name, ' '.join (['%s=%s' % item for item in p._attribute_dict.items ()])))
             p = p.get_parent ()
         
             p = p.get_parent ()
         
+    def dump (self, indent = ''):
+        sys.stderr.write ('%s<%s%s>' % (indent, self._name, ''.join ([' %s=%s' % item for item in self._attribute_dict.items ()])))
+        non_text_children = [c for c in self._children if not isinstance (c, Hash_text)]
+        if non_text_children:
+            sys.stderr.write ('\n')
+        for c in self._children:
+            c.dump (indent + "    ")
+        if non_text_children:
+            sys.stderr.write (indent)
+        sys.stderr.write ('</%s>\n' % self._name)
+
+        
     def get_typed_children (self, klass):
         if not klass:
             return []
     def get_typed_children (self, klass):
         if not klass:
             return []
@@ -81,36 +102,36 @@ class Xml_node:
             return [c for c in self._children if isinstance(c, klass)]
 
     def get_named_children (self, nm):
             return [c for c in self._children if isinstance(c, klass)]
 
     def get_named_children (self, nm):
-       return self.get_typed_children (get_class (nm))
+        return self.get_typed_children (get_class (nm))
 
     def get_named_child (self, nm):
 
     def get_named_child (self, nm):
-       return self.get_maybe_exist_named_child (nm)
+        return self.get_maybe_exist_named_child (nm)
 
     def get_children (self, predicate):
 
     def get_children (self, predicate):
-       return [c for c in self._children if predicate(c)]
+        return [c for c in self._children if predicate(c)]
 
     def get_all_children (self):
 
     def get_all_children (self):
-       return self._children
+        return self._children
 
     def get_maybe_exist_named_child (self, name):
 
     def get_maybe_exist_named_child (self, name):
-       return self.get_maybe_exist_typed_child (get_class (name))
+        return self.get_maybe_exist_typed_child (get_class (name))
 
     def get_maybe_exist_typed_child (self, klass):
 
     def get_maybe_exist_typed_child (self, klass):
-       cn = self.get_typed_children (klass)
-       if len (cn)==0:
-           return None
-       elif len (cn) == 1:
-           return cn[0]
-       else:
-           raise "More than 1 child", klass
+        cn = self.get_typed_children (klass)
+        if len (cn)==0:
+            return None
+        elif len (cn) == 1:
+            return cn[0]
+        else:
+            raise "More than 1 child", klass
 
     def get_unique_typed_child (self, klass):
 
     def get_unique_typed_child (self, klass):
-       cn = self.get_typed_children(klass)
-       if len (cn) <> 1:
-           sys.stderr.write (self.__dict__ + '\n')
-           raise 'Child is not unique for', (klass, 'found', cn)
+        cn = self.get_typed_children(klass)
+        if len (cn) <> 1:
+            sys.stderr.write (self.__dict__ + '\n')
+            raise 'Child is not unique for', (klass, 'found', cn)
 
 
-       return cn[0]
+        return cn[0]
 
     def get_named_child_value_number (self, name, default):
         n = self.get_maybe_exist_named_child (name)
 
     def get_named_child_value_number (self, name, default):
         n = self.get_maybe_exist_named_child (name)
@@ -122,9 +143,9 @@ class Xml_node:
 
 class Music_xml_node (Xml_node):
     def __init__ (self):
 
 class Music_xml_node (Xml_node):
     def __init__ (self):
-       Xml_node.__init__ (self)
-       self.duration = Rational (0)
-       self.start = Rational (0)
+        Xml_node.__init__ (self)
+        self.duration = Rational (0)
+        self.start = Rational (0)
 
 class Work (Xml_node):
     def get_work_information (self, tag):
 
 class Work (Xml_node):
     def get_work_information (self, tag):
@@ -143,11 +164,11 @@ class Work (Xml_node):
 
 class Identification (Xml_node):
     def get_rights (self):
 
 class Identification (Xml_node):
     def get_rights (self):
-        rights = self.get_maybe_exist_named_child ('rights')
-        if rights:
-            return rights.get_text ()
-        else:
-            return ''
+        rights = self.get_named_children ('rights')
+        ret = []
+        for r in rights:
+          ret.append (r.get_text ())
+        return string.join (ret, "\n")
 
     def get_creator (self, type):
         creators = self.get_named_children ('creator')
 
     def get_creator (self, type):
         creators = self.get_named_children ('creator')
@@ -205,97 +226,154 @@ class Identification (Xml_node):
                 software.append (s.get_text ())
         return software
 
                 software.append (s.get_text ())
         return software
 
+    def get_file_description (self):
+        misc = self.get_named_children ('miscellaneous')
+        for m in misc:
+            misc_fields = m.get_named_children ('miscellaneous-field')
+            for mf in misc_fields:
+                if hasattr (mf, 'name') and mf.name == 'description':
+                    return mf.get_text () 
+        return None
+
 
 
 class Duration (Music_xml_node):
     def get_length (self):
 
 
 class Duration (Music_xml_node):
     def get_length (self):
-       dur = int (self.get_text ()) * Rational (1,4)
-       return dur
+        dur = int (self.get_text ()) * Rational (1,4)
+        return dur
 
 class Hash_comment (Music_xml_node):
     pass
 class Hash_text (Music_xml_node):
 
 class Hash_comment (Music_xml_node):
     pass
 class Hash_text (Music_xml_node):
-    pass
+    def dump (self, indent = ''):
+        sys.stderr.write ('%s' % string.strip (self._data))
 
 class Pitch (Music_xml_node):
     def get_step (self):
 
 class Pitch (Music_xml_node):
     def get_step (self):
-       ch = self.get_unique_typed_child (get_class (u'step'))
-       step = ch.get_text ().strip ()
-       return step
+        ch = self.get_unique_typed_child (get_class (u'step'))
+        step = ch.get_text ().strip ()
+        return step
     def get_octave (self):
     def get_octave (self):
-       ch = self.get_unique_typed_child (get_class (u'octave'))
-
-       step = ch.get_text ().strip ()
-       return int (step)
+        ch = self.get_unique_typed_child (get_class (u'octave'))
+        octave = ch.get_text ().strip ()
+        return int (octave)
 
     def get_alteration (self):
 
     def get_alteration (self):
-       ch = self.get_maybe_exist_typed_child (get_class (u'alter'))
-       alter = 0
-       if ch:
-           alter = int (ch.get_text ().strip ())
-       return alter
+        ch = self.get_maybe_exist_typed_child (get_class (u'alter'))
+        return interpret_alter_element (ch)
 
 class Unpitched (Music_xml_node):
     def get_step (self):
 
 class Unpitched (Music_xml_node):
     def get_step (self):
-       ch = self.get_unique_typed_child (get_class (u'display-step'))
-       step = ch.get_text ().strip ()
-       return step
+        ch = self.get_unique_typed_child (get_class (u'display-step'))
+        step = ch.get_text ().strip ()
+        return step
 
     def get_octave (self):
 
     def get_octave (self):
-       ch = self.get_unique_typed_child (get_class (u'display-octave'))
+        ch = self.get_unique_typed_child (get_class (u'display-octave'))
 
 
-       if ch:
-           octave = ch.get_text ().strip ()
-           return int (octave)
-       else:
-           return None
+        if ch:
+            octave = ch.get_text ().strip ()
+            return int (octave)
+        else:
+            return None
 
 class Measure_element (Music_xml_node):
     def get_voice_id (self):
 
 class Measure_element (Music_xml_node):
     def get_voice_id (self):
-       voice_id = self.get_maybe_exist_named_child ('voice')
-       if voice_id:
-           return voice_id.get_text ()
-       else:
-           return None
+        voice_id = self.get_maybe_exist_named_child ('voice')
+        if voice_id:
+            return voice_id.get_text ()
+        else:
+            return None
 
     def is_first (self):
 
     def is_first (self):
-       cn = self._parent.get_typed_children (self.__class__)
-       cn = [c for c in cn if c.get_voice_id () == self.get_voice_id ()]
-       return cn[0] == self
+        # Look at all measure elements (previously we had self.__class__, which
+        # only looked at objects of the same type!
+        cn = self._parent.get_typed_children (Measure_element)
+        # But only look at the correct voice; But include Attributes, too, which
+        # are not tied to any particular voice
+        cn = [c for c in cn if (c.get_voice_id () == self.get_voice_id ()) or isinstance (c, Attributes)]
+        return cn[0] == self
 
 class Attributes (Measure_element):
     def __init__ (self):
 
 class Attributes (Measure_element):
     def __init__ (self):
-       Measure_element.__init__ (self)
-       self._dict = {}
+        Measure_element.__init__ (self)
+        self._dict = {}
+        self._original_tag = None
+        self._time_signature_cache = None
 
 
+    def is_first (self):
+        cn = self._parent.get_typed_children (self.__class__)
+        if self._original_tag:
+            return cn[0] == self._original_tag
+        else:
+            return cn[0] == self
+    
     def set_attributes_from_previous (self, dict):
     def set_attributes_from_previous (self, dict):
-       self._dict.update (dict)
+        self._dict.update (dict)
         
     def read_self (self):
         
     def read_self (self):
-       for c in self.get_all_children ():
-           self._dict[c.get_name()] = c
+        for c in self.get_all_children ():
+            self._dict[c.get_name()] = c
 
     def get_named_attribute (self, name):
 
     def get_named_attribute (self, name):
-       return self._dict.get (name)
+        return self._dict.get (name)
+        
+    def single_time_sig_to_fraction (self, sig):
+        if len (sig) < 2:
+            return 0
+        n = 0
+        for i in sig[0:-1]:
+          n += i
+        return Rational (n, sig[-1])
 
     def get_measure_length (self):
 
     def get_measure_length (self):
-        (n,d) = self.get_time_signature ()
-        return Rational (n,d)
+        sig = self.get_time_signature ()
+        if not sig or len (sig) == 0:
+            return 1
+        if isinstance (sig[0], list):
+            # Complex compound time signature
+            l = 0
+            for i in sig:
+                l += self.single_time_sig_to_fraction (i)
+            return l
+        else:
+           # Simple (maybe compound) time signature of the form (beat, ..., type)
+            return self.single_time_sig_to_fraction (sig)
+        return 0
         
     def get_time_signature (self):
         
     def get_time_signature (self):
-        "return time sig as a (beat, beat-type) tuple"
+        "Return time sig as a (beat, beat-type) tuple. For compound signatures,"
+        "return either (beat, beat,..., beat-type) or ((beat,..., type), "
+        "(beat,..., type), ...)."
+        if self._time_signature_cache:
+            return self._time_signature_cache
 
         try:
             mxl = self.get_named_attribute ('time')
 
         try:
             mxl = self.get_named_attribute ('time')
-            if mxl:
-                beats = mxl.get_maybe_exist_named_child ('beats')
-                type = mxl.get_maybe_exist_named_child ('beat-type')
-                return (int (beats.get_text ()),
-                        int (type.get_text ()))
-            else:
+            if not mxl:
+                return None
+
+            if mxl.get_maybe_exist_named_child ('senza-misura'):
+                # TODO: Handle pieces without a time signature!
+                error (_ ("Senza-misura time signatures are not yet supported!"))
                 return (4, 4)
                 return (4, 4)
-        except KeyError:
-            error (_ ("requested time signature, but time sig is unknown"))
+            else:
+                signature = []
+                current_sig = []
+                for i in mxl.get_all_children ():
+                    if isinstance (i, Beats):
+                        beats = string.split (i.get_text ().strip (), "+")
+                        current_sig = [int (j) for j in beats]
+                    elif isinstance (i, BeatType):
+                        current_sig.append (int (i.get_text ()))
+                        signature.append (current_sig)
+                        current_sig = []
+                if isinstance (signature[0], list) and len (signature) == 1:
+                    signature = signature[0]
+                self._time_signature_cache = signature
+                return signature
+        except (KeyError, ValueError):
+            self.message (_ ("Unable to interpret time signature! Falling back to 4/4."))
             return (4, 4)
 
     # returns clef information in the form ("cleftype", position, octave-shift)
             return (4, 4)
 
     # returns clef information in the form ("cleftype", position, octave-shift)
@@ -316,16 +394,55 @@ class Attributes (Measure_element):
         return clefinfo
 
     def get_key_signature (self):
         return clefinfo
 
     def get_key_signature (self):
-        "return (fifths, mode) tuple"
+        "return (fifths, mode) tuple if the key signatures is given as "
+        "major/minor in the Circle of fifths. Otherwise return an alterations"
+        "list of the form [[step,alter<,octave>], [step,alter<,octave>], ...], "
+        "where the octave values are optional."
 
         key = self.get_named_attribute ('key')
 
         key = self.get_named_attribute ('key')
-        mode_node = key.get_maybe_exist_named_child ('mode')
-        mode = 'major'
-        if mode_node:
-            mode = mode_node.get_text ()
+        if not key:
+            return None
+        fifths_elm = key.get_maybe_exist_named_child ('fifths')
+        if fifths_elm:
+            mode_node = key.get_maybe_exist_named_child ('mode')
+            mode = None
+            if mode_node:
+                mode = mode_node.get_text ()
+            if not mode or mode == '':
+                mode = 'major'
+            fifths = int (fifths_elm.get_text ())
+            # TODO: Shall we try to convert the key-octave and the cancel, too?
+            return (fifths, mode)
+        else:
+            alterations = []
+            current_step = 0
+            for i in key.get_all_children ():
+                if isinstance (i, KeyStep):
+                    current_step = i.get_text ().strip ()
+                elif isinstance (i, KeyAlter):
+                    alterations.append ([current_step, interpret_alter_element (i)])
+                elif isinstance (i, KeyOctave):
+                    nr = -1
+                    if hasattr (i, 'number'):
+                        nr = int (i.number)
+                    if (nr > 0) and (nr <= len (alterations)):
+                        # MusicXML Octave 4 is middle C -> shift to 0
+                        alterations[nr-1].append (int (i.get_text ())-4)
+                    else:
+                        i.message (_ ("Key alteration octave given for a "
+                            "non-existing alteration nr. %s, available numbers: %s!") % (nr, len(alterations)))
+            return alterations
+
+    def get_transposition (self):
+        return self.get_named_attribute ('transpose')
+
+class KeyAlter (Music_xml_node):
+    pass
+class KeyStep (Music_xml_node):
+    pass
+class KeyOctave (Music_xml_node):
+    pass
 
 
-        fifths = int (key.get_maybe_exist_named_child ('fifths').get_text ())
-        return (fifths, mode)
 
 class Barline (Measure_element):
     pass
 
 class Barline (Measure_element):
     pass
@@ -340,19 +457,34 @@ class Note (Measure_element):
     def __init__ (self):
         Measure_element.__init__ (self)
         self.instrument_name = ''
     def __init__ (self):
         Measure_element.__init__ (self)
         self.instrument_name = ''
-        
+        self._after_grace = False
+    def is_grace (self):
+        return self.get_maybe_exist_named_child (u'grace')
+    def is_after_grace (self):
+        if not self.is_grace():
+            return False;
+        gr = self.get_maybe_exist_typed_child (Grace)
+        return self._after_grace or hasattr (gr, 'steal-time-previous');
+
     def get_duration_log (self):
         ch = self.get_maybe_exist_named_child (u'type')
 
         if ch:
             log = ch.get_text ().strip()
             return musicxml_duration_to_log (log)
     def get_duration_log (self):
         ch = self.get_maybe_exist_named_child (u'type')
 
         if ch:
             log = ch.get_text ().strip()
             return musicxml_duration_to_log (log)
-       elif self.get_maybe_exist_named_child (u'grace'):
-           # FIXME: is it ok to default to eight note for grace notes?
-           return 3
+        elif self.get_maybe_exist_named_child (u'grace'):
+            # FIXME: is it ok to default to eight note for grace notes?
+            return 3
         else:
         else:
-            self.message (_ ("Encountered note at %s with %s duration (no <type> element):") % (self.start, self.duration) )
-            return 0
+            return None
+    
+    def get_duration_info (self):
+        log = self.get_duration_log ()
+        if log != None:
+            dots = len (self.get_typed_children (Dot))
+            return (log, dots)
+        else:
+            return None
 
     def get_factor (self):
         return 1
 
     def get_factor (self):
         return 1
@@ -400,12 +532,16 @@ class Measure (Music_xml_node):
     def is_implicit (self):
         return hasattr (self, 'implicit') and self.implicit == 'yes'
     def get_notes (self):
     def is_implicit (self):
         return hasattr (self, 'implicit') and self.implicit == 'yes'
     def get_notes (self):
-       return self.get_typed_children (get_class (u'note'))
+        return self.get_typed_children (get_class (u'note'))
 
 class Syllabic (Music_xml_node):
     def continued (self):
         text = self.get_text()
         return (text == "begin") or (text == "middle")
 
 class Syllabic (Music_xml_node):
     def continued (self):
         text = self.get_text()
         return (text == "begin") or (text == "middle")
+class Elision (Music_xml_node):
+    pass
+class Extend (Music_xml_node):
+    pass
 class Text (Music_xml_node):
     pass
 
 class Text (Music_xml_node):
     pass
 
@@ -416,49 +552,23 @@ class Lyric (Music_xml_node):
         else:
             return -1
 
         else:
             return -1
 
-    def lyric_to_text (self):
-        continued = False
-        syllabic = self.get_maybe_exist_typed_child (Syllabic)
-        if syllabic:
-            continued = syllabic.continued ()
-        text = self.get_maybe_exist_typed_child (Text)
-        
-        if text:
-            text = text.get_text()
-            # We need to convert soft hyphens to -, otherwise the ascii codec as well
-            # as lilypond will barf on that character
-            text = string.replace( text, u'\xad', '-' )
-        
-        if text == "-" and continued:
-            return "--"
-        elif text == "_" and continued:
-            return "__"
-        elif continued and text:
-            return escape_ly_output_string (text) + " --"
-        elif continued:
-            return "--"
-        elif text:
-            return escape_ly_output_string (text)
-        else:
-            return ""
-
 class Musicxml_voice:
     def __init__ (self):
 class Musicxml_voice:
     def __init__ (self):
-       self._elements = []
-       self._staves = {}
-       self._start_staff = None
+        self._elements = []
+        self._staves = {}
+        self._start_staff = None
         self._lyrics = []
         self._has_lyrics = False
 
     def add_element (self, e):
         self._lyrics = []
         self._has_lyrics = False
 
     def add_element (self, e):
-       self._elements.append (e)
-       if (isinstance (e, Note)
-           and e.get_maybe_exist_typed_child (Staff)):
-           name = e.get_maybe_exist_typed_child (Staff).get_text ()
+        self._elements.append (e)
+        if (isinstance (e, Note)
+            and e.get_maybe_exist_typed_child (Staff)):
+            name = e.get_maybe_exist_typed_child (Staff).get_text ()
 
 
-           if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
-               self._start_staff = name
-           self._staves[name] = True
+            if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
+                self._start_staff = name
+            self._staves[name] = True
 
         lyrics = e.get_typed_children (Lyric)
         if not self._has_lyrics:
 
         lyrics = e.get_typed_children (Lyric)
         if not self._has_lyrics:
@@ -470,7 +580,7 @@ class Musicxml_voice:
                 self._lyrics.append (nr)
 
     def insert (self, idx, e):
                 self._lyrics.append (nr)
 
     def insert (self, idx, e):
-       self._elements.insert (idx, e)
+        self._elements.insert (idx, e)
 
     def get_lyrics_numbers (self):
         if (len (self._lyrics) == 0) and self._has_lyrics:
 
     def get_lyrics_numbers (self):
         if (len (self._lyrics) == 0) and self._has_lyrics:
@@ -480,10 +590,17 @@ class Musicxml_voice:
             return self._lyrics
 
 
             return self._lyrics
 
 
+def graces_to_aftergraces (pending_graces):
+    for gr in pending_graces:
+        gr._when = gr._prev_when
+        gr._measure_position = gr._prev_measure_position
+        gr._after_grace = True
+
+
 class Part (Music_xml_node):
     def __init__ (self):
         Music_xml_node.__init__ (self)
 class Part (Music_xml_node):
     def __init__ (self):
         Music_xml_node.__init__ (self)
-       self._voices = {}
+        self._voices = {}
         self._staff_attributes_dict = {}
 
     def get_part_list (self):
         self._staff_attributes_dict = {}
 
     def get_part_list (self):
@@ -492,25 +609,28 @@ class Part (Music_xml_node):
             n = n._parent
 
         return n.get_named_child ('part-list')
             n = n._parent
 
         return n.get_named_child ('part-list')
-        
+       
     def interpret (self):
     def interpret (self):
-       """Set durations and starting points."""
+        """Set durations and starting points."""
         """The starting point of the very first note is 0!"""
         
         part_list = self.get_part_list ()
         
         """The starting point of the very first note is 0!"""
         
         part_list = self.get_part_list ()
         
-       now = Rational (0)
-       factor = Rational (1)
-       attributes_dict = {}
+        now = Rational (0)
+        factor = Rational (1)
+        attributes_dict = {}
         attributes_object = None
         attributes_object = None
-       measures = self.get_typed_children (Measure)
+        measures = self.get_typed_children (Measure)
         last_moment = Rational (-1)
         last_measure_position = Rational (-1)
         measure_position = Rational (0)
         measure_start_moment = now
         is_first_measure = True
         previous_measure = None
         last_moment = Rational (-1)
         last_measure_position = Rational (-1)
         measure_position = Rational (0)
         measure_start_moment = now
         is_first_measure = True
         previous_measure = None
-       for m in measures:
+        # Graces at the end of a measure need to have their position set to the
+        # previous number!
+        pending_graces = []
+        for m in measures:
             # implicit measures are used for artificial measures, e.g. when
             # a repeat bar line splits a bar into two halves. In this case,
             # don't reset the measure position to 0. They are also used for
             # implicit measures are used for artificial measures, e.g. when
             # a repeat bar line splits a bar into two halves. In this case,
             # don't reset the measure position to 0. They are also used for
@@ -539,45 +659,67 @@ class Part (Music_xml_node):
                 # and should not change the current measure position!
                 if isinstance (n, FiguredBass):
                     n._divisions = factor.denominator ()
                 # and should not change the current measure position!
                 if isinstance (n, FiguredBass):
                     n._divisions = factor.denominator ()
+                    n._when = now
+                    n._measure_position = measure_position
                     continue
 
                 if isinstance (n, Hash_text):
                     continue
                     continue
 
                 if isinstance (n, Hash_text):
                     continue
-               dur = Rational (0)
+                dur = Rational (0)
 
                 if n.__class__ == Attributes:
 
                 if n.__class__ == Attributes:
-                   n.set_attributes_from_previous (attributes_dict)
-                   n.read_self ()
-                   attributes_dict = n._dict.copy ()
+                    n.set_attributes_from_previous (attributes_dict)
+                    n.read_self ()
+                    attributes_dict = n._dict.copy ()
                     attributes_object = n
                     
                     attributes_object = n
                     
-                   factor = Rational (1,
-                                      int (attributes_dict.get ('divisions').get_text ()))
+                    factor = Rational (1,
+                                       int (attributes_dict.get ('divisions').get_text ()))
 
                 
 
                 
-               if (n.get_maybe_exist_typed_child (Duration)):
-                   mxl_dur = n.get_maybe_exist_typed_child (Duration)
-                   dur = mxl_dur.get_length () * factor
+                if (n.get_maybe_exist_typed_child (Duration)):
+                    mxl_dur = n.get_maybe_exist_typed_child (Duration)
+                    dur = mxl_dur.get_length () * factor
                     
                     
-                   if n.get_name() == 'backup':
-                       dur = - dur
-                   if n.get_maybe_exist_typed_child (Grace):
-                       dur = Rational (0)
+                    if n.get_name() == 'backup':
+                        dur = - dur
+                        # reset all graces before the backup to after-graces:
+                        graces_to_aftergraces (pending_graces)
+                        pending_graces = []
+                    if n.get_maybe_exist_typed_child (Grace):
+                        dur = Rational (0)
 
                     rest = n.get_maybe_exist_typed_child (Rest)
 
                     rest = n.get_maybe_exist_typed_child (Rest)
-                   if (rest
+                    if (rest
                         and attributes_object
                         and attributes_object.get_measure_length () == dur):
 
                         rest._is_whole_measure = True
 
                         and attributes_object
                         and attributes_object.get_measure_length () == dur):
 
                         rest._is_whole_measure = True
 
-                if (dur > Rational (0) 
+                if (dur > Rational (0)
                     and n.get_maybe_exist_typed_child (Chord)):
                     now = last_moment
                     measure_position = last_measure_position
 
                 n._when = now
                 n._measure_position = measure_position
                     and n.get_maybe_exist_typed_child (Chord)):
                     now = last_moment
                     measure_position = last_measure_position
 
                 n._when = now
                 n._measure_position = measure_position
+
+                # For all grace notes, store the previous note,  in case need
+                # to turn the grace note into an after-grace later on!
+                if isinstance(n, Note) and n.is_grace ():
+                    n._prev_when = last_moment
+                    n._prev_measure_position = last_measure_position
+                # After-graces are placed at the same position as the previous note
+                if isinstance(n, Note) and  n.is_after_grace ():
+                    # TODO: We should do the same for grace notes at the end of 
+                    # a measure with no following note!!!
+                    n._when = last_moment
+                    n._measure_position = last_measure_position
+                elif isinstance(n, Note) and n.is_grace ():
+                    pending_graces.append (n)
+                elif (dur > Rational (0)):
+                    pending_graces = [];
+
                 n._duration = dur
                 if dur > Rational (0):
                     last_moment = now
                 n._duration = dur
                 if dur > Rational (0):
                     last_moment = now
@@ -599,6 +741,9 @@ class Part (Music_xml_node):
                     if instrument:
                         n.instrument_name = part_list.get_instrument (instrument.id)
 
                     if instrument:
                         n.instrument_name = part_list.get_instrument (instrument.id)
 
+            # reset all graces at the end of the measure to after-graces:
+            graces_to_aftergraces (pending_graces)
+            pending_graces = []
             # Incomplete first measures are not padded, but registered as partial
             if is_first_measure:
                 is_first_measure = False
             # Incomplete first measures are not padded, but registered as partial
             if is_first_measure:
                 is_first_measure = False
@@ -613,21 +758,27 @@ class Part (Music_xml_node):
     # modify attributes so that only those applying to the given staff remain
     def extract_attributes_for_staff (part, attr, staff):
         attributes = copy.copy (attr)
     # modify attributes so that only those applying to the given staff remain
     def extract_attributes_for_staff (part, attr, staff):
         attributes = copy.copy (attr)
-        attributes._children = copy.copy (attr._children)
+        attributes._children = [];
         attributes._dict = attr._dict.copy ()
         attributes._dict = attr._dict.copy ()
-        for c in attributes._children:
-            if hasattr (c, 'number') and c.number != staff:
-                attributes._children.remove (c)
-        return attributes
+        attributes._original_tag = attr
+        # copy only the relevant children over for the given staff
+        for c in attr._children:
+            if (not (hasattr (c, 'number') and (c.number != staff)) and
+                not (isinstance (c, Hash_text))):
+                attributes._children.append (c)
+        if not attributes._children:
+            return None
+        else:
+            return attributes
 
     def extract_voices (part):
 
     def extract_voices (part):
-       voices = {}
-       measures = part.get_typed_children (Measure)
-       elements = []
-       for m in measures:
+        voices = {}
+        measures = part.get_typed_children (Measure)
+        elements = []
+        for m in measures:
             if m.partial > 0:
                 elements.append (Partial (m.partial))
             if m.partial > 0:
                 elements.append (Partial (m.partial))
-           elements.extend (m.get_all_children ())
+            elements.extend (m.get_all_children ())
         # make sure we know all voices already so that dynamics, clefs, etc.
         # can be assigned to the correct voices
         voice_to_staff_dict = {}
         # make sure we know all voices already so that dynamics, clefs, etc.
         # can be assigned to the correct voices
         voice_to_staff_dict = {}
@@ -636,6 +787,8 @@ class Part (Music_xml_node):
             vid = None
             if voice_id:
                 vid = voice_id.get_text ()
             vid = None
             if voice_id:
                 vid = voice_id.get_text ()
+            elif isinstance (n, Note):
+                vid = "None"
 
             staff_id = n.get_maybe_exist_named_child (u'staff')
             sid = None
 
             staff_id = n.get_maybe_exist_named_child (u'staff')
             sid = None
@@ -660,31 +813,39 @@ class Part (Music_xml_node):
                 staff_to_voice_dict[s].append (v)
 
 
                 staff_to_voice_dict[s].append (v)
 
 
-       start_attr = None
+        start_attr = None
         assign_to_next_note = []
         id = None
         assign_to_next_note = []
         id = None
-       for n in elements:
-           voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
+        for n in elements:
+            voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
+            if voice_id:
+                id = voice_id.get_text ()
+            else:
+                id = "None"
 
 
-           if not (voice_id or isinstance (n, Attributes) or
+            # We don't need backup/forward any more, since we have already 
+            # assigned the correct onset times. 
+            # TODO: Let Grouping through. Also: link, print, bokmark sound
+            if not (isinstance (n, Note) or isinstance (n, Attributes) or
                     isinstance (n, Direction) or isinstance (n, Partial) or
                     isinstance (n, Barline) or isinstance (n, Harmony) or
                     isinstance (n, Direction) or isinstance (n, Partial) or
                     isinstance (n, Barline) or isinstance (n, Harmony) or
-                    isinstance (n, FiguredBass) ):
-               continue
+                    isinstance (n, FiguredBass) or isinstance (n, Print)):
+                continue
 
 
-           if isinstance (n, Attributes) and not start_attr:
-               start_attr = n
-               continue
+            if isinstance (n, Attributes) and not start_attr:
+                start_attr = n
+                continue
 
             if isinstance (n, Attributes):
 
             if isinstance (n, Attributes):
-                # assign these only to the voices they really belongs to!
+                # assign these only to the voices they really belong to!
                 for (s, vids) in staff_to_voice_dict.items ():
                     staff_attributes = part.extract_attributes_for_staff (n, s)
                 for (s, vids) in staff_to_voice_dict.items ():
                     staff_attributes = part.extract_attributes_for_staff (n, s)
-                    for v in vids:
-                        voices[v].add_element (staff_attributes)
+                    if staff_attributes:
+                        for v in vids:
+                            voices[v].add_element (staff_attributes)
                 continue
 
                 continue
 
-            if isinstance (n, Partial) or isinstance (n, Barline):
+            if isinstance (n, Partial) or isinstance (n, Barline) or isinstance (n, Print):
                 for v in voices.keys ():
                     voices[v].add_element (n)
                 continue
                 for v in voices.keys ():
                     voices[v].add_element (n)
                 continue
@@ -707,7 +868,6 @@ class Part (Music_xml_node):
                 assign_to_next_note.append (n)
                 continue
 
                 assign_to_next_note.append (n)
                 continue
 
-           id = voice_id.get_text ()
             if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
                 #Skip this note. 
                 pass
             if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
                 #Skip this note. 
                 pass
@@ -723,7 +883,7 @@ class Part (Music_xml_node):
             voices[id].add_element (i)
         assign_to_next_note = []
 
             voices[id].add_element (i)
         assign_to_next_note = []
 
-       if start_attr:
+        if start_attr:
             for (s, vids) in staff_to_voice_dict.items ():
                 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
                 staff_attributes.read_self ()
             for (s, vids) in staff_to_voice_dict.items ():
                 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
                 staff_attributes.read_self ()
@@ -735,33 +895,43 @@ class Part (Music_xml_node):
         part._voices = voices
 
     def get_voices (self):
         part._voices = voices
 
     def get_voices (self):
-       return self._voices
+        return self._voices
     def get_staff_attributes (self):
         return self._staff_attributes_dict
 
 class Notations (Music_xml_node):
     def get_tie (self):
     def get_staff_attributes (self):
         return self._staff_attributes_dict
 
 class Notations (Music_xml_node):
     def get_tie (self):
-       ts = self.get_named_children ('tied')
-       starts = [t for t in ts if t.type == 'start']
-       if starts:
-           return starts[0]
-       else:
-           return None
+        ts = self.get_named_children ('tied')
+        starts = [t for t in ts if t.type == 'start']
+        if starts:
+            return starts[0]
+        else:
+            return None
 
     def get_tuplets (self):
 
     def get_tuplets (self):
-       return self.get_typed_children (Tuplet)
+        return self.get_typed_children (Tuplet)
 
 class Time_modification(Music_xml_node):
     def get_fraction (self):
 
 class Time_modification(Music_xml_node):
     def get_fraction (self):
-       b = self.get_maybe_exist_named_child ('actual-notes')
-       a = self.get_maybe_exist_named_child ('normal-notes')
-       return (int(a.get_text ()), int (b.get_text ()))
+        b = self.get_maybe_exist_named_child ('actual-notes')
+        a = self.get_maybe_exist_named_child ('normal-notes')
+        return (int(a.get_text ()), int (b.get_text ()))
+
+    def get_normal_type (self):
+        tuplet_type = self.get_maybe_exist_named_child ('normal-type')
+        if tuplet_type:
+            dots = self.get_named_children ('normal-dot')
+            log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
+            return (log , len (dots))
+        else:
+            return None
+
 
 class Accidental (Music_xml_node):
     def __init__ (self):
 
 class Accidental (Music_xml_node):
     def __init__ (self):
-       Music_xml_node.__init__ (self)
-       self.editorial = False
-       self.cautionary = False
+        Music_xml_node.__init__ (self)
+        self.editorial = False
+        self.cautionary = False
 
 class Music_xml_spanner (Music_xml_node):
     def get_type (self):
 
 class Music_xml_spanner (Music_xml_node):
     def get_type (self):
@@ -779,7 +949,40 @@ class Wedge (Music_xml_spanner):
     pass
 
 class Tuplet (Music_xml_spanner):
     pass
 
 class Tuplet (Music_xml_spanner):
-    pass
+    def duration_info_from_tuplet_note (self, tuplet_note):
+        tuplet_type = tuplet_note.get_maybe_exist_named_child ('tuplet-type')
+        if tuplet_type:
+            dots = tuplet_note.get_named_children ('tuplet-dot')
+            log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
+            return (log, len (dots))
+        else:
+            return None
+
+    # Return tuplet note type as (log, dots)
+    def get_normal_type (self):
+        tuplet = self.get_maybe_exist_named_child ('tuplet-normal')
+        if tuplet:
+            return self.duration_info_from_tuplet_note (tuplet)
+        else:
+            return None
+
+    def get_actual_type (self):
+        tuplet = self.get_maybe_exist_named_child ('tuplet-actual')
+        if tuplet:
+            return self.duration_info_from_tuplet_note (tuplet)
+        else:
+            return None
+
+    def get_tuplet_note_count (self, tuplet_note):
+        if tuplet_note:
+            tuplet_nr = tuplet_note.get_maybe_exist_named_child ('tuplet-number')
+            if tuplet_nr: 
+                return int (tuplet_nr.get_text ())
+        return None
+    def get_normal_nr (self):
+        return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-normal'))
+    def get_actual_nr (self):
+        return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-actual'))
 
 class Bracket (Music_xml_spanner):
     pass
 
 class Bracket (Music_xml_spanner):
     pass
@@ -789,13 +992,16 @@ class Dashes (Music_xml_spanner):
 
 class Slur (Music_xml_spanner):
     def get_type (self):
 
 class Slur (Music_xml_spanner):
     def get_type (self):
-       return self.type
+        return self.type
 
 class Beam (Music_xml_spanner):
     def get_type (self):
 
 class Beam (Music_xml_spanner):
     def get_type (self):
-       return self.get_text ()
+        return self.get_text ()
     def is_primary (self):
     def is_primary (self):
-        return self.number == "1"
+        if hasattr (self, 'number'):
+            return self.number == "1"
+        else:
+            return True
 
 class Wavy_line (Music_xml_spanner):
     pass
 
 class Wavy_line (Music_xml_spanner):
     pass
@@ -834,15 +1040,14 @@ class Rest (Music_xml_node):
     def get_step (self):
         ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
         if ch:
     def get_step (self):
         ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
         if ch:
-            step = ch.get_text ().strip ()
-            return step
+            return ch.get_text ().strip ()
         else:
             return None
     def get_octave (self):
         ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
         if ch:
         else:
             return None
     def get_octave (self):
         ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
         if ch:
-            step = ch.get_text ().strip ()
-            return int (step)
+            oct = ch.get_text ().strip ()
+            return int (oct)
         else:
             return None
 
         else:
             return None
 
@@ -861,10 +1066,7 @@ class DirType (Music_xml_node):
 class Bend (Music_xml_node):
     def bend_alter (self):
         alter = self.get_maybe_exist_named_child ('bend-alter')
 class Bend (Music_xml_node):
     def bend_alter (self):
         alter = self.get_maybe_exist_named_child ('bend-alter')
-        if alter:
-            return alter.get_text()
-        else:
-            return 0
+        return interpret_alter_element (alter)
 
 class Words (Music_xml_node):
     pass
 
 class Words (Music_xml_node):
     pass
@@ -872,16 +1074,41 @@ class Words (Music_xml_node):
 class Harmony (Music_xml_node):
     pass
 
 class Harmony (Music_xml_node):
     pass
 
-class Root (Music_xml_node):
+class ChordPitch (Music_xml_node):
+    def step_class_name (self):
+        return u'root-step'
+    def alter_class_name (self):
+        return u'root-alter'
     def get_step (self):
     def get_step (self):
-        ch = self.get_unique_typed_child (get_class (u'root-step'))
+        ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
         return ch.get_text ().strip ()
     def get_alteration (self):
         return ch.get_text ().strip ()
     def get_alteration (self):
-        ch = self.get_maybe_exist_typed_child (get_class (u'root-alter'))
-        alter = 0
+        ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
+        return interpret_alter_element (ch)
+
+class Root (ChordPitch):
+    pass
+
+class Bass (ChordPitch):
+    def step_class_name (self):
+        return u'bass-step'
+    def alter_class_name (self):
+        return u'bass-alter'
+
+class ChordModification (Music_xml_node):
+    def get_type (self):
+        ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
+        return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
+    def get_value (self):
+        ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
+        value = 0
         if ch:
         if ch:
-            alter = int (ch.get_text ().strip ())
-        return alter
+            value = int (ch.get_text ().strip ())
+        return value
+    def get_alter (self):
+        ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
+        return interpret_alter_element (ch)
+
 
 class Frame (Music_xml_node):
     def get_frets (self):
 
 class Frame (Music_xml_node):
     def get_frets (self):
@@ -908,6 +1135,12 @@ class Frame_Note (Music_xml_node):
 class FiguredBass (Music_xml_node):
     pass
 
 class FiguredBass (Music_xml_node):
     pass
 
+class Beats (Music_xml_node):
+    pass
+
+class BeatType (Music_xml_node):
+    pass
+
 class BeatUnit (Music_xml_node):
     pass
 
 class BeatUnit (Music_xml_node):
     pass
 
@@ -917,59 +1150,72 @@ class BeatUnitDot (Music_xml_node):
 class PerMinute (Music_xml_node):
     pass
 
 class PerMinute (Music_xml_node):
     pass
 
+class Print (Music_xml_node):
+    pass
+
 
 
 ## need this, not all classes are instantiated
 ## for every input file. Only add those classes, that are either directly
 ## used by class name or extend Music_xml_node in some way!
 class_dict = {
 
 
 ## need this, not all classes are instantiated
 ## for every input file. Only add those classes, that are either directly
 ## used by class name or extend Music_xml_node in some way!
 class_dict = {
-       '#comment': Hash_comment,
+        '#comment': Hash_comment,
         '#text': Hash_text,
         '#text': Hash_text,
-       'accidental': Accidental,
-       'attributes': Attributes,
+        'accidental': Accidental,
+        'attributes': Attributes,
         'barline': Barline,
         'bar-style': BarStyle,
         'barline': Barline,
         'bar-style': BarStyle,
-       'beam' : Beam,
+        'bass': Bass,
+        'beam' : Beam,
+        'beats': Beats,
+        'beat-type': BeatType,
         'beat-unit': BeatUnit,
         'beat-unit-dot': BeatUnitDot,
         'bend' : Bend,
         'bracket' : Bracket,
         'beat-unit': BeatUnit,
         'beat-unit-dot': BeatUnitDot,
         'bend' : Bend,
         'bracket' : Bracket,
-       'chord': Chord,
+        'chord': Chord,
         'dashes' : Dashes,
         'dashes' : Dashes,
-       'dot': Dot,
-       'direction': Direction,
+        'degree' : ChordModification,
+        'dot': Dot,
+        'direction': Direction,
         'direction-type': DirType,
         'direction-type': DirType,
-       'duration': Duration,
+        'duration': Duration,
+        'elision': Elision,
+        'extend': Extend,
         'frame': Frame,
         'frame-note': Frame_Note,
         'figured-bass': FiguredBass,
         'glissando': Glissando,
         'frame': Frame,
         'frame-note': Frame_Note,
         'figured-bass': FiguredBass,
         'glissando': Glissando,
-       'grace': Grace,
+        'grace': Grace,
         'harmony': Harmony,
         'identification': Identification,
         'harmony': Harmony,
         'identification': Identification,
+        'key-alter': KeyAlter,
+        'key-octave': KeyOctave,
+        'key-step': KeyStep,
         'lyric': Lyric,
         'lyric': Lyric,
-       'measure': Measure,
-       'notations': Notations,
-       'note': Note,
+        'measure': Measure,
+        'notations': Notations,
+        'note': Note,
         'octave-shift': Octave_shift,
         'octave-shift': Octave_shift,
-       'part': Part,
+        'part': Part,
     'part-group': Part_group,
     'part-group': Part_group,
-       'part-list': Part_list,
+        'part-list': Part_list,
         'pedal': Pedal,
         'per-minute': PerMinute,
         'pedal': Pedal,
         'per-minute': PerMinute,
-       'pitch': Pitch,
-       'rest': Rest,
+        'pitch': Pitch,
+        'print': Print,
+        'rest': Rest,
         'root': Root,
         'score-part': Score_part,
         'slide': Slide,
         'root': Root,
         'score-part': Score_part,
         'slide': Slide,
-       'slur': Slur,
-       'staff': Staff,
+        'slur': Slur,
+        'staff': Staff,
         'syllabic': Syllabic,
         'text': Text,
         'syllabic': Syllabic,
         'text': Text,
-       'time-modification': Time_modification,
+        'time-modification': Time_modification,
         'tuplet': Tuplet,
         'tuplet': Tuplet,
-       'type': Type,
-       'unpitched': Unpitched,
+        'type': Type,
+        'unpitched': Unpitched,
         'wavy-line': Wavy_line,
         'wedge': Wedge,
         'words': Words,
         'wavy-line': Wavy_line,
         'wedge': Wedge,
         'words': Words,
@@ -988,9 +1234,9 @@ def get_class (name):
     if classname:
         return classname
     else:
     if classname:
         return classname
     else:
-       class_name = name2class_name (name)
-       klass = new.classobj (class_name, (Music_xml_node,) , {})
-       class_dict[name] = klass
+        class_name = name2class_name (name)
+        klass = new.classobj (class_name, (Music_xml_node,) , {})
+        class_dict[name] = klass
         return klass
         
 def lxml_demarshal_node (node):
         return klass
         
 def lxml_demarshal_node (node):
@@ -1009,9 +1255,9 @@ def lxml_demarshal_node (node):
     py_node._children = filter (lambda x: x, py_node._children)
     
     for c in py_node._children:
     py_node._children = filter (lambda x: x, py_node._children)
     
     for c in py_node._children:
-       c._parent = py_node
+        c._parent = py_node
 
 
-    for (k,v) in node.items ():
+    for (k, v) in node.items ():
         py_node.__dict__[k] = v
         py_node._attribute_dict[k] = v
 
         py_node.__dict__[k] = v
         py_node._attribute_dict[k] = v
 
@@ -1021,30 +1267,30 @@ def minidom_demarshal_node (node):
     name = node.nodeName
 
     klass = get_class (name)
     name = node.nodeName
 
     klass = get_class (name)
-    py_node = klass()
+    py_node = klass ()
     py_node._name = name
     py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
     for c in py_node._children:
     py_node._name = name
     py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
     for c in py_node._children:
-       c._parent = py_node
+        c._parent = py_node
 
     if node.attributes:
 
     if node.attributes:
-       for (nm, value) in node.attributes.items():
-           py_node.__dict__[nm] = value
+        for (nm, value) in node.attributes.items ():
+            py_node.__dict__[nm] = value
             py_node._attribute_dict[nm] = value
             
     py_node._data = None
     if node.nodeType == node.TEXT_NODE and node.data:
             py_node._attribute_dict[nm] = value
             
     py_node._data = None
     if node.nodeType == node.TEXT_NODE and node.data:
-       py_node._data = node.data 
+        py_node._data = node.data
 
     py_node._original = node
     return py_node
 
 
 if __name__  == '__main__':
 
     py_node._original = node
     return py_node
 
 
 if __name__  == '__main__':
-        import lxml.etree
+    import lxml.etree
         
         
-        tree = lxml.etree.parse ('beethoven.xml')
-        mxl_tree = lxml_demarshal_node (tree.getroot ())
-        ks = class_dict.keys()
-        ks.sort()
-        print '\n'.join (ks)
+    tree = lxml.etree.parse ('beethoven.xml')
+    mxl_tree = lxml_demarshal_node (tree.getroot ())
+    ks = class_dict.keys ()
+    ks.sort ()
+    print '\n'.join (ks)