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')