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