1 # -*- coding: utf-8 -*-
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
);
20 return_string
= "\"" + string
.replace (return_string
, "\"", "\\\"") + "\""
24 def musicxml_duration_to_log (dur
):
36 'long': -2}.get (dur
, 0)
38 def interpret_alter_element (alter_elm
):
41 val
= eval(alter_elm
.get_text ())
42 if type (val
) in (int, float):
52 self
._name
= 'xml_node'
54 self
._attribute
_dict
= {}
56 def get_parent (self
):
60 return self
._parent
.get_typed_children (self
.__class
__)[0] == self
71 if not self
._children
:
74 return ''.join ([c
.get_text () for c
in self
._children
])
76 def message (self
, msg
):
77 ly
.stderr_write (msg
+'\n')
81 sys
.stderr
.write (' In: <%s %s>\n' % (p
._name
, ' '.join (['%s=%s' % item
for item
in p
._attribute
_dict
.items ()])))
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
)]
88 sys
.stderr
.write ('\n')
89 for c
in self
._children
:
92 sys
.stderr
.write (indent
)
93 sys
.stderr
.write ('</%s>\n' % self
._name
)
96 def get_typed_children (self
, klass
):
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
)
124 raise "More than 1 child", klass
126 def get_unique_typed_child (self
, klass
):
127 cn
= self
.get_typed_children(klass
)
129 sys
.stderr
.write (self
.__dict
__ + '\n')
130 raise 'Child is not unique for', (klass
, 'found', cn
)
134 def get_named_child_value_number (self
, name
, default
):
135 n
= self
.get_maybe_exist_named_child (name
)
137 return string
.atoi (n
.get_text())
142 class Music_xml_node (Xml_node
):
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
)
152 return wt
.get_text ()
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')
161 return self
.get_work_information ('opus')
163 class Identification (Xml_node
):
164 def get_rights (self
):
165 rights
= self
.get_named_children ('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
175 if hasattr (i
, 'type') and i
.type == type:
179 def get_composer (self
):
180 c
= self
.get_creator ('composer')
183 creators
= self
.get_named_children ('creator')
184 # return the first creator tag that has no type at all
186 if not hasattr (i
, 'type'):
189 def get_arranger (self
):
190 return self
.get_creator ('arranger')
191 def get_editor (self
):
192 return self
.get_creator ('editor')
194 v
= self
.get_creator ('lyricist')
197 v
= self
.get_creator ('poet')
200 def get_encoding_information (self
, type):
201 enc
= self
.get_named_children ('encoding')
203 children
= enc
[0].get_named_children (type)
205 return children
[0].get_text ()
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')
222 softwares
= e
.get_named_children ('software')
224 software
.append (s
.get_text ())
227 def get_file_description (self
):
228 misc
= self
.get_named_children ('miscellaneous')
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 ()
238 class Duration (Music_xml_node
):
239 def get_length (self
):
240 dur
= int (self
.get_text ()) * Rational (1,4)
243 class Hash_comment (Music_xml_node
):
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
):
251 ch
= self
.get_unique_typed_child (get_class (u
'step'))
252 step
= ch
.get_text ().strip ()
254 def get_octave (self
):
255 ch
= self
.get_unique_typed_child (get_class (u
'octave'))
257 step
= ch
.get_text ().strip ()
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
):
266 ch
= self
.get_unique_typed_child (get_class (u
'display-step'))
267 step
= ch
.get_text ().strip ()
270 def get_octave (self
):
271 ch
= self
.get_unique_typed_child (get_class (u
'display-octave'))
274 octave
= ch
.get_text ().strip ()
279 class Measure_element (Music_xml_node
):
280 def get_voice_id (self
):
281 voice_id
= self
.get_maybe_exist_named_child ('voice')
283 return voice_id
.get_text ()
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
)]
296 class Attributes (Measure_element
):
298 Measure_element
.__init
__ (self
)
300 self
._original
_tag
= None
301 self
._time
_signature
_cache
= None
304 cn
= self
._parent
.get_typed_children (self
.__class
__)
305 if self
._original
_tag
:
306 return cn
[0] == self
._original
_tag
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
):
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:
332 if isinstance (sig
[0], list):
333 # Complex compound time signature
336 l
+= self
.single_time_sig_to_fraction (i
)
339 # Simple (maybe compound) time signature of the form (beat, ..., type)
340 return self
.single_time_sig_to_fraction (sig
)
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
351 mxl
= self
.get_named_attribute ('time')
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!"))
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
)
370 if isinstance (signature
[0], list) and len (signature
) == 1:
371 signature
= signature
[0]
372 self
._time
_signature
_cache
= signature
374 except (KeyError, ValueError):
375 self
.message (_ ("Unable to interpret time signature! Falling back to 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')
384 sign
= mxl
.get_maybe_exist_named_child ('sign')
386 clefinfo
[0] = sign
.get_text()
387 line
= mxl
.get_maybe_exist_named_child ('line')
389 clefinfo
[1] = string
.atoi (line
.get_text ())
390 octave
= mxl
.get_maybe_exist_named_child ('clef-octave-change')
392 clefinfo
[2] = string
.atoi (octave
.get_text ())
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')
404 fifths_elm
= key
.get_maybe_exist_named_child ('fifths')
406 mode_node
= key
.get_maybe_exist_named_child ('mode')
409 mode
= mode_node
.get_text ()
410 if not mode
or mode
== '':
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
)
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
):
425 if hasattr (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)
431 i
.message (_ ("Key alteration octave given for a "
432 "non-existing alteration nr. %s, available numbers: %s!") % (nr
, len(alterations
)))
435 def get_transposition (self
):
436 return self
.get_named_attribute ('transpose')
438 class KeyAlter (Music_xml_node
):
440 class KeyStep (Music_xml_node
):
442 class KeyOctave (Music_xml_node
):
446 class Barline (Measure_element
):
448 class BarStyle (Music_xml_node
):
450 class Partial (Measure_element
):
451 def __init__ (self
, partial
):
452 Measure_element
.__init
__ (self
)
453 self
.partial
= partial
455 class Note (Measure_element
):
457 Measure_element
.__init
__ (self
)
458 self
.instrument_name
= ''
459 self
._after
_grace
= False
461 return self
.get_maybe_exist_named_child (u
'grace')
462 def is_after_grace (self
):
463 if not self
.is_grace():
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')
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?
480 def get_duration_info (self
):
481 log
= self
.get_duration_log ()
483 dots
= len (self
.get_typed_children (Dot
))
488 def get_factor (self
):
491 def get_pitches (self
):
492 return self
.get_typed_children (get_class (u
'pitch'))
494 class Part_list (Music_xml_node
):
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.
503 for score_part
in self
.get_named_children ('score-part'):
504 for instr
in score_part
.get_named_children ('score-instrument'):
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)
517 return instrument_name
519 ly
.stderr_write (_ ("Unable to find instrument for ID=%s\n") % id)
522 class Part_group (Music_xml_node
):
524 class Score_part (Music_xml_node
):
527 class Measure (Music_xml_node
):
529 Music_xml_node
.__init
__ (self
)
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
):
542 class Extend (Music_xml_node
):
544 class Text (Music_xml_node
):
547 class Lyric (Music_xml_node
):
548 def get_number (self
):
549 if hasattr (self
, 'number'):
554 class Musicxml_voice
:
558 self
._start
_staff
= None
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
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
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
):
601 Music_xml_node
.__init
__ (self
)
603 self
._staff
_attributes
_dict
= {}
605 def get_part_list (self
):
607 while n
and n
.get_name() != 'score-partwise':
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 ()
619 factor
= Rational (1)
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
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
646 problem
= 'incomplete'
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
))
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 ()
662 n
._measure
_position
= measure_position
665 if isinstance (n
, Hash_text
):
669 if n
.__class
__ == Attributes
:
670 n
.set_attributes_from_previous (attributes_dict
)
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':
685 # reset all graces before the backup to after-graces:
686 graces_to_aftergraces (pending_graces
)
688 if n
.get_maybe_exist_typed_child (Grace
):
691 rest
= n
.get_maybe_exist_typed_child (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
)):
701 measure_position
= last_measure_position
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)):
723 if dur
> Rational (0):
725 last_measure_position
= measure_position
727 measure_position
+= dur
728 elif dur
< Rational (0):
729 # backup element, reset measure position
731 measure_position
+= dur
732 if measure_position
< 0:
733 # backup went beyond the measure start => reset to 0
734 now
-= measure_position
737 last_measure_position
= measure_position
738 if n
._name
== 'note':
739 instrument
= n
.get_maybe_exist_named_child ('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
)
746 # Incomplete first measures are not padded, but registered as partial
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
:
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
:
773 def extract_voices (part
):
775 measures
= part
.get_typed_children (Measure
)
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
= {}
785 voice_id
= n
.get_maybe_exist_named_child (u
'voice')
788 vid
= voice_id
.get_text ()
789 elif isinstance (n
, Note
):
792 staff_id
= n
.get_maybe_exist_named_child (u
'staff')
795 sid
= staff_id
.get_text ()
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
]
812 staff_to_voice_dict
[s
].append (v
)
816 assign_to_next_note
= []
819 voice_id
= n
.get_maybe_exist_typed_child (get_class ('voice'))
821 id = voice_id
.get_text ()
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
)):
834 if isinstance (n
, Attributes
) and not start_attr
:
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
)
844 voices
[v
].add_element (staff_attributes
)
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
)
852 if isinstance (n
, Direction
):
853 staff_id
= n
.get_maybe_exist_named_child (u
'staff')
855 staff_id
= staff_id
.get_text ()
857 dir_voices
= staff_to_voice_dict
.get (staff_id
, voices
.keys ())
859 dir_voices
= voices
.keys ()
861 voices
[v
].add_element (n
)
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
)
870 if hasattr (n
, 'print-object') and getattr (n
, 'print-object') == "no":
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
= []
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
891 voices
[v
].insert (0, staff_attributes
)
892 voices
[v
]._elements
[0].read_self()
894 part
._voices
= voices
896 def get_voices (self
):
898 def get_staff_attributes (self
):
899 return self
._staff
_attributes
_dict
901 class Notations (Music_xml_node
):
903 ts
= self
.get_named_children ('tied')
904 starts
= [t
for t
in ts
if t
.type == 'start']
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')
922 dots
= self
.get_named_children ('normal-dot')
923 log
= musicxml_duration_to_log (tuplet_type
.get_text ().strip ())
924 return (log
, len (dots
))
929 class Accidental (Music_xml_node
):
931 Music_xml_node
.__init
__ (self
)
932 self
.editorial
= False
933 self
.cautionary
= False
935 class Music_xml_spanner (Music_xml_node
):
937 if hasattr (self
, 'type'):
942 if hasattr (self
, 'size'):
943 return string
.atoi (self
.size
)
947 class Wedge (Music_xml_spanner
):
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')
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
))
960 # Return tuplet note type as (log, dots)
961 def get_normal_type (self
):
962 tuplet
= self
.get_maybe_exist_named_child ('tuplet-normal')
964 return self
.duration_info_from_tuplet_note (tuplet
)
968 def get_actual_type (self
):
969 tuplet
= self
.get_maybe_exist_named_child ('tuplet-actual')
971 return self
.duration_info_from_tuplet_note (tuplet
)
975 def get_tuplet_note_count (self
, tuplet_note
):
977 tuplet_nr
= tuplet_note
.get_maybe_exist_named_child ('tuplet-number')
979 return int (tuplet_nr
.get_text ())
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
):
989 class Dashes (Music_xml_spanner
):
992 class Slur (Music_xml_spanner
):
996 class Beam (Music_xml_spanner
):
998 return self
.get_text ()
999 def is_primary (self
):
1000 return self
.number
== "1"
1002 class Wavy_line (Music_xml_spanner
):
1005 class Pedal (Music_xml_spanner
):
1008 class Glissando (Music_xml_spanner
):
1011 class Slide (Music_xml_spanner
):
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
)
1022 class Chord (Music_xml_node
):
1025 class Dot (Music_xml_node
):
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'))
1039 step
= ch
.get_text ().strip ()
1043 def get_octave (self
):
1044 ch
= self
.get_maybe_exist_typed_child (get_class (u
'display-octave'))
1046 step
= ch
.get_text ().strip ()
1051 class Type (Music_xml_node
):
1053 class Grace (Music_xml_node
):
1055 class Staff (Music_xml_node
):
1058 class Direction (Music_xml_node
):
1060 class DirType (Music_xml_node
):
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
):
1071 class Harmony (Music_xml_node
):
1074 class ChordPitch (Music_xml_node
):
1075 def step_class_name (self
):
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
):
1089 class Bass (ChordPitch
):
1090 def step_class_name (self
):
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'))
1103 value
= int (ch
.get_text ().strip ())
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')
1128 return getattr (n
, 'type', '')
1132 class FiguredBass (Music_xml_node
):
1135 class Beats (Music_xml_node
):
1138 class BeatType (Music_xml_node
):
1141 class BeatUnit (Music_xml_node
):
1144 class BeatUnitDot (Music_xml_node
):
1147 class PerMinute (Music_xml_node
):
1150 class Print (Music_xml_node
):
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!
1159 '#comment': Hash_comment
,
1161 'accidental': Accidental
,
1162 'attributes': Attributes
,
1164 'bar-style': BarStyle
,
1168 'beat-type': BeatType
,
1169 'beat-unit': BeatUnit
,
1170 'beat-unit-dot': BeatUnitDot
,
1172 'bracket' : Bracket
,
1175 'degree' : ChordModification
,
1177 'direction': Direction
,
1178 'direction-type': DirType
,
1179 'duration': Duration
,
1183 'frame-note': Frame_Note
,
1184 'figured-bass': FiguredBass
,
1185 'glissando': Glissando
,
1188 'identification': Identification
,
1189 'key-alter': KeyAlter
,
1190 'key-octave': KeyOctave
,
1191 'key-step': KeyStep
,
1194 'notations': Notations
,
1196 'octave-shift': Octave_shift
,
1198 'part-group': Part_group
,
1199 'part-list': Part_list
,
1201 'per-minute': PerMinute
,
1206 'score-part': Score_part
,
1210 'syllabic': Syllabic
,
1212 'time-modification': Time_modification
,
1215 'unpitched': Unpitched
,
1216 'wavy-line': Wavy_line
,
1222 def name2class_name (name
):
1223 name
= name
.replace ('-', '_')
1224 name
= name
.replace ('#', 'hash_')
1225 name
= name
[0].upper() + name
[1:].lower()
1229 def get_class (name
):
1230 classname
= class_dict
.get (name
)
1234 class_name
= name2class_name (name
)
1235 klass
= new
.classobj (class_name
, (Music_xml_node
,) , {})
1236 class_dict
[name
] = klass
1239 def lxml_demarshal_node (node
):
1242 # Ignore comment nodes, which are also returned by the etree parser!
1243 if name
is None or node
.__class
__.__name
__ == "_Comment":
1245 klass
= get_class (name
)
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
:
1257 for (k
, v
) in node
.items ():
1258 py_node
.__dict
__[k
] = v
1259 py_node
._attribute
_dict
[k
] = v
1263 def minidom_demarshal_node (node
):
1264 name
= node
.nodeName
1266 klass
= get_class (name
)
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
:
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
1286 if __name__
== '__main__':
1289 tree
= lxml
.etree
.parse ('beethoven.xml')
1290 mxl_tree
= lxml_demarshal_node (tree
.getroot ())
1291 ks
= class_dict
.keys ()
1293 print '\n'.join (ks
)