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