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