Nitpick: ly:spanner-bound grob name slur -> spanner.
[lilypond.git] / python / musicxml.py
blobdb53bc49106fa5217d4cec879849c91737672079
1 # -*- coding: utf-8 -*-
2 import new
3 import string
4 from rational import *
5 import re
6 import sys
7 import copy
8 import lilylib as ly
10 _ = ly._
12 def error (str):
13 ly.stderr_write ((_ ("error: %s") % str) + "\n")
16 def escape_ly_output_string (input_string):
17 return_string = input_string
18 needs_quotes = not re.match (u"^[a-zA-ZäöüÜÄÖßñ]*$", return_string);
19 if needs_quotes:
20 return_string = "\"" + string.replace (return_string, "\"", "\\\"") + "\""
21 return return_string
24 def musicxml_duration_to_log (dur):
25 return {'256th': 8,
26 '128th': 7,
27 '64th': 6,
28 '32nd': 5,
29 '16th': 4,
30 'eighth': 3,
31 'quarter': 2,
32 'half': 1,
33 'whole': 0,
34 'breve': -1,
35 'longa': -2,
36 'long': -2}.get (dur, 0)
38 def interpret_alter_element (alter_elm):
39 alter = 0
40 if alter_elm:
41 val = eval(alter_elm.get_text ())
42 if type (val) in (int, float):
43 alter = val
44 return alter
47 class Xml_node:
48 def __init__ (self):
49 self._children = []
50 self._data = None
51 self._original = None
52 self._name = 'xml_node'
53 self._parent = None
54 self._attribute_dict = {}
56 def get_parent (self):
57 return self._parent
59 def is_first (self):
60 return self._parent.get_typed_children (self.__class__)[0] == self
62 def original (self):
63 return self._original
64 def get_name (self):
65 return self._name
67 def get_text (self):
68 if self._data:
69 return self._data
71 if not self._children:
72 return ''
74 return ''.join ([c.get_text () for c in self._children])
76 def message (self, msg):
77 ly.stderr_write (msg+'\n')
79 p = self
80 while p:
81 sys.stderr.write (' In: <%s %s>\n' % (p._name, ' '.join (['%s=%s' % item for item in p._attribute_dict.items ()])))
82 p = p.get_parent ()
84 def dump (self, indent = ''):
85 sys.stderr.write ('%s<%s%s>' % (indent, self._name, ''.join ([' %s=%s' % item for item in self._attribute_dict.items ()])))
86 non_text_children = [c for c in self._children if not isinstance (c, Hash_text)]
87 if non_text_children:
88 sys.stderr.write ('\n')
89 for c in self._children:
90 c.dump (indent + " ")
91 if non_text_children:
92 sys.stderr.write (indent)
93 sys.stderr.write ('</%s>\n' % self._name)
96 def get_typed_children (self, klass):
97 if not klass:
98 return []
99 else:
100 return [c for c in self._children if isinstance(c, klass)]
102 def get_named_children (self, nm):
103 return self.get_typed_children (get_class (nm))
105 def get_named_child (self, nm):
106 return self.get_maybe_exist_named_child (nm)
108 def get_children (self, predicate):
109 return [c for c in self._children if predicate(c)]
111 def get_all_children (self):
112 return self._children
114 def get_maybe_exist_named_child (self, name):
115 return self.get_maybe_exist_typed_child (get_class (name))
117 def get_maybe_exist_typed_child (self, klass):
118 cn = self.get_typed_children (klass)
119 if len (cn)==0:
120 return None
121 elif len (cn) == 1:
122 return cn[0]
123 else:
124 raise "More than 1 child", klass
126 def get_unique_typed_child (self, klass):
127 cn = self.get_typed_children(klass)
128 if len (cn) <> 1:
129 sys.stderr.write (self.__dict__ + '\n')
130 raise 'Child is not unique for', (klass, 'found', cn)
132 return cn[0]
134 def get_named_child_value_number (self, name, default):
135 n = self.get_maybe_exist_named_child (name)
136 if n:
137 return string.atoi (n.get_text())
138 else:
139 return default
142 class Music_xml_node (Xml_node):
143 def __init__ (self):
144 Xml_node.__init__ (self)
145 self.duration = Rational (0)
146 self.start = Rational (0)
148 class Work (Xml_node):
149 def get_work_information (self, tag):
150 wt = self.get_maybe_exist_named_child (tag)
151 if wt:
152 return wt.get_text ()
153 else:
154 return ''
156 def get_work_title (self):
157 return self.get_work_information ('work-title')
158 def get_work_number (self):
159 return self.get_work_information ('work-number')
160 def get_opus (self):
161 return self.get_work_information ('opus')
163 class Identification (Xml_node):
164 def get_rights (self):
165 rights = self.get_named_children ('rights')
166 ret = []
167 for r in rights:
168 ret.append (r.get_text ())
169 return string.join (ret, "\n")
171 def get_creator (self, type):
172 creators = self.get_named_children ('creator')
173 # return the first creator tag that has the particular type
174 for i in creators:
175 if hasattr (i, 'type') and i.type == type:
176 return i.get_text ()
177 return None
179 def get_composer (self):
180 c = self.get_creator ('composer')
181 if c:
182 return c
183 creators = self.get_named_children ('creator')
184 # return the first creator tag that has no type at all
185 for i in creators:
186 if not hasattr (i, 'type'):
187 return i.get_text ()
188 return None
189 def get_arranger (self):
190 return self.get_creator ('arranger')
191 def get_editor (self):
192 return self.get_creator ('editor')
193 def get_poet (self):
194 v = self.get_creator ('lyricist')
195 if v:
196 return v
197 v = self.get_creator ('poet')
198 return v
200 def get_encoding_information (self, type):
201 enc = self.get_named_children ('encoding')
202 if enc:
203 children = enc[0].get_named_children (type)
204 if children:
205 return children[0].get_text ()
206 else:
207 return None
209 def get_encoding_software (self):
210 return self.get_encoding_information ('software')
211 def get_encoding_date (self):
212 return self.get_encoding_information ('encoding-date')
213 def get_encoding_person (self):
214 return self.get_encoding_information ('encoder')
215 def get_encoding_description (self):
216 return self.get_encoding_information ('encoding-description')
218 def get_encoding_software_list (self):
219 enc = self.get_named_children ('encoding')
220 software = []
221 for e in enc:
222 softwares = e.get_named_children ('software')
223 for s in softwares:
224 software.append (s.get_text ())
225 return software
227 def get_file_description (self):
228 misc = self.get_named_children ('miscellaneous')
229 for m in misc:
230 misc_fields = m.get_named_children ('miscellaneous-field')
231 for mf in misc_fields:
232 if hasattr (mf, 'name') and mf.name == 'description':
233 return mf.get_text ()
234 return None
238 class Duration (Music_xml_node):
239 def get_length (self):
240 dur = int (self.get_text ()) * Rational (1,4)
241 return dur
243 class Hash_comment (Music_xml_node):
244 pass
245 class Hash_text (Music_xml_node):
246 def dump (self, indent = ''):
247 sys.stderr.write ('%s' % string.strip (self._data))
249 class Pitch (Music_xml_node):
250 def get_step (self):
251 ch = self.get_unique_typed_child (get_class (u'step'))
252 step = ch.get_text ().strip ()
253 return step
254 def get_octave (self):
255 ch = self.get_unique_typed_child (get_class (u'octave'))
257 step = ch.get_text ().strip ()
258 return int (step)
260 def get_alteration (self):
261 ch = self.get_maybe_exist_typed_child (get_class (u'alter'))
262 return interpret_alter_element (ch)
264 class Unpitched (Music_xml_node):
265 def get_step (self):
266 ch = self.get_unique_typed_child (get_class (u'display-step'))
267 step = ch.get_text ().strip ()
268 return step
270 def get_octave (self):
271 ch = self.get_unique_typed_child (get_class (u'display-octave'))
273 if ch:
274 octave = ch.get_text ().strip ()
275 return int (octave)
276 else:
277 return None
279 class Measure_element (Music_xml_node):
280 def get_voice_id (self):
281 voice_id = self.get_maybe_exist_named_child ('voice')
282 if voice_id:
283 return voice_id.get_text ()
284 else:
285 return None
287 def is_first (self):
288 # Look at all measure elements (previously we had self.__class__, which
289 # only looked at objects of the same type!
290 cn = self._parent.get_typed_children (Measure_element)
291 # But only look at the correct voice; But include Attributes, too, which
292 # are not tied to any particular voice
293 cn = [c for c in cn if (c.get_voice_id () == self.get_voice_id ()) or isinstance (c, Attributes)]
294 return cn[0] == self
296 class Attributes (Measure_element):
297 def __init__ (self):
298 Measure_element.__init__ (self)
299 self._dict = {}
300 self._original_tag = None
301 self._time_signature_cache = None
303 def is_first (self):
304 cn = self._parent.get_typed_children (self.__class__)
305 if self._original_tag:
306 return cn[0] == self._original_tag
307 else:
308 return cn[0] == self
310 def set_attributes_from_previous (self, dict):
311 self._dict.update (dict)
313 def read_self (self):
314 for c in self.get_all_children ():
315 self._dict[c.get_name()] = c
317 def get_named_attribute (self, name):
318 return self._dict.get (name)
320 def single_time_sig_to_fraction (self, sig):
321 if len (sig) < 2:
322 return 0
323 n = 0
324 for i in sig[0:-1]:
325 n += i
326 return Rational (n, sig[-1])
328 def get_measure_length (self):
329 sig = self.get_time_signature ()
330 if not sig or len (sig) == 0:
331 return 1
332 if isinstance (sig[0], list):
333 # Complex compound time signature
334 l = 0
335 for i in sig:
336 l += self.single_time_sig_to_fraction (i)
337 return l
338 else:
339 # Simple (maybe compound) time signature of the form (beat, ..., type)
340 return self.single_time_sig_to_fraction (sig)
341 return 0
343 def get_time_signature (self):
344 "Return time sig as a (beat, beat-type) tuple. For compound signatures,"
345 "return either (beat, beat,..., beat-type) or ((beat,..., type), "
346 "(beat,..., type), ...)."
347 if self._time_signature_cache:
348 return self._time_signature_cache
350 try:
351 mxl = self.get_named_attribute ('time')
352 if not mxl:
353 return None
355 if mxl.get_maybe_exist_named_child ('senza-misura'):
356 # TODO: Handle pieces without a time signature!
357 error (_ ("Senza-misura time signatures are not yet supported!"))
358 return (4, 4)
359 else:
360 signature = []
361 current_sig = []
362 for i in mxl.get_all_children ():
363 if isinstance (i, Beats):
364 beats = string.split (i.get_text ().strip (), "+")
365 current_sig = [int (j) for j in beats]
366 elif isinstance (i, BeatType):
367 current_sig.append (int (i.get_text ()))
368 signature.append (current_sig)
369 current_sig = []
370 if isinstance (signature[0], list) and len (signature) == 1:
371 signature = signature[0]
372 self._time_signature_cache = signature
373 return signature
374 except (KeyError, ValueError):
375 self.message (_ ("Unable to interpret time signature! Falling back to 4/4."))
376 return (4, 4)
378 # returns clef information in the form ("cleftype", position, octave-shift)
379 def get_clef_information (self):
380 clefinfo = ['G', 2, 0]
381 mxl = self.get_named_attribute ('clef')
382 if not mxl:
383 return clefinfo
384 sign = mxl.get_maybe_exist_named_child ('sign')
385 if sign:
386 clefinfo[0] = sign.get_text()
387 line = mxl.get_maybe_exist_named_child ('line')
388 if line:
389 clefinfo[1] = string.atoi (line.get_text ())
390 octave = mxl.get_maybe_exist_named_child ('clef-octave-change')
391 if octave:
392 clefinfo[2] = string.atoi (octave.get_text ())
393 return clefinfo
395 def get_key_signature (self):
396 "return (fifths, mode) tuple if the key signatures is given as "
397 "major/minor in the Circle of fifths. Otherwise return an alterations"
398 "list of the form [[step,alter<,octave>], [step,alter<,octave>], ...], "
399 "where the octave values are optional."
401 key = self.get_named_attribute ('key')
402 if not key:
403 return None
404 fifths_elm = key.get_maybe_exist_named_child ('fifths')
405 if fifths_elm:
406 mode_node = key.get_maybe_exist_named_child ('mode')
407 mode = None
408 if mode_node:
409 mode = mode_node.get_text ()
410 if not mode or mode == '':
411 mode = 'major'
412 fifths = int (fifths_elm.get_text ())
413 # TODO: Shall we try to convert the key-octave and the cancel, too?
414 return (fifths, mode)
415 else:
416 alterations = []
417 current_step = 0
418 for i in key.get_all_children ():
419 if isinstance (i, KeyStep):
420 current_step = int (i.get_text ())
421 elif isinstance (i, KeyAlter):
422 alterations.append ([current_step, interpret_alter_element (i)])
423 elif isinstance (i, KeyOctave):
424 nr = -1
425 if hasattr (i, 'number'):
426 nr = int (i.number)
427 if (nr > 0) and (nr <= len (alterations)):
428 # MusicXML Octave 4 is middle C -> shift to 0
429 alterations[nr-1].append (int (i.get_text ())-4)
430 else:
431 i.message (_ ("Key alteration octave given for a "
432 "non-existing alteration nr. %s, available numbers: %s!") % (nr, len(alterations)))
433 return alterations
435 def get_transposition (self):
436 return self.get_named_attribute ('transpose')
438 class KeyAlter (Music_xml_node):
439 pass
440 class KeyStep (Music_xml_node):
441 pass
442 class KeyOctave (Music_xml_node):
443 pass
446 class Barline (Measure_element):
447 pass
448 class BarStyle (Music_xml_node):
449 pass
450 class Partial (Measure_element):
451 def __init__ (self, partial):
452 Measure_element.__init__ (self)
453 self.partial = partial
455 class Note (Measure_element):
456 def __init__ (self):
457 Measure_element.__init__ (self)
458 self.instrument_name = ''
459 self._after_grace = False
460 def is_grace (self):
461 return self.get_maybe_exist_named_child (u'grace')
462 def is_after_grace (self):
463 if not self.is_grace():
464 return False;
465 gr = self.get_maybe_exist_typed_child (Grace)
466 return self._after_grace or hasattr (gr, 'steal-time-previous');
468 def get_duration_log (self):
469 ch = self.get_maybe_exist_named_child (u'type')
471 if ch:
472 log = ch.get_text ().strip()
473 return musicxml_duration_to_log (log)
474 elif self.get_maybe_exist_named_child (u'grace'):
475 # FIXME: is it ok to default to eight note for grace notes?
476 return 3
477 else:
478 return None
480 def get_duration_info (self):
481 log = self.get_duration_log ()
482 if log != None:
483 dots = len (self.get_typed_children (Dot))
484 return (log, dots)
485 else:
486 return None
488 def get_factor (self):
489 return 1
491 def get_pitches (self):
492 return self.get_typed_children (get_class (u'pitch'))
494 class Part_list (Music_xml_node):
495 def __init__ (self):
496 Music_xml_node.__init__ (self)
497 self._id_instrument_name_dict = {}
499 def generate_id_instrument_dict (self):
501 ## not empty to make sure this happens only once.
502 mapping = {1: 1}
503 for score_part in self.get_named_children ('score-part'):
504 for instr in score_part.get_named_children ('score-instrument'):
505 id = instr.id
506 name = instr.get_named_child ("instrument-name")
507 mapping[id] = name.get_text ()
509 self._id_instrument_name_dict = mapping
511 def get_instrument (self, id):
512 if not self._id_instrument_name_dict:
513 self.generate_id_instrument_dict()
515 instrument_name = self._id_instrument_name_dict.get (id)
516 if instrument_name:
517 return instrument_name
518 else:
519 ly.stderr_write (_ ("Unable to find instrument for ID=%s\n") % id)
520 return "Grand Piano"
522 class Part_group (Music_xml_node):
523 pass
524 class Score_part (Music_xml_node):
525 pass
527 class Measure (Music_xml_node):
528 def __init__ (self):
529 Music_xml_node.__init__ (self)
530 self.partial = 0
531 def is_implicit (self):
532 return hasattr (self, 'implicit') and self.implicit == 'yes'
533 def get_notes (self):
534 return self.get_typed_children (get_class (u'note'))
536 class Syllabic (Music_xml_node):
537 def continued (self):
538 text = self.get_text()
539 return (text == "begin") or (text == "middle")
540 class Elision (Music_xml_node):
541 pass
542 class Extend (Music_xml_node):
543 pass
544 class Text (Music_xml_node):
545 pass
547 class Lyric (Music_xml_node):
548 def get_number (self):
549 if hasattr (self, 'number'):
550 return self.number
551 else:
552 return -1
554 class Musicxml_voice:
555 def __init__ (self):
556 self._elements = []
557 self._staves = {}
558 self._start_staff = None
559 self._lyrics = []
560 self._has_lyrics = False
562 def add_element (self, e):
563 self._elements.append (e)
564 if (isinstance (e, Note)
565 and e.get_maybe_exist_typed_child (Staff)):
566 name = e.get_maybe_exist_typed_child (Staff).get_text ()
568 if not self._start_staff and not e.get_maybe_exist_typed_child (Grace):
569 self._start_staff = name
570 self._staves[name] = True
572 lyrics = e.get_typed_children (Lyric)
573 if not self._has_lyrics:
574 self.has_lyrics = len (lyrics) > 0
576 for l in lyrics:
577 nr = l.get_number()
578 if (nr > 0) and not (nr in self._lyrics):
579 self._lyrics.append (nr)
581 def insert (self, idx, e):
582 self._elements.insert (idx, e)
584 def get_lyrics_numbers (self):
585 if (len (self._lyrics) == 0) and self._has_lyrics:
586 #only happens if none of the <lyric> tags has a number attribute
587 return [1]
588 else:
589 return self._lyrics
592 def graces_to_aftergraces (pending_graces):
593 for gr in pending_graces:
594 gr._when = gr._prev_when
595 gr._measure_position = gr._prev_measure_position
596 gr._after_grace = True
599 class Part (Music_xml_node):
600 def __init__ (self):
601 Music_xml_node.__init__ (self)
602 self._voices = {}
603 self._staff_attributes_dict = {}
605 def get_part_list (self):
606 n = self
607 while n and n.get_name() != 'score-partwise':
608 n = n._parent
610 return n.get_named_child ('part-list')
612 def interpret (self):
613 """Set durations and starting points."""
614 """The starting point of the very first note is 0!"""
616 part_list = self.get_part_list ()
618 now = Rational (0)
619 factor = Rational (1)
620 attributes_dict = {}
621 attributes_object = None
622 measures = self.get_typed_children (Measure)
623 last_moment = Rational (-1)
624 last_measure_position = Rational (-1)
625 measure_position = Rational (0)
626 measure_start_moment = now
627 is_first_measure = True
628 previous_measure = None
629 # Graces at the end of a measure need to have their position set to the
630 # previous number!
631 pending_graces = []
632 for m in measures:
633 # implicit measures are used for artificial measures, e.g. when
634 # a repeat bar line splits a bar into two halves. In this case,
635 # don't reset the measure position to 0. They are also used for
636 # upbeats (initial value of 0 fits these, too).
637 # Also, don't reset the measure position at the end of the loop,
638 # but rather when starting the next measure (since only then do we
639 # know if the next measure is implicit and continues that measure)
640 if not m.is_implicit ():
641 # Warn about possibly overfull measures and reset the position
642 if attributes_object and previous_measure and previous_measure.partial == 0:
643 length = attributes_object.get_measure_length ()
644 new_now = measure_start_moment + length
645 if now <> new_now:
646 problem = 'incomplete'
647 if now > new_now:
648 problem = 'overfull'
649 ## only for verbose operation.
650 if problem <> 'incomplete' and previous_measure:
651 previous_measure.message ('%s measure? Expected: %s, Difference: %s' % (problem, now, new_now - now))
652 now = new_now
653 measure_start_moment = now
654 measure_position = Rational (0)
656 for n in m.get_all_children ():
657 # figured bass has a duration, but applies to the next note
658 # and should not change the current measure position!
659 if isinstance (n, FiguredBass):
660 n._divisions = factor.denominator ()
661 n._when = now
662 n._measure_position = measure_position
663 continue
665 if isinstance (n, Hash_text):
666 continue
667 dur = Rational (0)
669 if n.__class__ == Attributes:
670 n.set_attributes_from_previous (attributes_dict)
671 n.read_self ()
672 attributes_dict = n._dict.copy ()
673 attributes_object = n
675 factor = Rational (1,
676 int (attributes_dict.get ('divisions').get_text ()))
679 if (n.get_maybe_exist_typed_child (Duration)):
680 mxl_dur = n.get_maybe_exist_typed_child (Duration)
681 dur = mxl_dur.get_length () * factor
683 if n.get_name() == 'backup':
684 dur = - dur
685 # reset all graces before the backup to after-graces:
686 graces_to_aftergraces (pending_graces)
687 pending_graces = []
688 if n.get_maybe_exist_typed_child (Grace):
689 dur = Rational (0)
691 rest = n.get_maybe_exist_typed_child (Rest)
692 if (rest
693 and attributes_object
694 and attributes_object.get_measure_length () == dur):
696 rest._is_whole_measure = True
698 if (dur > Rational (0)
699 and n.get_maybe_exist_typed_child (Chord)):
700 now = last_moment
701 measure_position = last_measure_position
703 n._when = now
704 n._measure_position = measure_position
706 # For all grace notes, store the previous note, in case need
707 # to turn the grace note into an after-grace later on!
708 if isinstance(n, Note) and n.is_grace ():
709 n._prev_when = last_moment
710 n._prev_measure_position = last_measure_position
711 # After-graces are placed at the same position as the previous note
712 if isinstance(n, Note) and n.is_after_grace ():
713 # TODO: We should do the same for grace notes at the end of
714 # a measure with no following note!!!
715 n._when = last_moment
716 n._measure_position = last_measure_position
717 elif isinstance(n, Note) and n.is_grace ():
718 pending_graces.append (n)
719 elif (dur > Rational (0)):
720 pending_graces = [];
722 n._duration = dur
723 if dur > Rational (0):
724 last_moment = now
725 last_measure_position = measure_position
726 now += dur
727 measure_position += dur
728 elif dur < Rational (0):
729 # backup element, reset measure position
730 now += dur
731 measure_position += dur
732 if measure_position < 0:
733 # backup went beyond the measure start => reset to 0
734 now -= measure_position
735 measure_position = 0
736 last_moment = now
737 last_measure_position = measure_position
738 if n._name == 'note':
739 instrument = n.get_maybe_exist_named_child ('instrument')
740 if instrument:
741 n.instrument_name = part_list.get_instrument (instrument.id)
743 # reset all graces at the end of the measure to after-graces:
744 graces_to_aftergraces (pending_graces)
745 pending_graces = []
746 # Incomplete first measures are not padded, but registered as partial
747 if is_first_measure:
748 is_first_measure = False
749 # upbeats are marked as implicit measures
750 if attributes_object and m.is_implicit ():
751 length = attributes_object.get_measure_length ()
752 measure_end = measure_start_moment + length
753 if measure_end <> now:
754 m.partial = now
755 previous_measure = m
757 # modify attributes so that only those applying to the given staff remain
758 def extract_attributes_for_staff (part, attr, staff):
759 attributes = copy.copy (attr)
760 attributes._children = [];
761 attributes._dict = attr._dict.copy ()
762 attributes._original_tag = attr
763 # copy only the relevant children over for the given staff
764 for c in attr._children:
765 if (not (hasattr (c, 'number') and (c.number != staff)) and
766 not (isinstance (c, Hash_text))):
767 attributes._children.append (c)
768 if not attributes._children:
769 return None
770 else:
771 return attributes
773 def extract_voices (part):
774 voices = {}
775 measures = part.get_typed_children (Measure)
776 elements = []
777 for m in measures:
778 if m.partial > 0:
779 elements.append (Partial (m.partial))
780 elements.extend (m.get_all_children ())
781 # make sure we know all voices already so that dynamics, clefs, etc.
782 # can be assigned to the correct voices
783 voice_to_staff_dict = {}
784 for n in elements:
785 voice_id = n.get_maybe_exist_named_child (u'voice')
786 vid = None
787 if voice_id:
788 vid = voice_id.get_text ()
789 elif isinstance (n, Note):
790 vid = "None"
792 staff_id = n.get_maybe_exist_named_child (u'staff')
793 sid = None
794 if staff_id:
795 sid = staff_id.get_text ()
796 else:
797 sid = "None"
798 if vid and not voices.has_key (vid):
799 voices[vid] = Musicxml_voice()
800 if vid and sid and not n.get_maybe_exist_typed_child (Grace):
801 if not voice_to_staff_dict.has_key (vid):
802 voice_to_staff_dict[vid] = sid
803 # invert the voice_to_staff_dict into a staff_to_voice_dict (since we
804 # need to assign staff-assigned objects like clefs, times, etc. to
805 # all the correct voices. This will never work entirely correct due
806 # to staff-switches, but that's the best we can do!
807 staff_to_voice_dict = {}
808 for (v,s) in voice_to_staff_dict.items ():
809 if not staff_to_voice_dict.has_key (s):
810 staff_to_voice_dict[s] = [v]
811 else:
812 staff_to_voice_dict[s].append (v)
815 start_attr = None
816 assign_to_next_note = []
817 id = None
818 for n in elements:
819 voice_id = n.get_maybe_exist_typed_child (get_class ('voice'))
820 if voice_id:
821 id = voice_id.get_text ()
822 else:
823 id = "None"
825 # We don't need backup/forward any more, since we have already
826 # assigned the correct onset times.
827 # TODO: Let Grouping through. Also: link, print, bokmark sound
828 if not (isinstance (n, Note) or isinstance (n, Attributes) or
829 isinstance (n, Direction) or isinstance (n, Partial) or
830 isinstance (n, Barline) or isinstance (n, Harmony) or
831 isinstance (n, FiguredBass) or isinstance (n, Print)):
832 continue
834 if isinstance (n, Attributes) and not start_attr:
835 start_attr = n
836 continue
838 if isinstance (n, Attributes):
839 # assign these only to the voices they really belongs to!
840 for (s, vids) in staff_to_voice_dict.items ():
841 staff_attributes = part.extract_attributes_for_staff (n, s)
842 if staff_attributes:
843 for v in vids:
844 voices[v].add_element (staff_attributes)
845 continue
847 if isinstance (n, Partial) or isinstance (n, Barline) or isinstance (n, Print):
848 for v in voices.keys ():
849 voices[v].add_element (n)
850 continue
852 if isinstance (n, Direction):
853 staff_id = n.get_maybe_exist_named_child (u'staff')
854 if staff_id:
855 staff_id = staff_id.get_text ()
856 if staff_id:
857 dir_voices = staff_to_voice_dict.get (staff_id, voices.keys ())
858 else:
859 dir_voices = voices.keys ()
860 for v in dir_voices:
861 voices[v].add_element (n)
862 continue
864 if isinstance (n, Harmony) or isinstance (n, FiguredBass):
865 # store the harmony or figured bass element until we encounter
866 # the next note and assign it only to that one voice.
867 assign_to_next_note.append (n)
868 continue
870 if hasattr (n, 'print-object') and getattr (n, 'print-object') == "no":
871 #Skip this note.
872 pass
873 else:
874 for i in assign_to_next_note:
875 voices[id].add_element (i)
876 assign_to_next_note = []
877 voices[id].add_element (n)
879 # Assign all remaining elements from assign_to_next_note to the voice
880 # of the previous note:
881 for i in assign_to_next_note:
882 voices[id].add_element (i)
883 assign_to_next_note = []
885 if start_attr:
886 for (s, vids) in staff_to_voice_dict.items ():
887 staff_attributes = part.extract_attributes_for_staff (start_attr, s)
888 staff_attributes.read_self ()
889 part._staff_attributes_dict[s] = staff_attributes
890 for v in vids:
891 voices[v].insert (0, staff_attributes)
892 voices[v]._elements[0].read_self()
894 part._voices = voices
896 def get_voices (self):
897 return self._voices
898 def get_staff_attributes (self):
899 return self._staff_attributes_dict
901 class Notations (Music_xml_node):
902 def get_tie (self):
903 ts = self.get_named_children ('tied')
904 starts = [t for t in ts if t.type == 'start']
905 if starts:
906 return starts[0]
907 else:
908 return None
910 def get_tuplets (self):
911 return self.get_typed_children (Tuplet)
913 class Time_modification(Music_xml_node):
914 def get_fraction (self):
915 b = self.get_maybe_exist_named_child ('actual-notes')
916 a = self.get_maybe_exist_named_child ('normal-notes')
917 return (int(a.get_text ()), int (b.get_text ()))
919 def get_normal_type (self):
920 tuplet_type = self.get_maybe_exist_named_child ('normal-type')
921 if tuplet_type:
922 dots = self.get_named_children ('normal-dot')
923 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
924 return (log , len (dots))
925 else:
926 return None
929 class Accidental (Music_xml_node):
930 def __init__ (self):
931 Music_xml_node.__init__ (self)
932 self.editorial = False
933 self.cautionary = False
935 class Music_xml_spanner (Music_xml_node):
936 def get_type (self):
937 if hasattr (self, 'type'):
938 return self.type
939 else:
940 return 0
941 def get_size (self):
942 if hasattr (self, 'size'):
943 return string.atoi (self.size)
944 else:
945 return 0
947 class Wedge (Music_xml_spanner):
948 pass
950 class Tuplet (Music_xml_spanner):
951 def duration_info_from_tuplet_note (self, tuplet_note):
952 tuplet_type = tuplet_note.get_maybe_exist_named_child ('tuplet-type')
953 if tuplet_type:
954 dots = tuplet_note.get_named_children ('tuplet-dot')
955 log = musicxml_duration_to_log (tuplet_type.get_text ().strip ())
956 return (log, len (dots))
957 else:
958 return None
960 # Return tuplet note type as (log, dots)
961 def get_normal_type (self):
962 tuplet = self.get_maybe_exist_named_child ('tuplet-normal')
963 if tuplet:
964 return self.duration_info_from_tuplet_note (tuplet)
965 else:
966 return None
968 def get_actual_type (self):
969 tuplet = self.get_maybe_exist_named_child ('tuplet-actual')
970 if tuplet:
971 return self.duration_info_from_tuplet_note (tuplet)
972 else:
973 return None
975 def get_tuplet_note_count (self, tuplet_note):
976 if tuplet_note:
977 tuplet_nr = tuplet_note.get_maybe_exist_named_child ('tuplet-number')
978 if tuplet_nr:
979 return int (tuplet_nr.get_text ())
980 return None
981 def get_normal_nr (self):
982 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-normal'))
983 def get_actual_nr (self):
984 return self.get_tuplet_note_count (self.get_maybe_exist_named_child ('tuplet-actual'))
986 class Bracket (Music_xml_spanner):
987 pass
989 class Dashes (Music_xml_spanner):
990 pass
992 class Slur (Music_xml_spanner):
993 def get_type (self):
994 return self.type
996 class Beam (Music_xml_spanner):
997 def get_type (self):
998 return self.get_text ()
999 def is_primary (self):
1000 return self.number == "1"
1002 class Wavy_line (Music_xml_spanner):
1003 pass
1005 class Pedal (Music_xml_spanner):
1006 pass
1008 class Glissando (Music_xml_spanner):
1009 pass
1011 class Slide (Music_xml_spanner):
1012 pass
1014 class Octave_shift (Music_xml_spanner):
1015 # default is 8 for the octave-shift!
1016 def get_size (self):
1017 if hasattr (self, 'size'):
1018 return string.atoi (self.size)
1019 else:
1020 return 8
1022 class Chord (Music_xml_node):
1023 pass
1025 class Dot (Music_xml_node):
1026 pass
1028 # Rests in MusicXML are <note> blocks with a <rest> inside. This class is only
1029 # for the inner <rest> element, not the whole rest block.
1030 class Rest (Music_xml_node):
1031 def __init__ (self):
1032 Music_xml_node.__init__ (self)
1033 self._is_whole_measure = False
1034 def is_whole_measure (self):
1035 return self._is_whole_measure
1036 def get_step (self):
1037 ch = self.get_maybe_exist_typed_child (get_class (u'display-step'))
1038 if ch:
1039 step = ch.get_text ().strip ()
1040 return step
1041 else:
1042 return None
1043 def get_octave (self):
1044 ch = self.get_maybe_exist_typed_child (get_class (u'display-octave'))
1045 if ch:
1046 step = ch.get_text ().strip ()
1047 return int (step)
1048 else:
1049 return None
1051 class Type (Music_xml_node):
1052 pass
1053 class Grace (Music_xml_node):
1054 pass
1055 class Staff (Music_xml_node):
1056 pass
1058 class Direction (Music_xml_node):
1059 pass
1060 class DirType (Music_xml_node):
1061 pass
1063 class Bend (Music_xml_node):
1064 def bend_alter (self):
1065 alter = self.get_maybe_exist_named_child ('bend-alter')
1066 return interpret_alter_element (alter)
1068 class Words (Music_xml_node):
1069 pass
1071 class Harmony (Music_xml_node):
1072 pass
1074 class ChordPitch (Music_xml_node):
1075 def step_class_name (self):
1076 return u'root-step'
1077 def alter_class_name (self):
1078 return u'root-alter'
1079 def get_step (self):
1080 ch = self.get_unique_typed_child (get_class (self.step_class_name ()))
1081 return ch.get_text ().strip ()
1082 def get_alteration (self):
1083 ch = self.get_maybe_exist_typed_child (get_class (self.alter_class_name ()))
1084 return interpret_alter_element (ch)
1086 class Root (ChordPitch):
1087 pass
1089 class Bass (ChordPitch):
1090 def step_class_name (self):
1091 return u'bass-step'
1092 def alter_class_name (self):
1093 return u'bass-alter'
1095 class ChordModification (Music_xml_node):
1096 def get_type (self):
1097 ch = self.get_maybe_exist_typed_child (get_class (u'degree-type'))
1098 return {'add': 1, 'alter': 1, 'subtract': -1}.get (ch.get_text ().strip (), 0)
1099 def get_value (self):
1100 ch = self.get_maybe_exist_typed_child (get_class (u'degree-value'))
1101 value = 0
1102 if ch:
1103 value = int (ch.get_text ().strip ())
1104 return value
1105 def get_alter (self):
1106 ch = self.get_maybe_exist_typed_child (get_class (u'degree-alter'))
1107 return interpret_alter_element (ch)
1110 class Frame (Music_xml_node):
1111 def get_frets (self):
1112 return self.get_named_child_value_number ('frame-frets', 4)
1113 def get_strings (self):
1114 return self.get_named_child_value_number ('frame-strings', 6)
1115 def get_first_fret (self):
1116 return self.get_named_child_value_number ('first-fret', 1)
1118 class Frame_Note (Music_xml_node):
1119 def get_string (self):
1120 return self.get_named_child_value_number ('string', 1)
1121 def get_fret (self):
1122 return self.get_named_child_value_number ('fret', 0)
1123 def get_fingering (self):
1124 return self.get_named_child_value_number ('fingering', -1)
1125 def get_barre (self):
1126 n = self.get_maybe_exist_named_child ('barre')
1127 if n:
1128 return getattr (n, 'type', '')
1129 else:
1130 return ''
1132 class FiguredBass (Music_xml_node):
1133 pass
1135 class Beats (Music_xml_node):
1136 pass
1138 class BeatType (Music_xml_node):
1139 pass
1141 class BeatUnit (Music_xml_node):
1142 pass
1144 class BeatUnitDot (Music_xml_node):
1145 pass
1147 class PerMinute (Music_xml_node):
1148 pass
1150 class Print (Music_xml_node):
1151 pass
1155 ## need this, not all classes are instantiated
1156 ## for every input file. Only add those classes, that are either directly
1157 ## used by class name or extend Music_xml_node in some way!
1158 class_dict = {
1159 '#comment': Hash_comment,
1160 '#text': Hash_text,
1161 'accidental': Accidental,
1162 'attributes': Attributes,
1163 'barline': Barline,
1164 'bar-style': BarStyle,
1165 'bass': Bass,
1166 'beam' : Beam,
1167 'beats': Beats,
1168 'beat-type': BeatType,
1169 'beat-unit': BeatUnit,
1170 'beat-unit-dot': BeatUnitDot,
1171 'bend' : Bend,
1172 'bracket' : Bracket,
1173 'chord': Chord,
1174 'dashes' : Dashes,
1175 'degree' : ChordModification,
1176 'dot': Dot,
1177 'direction': Direction,
1178 'direction-type': DirType,
1179 'duration': Duration,
1180 'elision': Elision,
1181 'extend': Extend,
1182 'frame': Frame,
1183 'frame-note': Frame_Note,
1184 'figured-bass': FiguredBass,
1185 'glissando': Glissando,
1186 'grace': Grace,
1187 'harmony': Harmony,
1188 'identification': Identification,
1189 'key-alter': KeyAlter,
1190 'key-octave': KeyOctave,
1191 'key-step': KeyStep,
1192 'lyric': Lyric,
1193 'measure': Measure,
1194 'notations': Notations,
1195 'note': Note,
1196 'octave-shift': Octave_shift,
1197 'part': Part,
1198 'part-group': Part_group,
1199 'part-list': Part_list,
1200 'pedal': Pedal,
1201 'per-minute': PerMinute,
1202 'pitch': Pitch,
1203 'print': Print,
1204 'rest': Rest,
1205 'root': Root,
1206 'score-part': Score_part,
1207 'slide': Slide,
1208 'slur': Slur,
1209 'staff': Staff,
1210 'syllabic': Syllabic,
1211 'text': Text,
1212 'time-modification': Time_modification,
1213 'tuplet': Tuplet,
1214 'type': Type,
1215 'unpitched': Unpitched,
1216 'wavy-line': Wavy_line,
1217 'wedge': Wedge,
1218 'words': Words,
1219 'work': Work,
1222 def name2class_name (name):
1223 name = name.replace ('-', '_')
1224 name = name.replace ('#', 'hash_')
1225 name = name[0].upper() + name[1:].lower()
1227 return str (name)
1229 def get_class (name):
1230 classname = class_dict.get (name)
1231 if classname:
1232 return classname
1233 else:
1234 class_name = name2class_name (name)
1235 klass = new.classobj (class_name, (Music_xml_node,) , {})
1236 class_dict[name] = klass
1237 return klass
1239 def lxml_demarshal_node (node):
1240 name = node.tag
1242 # Ignore comment nodes, which are also returned by the etree parser!
1243 if name is None or node.__class__.__name__ == "_Comment":
1244 return None
1245 klass = get_class (name)
1246 py_node = klass()
1248 py_node._original = node
1249 py_node._name = name
1250 py_node._data = node.text
1251 py_node._children = [lxml_demarshal_node (cn) for cn in node.getchildren()]
1252 py_node._children = filter (lambda x: x, py_node._children)
1254 for c in py_node._children:
1255 c._parent = py_node
1257 for (k, v) in node.items ():
1258 py_node.__dict__[k] = v
1259 py_node._attribute_dict[k] = v
1261 return py_node
1263 def minidom_demarshal_node (node):
1264 name = node.nodeName
1266 klass = get_class (name)
1267 py_node = klass ()
1268 py_node._name = name
1269 py_node._children = [minidom_demarshal_node (cn) for cn in node.childNodes]
1270 for c in py_node._children:
1271 c._parent = py_node
1273 if node.attributes:
1274 for (nm, value) in node.attributes.items ():
1275 py_node.__dict__[nm] = value
1276 py_node._attribute_dict[nm] = value
1278 py_node._data = None
1279 if node.nodeType == node.TEXT_NODE and node.data:
1280 py_node._data = node.data
1282 py_node._original = node
1283 return py_node
1286 if __name__ == '__main__':
1287 import lxml.etree
1289 tree = lxml.etree.parse ('beethoven.xml')
1290 mxl_tree = lxml_demarshal_node (tree.getroot ())
1291 ks = class_dict.keys ()
1292 ks.sort ()
1293 print '\n'.join (ks)