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