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