8 from gettext
import gettext
as _
19 from rational
import Rational
23 sys
.stderr
.write (str + '\n')
27 def musicxml_duration_to_lily (mxl_note
):
28 d
= musicexp
.Duration ()
29 if mxl_note
.get_maybe_exist_typed_child (musicxml
.Type
):
30 d
.duration_log
= mxl_note
.get_duration_log ()
34 d
.dots
= len (mxl_note
.get_typed_children (musicxml
.Dot
))
35 d
.factor
= mxl_note
._duration
/ d
.get_length ()
39 def group_tuplets (music_list
, events
):
42 """Collect Musics from
43 MUSIC_LIST demarcated by EVENTS_LIST in TimeScaledMusic objects.
50 for (ev_chord
, tuplet_elt
, fraction
) in events
:
51 while (j
< len (music_list
)):
52 if music_list
[j
]== ev_chord
:
55 if tuplet_elt
.type == 'start':
56 indices
.append ((j
, None, fraction
))
57 elif tuplet_elt
.type == 'stop':
58 indices
[-1] = (indices
[-1][0], j
, indices
[-1][2])
62 for (i1
, i2
, frac
) in indices
:
66 new_list
.extend (music_list
[last
:i1
])
67 seq
= musicexp
.SequentialMusic ()
69 seq
.elements
= music_list
[i1
:last
]
71 tsm
= musicexp
.TimeScaledMusic ()
74 tsm
.numerator
= frac
[0]
75 tsm
.denominator
= frac
[1]
79 new_list
.extend (music_list
[last
:])
83 def musicxml_clef_to_lily (attributes
):
84 change
= musicexp
.ClefChange ()
85 change
.type = attributes
.get_clef_sign ()
88 def musicxml_time_to_lily (attributes
):
89 (beats
, type) = attributes
.get_time_signature ()
91 change
= musicexp
.TimeSignatureChange()
92 change
.fraction
= (beats
, type)
96 def musicxml_key_to_lily (attributes
):
97 start_pitch
= musicexp
.Pitch ()
98 (fifths
, mode
) = attributes
.get_key_signature ()
105 start_pitch
.alteration
= a
107 print 'unknown mode', mode
109 fifth
= musicexp
.Pitch()
116 for x
in range (fifths
):
117 start_pitch
= start_pitch
.transposed (fifth
)
119 start_pitch
.octave
= 0
121 change
= musicexp
.KeySignatureChange()
123 change
.tonic
= start_pitch
126 def musicxml_attributes_to_lily (attrs
):
129 'clef': musicxml_clef_to_lily
,
130 'time': musicxml_time_to_lily
,
131 'key': musicxml_key_to_lily
133 for (k
, func
) in attr_dispatch
.items ():
134 childs
= attrs
.get_named_children (k
)
136 ## ugh: you get clefs spread over staves for piano
138 elts
.append (func (attrs
))
142 spanner_event_dict
= {
143 'slur' : musicexp
.SlurEvent
,
144 'beam' : musicexp
.BeamEvent
,
146 spanner_type_dict
= {
153 def musicxml_spanner_to_lily_event (mxl_event
):
156 name
= mxl_event
.get_name()
158 func
= spanner_event_dict
[name
]
161 print 'unknown span event ', mxl_event
164 key
= mxl_event
.get_type ()
165 ev
.span_direction
= spanner_type_dict
[key
]
167 print 'unknown span type', key
, 'for', name
171 instrument_drumtype_dict
= {
172 'Acoustic Snare Drum': 'acousticsnare',
173 'Side Stick': 'sidestick',
174 'Open Triangle': 'opentriangle',
175 'Mute Triangle': 'mutetriangle',
176 'Tambourine': 'tambourine',
180 def musicxml_note_to_lily_main_event (n
):
184 mxl_pitch
= n
.get_maybe_exist_typed_child (musicxml
.Pitch
)
187 pitch
= musicxml_pitch_to_lily (mxl_pitch
)
188 event
= musicexp
.NoteEvent()
191 acc
= n
.get_maybe_exist_named_child ('accidental')
193 # let's not force accs everywhere.
194 event
.cautionary
= acc
.editorial
196 elif n
.get_maybe_exist_typed_child (musicxml
.Rest
):
197 event
= musicexp
.RestEvent()
198 elif n
.instrument_name
:
199 event
= musicexp
.NoteEvent ()
201 event
.drum_type
= instrument_drumtype_dict
[n
.instrument_name
]
203 n
.message ("drum %s type unknow, please add to instrument_drumtype_dict" % n
.instrument_name
)
204 event
.drum_type
= 'acousticsnare'
207 n
.message ("cannot find suitable event")
209 event
.duration
= musicxml_duration_to_lily (n
)
215 def __init__ (self
, here
, dest
):
219 class LilyPondVoiceBuilder
:
222 self
.end_moment
= Rational (0)
223 self
.begin_moment
= Rational (0)
224 self
.pending_multibar
= Rational (0)
226 def _insert_multibar (self
):
227 r
= musicexp
.MultiMeasureRest ()
228 r
.duration
= musicexp
.Duration()
229 r
.duration
.duration_log
= 0
230 r
.duration
.factor
= self
.pending_multibar
231 self
.elements
.append (r
)
232 self
.begin_moment
= self
.end_moment
233 self
.end_moment
= self
.begin_moment
+ self
.pending_multibar
234 self
.pending_multibar
= Rational (0)
236 def add_multibar_rest (self
, duration
):
237 self
.pending_multibar
+= duration
240 def add_music (self
, music
, duration
):
241 assert isinstance (music
, musicexp
.Music
)
242 if self
.pending_multibar
> Rational (0):
243 self
._insert
_multibar
()
245 self
.elements
.append (music
)
246 self
.begin_moment
= self
.end_moment
247 self
.end_moment
= self
.begin_moment
+ duration
249 def add_bar_check (self
, number
):
250 b
= musicexp
.BarCheck ()
251 b
.bar_number
= number
252 self
.add_music (b
, Rational (0))
254 def jumpto (self
, moment
):
255 current_end
= self
.end_moment
+ self
.pending_multibar
256 diff
= moment
- current_end
258 if diff
< Rational (0):
259 print 'Negative skip', diff
262 if diff
> Rational (0):
263 skip
= musicexp
.SkipEvent()
264 skip
.duration
.duration_log
= 0
265 skip
.duration
.factor
= diff
267 evc
= musicexp
.EventChord ()
268 evc
.elements
.append (skip
)
269 self
.add_music (evc
, diff
)
271 def last_event_chord (self
, starting_at
):
275 and isinstance (self
.elements
[-1], musicexp
.EventChord
)
276 and self
.begin_moment
== starting_at
):
277 value
= self
.elements
[-1]
279 self
.jumpto (starting_at
)
284 def correct_negative_skip (self
, goto
):
285 self
.end_moment
= goto
286 self
.begin_moment
= goto
287 evc
= musicexp
.EventChord ()
288 self
.elements
.append (evc
)
290 def musicxml_voice_to_lily_voice (voice
):
294 voice_builder
= LilyPondVoiceBuilder()
296 for n
in voice
._elements
:
297 if n
.get_name () == 'forward':
300 if not n
.get_maybe_exist_named_child ('chord'):
302 voice_builder
.jumpto (n
._when
)
303 except NegativeSkip
, neg
:
304 voice_builder
.correct_negative_skip (n
._when
)
305 n
.message ("Negative skip? from %s to %s, diff %s" % (neg
.here
, neg
.dest
, neg
.dest
- neg
.here
))
307 if isinstance (n
, musicxml
.Attributes
):
308 if n
.is_first () and n
._measure
_position
== Rational (0):
310 number
= int (n
.get_parent ().number
)
314 voice_builder
.add_bar_check (number
)
315 for a
in musicxml_attributes_to_lily (n
):
316 voice_builder
.add_music (a
, Rational (0))
319 if not n
.__class
__.__name
__ == 'Note':
320 print 'not a Note or Attributes?', n
323 rest
= n
.get_maybe_exist_typed_child (musicxml
.Rest
)
325 and rest
.is_whole_measure ()):
327 voice_builder
.add_multibar_rest (n
._duration
)
330 if n
.is_first () and n
._measure
_position
== Rational (0):
332 num
= int (n
.get_parent ().number
)
335 voice_builder
.add_bar_check (num
)
337 main_event
= musicxml_note_to_lily_main_event (n
)
340 if main_event
.drum_type
:
341 modes_found
['drummode'] = True
342 except AttributeError:
346 ev_chord
= voice_builder
.last_event_chord (n
._when
)
348 ev_chord
= musicexp
.EventChord()
349 voice_builder
.add_music (ev_chord
, n
._duration
)
351 ev_chord
.append (main_event
)
353 notations
= n
.get_maybe_exist_typed_child (musicxml
.Notations
)
357 if notations
.get_tuplet():
358 tuplet_event
= notations
.get_tuplet()
359 mod
= n
.get_maybe_exist_typed_child (musicxml
.Time_modification
)
362 frac
= mod
.get_fraction ()
364 tuplet_events
.append ((ev_chord
, tuplet_event
, frac
))
366 slurs
= [s
for s
in notations
.get_named_children ('slur')
367 if s
.get_type () in ('start','stop')]
370 print 'more than 1 slur?'
372 lily_ev
= musicxml_spanner_to_lily_event (slurs
[0])
373 ev_chord
.append (lily_ev
)
375 mxl_tie
= notations
.get_tie ()
376 if mxl_tie
and mxl_tie
.type == 'start':
377 ev_chord
.append (musicexp
.TieEvent ())
379 mxl_beams
= [b
for b
in n
.get_named_children ('beam')
380 if (b
.get_type () in ('begin', 'end')
381 and b
.is_primary ())]
383 beam_ev
= musicxml_spanner_to_lily_event (mxl_beams
[0])
385 ev_chord
.append (beam_ev
)
388 mod
= n
.get_maybe_exist_typed_child (musicxml
.Time_modification
)
391 frac
= mod
.get_fraction ()
393 tuplet_events
.append ((ev_chord
, tuplet_event
, frac
))
395 ## force trailing mm rests to be written out.
396 voice_builder
.add_music (musicexp
.EventChord (), Rational (0))
398 ly_voice
= group_tuplets (voice_builder
.elements
, tuplet_events
)
400 seq_music
= musicexp
.SequentialMusic()
402 if 'drummode' in modes_found
.keys ():
403 ## \key <pitch> barfs in drummode.
404 ly_voice
= [e
for e
in ly_voice
405 if not isinstance(e
, musicexp
.KeySignatureChange
)]
407 seq_music
.elements
= ly_voice
411 if len (modes_found
) > 1:
412 print 'Too many modes found', modes_found
.keys ()
414 return_value
= seq_music
415 for mode
in modes_found
.keys ():
416 v
= musicexp
.ModeChangingMusicWrapper()
417 v
.element
= return_value
424 def musicxml_id_to_lily (id):
425 digits
= ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight',
429 d
= digits
.index (dig
) + 1
430 dig
= dig
[0].upper() + dig
[1:]
431 id = re
.sub ('%d' % d
, dig
, id)
433 id = re
.sub ('[^a-zA-Z]', 'X', id)
437 def musicxml_pitch_to_lily (mxl_pitch
):
439 p
.alteration
= mxl_pitch
.get_alteration ()
440 p
.step
= (ord (mxl_pitch
.get_step ()) - ord ('A') + 7 - 2) % 7
441 p
.octave
= mxl_pitch
.get_octave () - 4
444 def voices_in_part (part
):
445 """Return a Name -> Voice dictionary for PART"""
447 part
.extract_voices ()
448 voice_dict
= part
.get_voices ()
452 def voices_in_part_in_parts (parts
):
453 """return a Part -> Name -> Voice dictionary"""
454 return dict([(p
, voices_in_part (p
)) for p
in parts
])
457 def get_all_voices (parts
):
458 all_voices
= voices_in_part_in_parts (parts
)
461 for p
, name_voice
in all_voices
.items ():
464 for n
, v
in name_voice
.items ():
465 progress ("Converting to LilyPond expressions...")
466 part_ly_voices
[n
] = (musicxml_voice_to_lily_voice (v
), v
)
468 all_ly_voices
[p
] = part_ly_voices
473 def option_parser ():
474 p
= ly
.get_option_parser(usage
=_ ("musicxml2ly FILE.xml"),
475 version
=('''%prog (LilyPond) @TOPLEVEL_VERSION@\n\n'''
477 _ ("""This program is free software. It is covered by the GNU General Public
478 License and you are welcome to change it and/or distribute copies of it
479 under certain conditions. Invoke as `%s --warranty' for more
480 information.""") % 'lilypond'
482 Copyright (c) 2005--2006 by
483 Han-Wen Nienhuys <hanwen@xs4all.nl> and
484 Jan Nieuwenhuizen <janneke@gnu.org>
486 description
=_ ("Convert %s to LilyPond input.") % 'MusicXML' + "\n")
487 p
.add_option ('-v', '--verbose',
490 help=_ ("be verbose"))
492 p
.add_option ('', '--lxml',
496 help=_ ("Use lxml.etree; uses less memory and cpu time."))
498 p
.add_option ('-o', '--output',
504 help=_ ("set output filename to FILE"))
505 p
.add_option_group ('bugs',
506 description
=(_ ("Report bugs via")
507 + ''' http://post.gmane.org/post.php'''
508 '''?group=gmane.comp.gnu.lilypond.bugs\n'''))
511 def music_xml_voice_name_to_lily_name (part
, name
):
512 str = "Part%sVoice%s" % (part
.id, name
)
513 return musicxml_id_to_lily (str)
515 def print_voice_definitions (printer
, voices
):
516 for (part
, nv_dict
) in voices
.items():
518 for (name
, (voice
, mxlvoice
)) in nv_dict
.items ():
519 k
= music_xml_voice_name_to_lily_name (part
, name
)
520 printer
.dump ('%s = ' % k
)
521 voice
.print_ly (printer
)
526 return dict ([(elt
,1) for elt
in l
]).keys ()
528 def print_score_setup (printer
, part_list
, voices
):
529 part_dict
= dict ([(p
.id, p
) for p
in voices
.keys ()])
533 for part_definition
in part_list
:
534 part_name
= part_definition
.id
536 part
= part_dict
[part_name
]
538 print 'unknown part in part-list:', part_name
541 nv_dict
= voices
[part
]
542 staves
= reduce (lambda x
,y
: x
+ y
,
543 [mxlvoice
._staves
.keys ()
544 for (v
, mxlvoice
) in nv_dict
.values ()],
548 staves
= uniq_list (staves
)
550 printer ('\\context PianoStaff << ')
554 staff_voices
= [music_xml_voice_name_to_lily_name (part
, voice_name
)
555 for (voice_name
, (v
, mxlvoice
)) in nv_dict
.items ()
556 if mxlvoice
._start
_staff
== s
]
558 printer ('\\context Staff = "%s" << ' % s
)
560 for v
in staff_voices
:
561 printer ('\\context Voice = "%s" \\%s' % (v
,v
))
570 printer ('\\new Staff <<')
572 for (n
,v
) in nv_dict
.items ():
574 n
= music_xml_voice_name_to_lily_name (part
, n
)
575 printer ('\\context Voice = "%s" \\%s' % (n
,n
))
582 def print_ly_preamble (printer
, filename
):
583 printer
.dump_version ()
584 printer
.print_verbatim ('%% automatically converted from %s\n' % filename
)
586 def read_musicxml (filename
, use_lxml
):
590 tree
= lxml
.etree
.parse (filename
)
591 mxl_tree
= musicxml
.lxml_demarshal_node (tree
.getroot ())
594 from xml
.dom
import minidom
, Node
596 doc
= minidom
.parse(filename
)
597 node
= doc
.documentElement
598 return musicxml
.minidom_demarshal_node (node
)
603 def convert (filename
, options
):
604 progress ("Reading MusicXML from %s ..." % filename
)
606 tree
= read_musicxml (filename
, options
.use_lxml
)
609 id_instrument_map
= {}
610 if tree
.get_maybe_exist_typed_child (musicxml
.Part_list
):
611 mxl_pl
= tree
.get_maybe_exist_typed_child (musicxml
.Part_list
)
612 part_list
= mxl_pl
.get_named_children ("score-part")
614 parts
= tree
.get_typed_children (musicxml
.Part
)
615 voices
= get_all_voices (parts
)
617 if not options
.output_name
:
618 options
.output_name
= os
.path
.basename (filename
)
619 options
.output_name
= os
.path
.splitext (options
.output_name
)[0]
622 defs_ly_name
= options
.output_name
+ '-defs.ly'
623 driver_ly_name
= options
.output_name
+ '.ly'
625 printer
= musicexp
.Output_printer()
626 progress ("Output to `%s'" % defs_ly_name
)
627 printer
.set_file (open (defs_ly_name
, 'w'))
629 print_ly_preamble (printer
, filename
)
630 print_voice_definitions (printer
, voices
)
635 progress ("Output to `%s'" % driver_ly_name
)
636 printer
= musicexp
.Output_printer()
637 printer
.set_file (open (driver_ly_name
, 'w'))
638 print_ly_preamble (printer
, filename
)
639 printer
.dump (r
'\include "%s"' % defs_ly_name
)
640 print_score_setup (printer
, part_list
, voices
)
647 opt_parser
= option_parser()
649 (options
, args
) = opt_parser
.parse_args ()
651 opt_parser
.print_usage()
654 voices
= convert (args
[0], options
)
656 if __name__
== '__main__':