]> git.donarmstrong.com Git - lilypond.git/blob - python/musicxml.py
* python/musicxml.py (Accidental.__init__): new class.
[lilypond.git] / python / musicxml.py
1 import sys
2 import new
3 import re
4 import string
5 from rational import Rational
6
7 from xml.dom import minidom, Node
8
9
10 class Xml_node:
11         def __init__ (self):
12                 self._children = []
13                 self._data = None
14                 self._original = None
15                 self._name = 'xml_node'
16                 self._parent = None
17
18         def is_first (self):
19                 return self._parent.get_typed_children (self.__class__)[0] == self
20
21         def original (self):
22                 return self._original 
23         def get_name (self):
24                 return self._name
25         
26         def get_text (self):
27                 if self._data:
28                         return self._data
29
30                 if not self._children:
31                         return ''
32                 
33                 return ''.join ([c.get_text () for c in self._children])
34
35         def get_typed_children (self, klass):
36                 return [c for c in self._children if isinstance(c, klass)]
37         
38         def get_named_children (self, nm):
39                 return self.get_typed_children (class_dict[nm])
40
41         def get_children (self, predicate):
42                 return [c for c in self._children if predicate(c)]
43
44         def get_all_children (self):
45                 return self._children
46
47         def get_maybe_exist_named_child (self, name):
48                 return self.get_maybe_exist_typed_child (class_dict[name])
49         
50         def get_maybe_exist_typed_child (self, klass):
51                 cn = self.get_typed_children (klass)
52                 if len (cn)==0:
53                         return None
54                 elif len (cn) == 1:
55                         return cn[0]
56                 else:
57                         raise "More than 1 child", klass
58                 
59         def get_unique_typed_child (self, klass):
60                 cn = self.get_typed_children(klass)
61                 if len (cn) <> 1:
62                         print self.__dict__ 
63                         raise 'Child is not unique for', (klass, 'found', cn)
64
65                 return cn[0]
66         
67 class Music_xml_node (Xml_node):
68         def __init__ (self):
69                 Xml_node.__init__ (self)
70                 self.duration = Rational (0)
71                 self.start = Rational (0)
72                 
73
74 class Duration (Music_xml_node):
75         def get_length (self):
76                 dur = string.atoi (self.get_text ()) * Rational (1,4)
77                 return dur
78                 
79 class Hash_comment (Music_xml_node):
80         pass
81                 
82 class Pitch (Music_xml_node):
83         def get_step (self):
84                 ch = self.get_unique_typed_child (class_dict[u'step'])
85                 step = ch.get_text ().strip ()
86                 return step
87         def get_octave (self):
88                 ch = self.get_unique_typed_child (class_dict[u'octave'])
89
90                 step = ch.get_text ().strip ()
91                 return string.atoi (step)
92         
93         def get_alteration (self):
94                 ch = self.get_maybe_exist_typed_child (class_dict[u'alter'])
95                 alter = 0
96                 if ch:
97                         alter = string.atoi (ch.get_text ().strip ())
98                 return alter
99
100 class Measure_element (Music_xml_node):
101         def get_voice_id (self):
102                 voice_id = self.get_maybe_exist_named_child ('voice')
103                 if voice_id:
104                         return voice_id.get_text ()
105                 else:
106                         return None
107                 
108         def is_first (self):
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 ()]
111                 return cn[0] == self
112         
113 class Attributes (Measure_element):
114         def __init__ (self):
115                 Measure_element.__init__ (self)
116                 self._dict = {}
117         
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
123
124         def get_named_attribute (self, name):
125                 return self._dict[name]
126                 
127 class Note (Measure_element):
128         def get_duration_log (self):
129                 ch = self.get_maybe_exist_typed_child (class_dict[u'type'])
130
131                 if ch:
132                         log = ch.get_text ().strip()
133                         return  {'eighth': 3,
134                                  'quarter': 2,
135                                  'half': 1,
136                                  '16th': 4,
137                                  '32nd': 5,
138                                  'breve': -1,
139                                  'long': -2,
140                                  'whole': 0} [log]
141                 else:
142                         return 0
143                 
144         def get_factor (self):
145                 return 1
146         
147         def get_pitches (self):
148                 return self.get_typed_children (class_dict[u'pitch'])
149
150
151                 
152 class Measure(Music_xml_node):
153         def get_notes (self):
154                 return self.get_typed_children (class_dict[u'note'])
155
156 class Part (Music_xml_node):
157         def interpret (self):
158                 """Set durations and starting points."""
159                 
160                 now = Rational (0)
161                 factor = Rational (1)
162                 attr_dict = {}
163                 measures = self.get_typed_children (Measure)
164
165                 for m in measures:
166                         for n in m.get_all_children ():
167                                 dur = Rational (0)
168                                 
169                                 if n.__class__ == Attributes:
170                                         n.set_attributes_from_previous (attr_dict)
171                                         n.read_self ()
172                                         attr_dict = n._dict.copy ()
173                                         
174                                         factor = Rational (1,
175                                                            string.atoi (attr_dict['divisions']
176                                                                         .get_text ()))
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':
182                                                 dur = - dur
183                                         if n.get_maybe_exist_typed_child (Grace):
184                                                 dur = Rational (0)
185                                                 
186                                 n._when = now
187                                 n._duration = dur
188                                 now += dur
189
190         def extract_voices (part):
191                 voices = {}
192                 measures = part.get_typed_children (Measure)
193                 elements = []
194                 for m in measures:
195                         elements.extend (m.get_all_children ())
196
197                 start_attr = None
198                 for n in elements:
199                         voice_id = n.get_maybe_exist_typed_child (class_dict['voice'])
200
201                         if not (voice_id or isinstance (n, Attributes)):
202                                 continue
203
204                         if isinstance (n, Attributes) and not start_attr:
205                                 start_attr = n
206                                 continue
207
208                         if isinstance (n, Attributes):
209                                 for v in voices.values ():
210                                         v.append (n)
211                                 continue
212                         
213                         id = voice_id.get_text ()
214                         if not voices.has_key (id):
215                                 voices[id] = []
216
217                         voices[id].append (n)
218
219                 if start_attr:
220                         for (k,v) in voices.items ():
221                                 v.insert (0, start_attr)
222                 
223                 part._voices = voices
224         def get_voices (self):
225                 return self._voices
226
227 class Notations (Music_xml_node):
228         def get_tie (self):
229                 return self.get_maybe_exist_named_child ('tied')
230         
231         def get_tuplet (self):
232                 return self.get_maybe_exist_typed_child (Tuplet)
233         
234
235 class Time_modification(Music_xml_node):
236         def get_fraction (self):
237                 b = self.get_maybe_exist_typed_child (class_dict['actual-notes'])
238                 a = self.get_maybe_exist_typed_child (class_dict['normal-notes'])
239                 return (string.atoi(a.get_text ()), string.atoi (b.get_text ()))
240
241 class Accidental (Music_xml_node):
242         def __init__ (self):
243                 Music_xml_node.__init__ (self)
244                 self.editorial = False
245                 self.cautionary = False
246                 
247                 
248 class Tuplet(Music_xml_node):
249         pass
250
251 class Slur (Music_xml_node):
252         def get_type (self):
253                 return self.type
254
255 class Beam (Music_xml_node):
256         def get_type (self):
257                 return self.get_text ()
258
259 class Chord (Music_xml_node):
260         pass
261
262 class Dot (Music_xml_node):
263         pass
264
265 class Alter (Music_xml_node):
266         pass
267
268 class Rest (Music_xml_node):
269         pass
270 class Mode (Music_xml_node):
271         pass
272
273 class Type (Music_xml_node):
274         pass
275 class Grace (Music_xml_node):
276         pass
277
278 class_dict = {
279         '#comment': Hash_comment,
280         'accidental': Accidental,
281         'alter': Alter,
282         'attributes': Attributes,
283         'beam' : Beam,
284         'chord': Chord,
285         'dot': Dot,
286         'duration': Duration,
287         'grace': Grace,
288         'mode' : Mode,
289         'measure': Measure,
290         'notations': Notations,
291         'note': Note,
292         'part': Part,
293         'pitch': Pitch,
294         'rest':Rest,
295         'slur': Slur,
296         'time-modification': Time_modification,
297         'tuplet': Tuplet,
298         'type': Type,
299 }
300
301 def name2class_name (name):
302         name = name.replace ('-', '_')
303         name = name.replace ('#', 'hash_')
304         name = name[0].upper() + name[1:].lower()
305         
306         return str (name)
307         
308 def create_classes (names, dict):
309         for n in names:
310                 if dict.has_key (n):
311                         continue
312
313                 class_name = name2class_name (n)
314                 klass = new.classobj (class_name, (Music_xml_node,) , {})
315                 dict[n] = klass
316         
317 def element_names (node, dict):
318         dict[node.nodeName] = 1
319         for cn in node.childNodes:
320                 element_names (cn, dict)
321         return dict
322
323 def demarshal_node (node):
324         name = node.nodeName
325         klass = class_dict[name]
326         py_node = klass()
327         py_node._name = name
328         py_node._children = [demarshal_node (cn) for cn in node.childNodes]
329         for c in py_node._children:
330                 c._parent = py_node
331                 
332         if node.attributes:
333                 
334                 for (nm, value) in node.attributes.items():
335                         py_node.__dict__[nm] = value
336
337         py_node._data = None
338         if node.nodeType == node.TEXT_NODE and node.data:
339                 py_node._data = node.data 
340
341         py_node._original = node
342         return py_node
343                 
344 def strip_white_space (node):
345         node._children = \
346         [c for c in node._children
347          if not (c._original.nodeType == Node.TEXT_NODE and
348                  re.match (r'^\s*$', c._data))]
349         
350         for c in node._children:
351                 strip_white_space (c)
352
353 def create_tree (name):
354         doc = minidom.parse(name)
355         node = doc.documentElement
356         names = element_names (node, {}).keys()
357         create_classes (names, class_dict)
358     
359         return demarshal_node (node)
360         
361 def test_musicxml (tree):
362         m = tree._children[-2]
363         print m
364         
365 def read_musicxml (name):
366         tree = create_tree (name)
367         strip_white_space (tree)
368         return tree
369
370 if __name__  == '__main__':
371         tree = read_musicxml ('BeetAnGeSample.xml')
372         test_musicxml (tree)
373
374