5 from rational import Rational
7 from xml.dom import minidom, Node
15 self._name = 'xml_node'
19 return self._parent.get_typed_children (self.__class__)[0] == self
30 if not self._children:
33 return ''.join ([c.get_text () for c in self._children])
35 def get_typed_children (self, klass):
36 return [c for c in self._children if isinstance(c, klass)]
38 def get_named_children (self, nm):
39 return self.get_typed_children (class_dict[nm])
41 def get_children (self, predicate):
42 return [c for c in self._children if predicate(c)]
44 def get_all_children (self):
47 def get_maybe_exist_named_child (self, name):
48 return self.get_maybe_exist_typed_child (class_dict[name])
50 def get_maybe_exist_typed_child (self, klass):
51 cn = self.get_typed_children (klass)
57 raise "More than 1 child", klass
59 def get_unique_typed_child (self, klass):
60 cn = self.get_typed_children(klass)
63 raise 'Child is not unique for', (klass, 'found', cn)
67 class Music_xml_node (Xml_node):
69 Xml_node.__init__ (self)
70 self.duration = Rational (0)
71 self.start = Rational (0)
74 class Duration (Music_xml_node):
75 def get_length (self):
76 dur = string.atoi (self.get_text ()) * Rational (1,4)
79 class Hash_comment (Music_xml_node):
82 class Pitch (Music_xml_node):
84 ch = self.get_unique_typed_child (class_dict[u'step'])
85 step = ch.get_text ().strip ()
87 def get_octave (self):
88 ch = self.get_unique_typed_child (class_dict[u'octave'])
90 step = ch.get_text ().strip ()
91 return string.atoi (step)
93 def get_alteration (self):
94 ch = self.get_maybe_exist_typed_child (class_dict[u'alter'])
97 alter = string.atoi (ch.get_text ().strip ())
100 class Measure_element (Music_xml_node):
101 def get_voice_id (self):
102 voice_id = self.get_maybe_exist_named_child ('voice')
104 return voice_id.get_text ()
109 cn = self._parent.get_typed_children (self.__class__)
110 cn = [c for c in cn if c.get_voice_id () == self.get_voice_id ()]
113 class Attributes (Measure_element):
115 Measure_element.__init__ (self)
118 def set_attributes_from_previous (self, dict):
119 self._dict.update (dict)
120 def read_self (self):
121 for c in self.get_all_children ():
122 self._dict[c.get_name()] = c
124 def get_named_attribute (self, name):
125 return self._dict[name]
127 class Note (Measure_element):
128 def get_duration_log (self):
129 ch = self.get_maybe_exist_typed_child (class_dict[u'type'])
132 log = ch.get_text ().strip()
144 def get_factor (self):
147 def get_pitches (self):
148 return self.get_typed_children (class_dict[u'pitch'])
152 class Measure(Music_xml_node):
153 def get_notes (self):
154 return self.get_typed_children (class_dict[u'note'])
156 class Part (Music_xml_node):
157 def interpret (self):
158 """Set durations and starting points."""
161 factor = Rational (1)
163 measures = self.get_typed_children (Measure)
166 for n in m.get_all_children ():
169 if n.__class__ == Attributes:
170 n.set_attributes_from_previous (attr_dict)
172 attr_dict = n._dict.copy ()
174 factor = Rational (1,
175 string.atoi (attr_dict['divisions']
177 elif (n.get_maybe_exist_typed_child (Duration)
178 and not n.get_maybe_exist_typed_child (Chord)):
179 mxl_dur = n.get_maybe_exist_typed_child (Duration)
180 dur = mxl_dur.get_length () * factor
181 if n.get_name() == 'backup':
183 if n.get_maybe_exist_typed_child (Grace):
190 def extract_voices (part):
192 measures = part.get_typed_children (Measure)
195 elements.extend (m.get_all_children ())
199 voice_id = n.get_maybe_exist_typed_child (class_dict['voice'])
201 if not (voice_id or isinstance (n, Attributes)):
204 if isinstance (n, Attributes) and not start_attr:
208 if isinstance (n, Attributes):
209 for v in voices.values ():
213 id = voice_id.get_text ()
214 if not voices.has_key (id):
217 voices[id].append (n)
220 for (k,v) in voices.items ():
221 v.insert (0, start_attr)
223 part._voices = voices
224 def get_voices (self):
227 class Notations (Music_xml_node):
229 return self.get_maybe_exist_named_child ('tied')
231 def get_tuplet (self):
232 return self.get_maybe_exist_typed_child (Tuplet)
235 class Time_modification(Music_xml_node):
236 def get_fraction (self):
237 b = self.get_maybe_exist_typed_child (class_dict['actual-notes'])
238 a = self.get_maybe_exist_typed_child (class_dict['normal-notes'])
239 return (string.atoi(a.get_text ()), string.atoi (b.get_text ()))
241 class Accidental (Music_xml_node):
243 Music_xml_node.__init__ (self)
244 self.editorial = False
245 self.cautionary = False
248 class Tuplet(Music_xml_node):
251 class Slur (Music_xml_node):
255 class Beam (Music_xml_node):
257 return self.get_text ()
259 class Chord (Music_xml_node):
262 class Dot (Music_xml_node):
265 class Alter (Music_xml_node):
268 class Rest (Music_xml_node):
270 class Mode (Music_xml_node):
273 class Type (Music_xml_node):
275 class Grace (Music_xml_node):
279 '#comment': Hash_comment,
280 'accidental': Accidental,
282 'attributes': Attributes,
286 'duration': Duration,
290 'notations': Notations,
296 'time-modification': Time_modification,
301 def name2class_name (name):
302 name = name.replace ('-', '_')
303 name = name.replace ('#', 'hash_')
304 name = name[0].upper() + name[1:].lower()
308 def create_classes (names, dict):
313 class_name = name2class_name (n)
314 klass = new.classobj (class_name, (Music_xml_node,) , {})
317 def element_names (node, dict):
318 dict[node.nodeName] = 1
319 for cn in node.childNodes:
320 element_names (cn, dict)
323 def demarshal_node (node):
325 klass = class_dict[name]
328 py_node._children = [demarshal_node (cn) for cn in node.childNodes]
329 for c in py_node._children:
334 for (nm, value) in node.attributes.items():
335 py_node.__dict__[nm] = value
338 if node.nodeType == node.TEXT_NODE and node.data:
339 py_node._data = node.data
341 py_node._original = node
344 def strip_white_space (node):
346 [c for c in node._children
347 if not (c._original.nodeType == Node.TEXT_NODE and
348 re.match (r'^\s*$', c._data))]
350 for c in node._children:
351 strip_white_space (c)
353 def create_tree (name):
354 doc = minidom.parse(name)
355 node = doc.documentElement
356 names = element_names (node, {}).keys()
357 create_classes (names, class_dict)
359 return demarshal_node (node)
361 def test_musicxml (tree):
362 m = tree._children[-2]
365 def read_musicxml (name):
366 tree = create_tree (name)
367 strip_white_space (tree)
370 if __name__ == '__main__':
371 tree = read_musicxml ('BeetAnGeSample.xml')