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