]> git.donarmstrong.com Git - lilypond.git/blob - scripts/musicxml2ly.py
MusicXML: Implement church modes; fix empty <mode/> tags for keys
[lilypond.git] / scripts / musicxml2ly.py
1 #!@TARGET_PYTHON@
2 # -*- coding: utf-8 -*-
3 import optparse
4 import sys
5 import re
6 import os
7 import string
8 import codecs
9 import zipfile
10 import StringIO
11
12 """
13 @relocate-preamble@
14 """
15
16 import lilylib as ly
17 _ = ly._
18
19 import musicxml
20 import musicexp
21
22 from rational import Rational
23
24 # Store command-line options in a global variable, so we can access them everythwere
25 options = None
26
27 class Conversion_Settings:
28     def __init__(self):
29        self.ignore_beaming = False
30
31 conversion_settings = Conversion_Settings ()
32 # Use a global variable to store the setting needed inside a \layout block.
33 # whenever we need to change a setting or add/remove an engraver, we can access 
34 # this layout and add the corresponding settings
35 layout_information = musicexp.Layout ()
36
37 def progress (str):
38     ly.stderr_write (str + '\n')
39     sys.stderr.flush ()
40
41 def error_message (str):
42     ly.stderr_write (str + '\n')
43     sys.stderr.flush ()
44
45 needed_additional_definitions = []
46 additional_definitions = {
47   "snappizzicato": """#(define-markup-command (snappizzicato layout props) ()
48   (interpret-markup layout props
49     (markup #:stencil
50       (ly:stencil-translate-axis
51         (ly:stencil-add
52           (make-circle-stencil 0.7 0.1 #f)
53           (ly:make-stencil
54             (list 'draw-line 0.1 0 0.1 0 1)
55             '(-0.1 . 0.1) '(0.1 . 1)))
56         0.7 X))))""",
57   "eyeglasses": """eyeglassesps = #"0.15 setlinewidth
58       -0.9 0 translate
59       1.1 1.1 scale
60       1.2 0.7 moveto
61       0.7 0.7 0.5 0 361 arc
62       stroke
63       2.20 0.70 0.50 0 361 arc
64       stroke
65       1.45 0.85 0.30 0 180 arc
66       stroke
67       0.20 0.70 moveto
68       0.80 2.00 lineto
69       0.92 2.26 1.30 2.40 1.15 1.70 curveto
70       stroke
71       2.70 0.70 moveto
72       3.30 2.00 lineto
73       3.42 2.26 3.80 2.40 3.65 1.70 curveto
74       stroke"
75 eyeglasses =  \markup { \with-dimensions #'(0 . 4.4) #'(0 . 2.5) \postscript #eyeglassesps }"""
76 }
77
78 def round_to_two_digits (val):
79     return round (val * 100) / 100
80
81 def extract_paper_information (tree):
82     paper = musicexp.Paper ()
83     defaults = tree.get_maybe_exist_named_child ('defaults')
84     if not defaults:
85         return None
86     tenths = -1
87     scaling = defaults.get_maybe_exist_named_child ('scaling')
88     if scaling:
89         mm = scaling.get_named_child ('millimeters')
90         mm = string.atof (mm.get_text ())
91         tn = scaling.get_maybe_exist_named_child ('tenths')
92         tn = string.atof (tn.get_text ())
93         tenths = mm / tn
94         paper.global_staff_size = mm * 72.27 / 25.4
95     # We need the scaling (i.e. the size of staff tenths for everything!
96     if tenths < 0:
97         return None
98
99     def from_tenths (txt):
100         return round_to_two_digits (string.atof (txt) * tenths / 10)
101     def set_paper_variable (varname, parent, element_name):
102         el = parent.get_maybe_exist_named_child (element_name)
103         if el: # Convert to cm from tenths
104             setattr (paper, varname, from_tenths (el.get_text ()))
105
106     pagelayout = defaults.get_maybe_exist_named_child ('page-layout')
107     if pagelayout:
108         # TODO: How can one have different margins for even and odd pages???
109         set_paper_variable ("page_height", pagelayout, 'page-height')
110         set_paper_variable ("page_width", pagelayout, 'page-width')
111
112         pmargins = pagelayout.get_named_children ('page-margins')
113         for pm in pmargins:
114             set_paper_variable ("left_margin", pm, 'left-margin')
115             set_paper_variable ("right_margin", pm, 'right-margin')
116             set_paper_variable ("bottom_margin", pm, 'bottom-margin')
117             set_paper_variable ("top_margin", pm, 'top-margin')
118
119     systemlayout = defaults.get_maybe_exist_named_child ('system-layout')
120     if systemlayout:
121         sl = systemlayout.get_maybe_exist_named_child ('system-margins')
122         if sl:
123             set_paper_variable ("system_left_margin", sl, 'left-margin')
124             set_paper_variable ("system_right_margin", sl, 'right-margin')
125         set_paper_variable ("system_distance", systemlayout, 'system-distance')
126         set_paper_variable ("top_system_distance", systemlayout, 'top-system-distance')
127
128     stafflayout = defaults.get_named_children ('staff-layout')
129     for sl in stafflayout:
130         nr = getattr (sl, 'number', 1)
131         dist = sl.get_named_child ('staff-distance')
132         #TODO: the staff distance needs to be set in the Staff context!!!
133
134     # TODO: Finish appearance?, music-font?, word-font?, lyric-font*, lyric-language*
135     appearance = defaults.get_named_child ('appearance')
136     if appearance:
137         lws = appearance.get_named_children ('line-width')
138         for lw in lws:
139             # Possible types are: beam, bracket, dashes,
140             #    enclosure, ending, extend, heavy barline, leger,
141             #    light barline, octave shift, pedal, slur middle, slur tip,
142             #    staff, stem, tie middle, tie tip, tuplet bracket, and wedge
143             tp = lw.type
144             w = from_tenths (lw.get_text  ())
145             # TODO: Do something with these values!
146         nss = appearance.get_named_children ('note-size')
147         for ns in nss:
148             # Possible types are: cue, grace and large
149             tp = ns.type
150             sz = from_tenths (ns.get_text ())
151             # TODO: Do something with these values!
152         # <other-appearance> elements have no specified meaning
153
154     rawmusicfont = defaults.get_named_child ('music-font')
155     if rawmusicfont:
156         # TODO: Convert the font
157         pass
158     rawwordfont = defaults.get_named_child ('word-font')
159     if rawwordfont:
160         # TODO: Convert the font
161         pass
162     rawlyricsfonts = defaults.get_named_children ('lyric-font')
163     for lyricsfont in rawlyricsfonts:
164         # TODO: Convert the font
165         pass
166
167     return paper
168
169
170
171 # score information is contained in the <work>, <identification> or <movement-title> tags
172 # extract those into a hash, indexed by proper lilypond header attributes
173 def extract_score_information (tree):
174     header = musicexp.Header ()
175     def set_if_exists (field, value):
176         if value:
177             header.set_field (field, musicxml.escape_ly_output_string (value))
178
179     work = tree.get_maybe_exist_named_child ('work')
180     if work:
181         set_if_exists ('title', work.get_work_title ())
182         set_if_exists ('worknumber', work.get_work_number ())
183         set_if_exists ('opus', work.get_opus ())
184     else:
185         movement_title = tree.get_maybe_exist_named_child ('movement-title')
186         if movement_title:
187             set_if_exists ('title', movement_title.get_text ())
188     
189     identifications = tree.get_named_children ('identification')
190     for ids in identifications:
191         set_if_exists ('copyright', ids.get_rights ())
192         set_if_exists ('composer', ids.get_composer ())
193         set_if_exists ('arranger', ids.get_arranger ())
194         set_if_exists ('editor', ids.get_editor ())
195         set_if_exists ('poet', ids.get_poet ())
196             
197         set_if_exists ('tagline', ids.get_encoding_software ())
198         set_if_exists ('encodingsoftware', ids.get_encoding_software ())
199         set_if_exists ('encodingdate', ids.get_encoding_date ())
200         set_if_exists ('encoder', ids.get_encoding_person ())
201         set_if_exists ('encodingdescription', ids.get_encoding_description ())
202         
203         set_if_exists ('texidoc', ids.get_file_description ());
204
205         # Finally, apply the required compatibility modes
206         # Some applications created wrong MusicXML files, so we need to 
207         # apply some compatibility mode, e.g. ignoring some features/tags
208         # in those files
209         software = ids.get_encoding_software_list ()
210
211         # Case 1: "Sibelius 5.1" with the "Dolet 3.4 for Sibelius" plugin
212         #         is missing all beam ends => ignore all beaming information
213         if "Dolet 3.4 for Sibelius" in software:
214             conversion_settings.ignore_beaming = True
215             progress (_ ("Encountered file created by Dolet 3.4 for Sibelius, containing wrong beaming information. All beaming information in the MusicXML file will be ignored"))
216         # TODO: Check for other unsupported features
217
218     return header
219
220 class PartGroupInfo:
221     def __init__ (self):
222         self.start = {}
223         self.end = {}
224     def is_empty (self):
225         return len (self.start) + len (self.end) == 0
226     def add_start (self, g):
227         self.start[getattr (g, 'number', "1")] = g
228     def add_end (self, g):
229         self.end[getattr (g, 'number', "1")] = g
230     def print_ly (self, printer):
231         error_message (_ ("Unprocessed PartGroupInfo %s encountered") % self)
232     def ly_expression (self):
233         error_message (_ ("Unprocessed PartGroupInfo %s encountered") % self)
234         return ''
235
236 def staff_attributes_to_string_tunings (mxl_attr):
237     details = mxl_attr.get_maybe_exist_named_child ('staff-details')
238     if not details:
239         return []
240     lines = 6
241     staff_lines = details.get_maybe_exist_named_child ('staff-lines')
242     if staff_lines:
243         lines = string.atoi (staff_lines.get_text ())
244
245     tunings = [0]*lines
246     staff_tunings = details.get_named_children ('staff-tuning')
247     for i in staff_tunings:
248         p = musicexp.Pitch()
249         line = 0
250         try:
251             line = string.atoi (i.line) - 1
252         except ValueError:
253             pass
254         tunings[line] = p
255
256         step = i.get_named_child (u'tuning-step')
257         step = step.get_text ().strip ()
258         p.step = musicxml_step_to_lily (step)
259
260         octave = i.get_named_child (u'tuning-octave')
261         octave = octave.get_text ().strip ()
262         p.octave = int (octave) - 4
263
264         alter = i.get_named_child (u'tuning-alter')
265         if alter:
266             p.alteration = int (alter.get_text ().strip ())
267     # lilypond seems to use the opposite ordering than MusicXML...
268     tunings.reverse ()
269
270     return tunings
271
272
273 def staff_attributes_to_lily_staff (mxl_attr):
274     if not mxl_attr:
275         return musicexp.Staff ()
276
277     (staff_id, attributes) = mxl_attr.items ()[0]
278
279     # distinguish by clef:
280     # percussion (percussion and rhythmic), tab, and everything else
281     clef_sign = None
282     clef = attributes.get_maybe_exist_named_child ('clef')
283     if clef:
284         sign = clef.get_maybe_exist_named_child ('sign')
285         if sign:
286             clef_sign = {"percussion": "percussion", "TAB": "tab"}.get (sign.get_text (), None)
287
288     lines = 5
289     details = attributes.get_named_children ('staff-details')
290     for d in details:
291         staff_lines = d.get_maybe_exist_named_child ('staff-lines')
292         if staff_lines:
293             lines = string.atoi (staff_lines.get_text ())
294
295     staff = None
296     if clef_sign == "percussion" and lines == 1:
297         staff = musicexp.RhythmicStaff ()
298     elif clef_sign == "percussion":
299         staff = musicexp.DrumStaff ()
300         # staff.drum_style_table = ???
301     elif clef_sign == "tab":
302         staff = musicexp.TabStaff ()
303         staff.string_tunings = staff_attributes_to_string_tunings (attributes)
304         # staff.tablature_format = ???
305     else:
306         # TODO: Handle case with lines <> 5!
307         staff = musicexp.Staff ()
308
309     return staff
310
311
312 def extract_score_structure (part_list, staffinfo):
313     score = musicexp.Score ()
314     structure = musicexp.StaffGroup (None)
315     score.set_contents (structure)
316     
317     if not part_list:
318         return structure
319
320     def read_score_part (el):
321         if not isinstance (el, musicxml.Score_part):
322             return
323         # Depending on the attributes of the first measure, we create different
324         # types of staves (Staff, RhythmicStaff, DrumStaff, TabStaff, etc.)
325         staff = staff_attributes_to_lily_staff (staffinfo.get (el.id, None))
326         if not staff:
327             return None
328         staff.id = el.id
329         partname = el.get_maybe_exist_named_child ('part-name')
330         # Finale gives unnamed parts the name "MusicXML Part" automatically!
331         if partname and partname.get_text() != "MusicXML Part":
332             staff.instrument_name = partname.get_text ()
333         if el.get_maybe_exist_named_child ('part-abbreviation'):
334             staff.short_instrument_name = el.get_maybe_exist_named_child ('part-abbreviation').get_text ()
335         # TODO: Read in the MIDI device / instrument
336         return staff
337
338     def read_score_group (el):
339         if not isinstance (el, musicxml.Part_group):
340             return
341         group = musicexp.StaffGroup ()
342         if hasattr (el, 'number'):
343             id = el.number
344             group.id = id
345             #currentgroups_dict[id] = group
346             #currentgroups.append (id)
347         if el.get_maybe_exist_named_child ('group-name'):
348             group.instrument_name = el.get_maybe_exist_named_child ('group-name').get_text ()
349         if el.get_maybe_exist_named_child ('group-abbreviation'):
350             group.short_instrument_name = el.get_maybe_exist_named_child ('group-abbreviation').get_text ()
351         if el.get_maybe_exist_named_child ('group-symbol'):
352             group.symbol = el.get_maybe_exist_named_child ('group-symbol').get_text ()
353         if el.get_maybe_exist_named_child ('group-barline'):
354             group.spanbar = el.get_maybe_exist_named_child ('group-barline').get_text ()
355         return group
356
357
358     parts_groups = part_list.get_all_children ()
359
360     # the start/end group tags are not necessarily ordered correctly and groups
361     # might even overlap, so we can't go through the children sequentially!
362
363     # 1) Replace all Score_part objects by their corresponding Staff objects,
364     #    also collect all group start/stop points into one PartGroupInfo object
365     staves = []
366     group_info = PartGroupInfo ()
367     for el in parts_groups:
368         if isinstance (el, musicxml.Score_part):
369             if not group_info.is_empty ():
370                 staves.append (group_info)
371                 group_info = PartGroupInfo ()
372             staff = read_score_part (el)
373             if staff:
374                 staves.append (staff)
375         elif isinstance (el, musicxml.Part_group):
376             if el.type == "start":
377                 group_info.add_start (el)
378             elif el.type == "stop":
379                 group_info.add_end (el)
380     if not group_info.is_empty ():
381         staves.append (group_info)
382
383     # 2) Now, detect the groups:
384     group_starts = []
385     pos = 0
386     while pos < len (staves):
387         el = staves[pos]
388         if isinstance (el, PartGroupInfo):
389             prev_start = 0
390             if len (group_starts) > 0:
391                 prev_start = group_starts[-1]
392             elif len (el.end) > 0: # no group to end here
393                 el.end = {}
394             if len (el.end) > 0: # closes an existing group
395                 ends = el.end.keys ()
396                 prev_started = staves[prev_start].start.keys ()
397                 grpid = None
398                 intersection = filter(lambda x:x in ends, prev_started)
399                 if len (intersection) > 0:
400                     grpid = intersection[0]
401                 else:
402                     # Close the last started group
403                     grpid = staves[prev_start].start.keys () [0]
404                     # Find the corresponding closing tag and remove it!
405                     j = pos + 1
406                     foundclosing = False
407                     while j < len (staves) and not foundclosing:
408                         if isinstance (staves[j], PartGroupInfo) and staves[j].end.has_key (grpid):
409                             foundclosing = True
410                             del staves[j].end[grpid]
411                             if staves[j].is_empty ():
412                                 del staves[j]
413                         j += 1
414                 grpobj = staves[prev_start].start[grpid]
415                 group = read_score_group (grpobj)
416                 # remove the id from both the start and end
417                 if el.end.has_key (grpid):
418                     del el.end[grpid]
419                 del staves[prev_start].start[grpid]
420                 if el.is_empty ():
421                     del staves[pos]
422                 # replace the staves with the whole group
423                 for j in staves[(prev_start + 1):pos]:
424                     if j.is_group:
425                         j.stafftype = "InnerStaffGroup"
426                     group.append_staff (j)
427                 del staves[(prev_start + 1):pos]
428                 staves.insert (prev_start + 1, group)
429                 # reset pos so that we continue at the correct position
430                 pos = prev_start
431                 # remove an empty start group
432                 if staves[prev_start].is_empty ():
433                     del staves[prev_start]
434                     group_starts.remove (prev_start)
435                     pos -= 1
436             elif len (el.start) > 0: # starts new part groups
437                 group_starts.append (pos)
438         pos += 1
439
440     if len (staves) == 1:
441         return staves[0]
442     for i in staves:
443         structure.append_staff (i)
444     return score
445
446
447 def musicxml_duration_to_lily (mxl_note):
448     d = musicexp.Duration ()
449     # if the note has no Type child, then that method spits out a warning and 
450     # returns 0, i.e. a whole note
451     d.duration_log = mxl_note.get_duration_log ()
452
453     d.dots = len (mxl_note.get_typed_children (musicxml.Dot))
454     # Grace notes by specification have duration 0, so no time modification 
455     # factor is possible. It even messes up the output with *0/1
456     if not mxl_note.get_maybe_exist_typed_child (musicxml.Grace):
457         d.factor = mxl_note._duration / d.get_length ()
458
459     return d
460
461 def rational_to_lily_duration (rational_len):
462     d = musicexp.Duration ()
463
464     rational_len.normalize_self ()
465     d_log = {1: 0, 2: 1, 4:2, 8:3, 16:4, 32:5, 64:6, 128:7, 256:8, 512:9}.get (rational_len.denominator (), -1)
466
467     # Duration of the form 1/2^n or 3/2^n can be converted to a simple lilypond duration
468     if (d_log >= 0 and rational_len.numerator() in (1,3,5,7) ):
469         # account for the dots!
470         d.dots = (rational_len.numerator()-1)/2
471         d.duration_log = d_log - d.dots
472     elif (d_log >= 0):
473         d.duration_log = d_log
474         d.factor = Rational (rational_len.numerator ())
475     else:
476         error_message (_ ("Encountered rational duration with denominator %s, "
477                        "unable to convert to lilypond duration") %
478                        rational_len.denominator ())
479         # TODO: Test the above error message
480         return None
481
482     return d
483
484 def musicxml_partial_to_lily (partial_len):
485     if partial_len > 0:
486         p = musicexp.Partial ()
487         p.partial = rational_to_lily_duration (partial_len)
488         return p
489     else:
490         return Null
491
492 # Detect repeats and alternative endings in the chord event list (music_list)
493 # and convert them to the corresponding musicexp objects, containing nested
494 # music
495 def group_repeats (music_list):
496     repeat_replaced = True
497     music_start = 0
498     i = 0
499     # Walk through the list of expressions, looking for repeat structure
500     # (repeat start/end, corresponding endings). If we find one, try to find the
501     # last event of the repeat, replace the whole structure and start over again.
502     # For nested repeats, as soon as we encounter another starting repeat bar,
503     # treat that one first, and start over for the outer repeat.
504     while repeat_replaced and i < 100:
505         i += 1
506         repeat_start = -1  # position of repeat start / end
507         repeat_end = -1 # position of repeat start / end
508         repeat_times = 0
509         ending_start = -1 # position of current ending start
510         endings = [] # list of already finished endings
511         pos = 0
512         last = len (music_list) - 1
513         repeat_replaced = False
514         final_marker = 0
515         while pos < len (music_list) and not repeat_replaced:
516             e = music_list[pos]
517             repeat_finished = False
518             if isinstance (e, RepeatMarker):
519                 if not repeat_times and e.times:
520                     repeat_times = e.times
521                 if e.direction == -1:
522                     if repeat_end >= 0:
523                         repeat_finished = True
524                     else:
525                         repeat_start = pos
526                         repeat_end = -1
527                         ending_start = -1
528                         endings = []
529                 elif e.direction == 1:
530                     if repeat_start < 0:
531                         repeat_start = 0
532                     if repeat_end < 0:
533                         repeat_end = pos
534                     final_marker = pos
535             elif isinstance (e, EndingMarker):
536                 if e.direction == -1:
537                     if repeat_start < 0:
538                         repeat_start = 0
539                     if repeat_end < 0:
540                         repeat_end = pos
541                     ending_start = pos
542                 elif e.direction == 1:
543                     if ending_start < 0:
544                         ending_start = 0
545                     endings.append ([ending_start, pos])
546                     ending_start = -1
547                     final_marker = pos
548             elif not isinstance (e, musicexp.BarLine):
549                 # As soon as we encounter an element when repeat start and end
550                 # is set and we are not inside an alternative ending,
551                 # this whole repeat structure is finished => replace it
552                 if repeat_start >= 0 and repeat_end > 0 and ending_start < 0:
553                     repeat_finished = True
554
555             # Finish off all repeats without explicit ending bar (e.g. when
556             # we convert only one page of a multi-page score with repeats)
557             if pos == last and repeat_start >= 0:
558                 repeat_finished = True
559                 final_marker = pos
560                 if repeat_end < 0:
561                     repeat_end = pos
562                 if ending_start >= 0:
563                     endings.append ([ending_start, pos])
564                     ending_start = -1
565
566             if repeat_finished:
567                 # We found the whole structure replace it!
568                 r = musicexp.RepeatedMusic ()
569                 if repeat_times <= 0:
570                     repeat_times = 2
571                 r.repeat_count = repeat_times
572                 # don't erase the first element for "implicit" repeats (i.e. no
573                 # starting repeat bars at the very beginning)
574                 start = repeat_start+1
575                 if repeat_start == music_start:
576                     start = music_start
577                 r.set_music (music_list[start:repeat_end])
578                 for (start, end) in endings:
579                     s = musicexp.SequentialMusic ()
580                     s.elements = music_list[start+1:end]
581                     r.add_ending (s)
582                 del music_list[repeat_start:final_marker+1]
583                 music_list.insert (repeat_start, r)
584                 repeat_replaced = True
585             pos += 1
586         # TODO: Implement repeats until the end without explicit ending bar
587     return music_list
588
589
590
591 def group_tuplets (music_list, events):
592
593
594     """Collect Musics from
595     MUSIC_LIST demarcated by EVENTS_LIST in TimeScaledMusic objects.
596     """
597
598     
599     indices = []
600
601     j = 0
602     for (ev_chord, tuplet_elt, fraction) in events:
603         while (j < len (music_list)):
604             if music_list[j] == ev_chord:
605                 break
606             j += 1
607         if tuplet_elt.type == 'start':
608             indices.append ((j, None, fraction))
609         elif tuplet_elt.type == 'stop':
610             indices[-1] = (indices[-1][0], j, indices[-1][2])
611
612     new_list = []
613     last = 0
614     for (i1, i2, frac) in indices:
615         if i1 >= i2:
616             continue
617
618         new_list.extend (music_list[last:i1])
619         seq = musicexp.SequentialMusic ()
620         last = i2 + 1
621         seq.elements = music_list[i1:last]
622
623         tsm = musicexp.TimeScaledMusic ()
624         tsm.element = seq
625
626         tsm.numerator = frac[0]
627         tsm.denominator  = frac[1]
628
629         new_list.append (tsm)
630
631     new_list.extend (music_list[last:])
632     return new_list
633
634
635 def musicxml_clef_to_lily (attributes):
636     change = musicexp.ClefChange ()
637     (change.type, change.position, change.octave) = attributes.get_clef_information ()
638     return change
639     
640 def musicxml_time_to_lily (attributes):
641     (beats, type) = attributes.get_time_signature ()
642
643     change = musicexp.TimeSignatureChange()
644     change.fraction = (beats, type)
645     
646     return change
647
648 def musicxml_key_to_lily (attributes):
649     start_pitch  = musicexp.Pitch ()
650     (fifths, mode) = attributes.get_key_signature () 
651     try:
652         (n,a) = {
653             'major'     : (0,0),
654             'minor'     : (5,0),
655             'ionian'    : (0,0),
656             'dorian'    : (1,0),
657             'phrygian'  : (2,0),
658             'lydian'    : (3,0),
659             'mixolydian': (4,0),
660             'aeolian'   : (5,0),
661             'locrian'   : (6,0),
662             }[mode]
663         start_pitch.step = n
664         start_pitch.alteration = a
665     except  KeyError:
666         error_message (_ ("unknown mode %s, expecting 'major' or 'minor'") % mode)
667
668     fifth = musicexp.Pitch()
669     fifth.step = 4
670     if fifths < 0:
671         fifths *= -1
672         fifth.step *= -1
673         fifth.normalize ()
674     
675     for x in range (fifths):
676         start_pitch = start_pitch.transposed (fifth)
677
678     start_pitch.octave = 0
679
680     change = musicexp.KeySignatureChange()
681     change.mode = mode
682     change.tonic = start_pitch
683     return change
684     
685 def musicxml_attributes_to_lily (attrs):
686     elts = []
687     attr_dispatch =  {
688         'clef': musicxml_clef_to_lily,
689         'time': musicxml_time_to_lily,
690         'key': musicxml_key_to_lily
691     }
692     for (k, func) in attr_dispatch.items ():
693         children = attrs.get_named_children (k)
694         if children:
695             elts.append (func (attrs))
696     
697     return elts
698
699 class Marker (musicexp.Music):
700     def __init__ (self):
701         self.direction = 0
702         self.event = None
703     def print_ly (self, printer):
704         ly.stderr_write (_ ("Encountered unprocessed marker %s\n") % self)
705         pass
706     def ly_expression (self):
707         return ""
708 class RepeatMarker (Marker):
709     def __init__ (self):
710         Marker.__init__ (self)
711         self.times = 0
712 class EndingMarker (Marker):
713     pass
714
715 # Convert the <barline> element to musicxml.BarLine (for non-standard barlines)
716 # and to RepeatMarker and EndingMarker objects for repeat and
717 # alternatives start/stops
718 def musicxml_barline_to_lily (barline):
719     # retval contains all possible markers in the order:
720     # 0..bw_ending, 1..bw_repeat, 2..barline, 3..fw_repeat, 4..fw_ending
721     retval = {}
722     bartype_element = barline.get_maybe_exist_named_child ("bar-style")
723     repeat_element = barline.get_maybe_exist_named_child ("repeat")
724     ending_element = barline.get_maybe_exist_named_child ("ending")
725
726     bartype = None
727     if bartype_element:
728         bartype = bartype_element.get_text ()
729
730     if repeat_element and hasattr (repeat_element, 'direction'):
731         repeat = RepeatMarker ()
732         repeat.direction = {"forward": -1, "backward": 1}.get (repeat_element.direction, 0)
733
734         if ( (repeat_element.direction == "forward" and bartype == "heavy-light") or
735              (repeat_element.direction == "backward" and bartype == "light-heavy") ):
736             bartype = None
737         if hasattr (repeat_element, 'times'):
738             try:
739                 repeat.times = int (repeat_element.times)
740             except ValueError:
741                 repeat.times = 2
742         repeat.event = barline
743         if repeat.direction == -1:
744             retval[3] = repeat
745         else:
746             retval[1] = repeat
747
748     if ending_element and hasattr (ending_element, 'type'):
749         ending = EndingMarker ()
750         ending.direction = {"start": -1, "stop": 1, "discontinue": 1}.get (ending_element.type, 0)
751         ending.event = barline
752         if ending.direction == -1:
753             retval[4] = ending
754         else:
755             retval[0] = ending
756
757     if bartype:
758         b = musicexp.BarLine ()
759         b.type = bartype
760         retval[2] = b
761
762     return retval.values ()
763
764 spanner_event_dict = {
765     'beam' : musicexp.BeamEvent,
766     'dashes' : musicexp.TextSpannerEvent,
767     'bracket' : musicexp.BracketSpannerEvent,
768     'glissando' : musicexp.GlissandoEvent,
769     'octave-shift' : musicexp.OctaveShiftEvent,
770     'pedal' : musicexp.PedalEvent,
771     'slide' : musicexp.GlissandoEvent,
772     'slur' : musicexp.SlurEvent,
773     'wavy-line' : musicexp.TrillSpanEvent,
774     'wedge' : musicexp.HairpinEvent
775 }
776 spanner_type_dict = {
777     'start': -1,
778     'begin': -1,
779     'crescendo': -1,
780     'decreschendo': -1,
781     'diminuendo': -1,
782     'continue': 0,
783     'change': 0,
784     'up': -1,
785     'down': -1,
786     'stop': 1,
787     'end' : 1
788 }
789
790 def musicxml_spanner_to_lily_event (mxl_event):
791     ev = None
792     
793     name = mxl_event.get_name()
794     func = spanner_event_dict.get (name)
795     if func:
796         ev = func()
797     else:
798         error_message (_ ('unknown span event %s') % mxl_event)
799
800
801     type = mxl_event.get_type ()
802     span_direction = spanner_type_dict.get (type)
803     # really check for None, because some types will be translated to 0, which
804     # would otherwise also lead to the unknown span warning
805     if span_direction != None:
806         ev.span_direction = span_direction
807     else:
808         error_message (_ ('unknown span type %s for %s') % (type, name))
809
810     ev.set_span_type (type)
811     ev.line_type = getattr (mxl_event, 'line-type', 'solid')
812
813     # assign the size, which is used for octave-shift, etc.
814     ev.size = mxl_event.get_size ()
815
816     return ev
817
818 def musicxml_direction_to_indicator (direction):
819     return { "above": 1, "upright": 1, "up": 1, "below": -1, "downright": -1, "down": -1, "inverted": -1 }.get (direction, 0)
820
821 def musicxml_fermata_to_lily_event (mxl_event):
822     ev = musicexp.ArticulationEvent ()
823     txt = mxl_event.get_text ()
824     # The contents of the element defined the shape, possible are normal, angled and square
825     ev.type = { "angled": "shortfermata", "square": "longfermata" }.get (txt, "fermata")
826     if hasattr (mxl_event, 'type'):
827       dir = musicxml_direction_to_indicator (mxl_event.type)
828       if dir and options.convert_directions:
829         ev.force_direction = dir
830     return ev
831
832 def musicxml_arpeggiate_to_lily_event (mxl_event):
833     ev = musicexp.ArpeggioEvent ()
834     ev.direction = musicxml_direction_to_indicator (getattr (mxl_event, 'direction', None))
835     return ev
836
837 def musicxml_nonarpeggiate_to_lily_event (mxl_event):
838     ev = musicexp.ArpeggioEvent ()
839     ev.non_arpeggiate = True
840     ev.direction = musicxml_direction_to_indicator (getattr (mxl_event, 'direction', None))
841     return ev
842
843 def musicxml_tremolo_to_lily_event (mxl_event):
844     ev = musicexp.TremoloEvent ()
845     txt = mxl_event.get_text ()
846     if txt:
847       ev.bars = txt
848     else:
849       ev.bars = "3"
850     return ev
851
852 def musicxml_falloff_to_lily_event (mxl_event):
853     ev = musicexp.BendEvent ()
854     ev.alter = -4
855     return ev
856
857 def musicxml_doit_to_lily_event (mxl_event):
858     ev = musicexp.BendEvent ()
859     ev.alter = 4
860     return ev
861
862 def musicxml_bend_to_lily_event (mxl_event):
863     ev = musicexp.BendEvent ()
864     ev.alter = mxl_event.bend_alter ()
865     return ev
866
867 def musicxml_caesura_to_lily_event (mxl_event):
868     ev = musicexp.MarkupEvent ()
869     # FIXME: default to straight or curved caesura?
870     ev.contents = "\\musicglyph #\"scripts.caesura.straight\""
871     ev.force_direction = 1
872     return ev
873
874 def musicxml_fingering_event (mxl_event):
875     ev = musicexp.ShortArticulationEvent ()
876     ev.type = mxl_event.get_text ()
877     return ev
878
879 def musicxml_snappizzicato_event (mxl_event):
880     needed_additional_definitions.append ("snappizzicato")
881     ev = musicexp.MarkupEvent ()
882     ev.contents = "\\snappizzicato"
883     return ev
884
885 def musicxml_string_event (mxl_event):
886     ev = musicexp.NoDirectionArticulationEvent ()
887     ev.type = mxl_event.get_text ()
888     return ev
889
890 def musicxml_accidental_mark (mxl_event):
891     ev = musicexp.MarkupEvent ()
892     contents = { "sharp": "\\sharp",
893       "natural": "\\natural",
894       "flat": "\\flat",
895       "double-sharp": "\\doublesharp",
896       "sharp-sharp": "\\sharp\\sharp",
897       "flat-flat": "\\flat\\flat",
898       "flat-flat": "\\doubleflat",
899       "natural-sharp": "\\natural\\sharp",
900       "natural-flat": "\\natural\\flat",
901       "quarter-flat": "\\semiflat",
902       "quarter-sharp": "\\semisharp",
903       "three-quarters-flat": "\\sesquiflat",
904       "three-quarters-sharp": "\\sesquisharp",
905     }.get (mxl_event.get_text ())
906     if contents:
907         ev.contents = contents
908         return ev
909     else:
910         return None
911
912 # translate articulations, ornaments and other notations into ArticulationEvents
913 # possible values:
914 #   -) string  (ArticulationEvent with that name)
915 #   -) function (function(mxl_event) needs to return a full ArticulationEvent-derived object
916 #   -) (class, name)  (like string, only that a different class than ArticulationEvent is used)
917 # TODO: Some translations are missing!
918 articulations_dict = {
919     "accent": (musicexp.ShortArticulationEvent, ">"), # or "accent"
920     "accidental-mark": musicxml_accidental_mark,
921     "bend": musicxml_bend_to_lily_event,
922     "breath-mark": (musicexp.NoDirectionArticulationEvent, "breathe"),
923     "caesura": musicxml_caesura_to_lily_event,
924     #"delayed-turn": "?",
925     "detached-legato": (musicexp.ShortArticulationEvent, "_"), # or "portato"
926     "doit": musicxml_doit_to_lily_event,
927     #"double-tongue": "?",
928     "down-bow": "downbow",
929     "falloff": musicxml_falloff_to_lily_event,
930     "fingering": musicxml_fingering_event,
931     #"fingernails": "?",
932     #"fret": "?",
933     #"hammer-on": "?",
934     "harmonic": "flageolet",
935     #"heel": "?",
936     "inverted-mordent": "prall",
937     "inverted-turn": "reverseturn",
938     "mordent": "mordent",
939     "open-string": "open",
940     #"plop": "?",
941     #"pluck": "?",
942     #"pull-off": "?",
943     #"schleifer": "?",
944     #"scoop": "?",
945     #"shake": "?",
946     "snap-pizzicato": musicxml_snappizzicato_event,
947     #"spiccato": "?",
948     "staccatissimo": (musicexp.ShortArticulationEvent, "|"), # or "staccatissimo"
949     "staccato": (musicexp.ShortArticulationEvent, "."), # or "staccato"
950     "stopped": (musicexp.ShortArticulationEvent, "+"), # or "stopped"
951     #"stress": "?",
952     "string": musicxml_string_event,
953     "strong-accent": (musicexp.ShortArticulationEvent, "^"), # or "marcato"
954     #"tap": "?",
955     "tenuto": (musicexp.ShortArticulationEvent, "-"), # or "tenuto"
956     "thumb-position": "thumb",
957     #"toe": "?",
958     "turn": "turn",
959     "tremolo": musicxml_tremolo_to_lily_event,
960     "trill-mark": "trill",
961     #"triple-tongue": "?",
962     #"unstress": "?"
963     "up-bow": "upbow",
964     #"wavy-line": "?",
965 }
966 articulation_spanners = [ "wavy-line" ]
967
968 def musicxml_articulation_to_lily_event (mxl_event):
969     # wavy-line elements are treated as trill spanners, not as articulation ornaments
970     if mxl_event.get_name () in articulation_spanners:
971         return musicxml_spanner_to_lily_event (mxl_event)
972
973     tmp_tp = articulations_dict.get (mxl_event.get_name ())
974     if not tmp_tp:
975         return
976
977     if isinstance (tmp_tp, str):
978         ev = musicexp.ArticulationEvent ()
979         ev.type = tmp_tp
980     elif isinstance (tmp_tp, tuple):
981         ev = tmp_tp[0] ()
982         ev.type = tmp_tp[1]
983     else:
984         ev = tmp_tp (mxl_event)
985
986     # Some articulations use the type attribute, other the placement...
987     dir = None
988     if hasattr (mxl_event, 'type') and options.convert_directions:
989         dir = musicxml_direction_to_indicator (mxl_event.type)
990     if hasattr (mxl_event, 'placement') and options.convert_directions:
991         dir = musicxml_direction_to_indicator (mxl_event.placement)
992     if dir:
993         ev.force_direction = dir
994     return ev
995
996
997
998 def musicxml_dynamics_to_lily_event (dynentry):
999     dynamics_available = (
1000         "ppppp", "pppp", "ppp", "pp", "p", "mp", "mf", 
1001         "f", "ff", "fff", "ffff", "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" )
1002     dynamicsname = dynentry.get_name ()
1003     if dynamicsname == "other-dynamics":
1004         dynamicsname = dynentry.get_text ()
1005     if not dynamicsname or dynamicsname=="#text":
1006         return
1007
1008     if not dynamicsname in dynamics_available:
1009         # Get rid of - in tag names (illegal in ly tags!)
1010         dynamicstext = dynamicsname
1011         dynamicsname = string.replace (dynamicsname, "-", "")
1012         additional_definitions[dynamicsname] = dynamicsname + \
1013               " = #(make-dynamic-script \"" + dynamicstext + "\")"
1014         needed_additional_definitions.append (dynamicsname)
1015     event = musicexp.DynamicsEvent ()
1016     event.type = dynamicsname
1017     return event
1018
1019 # Convert single-color two-byte strings to numbers 0.0 - 1.0
1020 def hexcolorval_to_nr (hex_val):
1021     try:
1022         v = int (hex_val, 16)
1023         if v == 255:
1024             v = 256
1025         return v / 256.
1026     except ValueError:
1027         return 0.
1028
1029 def hex_to_color (hex_val):
1030     res = re.match (r'#([0-9a-f][0-9a-f]|)([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])$', hex_val, re.IGNORECASE)
1031     if res:
1032         return map (lambda x: hexcolorval_to_nr (x), res.group (2,3,4))
1033     else:
1034         return None
1035
1036 def musicxml_words_to_lily_event (words):
1037     event = musicexp.TextEvent ()
1038     text = words.get_text ()
1039     text = re.sub ('^ *\n? *', '', text)
1040     text = re.sub (' *\n? *$', '', text)
1041     event.text = text
1042
1043     if hasattr (words, 'default-y') and options.convert_directions:
1044         offset = getattr (words, 'default-y')
1045         try:
1046             off = string.atoi (offset)
1047             if off > 0:
1048                 event.force_direction = 1
1049             else:
1050                 event.force_direction = -1
1051         except ValueError:
1052             event.force_direction = 0
1053
1054     if hasattr (words, 'font-weight'):
1055         font_weight = { "normal": '', "bold": '\\bold' }.get (getattr (words, 'font-weight'), '')
1056         if font_weight:
1057             event.markup += font_weight
1058
1059     if hasattr (words, 'font-size'):
1060         size = getattr (words, 'font-size')
1061         font_size = {
1062             "xx-small": '\\teeny',
1063             "x-small": '\\tiny',
1064             "small": '\\small',
1065             "medium": '',
1066             "large": '\\large',
1067             "x-large": '\\huge',
1068             "xx-large": '\\larger\\huge'
1069         }.get (size, '')
1070         if font_size:
1071             event.markup += font_size
1072
1073     if hasattr (words, 'color'):
1074         color = getattr (words, 'color')
1075         rgb = hex_to_color (color)
1076         if rgb:
1077             event.markup += "\\with-color #(rgb-color %s %s %s)" % (rgb[0], rgb[1], rgb[2])
1078
1079     if hasattr (words, 'font-style'):
1080         font_style = { "italic": '\\italic' }.get (getattr (words, 'font-style'), '')
1081         if font_style:
1082             event.markup += font_style
1083
1084     # TODO: How should I best convert the font-family attribute?
1085
1086     # TODO: How can I represent the underline, overline and line-through
1087     #       attributes in Lilypond? Values of these attributes indicate
1088     #       the number of lines
1089
1090     return event
1091
1092
1093 # convert accordion-registration to lilypond.
1094 # Since lilypond does not have any built-in commands, we need to create
1095 # the markup commands manually and define our own variables.
1096 # Idea was taken from: http://lsr.dsi.unimi.it/LSR/Item?id=194
1097 def musicxml_accordion_to_markup (mxl_event):
1098     commandname = "accReg"
1099     command = ""
1100
1101     high = mxl_event.get_maybe_exist_named_child ('accordion-high')
1102     if high:
1103         commandname += "H"
1104         command += """\\combine
1105           \\raise #2.5 \\musicglyph #\"accordion.accDot\"
1106           """
1107     middle = mxl_event.get_maybe_exist_named_child ('accordion-middle')
1108     if middle:
1109         # By default, use one dot (when no or invalid content is given). The 
1110         # MusicXML spec is quiet about this case...
1111         txt = 1
1112         try:
1113           txt = string.atoi (middle.get_text ())
1114         except ValueError:
1115             pass
1116         if txt == 3:
1117             commandname += "MMM"
1118             command += """\\combine
1119           \\raise #1.5 \\musicglyph #\"accordion.accDot\"
1120           \\combine
1121           \\raise #1.5 \\translate #(cons 1 0) \\musicglyph #\"accordion.accDot\"
1122           \\combine
1123           \\raise #1.5 \\translate #(cons -1 0) \\musicglyph #\"accordion.accDot\"
1124           """
1125         elif txt == 2:
1126             commandname += "MM"
1127             command += """\\combine
1128           \\raise #1.5 \\translate #(cons 0.5 0) \\musicglyph #\"accordion.accDot\"
1129           \\combine
1130           \\raise #1.5 \\translate #(cons -0.5 0) \\musicglyph #\"accordion.accDot\"
1131           """
1132         elif not txt <= 0:
1133             commandname += "M"
1134             command += """\\combine
1135           \\raise #1.5 \\musicglyph #\"accordion.accDot\"
1136           """
1137     low = mxl_event.get_maybe_exist_named_child ('accordion-low')
1138     if low:
1139         commandname += "L"
1140         command += """\\combine
1141           \\raise #0.5 \musicglyph #\"accordion.accDot\"
1142           """
1143
1144     command += "\musicglyph #\"accordion.accDiscant\""
1145     command = "\\markup { \\normalsize %s }" % command
1146     # Define the newly built command \accReg[H][MMM][L]
1147     additional_definitions[commandname] = "%s = %s" % (commandname, command)
1148     needed_additional_definitions.append (commandname)
1149     return "\\%s" % commandname
1150
1151 def musicxml_accordion_to_ly (mxl_event):
1152     txt = musicxml_accordion_to_markup (mxl_event)
1153     if txt:
1154         ev = musicexp.MarkEvent (txt)
1155         return ev
1156     return
1157
1158
1159 def musicxml_rehearsal_to_ly_mark (mxl_event):
1160     text = mxl_event.get_text ()
1161     if not text:
1162         return
1163     # default is boxed rehearsal marks!
1164     encl = "box"
1165     if hasattr (mxl_event, 'enclosure'):
1166         encl = {"none": None, "square": "box", "circle": "circle" }.get (mxl_event.enclosure, None)
1167     if encl:
1168         text = "\\%s { %s }" % (encl, text)
1169     ev = musicexp.MarkEvent ("\\markup { %s }" % text)
1170     return ev
1171
1172 def musicxml_harp_pedals_to_ly (mxl_event):
1173     count = 0
1174     result = "\\harp-pedal #\""
1175     for t in mxl_event.get_named_children ('pedal-tuning'):
1176       alter = t.get_named_child ('pedal-alter')
1177       if alter:
1178         val = int (alter.get_text ().strip ())
1179         result += {1: "v", 0: "-", -1: "^"}.get (val, "")
1180       count += 1
1181       if count == 3:
1182         result += "|"
1183     ev = musicexp.MarkupEvent ()
1184     ev.contents = result + "\""
1185     return ev
1186
1187 def musicxml_eyeglasses_to_ly (mxl_event):
1188     needed_additional_definitions.append ("eyeglasses")
1189     return musicexp.MarkEvent ("\\eyeglasses")
1190
1191 def next_non_hash_index (lst, pos):
1192     pos += 1
1193     while pos < len (lst) and isinstance (lst[pos], musicxml.Hash_text):
1194         pos += 1
1195     return pos
1196
1197 def musicxml_metronome_to_ly (mxl_event):
1198     children = mxl_event.get_all_children ()
1199     if not children:
1200         return
1201
1202     index = -1
1203     index = next_non_hash_index (children, index)
1204     if isinstance (children[index], musicxml.BeatUnit): 
1205         # first form of metronome-mark, using unit and beats/min or other unit
1206         ev = musicexp.TempoMark ()
1207         if hasattr (mxl_event, 'parentheses'):
1208             ev.set_parentheses (mxl_event.parentheses == "yes")
1209
1210         d = musicexp.Duration ()
1211         d.duration_log = musicxml.musicxml_duration_to_log (children[index].get_text ())
1212         index = next_non_hash_index (children, index)
1213         if isinstance (children[index], musicxml.BeatUnitDot):
1214             d.dots = 1
1215             index = next_non_hash_index (children, index)
1216         ev.set_base_duration (d)
1217         if isinstance (children[index], musicxml.BeatUnit):
1218             # Form "note = newnote"
1219             newd = musicexp.Duration ()
1220             newd.duration_log = musicxml.musicxml_duration_to_log (children[index].get_text ())
1221             index = next_non_hash_index (children, index)
1222             if isinstance (children[index], musicxml.BeatUnitDot):
1223                 newd.dots = 1
1224                 index = next_non_hash_index (children, index)
1225             ev.set_new_duration (newd)
1226         elif isinstance (children[index], musicxml.PerMinute):
1227             # Form "note = bpm"
1228             try:
1229                 beats = int (children[index].get_text ())
1230                 ev.set_beats_per_minute (beats)
1231             except ValueError:
1232                 pass
1233         else:
1234             error_message (_ ("Unknown metronome mark, ignoring"))
1235             return
1236         return ev
1237     else:
1238         #TODO: Implement the other (more complex) way for tempo marks!
1239         error_message (_ ("Metronome marks with complex relations (<metronome-note> in MusicXML) are not yet implemented."))
1240         return
1241
1242 # translate directions into Events, possible values:
1243 #   -) string  (MarkEvent with that command)
1244 #   -) function (function(mxl_event) needs to return a full Event-derived object
1245 #   -) (class, name)  (like string, only that a different class than MarkEvent is used)
1246 directions_dict = {
1247     'accordion-registration' : musicxml_accordion_to_ly,
1248     'coda' : (musicexp.MusicGlyphMarkEvent, "coda"),
1249 #     'damp' : ???
1250 #     'damp-all' : ???
1251     'eyeglasses': musicxml_eyeglasses_to_ly,
1252     'harp-pedals' : musicxml_harp_pedals_to_ly,
1253 #     'image' : ???
1254     'metronome' : musicxml_metronome_to_ly,
1255     'rehearsal' : musicxml_rehearsal_to_ly_mark,
1256 #     'scordatura' : ???
1257     'segno' : (musicexp.MusicGlyphMarkEvent, "segno"),
1258     'words' : musicxml_words_to_lily_event,
1259 }
1260 directions_spanners = [ 'octave-shift', 'pedal', 'wedge', 'dashes', 'bracket' ]
1261
1262 def musicxml_direction_to_lily (n):
1263     # TODO: Handle the <staff> element!
1264     res = []
1265     # placement applies to all children!
1266     dir = None
1267     if hasattr (n, 'placement') and options.convert_directions:
1268         dir = musicxml_direction_to_indicator (n.placement)
1269     dirtype_children = []
1270     # TODO: The direction-type is used for grouping (e.g. dynamics with text), 
1271     #       so we can't simply flatten them out!
1272     for dt in n.get_typed_children (musicxml.DirType):
1273         dirtype_children += dt.get_all_children ()
1274
1275     for entry in dirtype_children:
1276         # backets, dashes, octave shifts. pedal marks, hairpins etc. are spanners:
1277         if entry.get_name() in directions_spanners:
1278             event = musicxml_spanner_to_lily_event (entry)
1279             if event:
1280                 res.append (event)
1281             continue
1282
1283         # now treat all the "simple" ones, that can be translated using the dict
1284         ev = None
1285         tmp_tp = directions_dict.get (entry.get_name (), None)
1286         if isinstance (tmp_tp, str): # string means MarkEvent
1287             ev = musicexp.MarkEvent (tmp_tp)
1288         elif isinstance (tmp_tp, tuple): # tuple means (EventClass, "text")
1289             ev = tmp_tp[0] (tmp_tp[1])
1290         elif tmp_tp:
1291             ev = tmp_tp (entry)
1292         if ev:
1293             # TODO: set the correct direction! Unfortunately, \mark in ly does
1294             #       not seem to support directions!
1295             res.append (ev)
1296             continue
1297
1298         if entry.get_name () == "dynamics":
1299             for dynentry in entry.get_all_children ():
1300                 ev = musicxml_dynamics_to_lily_event (dynentry)
1301                 if ev:
1302                     res.append (ev)
1303
1304     return res
1305
1306 def musicxml_frame_to_lily_event (frame):
1307     ev = musicexp.FretEvent ()
1308     ev.strings = frame.get_strings ()
1309     ev.frets = frame.get_frets ()
1310     #offset = frame.get_first_fret () - 1
1311     barre = []
1312     for fn in frame.get_named_children ('frame-note'):
1313         fret = fn.get_fret ()
1314         if fret <= 0:
1315             fret = "o"
1316         el = [ fn.get_string (), fret ]
1317         fingering = fn.get_fingering ()
1318         if fingering >= 0:
1319             el.append (fingering)
1320         ev.elements.append (el)
1321         b = fn.get_barre ()
1322         if b == 'start':
1323             barre[0] = el[0] # start string
1324             barre[2] = el[1] # fret
1325         elif b == 'stop':
1326             barre[1] = el[0] # end string
1327     if barre:
1328         ev.barre = barre
1329     return ev
1330
1331 def musicxml_harmony_to_lily (n):
1332     res = []
1333     for f in n.get_named_children ('frame'):
1334         ev = musicxml_frame_to_lily_event (f)
1335         if ev:
1336             res.append (ev)
1337     return res
1338
1339
1340 def musicxml_chordpitch_to_lily (mxl_cpitch):
1341     r = musicexp.ChordPitch ()
1342     r.alteration = mxl_cpitch.get_alteration ()
1343     r.step = musicxml_step_to_lily (mxl_cpitch.get_step ())
1344     return r
1345
1346 chordkind_dict = {
1347     'major': '5',
1348     'minor': 'm5',
1349     'augmented': 'aug5',
1350     'diminished': 'dim5',
1351         # Sevenths:
1352     'dominant': '7',
1353     'major-seventh': 'maj7',
1354     'minor-seventh': 'm7',
1355     'diminished-seventh': 'dim7',
1356     'augmented-seventh': 'aug7',
1357     'half-diminished': 'dim5m7',
1358     'major-minor': 'maj7m5',
1359         # Sixths:
1360     'major-sixth': '6',
1361     'minor-sixth': 'm6',
1362         # Ninths:
1363     'dominant-ninth': '9',
1364     'major-ninth': 'maj9',
1365     'minor-ninth': 'm9',
1366         # 11ths (usually as the basis for alteration):
1367     'dominant-11th': '11',
1368     'major-11th': 'maj11',
1369     'minor-11th': 'm11',
1370         # 13ths (usually as the basis for alteration):
1371     'dominant-13th': '13.11',
1372     'major-13th': 'maj13.11',
1373     'minor-13th': 'm13',
1374         # Suspended:
1375     'suspended-second': 'sus2',
1376     'suspended-fourth': 'sus4',
1377         # Functional sixths:
1378     # TODO
1379     #'Neapolitan': '???',
1380     #'Italian': '???',
1381     #'French': '???',
1382     #'German': '???',
1383         # Other:
1384     #'pedal': '???',(pedal-point bass)
1385     'power': '5^3',
1386     #'Tristan': '???',
1387     'other': '1',
1388     'none': None,
1389 }
1390
1391 def musicxml_chordkind_to_lily (kind):
1392     res = chordkind_dict.get (kind, None)
1393     # Check for None, since a major chord is converted to ''
1394     if res == None:
1395         error_message (_ ("Unable to convert chord type %s to lilypond.") % kind)
1396     return res
1397
1398 def musicxml_harmony_to_lily_chordname (n):
1399     res = []
1400     root = n.get_maybe_exist_named_child ('root')
1401     if root:
1402         ev = musicexp.ChordNameEvent ()
1403         ev.root = musicxml_chordpitch_to_lily (root)
1404         kind = n.get_maybe_exist_named_child ('kind')
1405         if kind:
1406             ev.kind = musicxml_chordkind_to_lily (kind.get_text ())
1407             if not ev.kind:
1408                 return res
1409         bass = n.get_maybe_exist_named_child ('bass')
1410         if bass:
1411             ev.bass = musicxml_chordpitch_to_lily (bass)
1412         inversion = n.get_maybe_exist_named_child ('inversion')
1413         if inversion:
1414             # TODO: Lilypond does not support inversions, does it?
1415
1416             # Mail from Carl Sorensen on lilypond-devel, June 11, 2008:
1417             # 4. LilyPond supports the first inversion in the form of added 
1418             # bass notes.  So the first inversion of C major would be c:/g.   
1419             # To get the second inversion of C major, you would need to do 
1420             # e:6-3-^5 or e:m6-^5.  However, both of these techniques 
1421             # require you to know the chord and calculate either the fifth 
1422             # pitch (for the first inversion) or the third pitch (for the 
1423             # second inversion) so they may not be helpful for musicxml2ly.
1424             inversion_count = string.atoi (inversion.get_text ())
1425             if inversion_count == 1:
1426               # TODO: Calculate the bass note for the inversion...
1427               pass
1428             pass
1429         for deg in n.get_named_children ('degree'):
1430             d = musicexp.ChordModification ()
1431             d.type = deg.get_type ()
1432             d.step = deg.get_value ()
1433             d.alteration = deg.get_alter ()
1434             ev.add_modification (d)
1435         #TODO: convert the user-symbols attribute: 
1436             #major: a triangle, like Unicode 25B3
1437             #minor: -, like Unicode 002D
1438             #augmented: +, like Unicode 002B
1439             #diminished: (degree), like Unicode 00B0
1440             #half-diminished: (o with slash), like Unicode 00F8
1441         if ev and ev.root:
1442             res.append (ev)
1443
1444     return res
1445
1446 def musicxml_figured_bass_note_to_lily (n):
1447     res = musicexp.FiguredBassNote ()
1448     suffix_dict = { 'sharp' : "+", 
1449                     'flat' : "-", 
1450                     'natural' : "!", 
1451                     'double-sharp' : "++", 
1452                     'flat-flat' : "--", 
1453                     'sharp-sharp' : "++", 
1454                     'slash' : "/" }
1455     prefix = n.get_maybe_exist_named_child ('prefix')
1456     if prefix:
1457         res.set_prefix (suffix_dict.get (prefix.get_text (), ""))
1458     fnumber = n.get_maybe_exist_named_child ('figure-number')
1459     if fnumber:
1460         res.set_number (fnumber.get_text ())
1461     suffix = n.get_maybe_exist_named_child ('suffix')
1462     if suffix:
1463         res.set_suffix (suffix_dict.get (suffix.get_text (), ""))
1464     if n.get_maybe_exist_named_child ('extend'):
1465         # TODO: Implement extender lines (unfortunately, in lilypond you have 
1466         #       to use \set useBassFigureExtenders = ##t, which turns them on
1467         #       globally, while MusicXML has a property for each note...
1468         #       I'm not sure there is a proper way to implement this cleanly
1469         #n.extend
1470         pass
1471     return res
1472
1473
1474
1475 def musicxml_figured_bass_to_lily (n):
1476     if not isinstance (n, musicxml.FiguredBass):
1477         return
1478     res = musicexp.FiguredBassEvent ()
1479     for i in n.get_named_children ('figure'):
1480         note = musicxml_figured_bass_note_to_lily (i)
1481         if note:
1482             res.append (note)
1483     dur = n.get_maybe_exist_named_child ('duration')
1484     if dur:
1485         # apply the duration to res
1486         length = Rational(int(dur.get_text()), n._divisions)*Rational(1,4)
1487         res.set_real_duration (length)
1488         duration = rational_to_lily_duration (length)
1489         if duration:
1490             res.set_duration (duration)
1491     if hasattr (n, 'parentheses') and n.parentheses == "yes":
1492         res.set_parentheses (True)
1493     return res
1494
1495 instrument_drumtype_dict = {
1496     'Acoustic Snare Drum': 'acousticsnare',
1497     'Side Stick': 'sidestick',
1498     'Open Triangle': 'opentriangle',
1499     'Mute Triangle': 'mutetriangle',
1500     'Tambourine': 'tambourine',
1501     'Bass Drum': 'bassdrum',
1502 }
1503
1504 def musicxml_note_to_lily_main_event (n):
1505     pitch  = None
1506     duration = None
1507     event = None
1508
1509     mxl_pitch = n.get_maybe_exist_typed_child (musicxml.Pitch)
1510     if mxl_pitch:
1511         pitch = musicxml_pitch_to_lily (mxl_pitch)
1512         event = musicexp.NoteEvent ()
1513         event.pitch = pitch
1514
1515         acc = n.get_maybe_exist_named_child ('accidental')
1516         if acc:
1517             # let's not force accs everywhere. 
1518             event.cautionary = acc.editorial
1519
1520     elif n.get_maybe_exist_typed_child (musicxml.Unpitched):
1521         # Unpitched elements have display-step and can also have
1522         # display-octave.
1523         unpitched = n.get_maybe_exist_typed_child (musicxml.Unpitched)
1524         event = musicexp.NoteEvent ()
1525         event.pitch = musicxml_unpitched_to_lily (unpitched)
1526         
1527     elif n.get_maybe_exist_typed_child (musicxml.Rest):
1528         # rests can have display-octave and display-step, which are
1529         # treated like an ordinary note pitch
1530         rest = n.get_maybe_exist_typed_child (musicxml.Rest)
1531         event = musicexp.RestEvent ()
1532         pitch = musicxml_restdisplay_to_lily (rest)
1533         event.pitch = pitch
1534
1535     elif n.instrument_name:
1536         event = musicexp.NoteEvent ()
1537         drum_type = instrument_drumtype_dict.get (n.instrument_name)
1538         if drum_type:
1539             event.drum_type = drum_type
1540         else:
1541             n.message (_ ("drum %s type unknown, please add to instrument_drumtype_dict") % n.instrument_name)
1542             event.drum_type = 'acousticsnare'
1543
1544     else:
1545         n.message (_ ("cannot find suitable event"))
1546
1547     if event:
1548         event.duration = musicxml_duration_to_lily (n)
1549
1550     return event
1551
1552
1553 ## TODO
1554 class NegativeSkip:
1555     def __init__ (self, here, dest):
1556         self.here = here
1557         self.dest = dest
1558
1559 class LilyPondVoiceBuilder:
1560     def __init__ (self):
1561         self.elements = []
1562         self.pending_dynamics = []
1563         self.end_moment = Rational (0)
1564         self.begin_moment = Rational (0)
1565         self.pending_multibar = Rational (0)
1566         self.ignore_skips = False
1567         self.has_relevant_elements = False
1568         self.measure_length = (4, 4)
1569
1570     def _insert_multibar (self):
1571         layout_information.set_context_item ('Score', 'skipBars = ##t')
1572         r = musicexp.MultiMeasureRest ()
1573         lenfrac = Rational (self.measure_length[0], self.measure_length[1])
1574         r.duration = rational_to_lily_duration (lenfrac)
1575         r.duration.factor *= self.pending_multibar / lenfrac
1576         self.elements.append (r)
1577         self.begin_moment = self.end_moment
1578         self.end_moment = self.begin_moment + self.pending_multibar
1579         self.pending_multibar = Rational (0)
1580
1581     def set_measure_length (self, mlen):
1582         if (mlen != self.measure_length) and self.pending_multibar:
1583             self._insert_multibar ()
1584         self.measure_length = mlen
1585
1586     def add_multibar_rest (self, duration):
1587         self.pending_multibar += duration
1588
1589     def set_duration (self, duration):
1590         self.end_moment = self.begin_moment + duration
1591     def current_duration (self):
1592         return self.end_moment - self.begin_moment
1593         
1594     def add_music (self, music, duration):
1595         assert isinstance (music, musicexp.Music)
1596         if self.pending_multibar > Rational (0):
1597             self._insert_multibar ()
1598
1599         self.has_relevant_elements = True
1600         self.elements.append (music)
1601         self.begin_moment = self.end_moment
1602         self.set_duration (duration)
1603         
1604         # Insert all pending dynamics right after the note/rest:
1605         if isinstance (music, musicexp.ChordEvent) and self.pending_dynamics:
1606             for d in self.pending_dynamics:
1607                 music.append (d)
1608             self.pending_dynamics = []
1609
1610     # Insert some music command that does not affect the position in the measure
1611     def add_command (self, command):
1612         assert isinstance (command, musicexp.Music)
1613         if self.pending_multibar > Rational (0):
1614             self._insert_multibar ()
1615         self.has_relevant_elements = True
1616         self.elements.append (command)
1617     def add_barline (self, barline):
1618         # TODO: Implement merging of default barline and custom bar line
1619         self.add_music (barline, Rational (0))
1620     def add_partial (self, command):
1621         self.ignore_skips = True
1622         self.add_command (command)
1623
1624     def add_dynamics (self, dynamic):
1625         # store the dynamic item(s) until we encounter the next note/rest:
1626         self.pending_dynamics.append (dynamic)
1627
1628     def add_bar_check (self, number):
1629         # re/store has_relevant_elements, so that a barline alone does not
1630         # trigger output for figured bass, chord names
1631         has_relevant = self.has_relevant_elements
1632         b = musicexp.BarLine ()
1633         b.bar_number = number
1634         self.add_barline (b)
1635         self.has_relevant_elements = has_relevant
1636
1637     def jumpto (self, moment):
1638         current_end = self.end_moment + self.pending_multibar
1639         diff = moment - current_end
1640         
1641         if diff < Rational (0):
1642             error_message (_ ('Negative skip %s (from position %s to %s)') % 
1643                              (diff, current_end, moment))
1644             diff = Rational (0)
1645
1646         if diff > Rational (0) and not (self.ignore_skips and moment == 0):
1647             skip = musicexp.SkipEvent()
1648             duration_factor = 1
1649             duration_log = {1: 0, 2: 1, 4:2, 8:3, 16:4, 32:5, 64:6, 128:7, 256:8, 512:9}.get (diff.denominator (), -1)
1650             duration_dots = 0
1651             # TODO: Use the time signature for skips, too. Problem: The skip 
1652             #       might not start at a measure boundary!
1653             if duration_log > 0: # denominator is a power of 2...
1654                 if diff.numerator () == 3:
1655                     duration_log -= 1
1656                     duration_dots = 1
1657                 else:
1658                     duration_factor = Rational (diff.numerator ())
1659             else:
1660                 # for skips of a whole or more, simply use s1*factor
1661                 duration_log = 0
1662                 duration_factor = diff
1663             skip.duration.duration_log = duration_log
1664             skip.duration.factor = duration_factor
1665             skip.duration.dots = duration_dots
1666
1667             evc = musicexp.ChordEvent ()
1668             evc.elements.append (skip)
1669             self.add_music (evc, diff)
1670
1671         if diff > Rational (0) and moment == 0:
1672             self.ignore_skips = False
1673
1674     def last_event_chord (self, starting_at):
1675
1676         value = None
1677
1678         # if the position matches, find the last ChordEvent, do not cross a bar line!
1679         at = len( self.elements ) - 1
1680         while (at >= 0 and
1681                not isinstance (self.elements[at], musicexp.ChordEvent) and
1682                not isinstance (self.elements[at], musicexp.BarLine)):
1683             at -= 1
1684
1685         if (self.elements
1686             and at >= 0
1687             and isinstance (self.elements[at], musicexp.ChordEvent)
1688             and self.begin_moment == starting_at):
1689             value = self.elements[at]
1690         else:
1691             self.jumpto (starting_at)
1692             value = None
1693         return value
1694         
1695     def correct_negative_skip (self, goto):
1696         self.end_moment = goto
1697         self.begin_moment = goto
1698         evc = musicexp.ChordEvent ()
1699         self.elements.append (evc)
1700
1701
1702 class VoiceData:
1703     def __init__ (self):
1704         self.voicename = None
1705         self.voicedata = None
1706         self.ly_voice = None
1707         self.figured_bass = None
1708         self.chordnames = None
1709         self.lyrics_dict = {}
1710         self.lyrics_order = []
1711
1712 def musicxml_step_to_lily (step):
1713     if step:
1714         return (ord (step) - ord ('A') + 7 - 2) % 7
1715     else:
1716         return None
1717
1718 def measure_length_from_attributes (attr, current_measure_length):
1719     mxl = attr.get_named_attribute ('time')
1720     if mxl:
1721         return attr.get_time_signature ()
1722     else:
1723         return current_measure_length
1724
1725 def musicxml_voice_to_lily_voice (voice):
1726     tuplet_events = []
1727     modes_found = {}
1728     lyrics = {}
1729     return_value = VoiceData ()
1730     return_value.voicedata = voice
1731     
1732     # First pitch needed for relative mode (if selected in command-line options)
1733     first_pitch = None
1734
1735     # Needed for melismata detection (ignore lyrics on those notes!):
1736     inside_slur = False
1737     is_tied = False
1738     is_chord = False
1739     is_beamed = False
1740     ignore_lyrics = False
1741
1742     current_staff = None
1743     
1744     pending_figured_bass = []
1745     pending_chordnames = []
1746
1747     # Make sure that the keys in the dict don't get reordered, since
1748     # we need the correct ordering of the lyrics stanzas! By default,
1749     # a dict will reorder its keys
1750     return_value.lyrics_order = voice.get_lyrics_numbers ()
1751     for k in return_value.lyrics_order:
1752         lyrics[k] = []
1753
1754     voice_builder = LilyPondVoiceBuilder ()
1755     figured_bass_builder = LilyPondVoiceBuilder ()
1756     chordnames_builder = LilyPondVoiceBuilder ()
1757     current_measure_length = (4, 4)
1758     voice_builder.set_measure_length (current_measure_length)
1759
1760     for n in voice._elements:
1761         if n.get_name () == 'forward':
1762             continue
1763         staff = n.get_maybe_exist_named_child ('staff')
1764         if staff:
1765             staff = staff.get_text ()
1766             if current_staff and staff <> current_staff and not n.get_maybe_exist_named_child ('chord'):
1767                 voice_builder.add_command (musicexp.StaffChange (staff))
1768             current_staff = staff
1769
1770         if isinstance (n, musicxml.Partial) and n.partial > 0:
1771             a = musicxml_partial_to_lily (n.partial)
1772             if a:
1773                 voice_builder.add_partial (a)
1774             continue
1775
1776         is_chord = n.get_maybe_exist_named_child ('chord')
1777         is_after_grace = (isinstance (n, musicxml.Note) and n.is_after_grace ());
1778         if not is_chord and not is_after_grace:
1779             try:
1780                 voice_builder.jumpto (n._when)
1781             except NegativeSkip, neg:
1782                 voice_builder.correct_negative_skip (n._when)
1783                 n.message (_ ("Negative skip found: from %s to %s, difference is %s") % (neg.here, neg.dest, neg.dest - neg.here))
1784
1785         if isinstance (n, musicxml.Barline):
1786             barlines = musicxml_barline_to_lily (n)
1787             for a in barlines:
1788                 if isinstance (a, musicexp.BarLine):
1789                     voice_builder.add_barline (a)
1790                 elif isinstance (a, RepeatMarker) or isinstance (a, EndingMarker):
1791                     voice_builder.add_command (a)
1792             continue
1793
1794         # Continue any multimeasure-rests before trying to add bar checks!
1795         # Don't handle new MM rests yet, because for them we want bar checks!
1796         rest = n.get_maybe_exist_typed_child (musicxml.Rest)
1797         if (rest and rest.is_whole_measure ()
1798                  and voice_builder.pending_multibar > Rational (0)):
1799             voice_builder.add_multibar_rest (n._duration)
1800             continue
1801
1802
1803         # print a bar check at the beginning of each measure!
1804         if n.is_first () and n._measure_position == Rational (0) and n != voice._elements[0]:
1805             try:
1806                 num = int (n.get_parent ().number)
1807             except ValueError:
1808                 num = 0
1809             if num > 0:
1810                 voice_builder.add_bar_check (num)
1811                 figured_bass_builder.add_bar_check (num)
1812                 chordnames_builder.add_bar_check (num)
1813
1814         # Start any new multimeasure rests
1815         if (rest and rest.is_whole_measure ()):
1816             voice_builder.add_multibar_rest (n._duration)
1817             continue
1818
1819
1820         if isinstance (n, musicxml.Direction):
1821             for a in musicxml_direction_to_lily (n):
1822                 if a.wait_for_note ():
1823                     voice_builder.add_dynamics (a)
1824                 else:
1825                     voice_builder.add_command (a)
1826             continue
1827
1828         if isinstance (n, musicxml.Harmony):
1829             for a in musicxml_harmony_to_lily (n):
1830                 if a.wait_for_note ():
1831                     voice_builder.add_dynamics (a)
1832                 else:
1833                     voice_builder.add_command (a)
1834             for a in musicxml_harmony_to_lily_chordname (n):
1835                 pending_chordnames.append (a)
1836             continue
1837
1838         if isinstance (n, musicxml.FiguredBass):
1839             a = musicxml_figured_bass_to_lily (n)
1840             if a:
1841                 pending_figured_bass.append (a)
1842             continue
1843
1844         if isinstance (n, musicxml.Attributes):
1845             for a in musicxml_attributes_to_lily (n):
1846                 voice_builder.add_command (a)
1847             measure_length = measure_length_from_attributes (n, current_measure_length)
1848             if current_measure_length != measure_length:
1849                 current_measure_length = measure_length
1850                 voice_builder.set_measure_length (current_measure_length)
1851             continue
1852
1853         if not n.__class__.__name__ == 'Note':
1854             n.message (_ ('unexpected %s; expected %s or %s or %s') % (n, 'Note', 'Attributes', 'Barline'))
1855             continue
1856         
1857         main_event = musicxml_note_to_lily_main_event (n)
1858         if main_event and not first_pitch:
1859             first_pitch = main_event.pitch
1860         # ignore lyrics for notes inside a slur, tie, chord or beam
1861         ignore_lyrics = inside_slur or is_tied or is_chord or is_beamed
1862
1863         if main_event and hasattr (main_event, 'drum_type') and main_event.drum_type:
1864             modes_found['drummode'] = True
1865
1866         ev_chord = voice_builder.last_event_chord (n._when)
1867         if not ev_chord: 
1868             ev_chord = musicexp.ChordEvent()
1869             voice_builder.add_music (ev_chord, n._duration)
1870
1871         # For grace notes:
1872         grace = n.get_maybe_exist_typed_child (musicxml.Grace)
1873         if n.is_grace ():
1874             is_after_grace = ev_chord.has_elements () or n.is_after_grace ();
1875             is_chord = n.get_maybe_exist_typed_child (musicxml.Chord)
1876
1877             grace_chord = None
1878
1879             # after-graces and other graces use different lists; Depending on
1880             # whether we have a chord or not, obtain either a new ChordEvent or 
1881             # the previous one to create a chord
1882             if is_after_grace:
1883                 if ev_chord.after_grace_elements and n.get_maybe_exist_typed_child (musicxml.Chord):
1884                     grace_chord = ev_chord.after_grace_elements.get_last_event_chord ()
1885                 if not grace_chord:
1886                     grace_chord = musicexp.ChordEvent ()
1887                     ev_chord.append_after_grace (grace_chord)
1888             elif n.is_grace ():
1889                 if ev_chord.grace_elements and n.get_maybe_exist_typed_child (musicxml.Chord):
1890                     grace_chord = ev_chord.grace_elements.get_last_event_chord ()
1891                 if not grace_chord:
1892                     grace_chord = musicexp.ChordEvent ()
1893                     ev_chord.append_grace (grace_chord)
1894
1895             if hasattr (grace, 'slash') and not is_after_grace:
1896                 # TODO: use grace_type = "appoggiatura" for slurred grace notes
1897                 if grace.slash == "yes":
1898                     ev_chord.grace_type = "acciaccatura"
1899             # now that we have inserted the chord into the grace music, insert
1900             # everything into that chord instead of the ev_chord
1901             ev_chord = grace_chord
1902             ev_chord.append (main_event)
1903             ignore_lyrics = True
1904         else:
1905             ev_chord.append (main_event)
1906             # When a note/chord has grace notes (duration==0), the duration of the
1907             # event chord is not yet known, but the event chord was already added
1908             # with duration 0. The following correct this when we hit the real note!
1909             if voice_builder.current_duration () == 0 and n._duration > 0:
1910                 voice_builder.set_duration (n._duration)
1911         
1912         # if we have a figured bass, set its voice builder to the correct position
1913         # and insert the pending figures
1914         if pending_figured_bass:
1915             try:
1916                 figured_bass_builder.jumpto (n._when)
1917             except NegativeSkip, neg:
1918                 pass
1919             for fb in pending_figured_bass:
1920                 # if a duration is given, use that, otherwise the one of the note
1921                 dur = fb.real_duration
1922                 if not dur:
1923                     dur = ev_chord.get_length ()
1924                 if not fb.duration:
1925                     fb.duration = ev_chord.get_duration ()
1926                 figured_bass_builder.add_music (fb, dur)
1927             pending_figured_bass = []
1928         
1929         if pending_chordnames:
1930             try:
1931                 chordnames_builder.jumpto (n._when)
1932             except NegativeSkip, neg:
1933                 pass
1934             for cn in pending_chordnames:
1935                 # Assign the duration of the EventChord
1936                 cn.duration = ev_chord.get_duration ()
1937                 chordnames_builder.add_music (cn, ev_chord.get_length ())
1938             pending_chordnames = []
1939
1940
1941         notations_children = n.get_typed_children (musicxml.Notations)
1942         tuplet_event = None
1943         span_events = []
1944
1945         # The <notation> element can have the following children (+ means implemented, ~ partially, - not):
1946         # +tied | +slur | +tuplet | glissando | slide | 
1947         #    ornaments | technical | articulations | dynamics |
1948         #    +fermata | arpeggiate | non-arpeggiate | 
1949         #    accidental-mark | other-notation
1950         for notations in notations_children:
1951             for tuplet_event in notations.get_tuplets():
1952                 mod = n.get_maybe_exist_typed_child (musicxml.Time_modification)
1953                 frac = (1,1)
1954                 if mod:
1955                     frac = mod.get_fraction ()
1956
1957                 tuplet_events.append ((ev_chord, tuplet_event, frac))
1958
1959             # First, close all open slurs, only then start any new slur
1960             # TODO: Record the number of the open slur to dtermine the correct
1961             #       closing slur!
1962             endslurs = [s for s in notations.get_named_children ('slur')
1963                 if s.get_type () in ('stop')]
1964             if endslurs and not inside_slur:
1965                 endslurs[0].message (_ ('Encountered closing slur, but no slur is open'))
1966             elif endslurs:
1967                 if len (endslurs) > 1:
1968                     endslurs[0].message (_ ('Cannot have two simultaneous (closing) slurs'))
1969                 # record the slur status for the next note in the loop
1970                 if not grace:
1971                     inside_slur = False
1972                 lily_ev = musicxml_spanner_to_lily_event (endslurs[0])
1973                 ev_chord.append (lily_ev)
1974
1975             startslurs = [s for s in notations.get_named_children ('slur')
1976                 if s.get_type () in ('start')]
1977             if startslurs and inside_slur:
1978                 startslurs[0].message (_ ('Cannot have a slur inside another slur'))
1979             elif startslurs:
1980                 if len (startslurs) > 1:
1981                     startslurs[0].message (_ ('Cannot have two simultaneous slurs'))
1982                 # record the slur status for the next note in the loop
1983                 if not grace:
1984                     inside_slur = True
1985                 lily_ev = musicxml_spanner_to_lily_event (startslurs[0])
1986                 ev_chord.append (lily_ev)
1987
1988
1989             if not grace:
1990                 mxl_tie = notations.get_tie ()
1991                 if mxl_tie and mxl_tie.type == 'start':
1992                     ev_chord.append (musicexp.TieEvent ())
1993                     is_tied = True
1994                 else:
1995                     is_tied = False
1996
1997             fermatas = notations.get_named_children ('fermata')
1998             for a in fermatas:
1999                 ev = musicxml_fermata_to_lily_event (a)
2000                 if ev: 
2001                     ev_chord.append (ev)
2002
2003             arpeggiate = notations.get_named_children ('arpeggiate')
2004             for a in arpeggiate:
2005                 ev = musicxml_arpeggiate_to_lily_event (a)
2006                 if ev:
2007                     ev_chord.append (ev)
2008
2009             arpeggiate = notations.get_named_children ('non-arpeggiate')
2010             for a in arpeggiate:
2011                 ev = musicxml_nonarpeggiate_to_lily_event (a)
2012                 if ev:
2013                     ev_chord.append (ev)
2014
2015             glissandos = notations.get_named_children ('glissando')
2016             glissandos += notations.get_named_children ('slide')
2017             for a in glissandos:
2018                 ev = musicxml_spanner_to_lily_event (a)
2019                 if ev:
2020                     ev_chord.append (ev)
2021
2022             # accidental-marks are direct children of <notation>!
2023             for a in notations.get_named_children ('accidental-mark'):
2024                 ev = musicxml_articulation_to_lily_event (a)
2025                 if ev:
2026                     ev_chord.append (ev)
2027
2028             # Articulations can contain the following child elements:
2029             #         accent | strong-accent | staccato | tenuto |
2030             #         detached-legato | staccatissimo | spiccato |
2031             #         scoop | plop | doit | falloff | breath-mark | 
2032             #         caesura | stress | unstress
2033             # Technical can contain the following child elements:
2034             #         up-bow | down-bow | harmonic | open-string |
2035             #         thumb-position | fingering | pluck | double-tongue |
2036             #         triple-tongue | stopped | snap-pizzicato | fret |
2037             #         string | hammer-on | pull-off | bend | tap | heel |
2038             #         toe | fingernails | other-technical
2039             # Ornaments can contain the following child elements:
2040             #         trill-mark | turn | delayed-turn | inverted-turn |
2041             #         shake | wavy-line | mordent | inverted-mordent | 
2042             #         schleifer | tremolo | other-ornament, accidental-mark
2043             ornaments = notations.get_named_children ('ornaments')
2044             ornaments += notations.get_named_children ('articulations')
2045             ornaments += notations.get_named_children ('technical')
2046
2047             for a in ornaments:
2048                 for ch in a.get_all_children ():
2049                     ev = musicxml_articulation_to_lily_event (ch)
2050                     if ev:
2051                         ev_chord.append (ev)
2052
2053             dynamics = notations.get_named_children ('dynamics')
2054             for a in dynamics:
2055                 for ch in a.get_all_children ():
2056                     ev = musicxml_dynamics_to_lily_event (ch)
2057                     if ev:
2058                         ev_chord.append (ev)
2059
2060
2061         mxl_beams = [b for b in n.get_named_children ('beam')
2062                      if (b.get_type () in ('begin', 'end')
2063                          and b.is_primary ())] 
2064         if mxl_beams and not conversion_settings.ignore_beaming:
2065             beam_ev = musicxml_spanner_to_lily_event (mxl_beams[0])
2066             if beam_ev:
2067                 ev_chord.append (beam_ev)
2068                 if beam_ev.span_direction == -1: # beam and thus melisma starts here
2069                     is_beamed = True
2070                 elif beam_ev.span_direction == 1: # beam and thus melisma ends here
2071                     is_beamed = False
2072             
2073         if tuplet_event:
2074             mod = n.get_maybe_exist_typed_child (musicxml.Time_modification)
2075             frac = (1,1)
2076             if mod:
2077                 frac = mod.get_fraction ()
2078                 
2079             tuplet_events.append ((ev_chord, tuplet_event, frac))
2080
2081         # Extract the lyrics
2082         if not rest and not ignore_lyrics:
2083             note_lyrics_processed = []
2084             note_lyrics_elements = n.get_typed_children (musicxml.Lyric)
2085             for l in note_lyrics_elements:
2086                 if l.get_number () < 0:
2087                     for k in lyrics.keys ():
2088                         lyrics[k].append (l.lyric_to_text ())
2089                         note_lyrics_processed.append (k)
2090                 else:
2091                     lyrics[l.number].append(l.lyric_to_text ())
2092                     note_lyrics_processed.append (l.number)
2093             for lnr in lyrics.keys ():
2094                 if not lnr in note_lyrics_processed:
2095                     lyrics[lnr].append ("\skip4")
2096
2097     ## force trailing mm rests to be written out.   
2098     voice_builder.add_music (musicexp.ChordEvent (), Rational (0))
2099     
2100     ly_voice = group_tuplets (voice_builder.elements, tuplet_events)
2101     ly_voice = group_repeats (ly_voice)
2102
2103     seq_music = musicexp.SequentialMusic ()
2104
2105     if 'drummode' in modes_found.keys ():
2106         ## \key <pitch> barfs in drummode.
2107         ly_voice = [e for e in ly_voice
2108                     if not isinstance(e, musicexp.KeySignatureChange)]
2109     
2110     seq_music.elements = ly_voice
2111     for k in lyrics.keys ():
2112         return_value.lyrics_dict[k] = musicexp.Lyrics ()
2113         return_value.lyrics_dict[k].lyrics_syllables = lyrics[k]
2114     
2115     
2116     if len (modes_found) > 1:
2117        error_message (_ ('cannot simultaneously have more than one mode: %s') % modes_found.keys ())
2118        
2119     if options.relative:
2120         v = musicexp.RelativeMusic ()
2121         v.element = seq_music
2122         v.basepitch = first_pitch
2123         seq_music = v
2124
2125     return_value.ly_voice = seq_music
2126     for mode in modes_found.keys ():
2127         v = musicexp.ModeChangingMusicWrapper()
2128         v.element = seq_music
2129         v.mode = mode
2130         return_value.ly_voice = v
2131     
2132     # create \figuremode { figured bass elements }
2133     if figured_bass_builder.has_relevant_elements:
2134         fbass_music = musicexp.SequentialMusic ()
2135         fbass_music.elements = figured_bass_builder.elements
2136         v = musicexp.ModeChangingMusicWrapper()
2137         v.mode = 'figuremode'
2138         v.element = fbass_music
2139         return_value.figured_bass = v
2140     
2141     # create \chordmode { chords }
2142     if chordnames_builder.has_relevant_elements:
2143         cname_music = musicexp.SequentialMusic ()
2144         cname_music.elements = chordnames_builder.elements
2145         v = musicexp.ModeChangingMusicWrapper()
2146         v.mode = 'chordmode'
2147         v.element = cname_music
2148         return_value.chordnames = v
2149     
2150     return return_value
2151
2152 def musicxml_id_to_lily (id):
2153     digits = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five',
2154               'Six', 'Seven', 'Eight', 'Nine', 'Ten']
2155     
2156     for digit in digits:
2157         d = digits.index (digit)
2158         id = re.sub ('%d' % d, digit, id)
2159
2160     id = re.sub  ('[^a-zA-Z]', 'X', id)
2161     return id
2162
2163 def musicxml_pitch_to_lily (mxl_pitch):
2164     p = musicexp.Pitch ()
2165     p.alteration = mxl_pitch.get_alteration ()
2166     p.step = musicxml_step_to_lily (mxl_pitch.get_step ())
2167     p.octave = mxl_pitch.get_octave () - 4
2168     return p
2169
2170 def musicxml_unpitched_to_lily (mxl_unpitched):
2171     p = None
2172     step = mxl_unpitched.get_step ()
2173     if step:
2174         p = musicexp.Pitch ()
2175         p.step = musicxml_step_to_lily (step)
2176     octave = mxl_unpitched.get_octave ()
2177     if octave and p:
2178         p.octave = octave - 4
2179     return p
2180
2181 def musicxml_restdisplay_to_lily (mxl_rest):
2182     p = None
2183     step = mxl_rest.get_step ()
2184     if step:
2185         p = musicexp.Pitch ()
2186         p.step = musicxml_step_to_lily (step)
2187     octave = mxl_rest.get_octave ()
2188     if octave and p:
2189         p.octave = octave - 4
2190     return p
2191
2192 def voices_in_part (part):
2193     """Return a Name -> Voice dictionary for PART"""
2194     part.interpret ()
2195     part.extract_voices ()
2196     voices = part.get_voices ()
2197     part_info = part.get_staff_attributes ()
2198
2199     return (voices, part_info)
2200
2201 def voices_in_part_in_parts (parts):
2202     """return a Part -> Name -> Voice dictionary"""
2203     return dict([(p.id, voices_in_part (p)) for p in parts])
2204
2205
2206 def get_all_voices (parts):
2207     all_voices = voices_in_part_in_parts (parts)
2208
2209     all_ly_voices = {}
2210     all_ly_staffinfo = {}
2211     for p, (name_voice, staff_info) in all_voices.items ():
2212
2213         part_ly_voices = {}
2214         for n, v in name_voice.items ():
2215             progress (_ ("Converting to LilyPond expressions..."))
2216             # musicxml_voice_to_lily_voice returns (lily_voice, {nr->lyrics, nr->lyrics})
2217             part_ly_voices[n] = musicxml_voice_to_lily_voice (v)
2218
2219         all_ly_voices[p] = part_ly_voices
2220         all_ly_staffinfo[p] = staff_info
2221
2222     return (all_ly_voices, all_ly_staffinfo)
2223
2224
2225 def option_parser ():
2226     p = ly.get_option_parser (usage = _ ("musicxml2ly [OPTION]... FILE.xml"),
2227                              description =
2228 _ ("""Convert MusicXML from FILE.xml to LilyPond input.
2229 If the given filename is -, musicxml2ly reads from the command line.
2230 """), add_help_option=False)
2231
2232     p.add_option("-h", "--help",
2233                  action="help",
2234                  help=_ ("show this help and exit"))
2235
2236     p.version = ('''%prog (LilyPond) @TOPLEVEL_VERSION@\n\n'''
2237 +
2238 _ ("""Copyright (c) 2005--2008 by
2239     Han-Wen Nienhuys <hanwen@xs4all.nl>,
2240     Jan Nieuwenhuizen <janneke@gnu.org> and
2241     Reinhold Kainhofer <reinhold@kainhofer.com>
2242 """
2243 +
2244 """
2245 This program is free software.  It is covered by the GNU General Public
2246 License and you are welcome to change it and/or distribute copies of it
2247 under certain conditions.  Invoke as `%s --warranty' for more
2248 information.""") % 'lilypond')
2249
2250     p.add_option("--version",
2251                  action="version",
2252                  help=_ ("show version number and exit"))
2253
2254     p.add_option ('-v', '--verbose',
2255                   action = "store_true",
2256                   dest = 'verbose',
2257                   help = _ ("be verbose"))
2258
2259     p.add_option ('', '--lxml',
2260                   action = "store_true",
2261                   default = False,
2262                   dest = "use_lxml",
2263                   help = _ ("use lxml.etree; uses less memory and cpu time"))
2264
2265     p.add_option ('-z', '--compressed',
2266                   action = "store_true",
2267                   dest = 'compressed',
2268                   default = False,
2269                   help = _ ("input file is a zip-compressed MusicXML file"))
2270
2271     p.add_option ('-r', '--relative',
2272                   action = "store_true",
2273                   default = True,
2274                   dest = "relative",
2275                   help = _ ("convert pitches in relative mode (default)"))
2276
2277     p.add_option ('-a', '--absolute',
2278                   action = "store_false",
2279                   dest = "relative",
2280                   help = _ ("convert pitches in absolute mode"))
2281
2282     p.add_option ('-l', '--language',
2283                   metavar = _ ("LANG"),
2284                   action = "store",
2285                   help = _ ("use a different language file 'LANG.ly' and corresponding pitch names, e.g. 'deutsch' for deutsch.ly"))
2286
2287     p.add_option ('--nd', '--no-articulation-directions', 
2288                   action = "store_false",
2289                   default = True,
2290                   dest = "convert_directions",
2291                   help = _ ("do not convert directions (^, _ or -) for articulations, dynamics, etc."))
2292
2293     p.add_option ('--no-beaming', 
2294                   action = "store_false",
2295                   default = True,
2296                   dest = "convert_beaming",
2297                   help = _ ("do not convert beaming information, use lilypond's automatic beaming instead"))
2298
2299     p.add_option ('-o', '--output',
2300                   metavar = _ ("FILE"),
2301                   action = "store",
2302                   default = None,
2303                   type = 'string',
2304                   dest = 'output_name',
2305                   help = _ ("set output filename to FILE, stdout if -"))
2306     p.add_option_group ('',
2307                         description = (_ ("Report bugs via")
2308                                      + ''' http://post.gmane.org/post.php'''
2309                                      '''?group=gmane.comp.gnu.lilypond.bugs\n'''))
2310     return p
2311
2312 def music_xml_voice_name_to_lily_name (part_id, name):
2313     str = "Part%sVoice%s" % (part_id, name)
2314     return musicxml_id_to_lily (str) 
2315
2316 def music_xml_lyrics_name_to_lily_name (part_id, name, lyricsnr):
2317     str = "Part%sVoice%sLyrics%s" % (part_id, name, lyricsnr)
2318     return musicxml_id_to_lily (str) 
2319
2320 def music_xml_figuredbass_name_to_lily_name (part_id, voicename):
2321     str = "Part%sVoice%sFiguredBass" % (part_id, voicename)
2322     return musicxml_id_to_lily (str) 
2323
2324 def music_xml_chordnames_name_to_lily_name (part_id, voicename):
2325     str = "Part%sVoice%sChords" % (part_id, voicename)
2326     return musicxml_id_to_lily (str) 
2327
2328 def print_voice_definitions (printer, part_list, voices):
2329     for part in part_list:
2330         part_id = part.id
2331         nv_dict = voices.get (part_id, {})
2332         for (name, voice) in nv_dict.items ():
2333             k = music_xml_voice_name_to_lily_name (part_id, name)
2334             printer.dump ('%s = ' % k)
2335             voice.ly_voice.print_ly (printer)
2336             printer.newline()
2337             if voice.chordnames:
2338                 cnname = music_xml_chordnames_name_to_lily_name (part_id, name)
2339                 printer.dump ('%s = ' % cnname )
2340                 voice.chordnames.print_ly (printer)
2341                 printer.newline()
2342             for l in voice.lyrics_order:
2343                 lname = music_xml_lyrics_name_to_lily_name (part_id, name, l)
2344                 printer.dump ('%s = ' % lname )
2345                 voice.lyrics_dict[l].print_ly (printer)
2346                 printer.newline()
2347             if voice.figured_bass:
2348                 fbname = music_xml_figuredbass_name_to_lily_name (part_id, name)
2349                 printer.dump ('%s = ' % fbname )
2350                 voice.figured_bass.print_ly (printer)
2351                 printer.newline()
2352
2353
2354 def uniq_list (l):
2355     return dict ([(elt,1) for elt in l]).keys ()
2356
2357 # format the information about the staff in the form 
2358 #     [staffid,
2359 #         [
2360 #            [voiceid1, [lyricsid11, lyricsid12,...], figuredbassid1],
2361 #            [voiceid2, [lyricsid21, lyricsid22,...], figuredbassid2],
2362 #            ...
2363 #         ]
2364 #     ]
2365 # raw_voices is of the form [(voicename, lyricsids, havefiguredbass)*]
2366 def format_staff_info (part_id, staff_id, raw_voices):
2367     voices = []
2368     for (v, lyricsids, figured_bass, chordnames) in raw_voices:
2369         voice_name = music_xml_voice_name_to_lily_name (part_id, v)
2370         voice_lyrics = [music_xml_lyrics_name_to_lily_name (part_id, v, l)
2371                    for l in lyricsids]
2372         figured_bass_name = ''
2373         if figured_bass:
2374             figured_bass_name = music_xml_figuredbass_name_to_lily_name (part_id, v)
2375         chordnames_name = ''
2376         if chordnames:
2377             chordnames_name = music_xml_chordnames_name_to_lily_name (part_id, v)
2378         voices.append ([voice_name, voice_lyrics, figured_bass_name, chordnames_name])
2379     return [staff_id, voices]
2380
2381 def update_score_setup (score_structure, part_list, voices):
2382
2383     for part_definition in part_list:
2384         part_id = part_definition.id
2385         nv_dict = voices.get (part_id)
2386         if not nv_dict:
2387             error_message (_ ('unknown part in part-list: %s') % part_id)
2388             continue
2389
2390         staves = reduce (lambda x,y: x+ y,
2391                 [voice.voicedata._staves.keys ()
2392                  for voice in nv_dict.values ()],
2393                 [])
2394         staves_info = []
2395         if len (staves) > 1:
2396             staves_info = []
2397             staves = uniq_list (staves)
2398             staves.sort ()
2399             for s in staves:
2400                 thisstaff_raw_voices = [(voice_name, voice.lyrics_order, voice.figured_bass, voice.chordnames) 
2401                     for (voice_name, voice) in nv_dict.items ()
2402                     if voice.voicedata._start_staff == s]
2403                 staves_info.append (format_staff_info (part_id, s, thisstaff_raw_voices))
2404         else:
2405             thisstaff_raw_voices = [(voice_name, voice.lyrics_order, voice.figured_bass, voice.chordnames) 
2406                 for (voice_name, voice) in nv_dict.items ()]
2407             staves_info.append (format_staff_info (part_id, None, thisstaff_raw_voices))
2408         score_structure.set_part_information (part_id, staves_info)
2409
2410 # Set global values in the \layout block, like auto-beaming etc.
2411 def update_layout_information ():
2412     if not conversion_settings.ignore_beaming and layout_information:
2413         layout_information.set_context_item ('Score', 'autoBeaming = ##f')
2414
2415 def print_ly_preamble (printer, filename):
2416     printer.dump_version ()
2417     printer.print_verbatim ('%% automatically converted from %s\n' % filename)
2418
2419 def print_ly_additional_definitions (printer, filename):
2420     if needed_additional_definitions:
2421         printer.newline ()
2422         printer.print_verbatim ('%% additional definitions required by the score:')
2423         printer.newline ()
2424     for a in set(needed_additional_definitions):
2425         printer.print_verbatim (additional_definitions.get (a, ''))
2426         printer.newline ()
2427     printer.newline ()
2428
2429 # Read in the tree from the given I/O object (either file or string) and 
2430 # demarshall it using the classes from the musicxml.py file
2431 def read_xml (io_object, use_lxml):
2432     if use_lxml:
2433         import lxml.etree
2434         tree = lxml.etree.parse (io_object)
2435         mxl_tree = musicxml.lxml_demarshal_node (tree.getroot ())
2436         return mxl_tree
2437     else:
2438         from xml.dom import minidom, Node
2439         doc = minidom.parse(io_object)
2440         node = doc.documentElement
2441         return musicxml.minidom_demarshal_node (node)
2442     return None
2443
2444
2445 def read_musicxml (filename, compressed, use_lxml):
2446     raw_string = None
2447     if compressed:
2448         if filename == "-":
2449              progress (_ ("Input is compressed, extracting raw MusicXML data from stdin") )
2450              z = zipfile.ZipFile (sys.stdin)
2451         else:
2452             progress (_ ("Input file %s is compressed, extracting raw MusicXML data") % filename)
2453             z = zipfile.ZipFile (filename, "r")
2454         container_xml = z.read ("META-INF/container.xml")
2455         if not container_xml:
2456             return None
2457         container = read_xml (StringIO.StringIO (container_xml), use_lxml)
2458         if not container:
2459             return None
2460         rootfiles = container.get_maybe_exist_named_child ('rootfiles')
2461         if not rootfiles:
2462             return None
2463         rootfile_list = rootfiles.get_named_children ('rootfile')
2464         mxml_file = None
2465         if len (rootfile_list) > 0:
2466             mxml_file = getattr (rootfile_list[0], 'full-path', None)
2467         if mxml_file:
2468             raw_string = z.read (mxml_file)
2469
2470     if raw_string:
2471         io_object = StringIO.StringIO (raw_string)
2472     elif filename == "-":
2473         io_object = sys.stdin
2474     else:
2475         io_object = filename
2476
2477     return read_xml (io_object, use_lxml)
2478
2479
2480 def convert (filename, options):
2481     if filename == "-":
2482         progress (_ ("Reading MusicXML from Standard input ...") )
2483     else:
2484         progress (_ ("Reading MusicXML from %s ...") % filename)
2485
2486     tree = read_musicxml (filename, options.compressed, options.use_lxml)
2487     score_information = extract_score_information (tree)
2488     paper_information = extract_paper_information (tree)
2489
2490     parts = tree.get_typed_children (musicxml.Part)
2491     (voices, staff_info) = get_all_voices (parts)
2492
2493     score = None
2494     mxl_pl = tree.get_maybe_exist_typed_child (musicxml.Part_list)
2495     if mxl_pl:
2496         score = extract_score_structure (mxl_pl, staff_info)
2497         part_list = mxl_pl.get_named_children ("score-part")
2498
2499     # score information is contained in the <work>, <identification> or <movement-title> tags
2500     update_score_setup (score, part_list, voices)
2501     # After the conversion, update the list of settings for the \layout block
2502     update_layout_information ()
2503
2504     if not options.output_name:
2505         options.output_name = os.path.basename (filename) 
2506         options.output_name = os.path.splitext (options.output_name)[0]
2507     elif re.match (".*\.ly", options.output_name):
2508         options.output_name = os.path.splitext (options.output_name)[0]
2509
2510
2511     #defs_ly_name = options.output_name + '-defs.ly'
2512     if (options.output_name == "-"):
2513       output_ly_name = 'Standard output'
2514     else:
2515       output_ly_name = options.output_name + '.ly'
2516
2517     progress (_ ("Output to `%s'") % output_ly_name)
2518     printer = musicexp.Output_printer()
2519     #progress (_ ("Output to `%s'") % defs_ly_name)
2520     if (options.output_name == "-"):
2521       printer.set_file (codecs.getwriter ("utf-8")(sys.stdout))
2522     else:
2523       printer.set_file (codecs.open (output_ly_name, 'wb', encoding='utf-8'))
2524     print_ly_preamble (printer, filename)
2525     print_ly_additional_definitions (printer, filename)
2526     if score_information:
2527         score_information.print_ly (printer)
2528     if paper_information:
2529         paper_information.print_ly (printer)
2530     if layout_information:
2531         layout_information.print_ly (printer)
2532     print_voice_definitions (printer, part_list, voices)
2533     
2534     printer.newline ()
2535     printer.dump ("% The score definition")
2536     printer.newline ()
2537     score.print_ly (printer)
2538     printer.newline ()
2539
2540     return voices
2541
2542 def get_existing_filename_with_extension (filename, ext):
2543     if os.path.exists (filename):
2544         return filename
2545     newfilename = filename + "." + ext
2546     if os.path.exists (newfilename):
2547         return newfilename;
2548     newfilename = filename + ext
2549     if os.path.exists (newfilename):
2550         return newfilename;
2551     return ''
2552
2553 def main ():
2554     opt_parser = option_parser()
2555
2556     global options
2557     (options, args) = opt_parser.parse_args ()
2558     if not args:
2559         opt_parser.print_usage()
2560         sys.exit (2)
2561
2562     if options.language:
2563         musicexp.set_pitch_language (options.language)
2564         needed_additional_definitions.append (options.language)
2565         additional_definitions[options.language] = "\\include \"%s.ly\"\n" % options.language
2566     conversion_settings.ignore_beaming = not options.convert_beaming
2567
2568     # Allow the user to leave out the .xml or xml on the filename
2569     basefilename = args[0].decode('utf-8')
2570     if basefilename == "-": # Read from stdin
2571         basefilename = "-"
2572     else:
2573         filename = get_existing_filename_with_extension (basefilename, "xml")
2574         if not filename:
2575             filename = get_existing_filename_with_extension (basefilename, "mxl")
2576             options.compressed = True
2577     if filename and filename.endswith ("mxl"):
2578         options.compressed = True
2579
2580     if filename and (filename == "-" or os.path.exists (filename)):
2581         voices = convert (filename, options)
2582     else:
2583         progress (_ ("Unable to find input file %s") % basefilename)
2584
2585 if __name__ == '__main__':
2586     main()