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_named_child (self, nm):
42 return self.get_maybe_exist_named_child (nm)
44 def get_children (self, predicate):
45 return [c for c in self._children if predicate(c)]
47 def get_all_children (self):
50 def get_maybe_exist_named_child (self, name):
51 return self.get_maybe_exist_typed_child (class_dict[name])
53 def get_maybe_exist_typed_child (self, klass):
54 cn = self.get_typed_children (klass)
60 raise "More than 1 child", klass
62 def get_unique_typed_child (self, klass):
63 cn = self.get_typed_children(klass)
66 raise 'Child is not unique for', (klass, 'found', cn)
70 class Music_xml_node (Xml_node):
72 Xml_node.__init__ (self)
73 self.duration = Rational (0)
74 self.start = Rational (0)
77 class Duration (Music_xml_node):
78 def get_length (self):
79 dur = string.atoi (self.get_text ()) * Rational (1,4)
82 class Hash_comment (Music_xml_node):
85 class Pitch (Music_xml_node):
87 ch = self.get_unique_typed_child (class_dict[u'step'])
88 step = ch.get_text ().strip ()
90 def get_octave (self):
91 ch = self.get_unique_typed_child (class_dict[u'octave'])
93 step = ch.get_text ().strip ()
94 return string.atoi (step)
96 def get_alteration (self):
97 ch = self.get_maybe_exist_typed_child (class_dict[u'alter'])
100 alter = string.atoi (ch.get_text ().strip ())
103 class Measure_element (Music_xml_node):
104 def get_voice_id (self):
105 voice_id = self.get_maybe_exist_named_child ('voice')
107 return voice_id.get_text ()
112 cn = self._parent.get_typed_children (self.__class__)
113 cn = [c for c in cn if c.get_voice_id () == self.get_voice_id ()]
116 class Attributes (Measure_element):
118 Measure_element.__init__ (self)
121 def set_attributes_from_previous (self, dict):
122 self._dict.update (dict)
123 def read_self (self):
124 for c in self.get_all_children ():
125 self._dict[c.get_name()] = c
127 def get_named_attribute (self, name):
128 return self._dict[name]
130 class Note (Measure_element):
131 def get_duration_log (self):
132 ch = self.get_maybe_exist_typed_child (class_dict[u'type'])
135 log = ch.get_text ().strip()
147 def get_factor (self):
150 def get_pitches (self):
151 return self.get_typed_children (class_dict[u'pitch'])
153 class Part_list (Music_xml_node):
156 class Measure(Music_xml_node):
157 def get_notes (self):
158 return self.get_typed_children (class_dict[u'note'])
161 class Musicxml_voice:
165 self._start_staff = None
167 def add_element (self, e):
168 self._elements.append (e)
169 if (isinstance (e, Note)
170 and e.get_maybe_exist_typed_child (Staff)):
171 name = e.get_maybe_exist_typed_child (Staff).get_text ()
173 if not self._start_staff:
174 self._start_staff = name
175 self._staves[name] = True
177 def insert (self, idx, e):
178 self._elements.insert (idx, e)
182 class Part (Music_xml_node):
186 def interpret (self):
187 """Set durations and starting points."""
190 factor = Rational (1)
192 measures = self.get_typed_children (Measure)
195 for n in m.get_all_children ():
198 if n.__class__ == Attributes:
199 n.set_attributes_from_previous (attr_dict)
201 attr_dict = n._dict.copy ()
203 factor = Rational (1,
204 string.atoi (attr_dict['divisions']
206 elif (n.get_maybe_exist_typed_child (Duration)
207 and not n.get_maybe_exist_typed_child (Chord)):
208 mxl_dur = n.get_maybe_exist_typed_child (Duration)
209 dur = mxl_dur.get_length () * factor
210 if n.get_name() == 'backup':
212 if n.get_maybe_exist_typed_child (Grace):
219 def extract_voices (part):
221 measures = part.get_typed_children (Measure)
224 elements.extend (m.get_all_children ())
228 voice_id = n.get_maybe_exist_typed_child (class_dict['voice'])
230 if not (voice_id or isinstance (n, Attributes)):
233 if isinstance (n, Attributes) and not start_attr:
237 if isinstance (n, Attributes):
238 for v in voices.values ():
242 id = voice_id.get_text ()
243 if not voices.has_key (id):
244 voices[id] = Musicxml_voice()
246 voices[id].add_element (n)
249 for (k,v) in voices.items ():
250 v.insert (0, start_attr)
252 part._voices = voices
254 def get_voices (self):
257 class Notations (Music_xml_node):
259 ts = self.get_named_children ('tied')
260 starts = [t for t in ts if t.type == 'start']
266 def get_tuplet (self):
267 return self.get_maybe_exist_typed_child (Tuplet)
269 class Time_modification(Music_xml_node):
270 def get_fraction (self):
271 b = self.get_maybe_exist_typed_child (class_dict['actual-notes'])
272 a = self.get_maybe_exist_typed_child (class_dict['normal-notes'])
273 return (string.atoi(a.get_text ()), string.atoi (b.get_text ()))
275 class Accidental (Music_xml_node):
277 Music_xml_node.__init__ (self)
278 self.editorial = False
279 self.cautionary = False
282 class Tuplet(Music_xml_node):
285 class Slur (Music_xml_node):
289 class Beam (Music_xml_node):
291 return self.get_text ()
293 class Chord (Music_xml_node):
296 class Dot (Music_xml_node):
299 class Alter (Music_xml_node):
302 class Rest (Music_xml_node):
304 class Mode (Music_xml_node):
306 class Tied (Music_xml_node):
309 class Type (Music_xml_node):
311 class Grace (Music_xml_node):
313 class Staff (Music_xml_node):
317 '#comment': Hash_comment,
318 'accidental': Accidental,
320 'attributes': Attributes,
324 'duration': Duration,
328 'notations': Notations,
335 'time-modification': Time_modification,
338 'part-list': Part_list,
342 def name2class_name (name):
343 name = name.replace ('-', '_')
344 name = name.replace ('#', 'hash_')
345 name = name[0].upper() + name[1:].lower()
349 def create_classes (names, dict):
354 class_name = name2class_name (n)
355 klass = new.classobj (class_name, (Music_xml_node,) , {})
358 def element_names (node, dict):
359 dict[node.nodeName] = 1
360 for cn in node.childNodes:
361 element_names (cn, dict)
364 def demarshal_node (node):
366 klass = class_dict[name]
369 py_node._children = [demarshal_node (cn) for cn in node.childNodes]
370 for c in py_node._children:
375 for (nm, value) in node.attributes.items():
376 py_node.__dict__[nm] = value
379 if node.nodeType == node.TEXT_NODE and node.data:
380 py_node._data = node.data
382 py_node._original = node
385 def strip_white_space (node):
387 [c for c in node._children
388 if not (c._original.nodeType == Node.TEXT_NODE and
389 re.match (r'^\s*$', c._data))]
391 for c in node._children:
392 strip_white_space (c)
394 def create_tree (name):
395 doc = minidom.parse(name)
396 node = doc.documentElement
397 names = element_names (node, {}).keys()
398 create_classes (names, class_dict)
400 return demarshal_node (node)
402 def test_musicxml (tree):
403 m = tree._children[-2]
406 def read_musicxml (name):
407 tree = create_tree (name)
408 strip_white_space (tree)
411 if __name__ == '__main__':
412 tree = read_musicxml ('BeetAnGeSample.xml')