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