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