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