5 from rational import Rational
7 from xml.dom import minidom, Node
14 self._name = 'xml_node'
18 return self._parent.get_typed_children (self.__class__)[0] == self
29 if not self._children:
32 return ''.join ([c.get_text () for c in self._children])
34 def get_typed_children (self, klass):
35 return [c for c in self._children if isinstance(c, klass)]
37 def get_named_children (self, nm):
38 return self.get_typed_children (class_dict[nm])
40 def get_named_child (self, nm):
41 return self.get_maybe_exist_named_child (nm)
43 def get_children (self, predicate):
44 return [c for c in self._children if predicate(c)]
46 def get_all_children (self):
49 def get_maybe_exist_named_child (self, name):
50 return self.get_maybe_exist_typed_child (class_dict[name])
52 def get_maybe_exist_typed_child (self, klass):
53 cn = self.get_typed_children (klass)
59 raise "More than 1 child", klass
61 def get_unique_typed_child (self, klass):
62 cn = self.get_typed_children(klass)
65 raise 'Child is not unique for', (klass, 'found', cn)
69 class Music_xml_node (Xml_node):
71 Xml_node.__init__ (self)
72 self.duration = Rational (0)
73 self.start = Rational (0)
76 class Duration (Music_xml_node):
77 def get_length (self):
78 dur = string.atoi (self.get_text ()) * Rational (1,4)
81 class Hash_comment (Music_xml_node):
84 class Pitch (Music_xml_node):
86 ch = self.get_unique_typed_child (class_dict[u'step'])
87 step = ch.get_text ().strip ()
89 def get_octave (self):
90 ch = self.get_unique_typed_child (class_dict[u'octave'])
92 step = ch.get_text ().strip ()
93 return string.atoi (step)
95 def get_alteration (self):
96 ch = self.get_maybe_exist_typed_child (class_dict[u'alter'])
99 alter = string.atoi (ch.get_text ().strip ())
102 class Measure_element (Music_xml_node):
103 def get_voice_id (self):
104 voice_id = self.get_maybe_exist_named_child ('voice')
106 return voice_id.get_text ()
111 cn = self._parent.get_typed_children (self.__class__)
112 cn = [c for c in cn if c.get_voice_id () == self.get_voice_id ()]
115 class Attributes (Measure_element):
117 Measure_element.__init__ (self)
120 def set_attributes_from_previous (self, dict):
121 self._dict.update (dict)
122 def read_self (self):
123 for c in self.get_all_children ():
124 self._dict[c.get_name()] = c
126 def get_named_attribute (self, name):
127 return self._dict[name]
129 class Note (Measure_element):
130 def get_duration_log (self):
131 ch = self.get_maybe_exist_typed_child (class_dict[u'type'])
134 log = ch.get_text ().strip()
146 def get_factor (self):
149 def get_pitches (self):
150 return self.get_typed_children (class_dict[u'pitch'])
152 class Part_list (Music_xml_node):
155 class Measure(Music_xml_node):
156 def get_notes (self):
157 return self.get_typed_children (class_dict[u'note'])
160 class Musicxml_voice:
164 self._start_staff = None
166 def add_element (self, e):
167 self._elements.append (e)
168 if (isinstance (e, Note)
169 and e.get_maybe_exist_typed_child (Staff)):
170 name = e.get_maybe_exist_typed_child (Staff).get_text ()
172 if not self._start_staff:
173 self._start_staff = name
174 self._staves[name] = True
176 def insert (self, idx, e):
177 self._elements.insert (idx, e)
181 class Part (Music_xml_node):
185 def interpret (self):
186 """Set durations and starting points."""
189 factor = Rational (1)
191 measures = self.get_typed_children (Measure)
194 for n in m.get_all_children ():
197 if n.__class__ == Attributes:
198 n.set_attributes_from_previous (attr_dict)
200 attr_dict = n._dict.copy ()
202 factor = Rational (1,
203 string.atoi (attr_dict['divisions']
205 elif (n.get_maybe_exist_typed_child (Duration)
206 and not n.get_maybe_exist_typed_child (Chord)):
207 mxl_dur = n.get_maybe_exist_typed_child (Duration)
208 dur = mxl_dur.get_length () * factor
209 if n.get_name() == 'backup':
211 if n.get_maybe_exist_typed_child (Grace):
218 def extract_voices (part):
220 measures = part.get_typed_children (Measure)
223 elements.extend (m.get_all_children ())
227 voice_id = n.get_maybe_exist_typed_child (class_dict['voice'])
229 if not (voice_id or isinstance (n, Attributes)):
232 if isinstance (n, Attributes) and not start_attr:
236 if isinstance (n, Attributes):
237 for v in voices.values ():
241 id = voice_id.get_text ()
242 if not voices.has_key (id):
243 voices[id] = Musicxml_voice()
245 voices[id].add_element (n)
248 for (k,v) in voices.items ():
249 v.insert (0, start_attr)
251 part._voices = voices
253 def get_voices (self):
256 class Notations (Music_xml_node):
258 ts = self.get_named_children ('tied')
259 starts = [t for t in ts if t.type == 'start']
265 def get_tuplet (self):
266 return self.get_maybe_exist_typed_child (Tuplet)
268 class Time_modification(Music_xml_node):
269 def get_fraction (self):
270 b = self.get_maybe_exist_typed_child (class_dict['actual-notes'])
271 a = self.get_maybe_exist_typed_child (class_dict['normal-notes'])
272 return (string.atoi(a.get_text ()), string.atoi (b.get_text ()))
274 class Accidental (Music_xml_node):
276 Music_xml_node.__init__ (self)
277 self.editorial = False
278 self.cautionary = False
281 class Tuplet(Music_xml_node):
284 class Slur (Music_xml_node):
288 class Beam (Music_xml_node):
290 return self.get_text ()
292 class Chord (Music_xml_node):
295 class Dot (Music_xml_node):
298 class Alter (Music_xml_node):
301 class Rest (Music_xml_node):
303 class Mode (Music_xml_node):
305 class Tied (Music_xml_node):
308 class Type (Music_xml_node):
310 class Grace (Music_xml_node):
312 class Staff (Music_xml_node):
316 '#comment': Hash_comment,
317 'accidental': Accidental,
319 'attributes': Attributes,
323 'duration': Duration,
327 'notations': Notations,
334 'time-modification': Time_modification,
337 'part-list': Part_list,
341 def name2class_name (name):
342 name = name.replace ('-', '_')
343 name = name.replace ('#', 'hash_')
344 name = name[0].upper() + name[1:].lower()
348 def create_classes (names, dict):
353 class_name = name2class_name (n)
354 klass = new.classobj (class_name, (Music_xml_node,) , {})
357 def element_names (node, dict):
358 dict[node.nodeName] = 1
359 for cn in node.childNodes:
360 element_names (cn, dict)
363 def demarshal_node (node):
365 klass = class_dict[name]
368 py_node._children = [demarshal_node (cn) for cn in node.childNodes]
369 for c in py_node._children:
374 for (nm, value) in node.attributes.items():
375 py_node.__dict__[nm] = value
378 if node.nodeType == node.TEXT_NODE and node.data:
379 py_node._data = node.data
381 py_node._original = node
384 def strip_white_space (node):
386 [c for c in node._children
387 if not (c._original.nodeType == Node.TEXT_NODE and
388 re.match (r'^\s*$', c._data))]
390 for c in node._children:
391 strip_white_space (c)
393 def create_tree (name):
394 doc = minidom.parse(name)
395 node = doc.documentElement
396 names = element_names (node, {}).keys()
397 create_classes (names, class_dict)
399 return demarshal_node (node)
401 def test_musicxml (tree):
402 m = tree._children[-2]
405 def read_musicxml (name):
406 tree = create_tree (name)
407 strip_white_space (tree)
410 if __name__ == '__main__':
411 tree = read_musicxml ('BeetAnGeSample.xml')