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):
228 def get_tuplet (self):
229 return self.get_maybe_exist_typed_child (Tuplet)
231 slurs = self.get_typed_children (Slur)
237 print "More than one slur?!"
241 class Time_modification(Music_xml_node):
242 def get_fraction (self):
243 b = self.get_maybe_exist_typed_child (class_dict['actual-notes'])
244 a = self.get_maybe_exist_typed_child (class_dict['normal-notes'])
245 return (string.atoi(a.get_text ()), string.atoi (b.get_text ()))
249 class Tuplet(Music_xml_node):
251 class Slur (Music_xml_node):
254 class Chord (Music_xml_node):
256 class Dot (Music_xml_node):
258 class Alter (Music_xml_node):
261 class Rest (Music_xml_node):
264 class Type (Music_xml_node):
266 class Grace (Music_xml_node):
270 'notations': Notations,
271 'time-modification': Time_modification,
277 'duration': Duration,
278 'attributes': Attributes,
286 '#comment': Hash_comment,
289 def name2class_name (name):
290 name = name.replace ('-', '_')
291 name = name.replace ('#', 'hash_')
292 name = name[0].upper() + name[1:].lower()
296 def create_classes (names, dict):
301 class_name = name2class_name (n)
302 klass = new.classobj (class_name, (Music_xml_node,) , {})
305 def element_names (node, dict):
306 dict[node.nodeName] = 1
307 for cn in node.childNodes:
308 element_names (cn, dict)
311 def demarshal_node (node):
313 klass = class_dict[name]
316 py_node._children = [demarshal_node (cn) for cn in node.childNodes]
317 for c in py_node._children:
322 for (nm, value) in node.attributes.items():
323 py_node.__dict__[nm] = value
326 if node.nodeType == node.TEXT_NODE and node.data:
327 py_node._data = node.data
329 py_node._original = node
332 def strip_white_space (node):
334 [c for c in node._children
335 if not (c._original.nodeType == Node.TEXT_NODE and
336 re.match (r'^\s*$', c._data))]
338 for c in node._children:
339 strip_white_space (c)
341 def create_tree (name):
342 doc = minidom.parse(name)
343 node = doc.documentElement
344 names = element_names (node, {}).keys()
345 create_classes (names, class_dict)
347 return demarshal_node (node)
349 def test_musicxml (tree):
350 m = tree._children[-2]
353 def read_musicxml (name):
354 tree = create_tree (name)
355 strip_white_space (tree)
358 if __name__ == '__main__':
359 tree = read_musicxml ('BeetAnGeSample.xml')