5 from rational import Rational
7 from xml.dom import minidom, Node
15 self._name = 'xml_node'
26 if not self._children:
29 return ''.join ([c.get_text () for c in self._children])
31 def get_typed_children (self, klass):
32 return [c for c in self._children if c.__class__ == klass]
34 def get_children (self, predicate):
35 return [c for c in self._children if predicate(c)]
37 def get_all_children (self):
40 def get_maybe_exist_typed_child (self, klass):
41 cn = self.get_typed_children (klass)
47 raise "More than 1 child", klass
49 def get_unique_typed_child (self, klass):
50 cn = self.get_typed_children(klass)
53 raise 'Child is not unique for', (klass, 'found', cn)
57 class Music_xml_node (Xml_node):
59 Xml_node.__init__ (self)
60 self.duration = Rational (0)
61 self.start = Rational (0)
63 class Attributes (Music_xml_node):
67 def set_attributes_from_previous (self, dict):
68 self._dict.update (dict)
70 for c in self.get_all_children ():
71 self._dict[c.get_name()] = c
73 def get_named_attribute (self, name):
74 return self._dict[name]
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):
82 def to_ly (self, output_func):
83 output_func ('%% %s\n ' % self.get_text())
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 def to_ly (self, output_func):
104 oct = (self.get_octave () - 4)
111 alt = self.get_alteration ()
118 output_func ('%s%s%s' % (self.get_step ().lower(), alt_str, oct_str))
120 class Note (Music_xml_node):
121 def get_duration_log (self):
122 ch = self.get_maybe_exist_typed_child (class_dict[u'type'])
125 log = ch.get_text ().strip()
137 def get_factor (self):
140 def get_pitches (self):
141 return self.get_typed_children (class_dict[u'pitch'])
143 def to_ly (self, func):
144 ps = self.get_pitches ()
154 func ('%d ' % (1 << self.get_duration_log ()))
159 class Measure(Music_xml_node):
160 def get_notes (self):
161 return self.get_typed_children (class_dict[u'note'])
162 def to_ly (self, func):
163 func (' { % measure \n ')
164 for c in self._children:
168 class Part (Music_xml_node):
169 def to_ly (self, func):
170 func (' { %% part %s \n ' % self.name)
171 for c in self._children:
175 def interpret (self):
176 """Set durations and starting points."""
179 factor = Rational (1)
181 measures = self.get_typed_children (Measure)
184 for n in m.get_all_children ():
187 if n.__class__ == Attributes:
188 n.set_attributes_from_previous (attr_dict)
190 attr_dict = n._dict.copy ()
192 factor = Rational (1,
193 string.atoi (attr_dict['divisions']
195 elif (n.get_maybe_exist_typed_child (Duration)
196 and not n.get_maybe_exist_typed_child (Chord)):
197 mxl_dur = n.get_maybe_exist_typed_child (Duration)
198 dur = mxl_dur.get_length () * factor
199 if n.get_name() == 'backup':
201 if n.get_maybe_exist_typed_child (Grace):
208 def extract_voices (part):
210 measures = part.get_typed_children (Measure)
213 elements.extend (m.get_typed_children (Note))
216 voice_id = n.get_maybe_exist_typed_child (class_dict['voice'])
221 id = voice_id.get_text ()
222 if not voices.has_key (id):
225 voices[id].append (n)
227 part._voices = voices
228 def get_voices (self):
231 class Chord (Music_xml_node):
233 class Dot (Music_xml_node):
235 class Alter (Music_xml_node):
238 class Rest (Music_xml_node):
241 class Type (Music_xml_node):
243 class Grace (Music_xml_node):
252 'duration': Duration,
253 'attributes': Attributes,
259 '#comment': Hash_comment,
262 def name2class_name (name):
263 name = name.replace ('-', '_')
264 name = name.replace ('#', 'hash_')
265 name = name[0].upper() + name[1:].lower()
269 def create_classes (names, dict):
274 class_name = name2class_name (n)
275 klass = new.classobj (class_name, (Music_xml_node,) , {})
278 def element_names (node, dict):
279 dict[node.nodeName] = 1
280 for cn in node.childNodes:
281 element_names (cn, dict)
284 def demarshal_node (node):
286 klass = class_dict[name]
289 py_node._children = [demarshal_node (cn) for cn in node.childNodes]
291 for (name, value) in node.attributes.items():
295 if node.nodeType == node.TEXT_NODE and node.data:
296 py_node._data = node.data
298 py_node._original = node
301 def strip_white_space (node):
303 [c for c in node._children
304 if not (c._original.nodeType == Node.TEXT_NODE and
305 re.match (r'^\s*$', c._data))]
307 for c in node._children:
308 strip_white_space (c)
310 def create_tree (name):
311 doc = minidom.parse(name)
312 node = doc.documentElement
313 names = element_names (node, {}).keys()
314 create_classes (names, class_dict)
316 return demarshal_node (node)
319 n = tree._children[-2]._children[-1]._children[0]
321 print n.get_duration_log()
322 print n.get_pitches()
323 print n.get_pitches()[0].get_alteration()
326 def test_musicxml (tree):
327 m = tree._children[-2]
330 m.to_ly (lambda str: sys.stdout.write (str))
332 def read_musicxml (name):
333 tree = create_tree (name)
334 strip_white_space (tree)
337 if __name__ == '__main__':
338 tree = read_musicxml ('BeetAnGeSample.xml')