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