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