]> git.donarmstrong.com Git - lilypond.git/blob - scripts/musicxml2ly.py
307650e2f77c620ac5a358362b9264a4304f6318
[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 tempfile
11 import StringIO
12 import warnings
13
14 """
15 @relocate-preamble@
16 """
17
18 import lilylib as ly
19 _ = ly._
20
21 import utilities
22 import musicxml
23 import musicexp
24
25 from rational import Rational
26
27 lilypond_version = "@TOPLEVEL_VERSION@"
28
29 # Store command-line options in a global variable, so we can access them everywhere
30 options = None
31
32 class Conversion_Settings:
33     def __init__(self):
34        self.ignore_beaming = False
35        self.convert_stem_directions = False
36        self.convert_rest_positions = True
37
38 conversion_settings = Conversion_Settings()
39 # Use a global variable to store the setting needed inside a \layout block.
40 # whenever we need to change a setting or add/remove an engraver, we can access
41 # this layout and add the corresponding settings
42 layout_information = musicexp.Layout()
43 # Use a global variable to store the setting needed inside a \paper block.
44 paper = musicexp.Paper()
45
46 needed_additional_definitions = []
47 additional_definitions = {
48   "tuplet-note-wrapper": """      % a formatter function, which is simply a wrapper around an existing
49       % tuplet formatter function. It takes the value returned by the given
50       % function and appends a note of given length.
51   #(define-public ((tuplet-number::append-note-wrapper function note) grob)
52     (let* ((txt (if function (function grob) #f)))
53       (if txt
54         (markup txt #:fontsize -5 #:note note UP)
55         (markup #:fontsize -5 #:note note UP)
56       )
57     )
58   )""",
59
60   "tuplet-non-default-denominator": """#(define ((tuplet-number::non-default-tuplet-denominator-text denominator) grob)
61   (number->string (if denominator
62                       denominator
63                       (ly:event-property (event-cause grob) 'denominator))))
64 """,
65
66   "tuplet-non-default-fraction": """#(define ((tuplet-number::non-default-tuplet-fraction-text denominator numerator) grob)
67     (let* ((ev (event-cause grob))
68            (den (if denominator denominator (ly:event-property ev 'denominator)))
69            (num (if numerator numerator (ly:event-property ev 'numerator))))
70        (format #f "~a:~a" den num)))
71 """,
72 }
73
74 def round_to_two_digits(val):
75     return round(val * 100) / 100
76
77 def extract_paper_information(score_partwise):
78     defaults = score_partwise.get_maybe_exist_named_child('defaults')
79     if not defaults:
80         return None
81     tenths = -1
82     scaling = defaults.get_maybe_exist_named_child('scaling')
83     default_tenths_to_millimeters_ratio = 0.175
84     default_staff_size = 20
85     if scaling:
86         mm = scaling.get_named_child('millimeters')
87         mm = float(mm.get_text())
88         tn = scaling.get_maybe_exist_named_child('tenths')
89         tn = float(tn.get_text())
90         # The variable 'tenths' is actually a ratio, NOT the value of <tenths>.
91         # TODO: rename and replace.
92         tenths = mm / tn
93         ratio = tenths / default_tenths_to_millimeters_ratio
94         staff_size = default_staff_size * ratio
95
96         if 1 < staff_size < 100:
97             paper.global_staff_size = staff_size
98         else:
99             msg = "paper.global_staff_size {} is too large, using defaults=20".format(
100                 staff_size)
101             warnings.warn(msg)
102             paper.global_staff_size = 20
103
104     # We need the scaling(i.e. the size of staff tenths for everything!
105     if tenths < 0:
106         return None
107
108     def from_tenths(txt):
109         return round_to_two_digits(float(txt) * tenths / 10)
110     def set_paper_variable(varname, parent, element_name):
111         el = parent.get_maybe_exist_named_child(element_name)
112         if el: # Convert to cm from tenths
113             setattr(paper, varname, from_tenths(el.get_text()))
114
115     pagelayout = defaults.get_maybe_exist_named_child('page-layout')
116     if pagelayout:
117         # TODO: How can one have different margins for even and odd pages???
118         set_paper_variable("page_height", pagelayout, 'page-height')
119         set_paper_variable("page_width", pagelayout, 'page-width')
120
121         if conversion_settings.convert_page_margins:
122             pmargins = pagelayout.get_named_children('page-margins')
123             for pm in pmargins:
124                 set_paper_variable("left_margin", pm, 'left-margin')
125                 set_paper_variable("right_margin", pm, 'right-margin')
126                 set_paper_variable("bottom_margin", pm, 'bottom-margin')
127                 set_paper_variable("top_margin", pm, 'top-margin')
128
129     systemlayout = defaults.get_maybe_exist_named_child('system-layout')
130     if systemlayout:
131         sl = systemlayout.get_maybe_exist_named_child('system-margins')
132         if sl:
133             set_paper_variable("system_left_margin", sl, 'left-margin')
134             set_paper_variable("system_right_margin", sl, 'right-margin')
135         set_paper_variable("system_distance", systemlayout, 'system-distance')
136         set_paper_variable("top_system_distance", systemlayout, 'top-system-distance')
137
138     stafflayout = defaults.get_named_children('staff-layout')
139     for sl in stafflayout:
140         nr = getattr(sl, 'number', 1)
141         dist = sl.get_named_child('staff-distance')
142         #TODO: the staff distance needs to be set in the Staff context!!!
143
144     # TODO: Finish appearance?, music-font?, word-font?, lyric-font*, lyric-language*
145     appearance = defaults.get_named_child('appearance')
146     if appearance:
147         lws = appearance.get_named_children('line-width')
148         for lw in lws:
149             # Possible types are: beam, bracket, dashes,
150             #    enclosure, ending, extend, heavy barline, leger,
151             #    light barline, octave shift, pedal, slur middle, slur tip,
152             #    staff, stem, tie middle, tie tip, tuplet bracket, and wedge
153             tp = lw.type
154             w = from_tenths(lw.get_text())
155             # TODO: Do something with these values!
156         nss = appearance.get_named_children('note-size')
157         for ns in nss:
158             # Possible types are: cue, grace and large
159             tp = ns.type
160             sz = from_tenths(ns.get_text())
161             # TODO: Do something with these values!
162         # <other-appearance> elements have no specified meaning
163
164     rawmusicfont = defaults.get_named_child('music-font')
165     if rawmusicfont:
166         # TODO: Convert the font
167         pass
168     rawwordfont = defaults.get_named_child('word-font')
169     if rawwordfont:
170         # TODO: Convert the font
171         pass
172     rawlyricsfonts = defaults.get_named_children('lyric-font')
173     for lyricsfont in rawlyricsfonts:
174         # TODO: Convert the font
175         pass
176
177     return paper
178
179
180 credit_dict = {
181     None:None,
182     '':None,
183     'page number':None, #TODO: what is it used for ?
184     'title':'title',
185     'subtitle':'subtitle',
186     'composer':'composer',
187     'arranger':'arranger',
188     'lyricist':'poet',
189     'rights':'copyright'
190 }
191 # score information is contained in the <work>, <identification> or <movement-title> tags
192 # extract those into a hash, indexed by proper lilypond header attributes
193 def extract_score_information(tree):
194     header = musicexp.Header()
195     def set_if_exists(field, value):
196         if value:
197             header.set_field(field, utilities.escape_ly_output_string(value))
198
199     movement_title = tree.get_maybe_exist_named_child('movement-title')
200     movement_number = tree.get_maybe_exist_named_child('movement-number')
201     if movement_title:
202         set_if_exists('title', movement_title.get_text())
203     if movement_number:
204           set_if_exists('movementnumber', movement_number.get_text())
205           # set_if_exists('piece', movement_number.get_text()) # the movement number should be visible in the score.
206
207     work = tree.get_maybe_exist_named_child('work')
208     if work:
209         work_number = work.get_work_number()
210         work_title = work.get_work_title()
211         # Overwrite the title from movement-title with work->title
212         set_if_exists('title', work.get_work_title())
213         set_if_exists('opus', work.get_work_number())
214         # Use movement-title as subtitle
215         if movement_title:
216               set_if_exists('subtitle', movement_title.get_text())
217
218 # TODO: Translation of opus element. Not to be confused with opus in LilyPond. MusicXML opus is a document element for opus DTD
219     identifications = tree.get_named_children('identification')
220     for ids in identifications:
221         set_if_exists('copyright', ids.get_rights())
222         set_if_exists('composer', ids.get_composer())
223         set_if_exists('arranger', ids.get_arranger())
224         set_if_exists('editor', ids.get_editor())
225         set_if_exists('poet', ids.get_poet())
226
227         set_if_exists('encodingsoftware', ids.get_encoding_software())
228         set_if_exists('encodingdate', ids.get_encoding_date())
229         set_if_exists('encoder', ids.get_encoding_person())
230         set_if_exists('encodingdescription', ids.get_encoding_description())
231         set_if_exists('source', ids.get_source())
232
233         # <miscellaneous><miscellaneous-field name="description"> ... becomes
234         # \header { texidoc = ...
235         set_if_exists('texidoc', ids.get_file_description());
236
237         # Finally, apply the required compatibility modes
238         # Some applications created wrong MusicXML files, so we need to
239         # apply some compatibility mode, e.g. ignoring some features/tags
240         # in those files
241         software = ids.get_encoding_software_list()
242
243         # Case 1: "Sibelius 5.1" with the "Dolet 3.4 for Sibelius" plugin
244         #         is missing all beam ends => ignore all beaming information
245         ignore_beaming_software = {
246             "Dolet 4 for Sibelius, Beta 2": "Dolet 4 for Sibelius, Beta 2",
247             "Dolet 3.5 for Sibelius": "Dolet 3.5 for Sibelius",
248             "Dolet 3.4 for Sibelius": "Dolet 3.4 for Sibelius",
249             "Dolet 3.3 for Sibelius": "Dolet 3.3 for Sibelius",
250             "Dolet 3.2 for Sibelius": "Dolet 3.2 for Sibelius",
251             "Dolet 3.1 for Sibelius": "Dolet 3.1 for Sibelius",
252             "Dolet for Sibelius 1.3": "Dolet for Sibelius 1.3",
253             "Noteworthy Composer": "Noteworthy Composer's nwc2xm[",
254         }
255         for s in software:
256             app_description = ignore_beaming_software.get(s, False);
257             if app_description:
258                 conversion_settings.ignore_beaming = True
259                 ly.warning(_("Encountered file created by %s, containing "
260                                "wrong beaming information. All beaming "
261                                "information in the MusicXML file will be "
262                                "ignored") % app_description)
263
264     credits = tree.get_named_children('credit')
265     has_composer = False
266     for cred in credits:
267         type = credit_dict.get(cred.get_type())
268         if(type == None):
269             type = credit_dict.get(cred.find_type(credits))
270         if(type == 'composer'):
271             if(has_composer):
272                 type = 'poet'
273             else:
274                 has_composer = True
275             set_if_exists(type, cred.get_text())
276         elif(type == 'title'):
277             if(not(work) and not(movement_title)):
278                 set_if_exists('title', cred.get_text())
279             #elif(not(movement_title)): #bullshit!
280             #    set_if_exists('subtitle', cred.get_text()) #bullshit! otherwise both title and subtitle show the work-title.
281         elif(type == None):
282             pass
283         else:
284             set_if_exists(type, cred.get_text())
285
286
287     # TODO: Check for other unsupported features
288     return header
289
290 class PartGroupInfo:
291     def __init__(self):
292         self.start = {}
293         self.end = {}
294     def is_empty(self):
295         return len(self.start) + len(self.end) == 0
296     def add_start(self, g):
297         self.start[getattr(g, 'number', "1")] = g
298     def add_end(self, g):
299         self.end[getattr(g, 'number', "1")] = g
300     def print_ly(self, printer):
301         ly.warning(_("Unprocessed PartGroupInfo %s encountered") % self)
302     def ly_expression(self):
303         ly.warning(_("Unprocessed PartGroupInfo %s encountered") % self)
304         return ''
305
306 def musicxml_step_to_lily(step):
307     if step:
308         return (ord(step) - ord('A') + 7 - 2) % 7
309     else:
310         return None
311
312 def staff_attributes_to_string_tunings(mxl_attr):
313     details = mxl_attr.get_maybe_exist_named_child('staff-details')
314     if not details:
315         return []
316     lines = 6
317     staff_lines = details.get_maybe_exist_named_child('staff-lines')
318     if staff_lines:
319         lines = string.atoi(staff_lines.get_text())
320     tunings = [musicexp.Pitch()] * lines
321     staff_tunings = details.get_named_children('staff-tuning')
322     for i in staff_tunings:
323         p = musicexp.Pitch()
324         line = 0
325         try:
326             line = string.atoi(i.line) - 1
327         except ValueError:
328             pass
329         tunings[line] = p
330
331         step = i.get_named_child(u'tuning-step')
332         step = step.get_text().strip()
333         p.step = musicxml_step_to_lily(step)
334
335         octave = i.get_named_child(u'tuning-octave')
336         octave = octave.get_text().strip()
337         p.octave = int(octave) - 4
338
339         alter = i.get_named_child(u'tuning-alter')
340         if alter:
341             p.alteration = int(alter.get_text().strip())
342     # lilypond seems to use the opposite ordering than MusicXML...
343     tunings.reverse()
344     return tunings
345
346 def staff_attributes_to_lily_staff(mxl_attr):
347     if not mxl_attr:
348         return musicexp.Staff()
349
350     (staff_id, attributes) = mxl_attr.items()[0]
351
352     # distinguish by clef:
353     # percussion(percussion and rhythmic), tab, and everything else
354     clef_sign = None
355     clef = attributes.get_maybe_exist_named_child('clef')
356     if clef:
357         sign = clef.get_maybe_exist_named_child('sign')
358         if sign:
359             clef_sign = {"percussion": "percussion", "TAB": "tab"}.get(sign.get_text(), None)
360
361     lines = 5
362     details = attributes.get_named_children('staff-details')
363     for d in details:
364         staff_lines = d.get_maybe_exist_named_child('staff-lines')
365         if staff_lines:
366             lines = string.atoi(staff_lines.get_text())
367
368     # TODO: Handle other staff attributes like staff-space, etc.
369
370     staff = None
371     if clef_sign == "percussion" and lines == 1:
372         staff = musicexp.RhythmicStaff()
373     elif clef_sign == "percussion":
374         staff = musicexp.DrumStaff()
375         # staff.drum_style_table = ???
376     elif clef_sign == "tab":
377         staff = musicexp.TabStaff()
378         staff.string_tunings = staff_attributes_to_string_tunings(attributes)
379         # staff.tablature_format = ???
380     else:
381         staff = musicexp.Staff()
382         # TODO: Handle case with lines <> 5!
383         if(lines != 5):
384             staff.add_context_modification("\\override StaffSymbol #'line-count = #%s" % lines)
385
386     return staff
387
388 def extract_instrument_sound(score_part):
389     score_instrument = score_part.get_maybe_exist_named_child('score-instrument')
390     if not score_instrument:
391         return None
392     sound = score_instrument.get_maybe_exist_named_child('instrument-sound')
393     if sound:
394         return utilities.musicxml_sound_to_lilypond_midi_instrument(sound.get_text())
395
396 def extract_score_structure(part_list, staffinfo):
397     score = musicexp.Score()
398     structure = musicexp.StaffGroup(None)
399     score.set_contents(structure)
400
401     if not part_list:
402         return structure
403
404     def read_score_part(el):
405         if not isinstance(el, musicxml.Score_part):
406             return
407         # Depending on the attributes of the first measure, we create different
408         # types of staves(Staff, RhythmicStaff, DrumStaff, TabStaff, etc.)
409         staff = staff_attributes_to_lily_staff(staffinfo.get(el.id, None))
410         if not staff:
411             return None
412         staff.id = el.id
413         partname = el.get_maybe_exist_named_child('part-name')
414         # Finale gives unnamed parts the name "MusicXML Part" automatically!
415         if partname and partname.get_text() != "MusicXML Part":
416             staff.instrument_name = partname.get_text()
417         # part-name-display overrides part-name!
418         partname = el.get_maybe_exist_named_child("part-name-display")
419         if partname:
420             staff.instrument_name = extract_display_text(partname)
421         if hasattr(options, 'midi') and options.midi:
422             staff.sound = extract_instrument_sound(el)
423         if staff.instrument_name:
424             paper.indent = max(paper.indent, len(staff.instrument_name))
425             paper.instrument_names.append(staff.instrument_name)
426         partdisplay = el.get_maybe_exist_named_child('part-abbreviation')
427         if partdisplay:
428             staff.short_instrument_name = partdisplay.get_text()
429         # part-abbreviation-display overrides part-abbreviation!
430         partdisplay = el.get_maybe_exist_named_child("part-abbreviation-display")
431         if partdisplay:
432             staff.short_instrument_name = extract_display_text(partdisplay)
433         # TODO: Read in the MIDI device / instrument
434         if staff.short_instrument_name:
435             paper.short_indent = max(paper.short_indent, len(staff.short_instrument_name))
436
437         return staff
438
439     def read_score_group(el):
440         if not isinstance(el, musicxml.Part_group):
441             return
442         group = musicexp.StaffGroup()
443         if hasattr(el, 'number'):
444             id = el.number
445             group.id = id
446             #currentgroups_dict[id] = group
447             #currentgroups.append(id)
448         if el.get_maybe_exist_named_child('group-name'):
449             group.instrument_name = el.get_maybe_exist_named_child('group-name').get_text()
450         if el.get_maybe_exist_named_child('group-abbreviation'):
451             group.short_instrument_name = el.get_maybe_exist_named_child('group-abbreviation').get_text()
452         if el.get_maybe_exist_named_child('group-symbol'):
453             group.symbol = el.get_maybe_exist_named_child('group-symbol').get_text()
454         if el.get_maybe_exist_named_child('group-barline'):
455             group.spanbar = el.get_maybe_exist_named_child('group-barline').get_text()
456         return group
457
458
459     parts_groups = part_list.get_all_children()
460
461     # the start/end group tags are not necessarily ordered correctly and groups
462     # might even overlap, so we can't go through the children sequentially!
463
464     # 1) Replace all Score_part objects by their corresponding Staff objects,
465     #    also collect all group start/stop points into one PartGroupInfo object
466     staves = []
467     group_info = PartGroupInfo()
468     for el in parts_groups:
469         if isinstance(el, musicxml.Score_part):
470             if not group_info.is_empty():
471                 staves.append(group_info)
472                 group_info = PartGroupInfo()
473             staff = read_score_part(el)
474             if staff:
475                 staves.append(staff)
476         elif isinstance(el, musicxml.Part_group):
477             if el.type == "start":
478                 group_info.add_start(el)
479             elif el.type == "stop":
480                 group_info.add_end(el)
481     if not group_info.is_empty():
482         staves.append(group_info)
483
484     # 2) Now, detect the groups:
485     group_starts = []
486     pos = 0
487     while pos < len(staves):
488         el = staves[pos]
489         if isinstance(el, PartGroupInfo):
490             prev_start = 0
491             if len(group_starts) > 0:
492                 prev_start = group_starts[-1]
493             elif len(el.end) > 0: # no group to end here
494                 el.end = {}
495             if len(el.end) > 0: # closes an existing group
496                 ends = el.end.keys()
497                 prev_started = staves[prev_start].start.keys()
498                 grpid = None
499                 intersection = filter(lambda x:x in ends, prev_started)
500                 if len(intersection) > 0:
501                     grpid = intersection[0]
502                 else:
503                     # Close the last started group
504                     grpid = staves[prev_start].start.keys() [0]
505                     # Find the corresponding closing tag and remove it!
506                     j = pos + 1
507                     foundclosing = False
508                     while j < len(staves) and not foundclosing:
509                         if isinstance(staves[j], PartGroupInfo) and staves[j].end.has_key(grpid):
510                             foundclosing = True
511                             del staves[j].end[grpid]
512                             if staves[j].is_empty():
513                                 del staves[j]
514                         j += 1
515                 grpobj = staves[prev_start].start[grpid]
516                 group = read_score_group(grpobj)
517                 # remove the id from both the start and end
518                 if el.end.has_key(grpid):
519                     del el.end[grpid]
520                 del staves[prev_start].start[grpid]
521                 if el.is_empty():
522                     del staves[pos]
523                 # replace the staves with the whole group
524                 for j in staves[(prev_start + 1):pos]:
525                     group.append_staff(j)
526                 del staves[(prev_start + 1):pos]
527                 staves.insert(prev_start + 1, group)
528                 # reset pos so that we continue at the correct position
529                 pos = prev_start
530                 # remove an empty start group
531                 if staves[prev_start].is_empty():
532                     del staves[prev_start]
533                     group_starts.remove(prev_start)
534                     pos -= 1
535             elif len(el.start) > 0: # starts new part groups
536                 group_starts.append(pos)
537         pos += 1
538
539     for i in staves:
540         structure.append_staff(i)
541     return score
542
543
544
545 def rational_to_lily_duration(rational_len):
546     d = musicexp.Duration()
547
548     rational_len.normalize_self()
549     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)
550
551     # Duration of the form 1/2^n or 3/2^n can be converted to a simple lilypond duration
552     dots = {1: 0, 3: 1, 7: 2, 15: 3, 31: 4, 63: 5, 127: 6}.get(rational_len.numerator(), -1)
553     if(d_log >= dots >= 0):
554         # account for the dots!
555         d.duration_log = d_log - dots
556         d.dots = dots
557     elif(d_log >= 0):
558         d.duration_log = d_log
559         d.factor = Rational(rational_len.numerator())
560     else:
561         ly.warning(_("Encountered rational duration with denominator %s, "
562                        "unable to convert to lilypond duration") %
563                     rational_len.denominator())
564         # TODO: Test the above error message
565         return None
566
567     return d
568
569 def musicxml_partial_to_lily(partial_len):
570     if partial_len > 0:
571         p = musicexp.Partial()
572         p.partial = rational_to_lily_duration(partial_len)
573         return p
574     else:
575         return None
576
577 # Detect repeats and alternative endings in the chord event list(music_list)
578 # and convert them to the corresponding musicexp objects, containing nested
579 # music
580 def group_repeats(music_list):
581     repeat_replaced = True
582     music_start = 0
583     i = 0
584     # Walk through the list of expressions, looking for repeat structure
585     #(repeat start/end, corresponding endings). If we find one, try to find the
586     # last event of the repeat, replace the whole structure and start over again.
587     # For nested repeats, as soon as we encounter another starting repeat bar,
588     # treat that one first, and start over for the outer repeat.
589     while repeat_replaced and i < 100:
590         i += 1
591         repeat_start = -1  # position of repeat start / end
592         repeat_end = -1 # position of repeat start / end
593         repeat_times = 0
594         ending_start = -1 # position of current ending start
595         endings = [] # list of already finished endings
596         pos = 0
597         last = len(music_list) - 1
598         repeat_replaced = False
599         final_marker = 0
600         while pos < len(music_list) and not repeat_replaced:
601             e = music_list[pos]
602             repeat_finished = False
603             if isinstance(e, RepeatMarker):
604                 if not repeat_times and e.times:
605                     repeat_times = e.times
606                 if e.direction == -1:
607                     if repeat_end >= 0:
608                         repeat_finished = True
609                     else:
610                         repeat_start = pos
611                         repeat_end = -1
612                         ending_start = -1
613                         endings = []
614                 elif e.direction == 1:
615                     if repeat_start < 0:
616                         repeat_start = 0
617                     if repeat_end < 0:
618                         repeat_end = pos
619                     final_marker = pos
620             elif isinstance(e, EndingMarker):
621                 if e.direction == -1:
622                     if repeat_start < 0:
623                         repeat_start = 0
624                     if repeat_end < 0:
625                         repeat_end = pos
626                     ending_start = pos
627                 elif e.direction == 1:
628                     if ending_start < 0:
629                         ending_start = 0
630                     endings.append([ending_start, pos])
631                     ending_start = -1
632                     final_marker = pos
633             elif not isinstance(e, musicexp.BarLine):
634                 # As soon as we encounter an element when repeat start and end
635                 # is set and we are not inside an alternative ending,
636                 # this whole repeat structure is finished => replace it
637                 if repeat_start >= 0 and repeat_end > 0 and ending_start < 0:
638                     repeat_finished = True
639
640             # Finish off all repeats without explicit ending bar(e.g. when
641             # we convert only one page of a multi-page score with repeats)
642             if pos == last and repeat_start >= 0:
643                 repeat_finished = True
644                 final_marker = pos
645                 if repeat_end < 0:
646                     repeat_end = pos
647                 if ending_start >= 0:
648                     endings.append([ending_start, pos])
649                     ending_start = -1
650
651             if repeat_finished:
652                 # We found the whole structure replace it!
653                 r = musicexp.RepeatedMusic()
654                 if repeat_times <= 0:
655                     repeat_times = 2
656                 r.repeat_count = repeat_times
657                 # don't erase the first element for "implicit" repeats(i.e. no
658                 # starting repeat bars at the very beginning)
659                 start = repeat_start + 1
660                 if repeat_start == music_start:
661                     start = music_start
662                 r.set_music(music_list[start:repeat_end])
663                 for(start, end) in endings:
664                     s = musicexp.SequentialMusic()
665                     s.elements = music_list[start + 1:end]
666                     r.add_ending(s)
667                 del music_list[repeat_start:final_marker + 1]
668                 music_list.insert(repeat_start, r)
669                 repeat_replaced = True
670             pos += 1
671         # TODO: Implement repeats until the end without explicit ending bar
672     return music_list
673
674
675 # Extract the settings for tuplets from the <notations><tuplet> and the
676 # <time-modification> elements of the note:
677 def musicxml_tuplet_to_lily(tuplet_elt, time_modification):
678     tsm = musicexp.TimeScaledMusic()
679     fraction =(1, 1)
680     if time_modification:
681         fraction = time_modification.get_fraction()
682     tsm.numerator = fraction[0]
683     tsm.denominator = fraction[1]
684
685
686     normal_type = tuplet_elt.get_normal_type()
687     if not normal_type and time_modification:
688         normal_type = time_modification.get_normal_type()
689     if not normal_type and time_modification:
690         note = time_modification.get_parent()
691         if note:
692             normal_type = note.get_duration_info()
693     if normal_type:
694         normal_note = musicexp.Duration()
695         (normal_note.duration_log, normal_note.dots) = normal_type
696         tsm.normal_type = normal_note
697
698     actual_type = tuplet_elt.get_actual_type()
699     if actual_type:
700         actual_note = musicexp.Duration()
701         (actual_note.duration_log, actual_note.dots) = actual_type
702         tsm.actual_type = actual_note
703
704     # Obtain non-default nrs of notes from the tuplet object!
705     tsm.display_numerator = tuplet_elt.get_normal_nr()
706     tsm.display_denominator = tuplet_elt.get_actual_nr()
707
708
709     if hasattr(tuplet_elt, 'bracket') and tuplet_elt.bracket == "no":
710         tsm.display_bracket = None
711     elif hasattr(tuplet_elt, 'line-shape') and getattr(tuplet_elt, 'line-shape') == "curved":
712         tsm.display_bracket = "curved"
713     else:
714         tsm.display_bracket = "bracket"
715
716     display_values = {"none": None, "actual": "actual", "both": "both"}
717     if hasattr(tuplet_elt, "show-number"):
718         tsm.display_number = display_values.get(getattr(tuplet_elt, "show-number"), "actual")
719
720     if hasattr(tuplet_elt, "show-type"):
721         tsm.display_type = display_values.get(getattr(tuplet_elt, "show-type"), None)
722
723     return tsm
724
725
726 def group_tuplets(music_list, events):
727
728
729     """Collect Musics from
730     MUSIC_LIST demarcated by EVENTS_LIST in TimeScaledMusic objects.
731     """
732
733     indices = []
734     brackets = {}
735
736     j = 0
737     for(ev_chord, tuplet_elt, time_modification) in events:
738         while(j < len(music_list)):
739             if music_list[j] == ev_chord:
740                 break
741             j += 1
742         nr = 0
743         if hasattr(tuplet_elt, 'number'):
744             nr = getattr(tuplet_elt, 'number')
745         if tuplet_elt.type == 'start':
746             tuplet_object = musicxml_tuplet_to_lily(tuplet_elt, time_modification)
747             tuplet_info = [j, None, tuplet_object]
748             indices.append(tuplet_info)
749             brackets[nr] = tuplet_info
750         elif tuplet_elt.type == 'stop':
751             bracket_info = brackets.get(nr, None)
752             if bracket_info:
753                 bracket_info[1] = j # Set the ending position to j
754                 del brackets[nr]
755
756     new_list = []
757     last = 0
758     for(i1, i2, tsm) in indices:
759         if i1 > i2:
760             continue
761
762         new_list.extend(music_list[last:i1])
763         seq = musicexp.SequentialMusic()
764         last = i2 + 1
765
766         # At this point music_list[i1:last] encompasses all the notes of the
767         # tuplet. There might be dynamics following this range, however, which
768         # apply to the last note of the tuplet. Advance last to include them
769         # in the range.
770         while last < len(music_list) and isinstance(music_list[last], musicexp.DynamicsEvent):
771             last += 1
772
773         seq.elements = music_list[i1:last]
774
775         tsm.element = seq
776
777         new_list.append(tsm)
778         #TODO: Handle nested tuplets!!!!
779
780     new_list.extend(music_list[last:])
781     return new_list
782
783
784 def musicxml_clef_to_lily(attributes):
785     change = musicexp.ClefChange()
786     (change.type, change.position, change.octave) = attributes.get_clef_information()
787     return change
788
789 def musicxml_time_to_lily(attributes):
790     change = musicexp.TimeSignatureChange()
791     # time signature function
792     if hasattr(options, 'shift_meter') and options.shift_meter:
793         tmp_meter = options.shift_meter.split("/",1)
794         sig = [int(tmp_meter[0]), int(tmp_meter[1])]
795         change.originalFractions = attributes.get_time_signature()
796     else:
797         sig = attributes.get_time_signature()
798     if not sig:
799         return None
800     change.fractions = sig
801
802     time_elm = attributes.get_maybe_exist_named_child('time')
803     if time_elm and hasattr(time_elm, 'symbol'):
804         change.style = { 'single-number': "'single-digit",
805                          'cut': None,
806                          'common': None,
807                          'normal': "'()"}.get(time_elm.symbol, "'()")
808     else:
809         change.style = "'()"
810
811     # TODO: Handle senza-misura measures
812     # TODO: Handle hidden time signatures(print-object="no")
813     # TODO: What shall we do if the symbol clashes with the sig? e.g. "cut"
814     #       with 3/8 or "single-number" with(2+3)/8 or 3/8+2/4?
815     return change
816
817 def musicxml_key_to_lily(attributes):
818     key_sig = attributes.get_key_signature()
819     if not key_sig or not(isinstance(key_sig, list) or isinstance(key_sig, tuple)):
820         ly.warning(_("Unable to extract key signature!"))
821         return None
822
823     change = musicexp.KeySignatureChange()
824
825     if len(key_sig) == 2 and not isinstance(key_sig[0], list):
826         # standard key signature,(fifths, mode)
827         (fifths, mode) = key_sig
828         change.mode = mode
829
830         start_pitch = musicexp.Pitch()
831         start_pitch.octave = 0
832         try:
833             (n, a) = {
834                 'major'     : (0, 0),
835                 'minor'     : (5, 0),
836                 'ionian'    : (0, 0),
837                 'dorian'    : (1, 0),
838                 'phrygian'  : (2, 0),
839                 'lydian'    : (3, 0),
840                 'mixolydian': (4, 0),
841                 'aeolian'   : (5, 0),
842                 'locrian'   : (6, 0),
843                 }[mode]
844             start_pitch.step = n
845             start_pitch.alteration = a
846         except  KeyError:
847             ly.warning(_("unknown mode %s, expecting 'major' or 'minor' "
848                 "or a church mode!") % mode)
849
850         fifth = musicexp.Pitch()
851         fifth.step = 4
852         if fifths < 0:
853             fifths *= -1
854             fifth.step *= -1
855             fifth.normalize()
856         for x in range(fifths):
857             start_pitch = start_pitch.transposed(fifth)
858         change.tonic = start_pitch
859
860     else:
861         # Non-standard key signature of the form [[step,alter<,octave>],...]
862         # MusicXML contains C,D,E,F,G,A,B as steps, lily uses 0-7, so convert
863         alterations = []
864         for k in key_sig:
865             k[0] = musicxml_step_to_lily(k[0])
866             alterations.append(k)
867         change.non_standard_alterations = alterations
868     return change
869
870 def musicxml_transpose_to_lily(attributes):
871     transpose = attributes.get_transposition()
872     if not transpose:
873         return None
874
875     shift = musicexp.Pitch()
876     octave_change = transpose.get_maybe_exist_named_child('octave-change')
877     if octave_change:
878         shift.octave = string.atoi(octave_change.get_text())
879     chromatic_shift = string.atoi(transpose.get_named_child('chromatic').get_text())
880     chromatic_shift_normalized = chromatic_shift % 12;
881     (shift.step, shift.alteration) = [
882         (0, 0), (0, 1), (1, 0), (2, -1), (2, 0),
883         (3, 0), (3, 1), (4, 0), (5, -1), (5, 0),
884         (6, -1), (6, 0)][chromatic_shift_normalized];
885
886     shift.octave += (chromatic_shift - chromatic_shift_normalized) / 12
887
888     diatonic = transpose.get_maybe_exist_named_child('diatonic')
889     if diatonic:
890         diatonic_step = string.atoi(diatonic.get_text()) % 7
891         if diatonic_step != shift.step:
892             # We got the alter incorrect!
893             old_semitones = shift.semitones()
894             shift.step = diatonic_step
895             new_semitones = shift.semitones()
896             shift.alteration += old_semitones - new_semitones
897
898     transposition = musicexp.Transposition()
899     transposition.pitch = musicexp.Pitch().transposed(shift)
900     return transposition
901
902 def musicxml_staff_details_to_lily(attributes):
903     details = attributes.get_maybe_exist_named_child('staff-details')
904     if not details:
905         return None
906
907     ## TODO: Handle staff-type, staff-lines, staff-tuning, capo, staff-size
908     ret = []
909
910     stafflines = details.get_maybe_exist_named_child('staff-lines')
911     if stafflines:
912         lines = string.atoi(stafflines.get_text());
913         lines_event = musicexp.StaffLinesEvent(lines);
914         ret.append(lines_event);
915
916     return ret;
917
918
919 def musicxml_attributes_to_lily(attrs):
920     elts = []
921     attr_dispatch = {
922         'clef': musicxml_clef_to_lily,
923         'time': musicxml_time_to_lily,
924         'key': musicxml_key_to_lily,
925         'transpose': musicxml_transpose_to_lily,
926         'staff-details': musicxml_staff_details_to_lily,
927     }
928     for (k, func) in attr_dispatch.items():
929         children = attrs.get_named_children(k)
930         if children:
931             ev = func(attrs)
932             if isinstance(ev, list):
933               for e in ev:
934                 elts.append(e)
935             elif ev:
936                 elts.append(ev)
937
938     return elts
939
940 def extract_display_text(el):
941     child = el.get_maybe_exist_named_child("display-text")
942     if child:
943         return child.get_text()
944     else:
945         return False
946
947
948 def musicxml_print_to_lily(el):
949     # TODO: Implement other print attributes
950     #  <!ELEMENT print (page-layout?, system-layout?, staff-layout*,
951     #          measure-layout?, measure-numbering?, part-name-display?,
952     #          part-abbreviation-display?)>
953     #  <!ATTLIST print
954     #      staff-spacing %tenths; #IMPLIED
955     #      new-system %yes-no; #IMPLIED
956     #      new-page %yes-no-number; #IMPLIED
957     #      blank-page NMTOKEN #IMPLIED
958     #      page-number CDATA #IMPLIED
959     #  >
960     elts = []
961     if (hasattr(el, "new-system") and conversion_settings.convert_system_breaks):
962         val = getattr(el, "new-system")
963         if (val == "yes"):
964             elts.append(musicexp.Break("break"))
965     if (hasattr(el, "new-page") and conversion_settings.convert_page_breaks):
966         val = getattr(el, "new-page")
967         if (val == "yes"):
968             elts.append(musicexp.Break("pageBreak"))
969     child = el.get_maybe_exist_named_child("part-name-display")
970     if child:
971         elts.append(musicexp.SetEvent("Staff.instrumentName",
972                                         "\"%s\"" % extract_display_text(child)))
973     child = el.get_maybe_exist_named_child("part-abbreviation-display")
974     if child:
975         elts.append(musicexp.SetEvent("Staff.shortInstrumentName",
976                                         "\"%s\"" % extract_display_text(child)))
977     return elts
978
979
980 class Marker(musicexp.Music):
981     def __init__(self):
982         self.direction = 0
983         self.event = None
984     def print_ly(self, printer):
985         ly.warning(_("Encountered unprocessed marker %s\n") % self)
986         pass
987     def ly_expression(self):
988         return ""
989 class RepeatMarker(Marker):
990     def __init__(self):
991         Marker.__init__(self)
992         self.times = 0
993 class EndingMarker(Marker):
994     pass
995
996
997 spanner_event_dict = {
998     'beam' : musicexp.BeamEvent,
999     'dashes' : musicexp.TextSpannerEvent,
1000     'bracket' : musicexp.BracketSpannerEvent,
1001     'glissando' : musicexp.GlissandoEvent,
1002     'octave-shift' : musicexp.OctaveShiftEvent,
1003     'pedal' : musicexp.PedalEvent,
1004     'slide' : musicexp.GlissandoEvent,
1005     'slur' : musicexp.SlurEvent,
1006     'wavy-line' : musicexp.TextSpannerEvent,
1007     'wedge' : musicexp.HairpinEvent
1008 }
1009 spanner_type_dict = {
1010     'start':-1,
1011     'begin':-1,
1012     'crescendo':-1,
1013     'decreschendo':-1,
1014     'diminuendo':-1,
1015     'continue': 0,
1016     'change': 0,
1017     'up':-1,
1018     'down':-1,
1019     'stop': 1,
1020     'end' : 1
1021 }
1022
1023 def musicxml_spanner_to_lily_event(mxl_event):
1024     ev = None
1025
1026     name = mxl_event.get_name()
1027     func = spanner_event_dict.get(name)
1028     if func:
1029         ev = func()
1030     else:
1031         ly.warning(_('unknown span event %s') % mxl_event)
1032
1033     if name == "wavy-line":
1034         ev.style=OrnamenthasWhat(mxl_event)
1035
1036     type = mxl_event.get_type()
1037     span_direction = spanner_type_dict.get(type)
1038     # really check for None, because some types will be translated to 0, which
1039     # would otherwise also lead to the unknown span warning
1040     if span_direction != None:
1041         ev.span_direction = span_direction
1042     else:
1043         ly.warning(_('unknown span type %s for %s') %(type, name))
1044
1045     ev.set_span_type(type)
1046     ev.line_type = getattr(mxl_event, 'line-type', 'solid')
1047
1048     # assign the size, which is used for octave-shift, etc.
1049     ev.size = mxl_event.get_size()
1050
1051     return ev
1052
1053 def musicxml_direction_to_indicator(direction):
1054     return { "above": 1, "upright": 1, "up": 1, "below":-1, "downright":-1, "down":-1, "inverted":-1 }.get(direction, 0)
1055
1056 def musicxml_fermata_to_lily_event(mxl_event):
1057
1058     ev = musicexp.ArticulationEvent()
1059     txt = mxl_event.get_text()
1060
1061     # The contents of the element defined the shape, possible are normal, angled and square
1062     ev.type = { "angled": "shortfermata", "square": "longfermata" }.get(txt, "fermata")
1063     fermata_types= { "angled": "shortfermata",
1064                      "square": "longfermata" }
1065
1066     # MusicXML fermata types can be specified in two different ways:
1067     # 1. <fermata>angled</fermata> and
1068     # 2. <fermata type="angled"/> -- both need to be handled.
1069     if hasattr(mxl_event, 'type'):
1070         fermata_type = fermata_types.get(mxl_event.type, 'fermata')
1071     else:
1072         fermata_type = fermata_types.get(mxl_event.get_text(), 'fermata')
1073
1074     ev.type = fermata_type
1075
1076     if hasattr(mxl_event, 'type'):
1077         dir = musicxml_direction_to_indicator(mxl_event.type)
1078         if dir and options.convert_directions:
1079             ev.force_direction = dir
1080     return ev
1081
1082 def musicxml_arpeggiate_to_lily_event(mxl_event):
1083     ev = musicexp.ArpeggioEvent()
1084     ev.direction = musicxml_direction_to_indicator(getattr(mxl_event, 'direction', None))
1085     return ev
1086
1087 def musicxml_nonarpeggiate_to_lily_event(mxl_event):
1088     ev = musicexp.ArpeggioEvent()
1089     ev.non_arpeggiate = True
1090     ev.direction = musicxml_direction_to_indicator(getattr(mxl_event, 'direction', None))
1091     return ev
1092
1093 def musicxml_tremolo_to_lily_event(mxl_event):
1094     ev = musicexp.TremoloEvent()
1095     txt = mxl_event.get_text()
1096     if txt:
1097       ev.strokes = txt
1098     else:
1099       # This is supposed to be a default for empty tremolo elements
1100       # TODO: Add empty tremolo element to test cases in tremolo.xml
1101       # TODO: Test empty tremolo element
1102       # TODO: Consideration: Is 3 really a reasonable default?
1103       ev.strokes = "3"
1104     return ev
1105
1106 def musicxml_falloff_to_lily_event(mxl_event):
1107     ev = musicexp.BendEvent()
1108     ev.alter = -4
1109     return ev
1110
1111 def musicxml_doit_to_lily_event(mxl_event):
1112     ev = musicexp.BendEvent()
1113     ev.alter = 4
1114     return ev
1115
1116 def musicxml_bend_to_lily_event(mxl_event):
1117     ev = musicexp.BendEvent()
1118     ev.alter = mxl_event.bend_alter()
1119     return ev
1120
1121 def musicxml_caesura_to_lily_event(mxl_event):
1122     ev = musicexp.MarkupEvent()
1123     # FIXME: default to straight or curved caesura?
1124     ev.contents = "\\musicglyph #\"scripts.caesura.straight\""
1125     ev.force_direction = 1
1126     return ev
1127
1128 def musicxml_fingering_event(mxl_event):
1129     ev = musicexp.ShortArticulationEvent()
1130     ev.type = mxl_event.get_text()
1131     return ev
1132
1133 def musicxml_string_event(mxl_event):
1134     ev = musicexp.NoDirectionArticulationEvent()
1135     ev.type = mxl_event.get_text()
1136     return ev
1137
1138 def musicxml_accidental_mark(mxl_event):
1139     ev = musicexp.MarkupEvent()
1140     contents = { "sharp": "\\sharp",
1141       "natural": "\\natural",
1142       "flat": "\\flat",
1143       "double-sharp": "\\doublesharp",
1144       "sharp-sharp": "\\sharp\\sharp",
1145       "flat-flat": "\\flat\\flat",
1146       "flat-flat": "\\doubleflat",
1147       "natural-sharp": "\\natural\\sharp",
1148       "natural-flat": "\\natural\\flat",
1149       "quarter-flat": "\\semiflat",
1150       "quarter-sharp": "\\semisharp",
1151       "three-quarters-flat": "\\sesquiflat",
1152       "three-quarters-sharp": "\\sesquisharp",
1153     }.get(mxl_event.get_text())
1154     if contents:
1155         ev.contents = contents
1156         return ev
1157     else:
1158         return None
1159
1160 # translate articulations, ornaments and other notations into ArticulationEvents
1161 # possible values:
1162 #   -) string  (ArticulationEvent with that name)
1163 #   -) function (function(mxl_event) needs to return a full ArticulationEvent-derived object
1164 #   -) (class, name)  (like string, only that a different class than ArticulationEvent is used)
1165 # TODO: Some translations are missing!
1166 articulations_dict = {
1167     "accent": (musicexp.ShortArticulationEvent, ">"), # or "accent"
1168     "accidental-mark": musicxml_accidental_mark,
1169     "bend": musicxml_bend_to_lily_event,
1170     "breath-mark": (musicexp.NoDirectionArticulationEvent, "breathe"),
1171     "caesura": musicxml_caesura_to_lily_event,
1172     #"delayed-turn": "?",
1173     "detached-legato": (musicexp.ShortArticulationEvent, "_"), # or "portato"
1174     "doit": musicxml_doit_to_lily_event,
1175     #"double-tongue": "?",
1176     "down-bow": "downbow",
1177     "falloff": musicxml_falloff_to_lily_event,
1178     "fingering": musicxml_fingering_event,
1179     #"fingernails": "?",
1180     #"fret": "?",
1181     #"hammer-on": "?",
1182     "harmonic": "flageolet",
1183     #"heel": "?",
1184     "inverted-mordent": "prall",
1185     "inverted-turn": "reverseturn",
1186     "mordent": "mordent",
1187     "open-string": "open",
1188     #"plop": "?",
1189     #"pluck": "?",
1190     #"pull-off": "?",
1191     #"schleifer": "?",
1192     #"scoop": "?",
1193     #"shake": "?",
1194     "snap-pizzicato": "snappizzicato",
1195     #"spiccato": "?",
1196     "staccatissimo": (musicexp.ShortArticulationEvent, "!"), # or "staccatissimo"
1197     "staccato": (musicexp.ShortArticulationEvent, "."), # or "staccato"
1198     "stopped": (musicexp.ShortArticulationEvent, "+"), # or "stopped"
1199     #"stress": "?",
1200     "string": musicxml_string_event,
1201     "strong-accent": (musicexp.ShortArticulationEvent, "^"), # or "marcato"
1202     #"tap": "?",
1203     "tenuto": (musicexp.ShortArticulationEvent, "-"), # or "tenuto"
1204     "thumb-position": "thumb",
1205     #"toe": "?",
1206     "turn": "turn",
1207     "tremolo": musicxml_tremolo_to_lily_event,
1208     "trill-mark": "trill",
1209     #"triple-tongue": "?",
1210     #"unstress": "?"
1211     "up-bow": "upbow",
1212     #"wavy-line": "?",
1213 }
1214 articulation_spanners = [ "wavy-line" ]
1215
1216 def OrnamenthasWhat(mxl_event):
1217     wavy = trilly = ignore = start = stop = False
1218     for i in mxl_event._parent._children:
1219             if i._name == "wavy-line": wavy = True
1220             elif i._name == "trill-mark": trilly = True
1221             try:
1222                 if i.type == "continue": ignore = True
1223                 elif i.type == "start": start = True
1224                 elif i.type == "stop": stop = True
1225             except:
1226                 pass
1227     if start == True:
1228         if wavy == True and trilly == False: musicexp.whatOrnament = "wave"
1229         else: musicexp.whatOrnament = "trill"
1230     if ignore == True: return "ignore"
1231     elif stop == True: return "stop"
1232     elif wavy == True and trilly == True: return "trill and wave"
1233     elif wavy == True: return "wave"
1234     elif trilly == True: return "trill"
1235
1236 def OrnamenthasWavyline(mxl_event):
1237     for i in mxl_event._parent._children:
1238             if i._name == "wavy-line": return True
1239     return False
1240
1241
1242 def musicxml_articulation_to_lily_event(mxl_event):
1243     # wavy-line elements are treated as trill spanners, not as articulation ornaments
1244     if mxl_event.get_name() in articulation_spanners:
1245         return musicxml_spanner_to_lily_event(mxl_event)
1246
1247     tmp_tp = articulations_dict.get(mxl_event.get_name())
1248     if OrnamenthasWavyline(mxl_event):
1249         return
1250     if not tmp_tp:
1251         return
1252
1253     if isinstance(tmp_tp, str):
1254         ev = musicexp.ArticulationEvent()
1255         ev.type = tmp_tp
1256     elif isinstance(tmp_tp, tuple):
1257         ev = tmp_tp[0]()
1258         ev.type = tmp_tp[1]
1259     else:
1260         ev = tmp_tp(mxl_event)
1261
1262     # Some articulations use the type attribute, other the placement...
1263     dir = None
1264     if hasattr(mxl_event, 'type') and hasattr(options, 'convert_directions') and options.convert_directions:
1265         dir = musicxml_direction_to_indicator(mxl_event.type)
1266     if hasattr(mxl_event, 'placement') and hasattr(options, 'convert_directions') and options.convert_directions:
1267         dir = musicxml_direction_to_indicator(mxl_event.placement)
1268     if dir:
1269         ev.force_direction = dir
1270     return ev
1271
1272
1273
1274 def musicxml_dynamics_to_lily_event(dynentry):
1275     dynamics_available = (
1276         "ppppp", "pppp", "ppp", "pp", "p", "mp", "mf",
1277         "f", "ff", "fff", "ffff", "fp", "sf", "sff", "sp", "spp", "sfz", "rfz")
1278     dynamicsname = dynentry.get_name()
1279     if dynamicsname == "other-dynamics":
1280         dynamicsname = dynentry.get_text()
1281     if not dynamicsname or dynamicsname == "#text":
1282         return None
1283
1284     if not dynamicsname in dynamics_available:
1285         # Get rid of - in tag names (illegal in ly tags!)
1286         dynamicstext = dynamicsname
1287         dynamicsname = string.replace(dynamicsname, "-", "")
1288         additional_definitions[dynamicsname] = dynamicsname + \
1289               " = #(make-dynamic-script \"" + dynamicstext + "\")"
1290         needed_additional_definitions.append(dynamicsname)
1291     event = musicexp.DynamicsEvent()
1292     event.type = dynamicsname
1293     return event
1294
1295 # Convert single-color two-byte strings to numbers 0.0 - 1.0
1296 def hexcolorval_to_nr(hex_val):
1297     try:
1298         v = int(hex_val, 16)
1299         if v == 255:
1300             v = 256
1301         return v / 256.
1302     except ValueError:
1303         return 0.
1304
1305 def hex_to_color(hex_val):
1306     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)
1307     if res:
1308         return map(lambda x: hexcolorval_to_nr(x), res.group(2, 3, 4))
1309     else:
1310         return None
1311
1312 def font_size_number_to_lily_command(size):
1313     d = {
1314         (0, 8): r'\teeny',
1315         (8, 10): r'\tiny',
1316         (10, 12): r'\small',
1317         (12, 16): r'',
1318         (16, 24): r'\large',
1319         (24, float('inf')): r'\huge',
1320     }
1321     result = None
1322     for r in d.keys():
1323         if r[0] <= size < r[1]:
1324             result = d[r]
1325             break
1326     return result
1327
1328 def font_size_word_to_lily_command(size):
1329     font_size_dict = {
1330         "xx-small": '\\teeny',
1331         "x-small": '\\tiny',
1332         "small": '\\small',
1333         "medium": '',
1334         "large": '\\large',
1335         "x-large": '\\huge',
1336         "xx-large": '\\larger\\huge'
1337         }
1338     return font_size_dict.get(size, '')
1339
1340 def get_font_size(size):
1341     try:
1342         size = float(size)
1343         return font_size_number_to_lily_command(size)
1344     except ValueError:
1345         return font_size_word_to_lily_command(size)
1346
1347 def musicxml_words_to_lily_event(words):
1348     event = musicexp.TextEvent()
1349     text = words.get_text()
1350     text = re.sub('^ *\n? *', '', text) #remove white spaces and line breaks before text
1351     text = re.sub(' *\n? *$', '', text) #remove white spaces and line breaks before text
1352     event.text = text
1353
1354     if hasattr(words, 'default-y') and hasattr(options, 'convert_directions') and options.convert_directions:
1355         offset = getattr(words, 'default-y')
1356         try:
1357             off = string.atoi(offset)
1358             if off > 0:
1359                 event.force_direction = 1
1360             else:
1361                 event.force_direction = -1
1362         except ValueError:
1363             event.force_direction = 0
1364
1365     if hasattr(words, 'font-weight'):
1366         font_weight = { "normal": '', "bold": '\\bold' }.get(getattr(words, 'font-weight'), '')
1367         if font_weight:
1368             event.markup += font_weight
1369
1370     if hasattr(words, 'font-size'):
1371         size = getattr(words, 'font-size')
1372         # font_size = font_size_dict.get(size, '')
1373         font_size = get_font_size(size)
1374         if font_size:
1375             event.markup += font_size
1376
1377     if hasattr(words, 'color'):
1378         color = getattr(words, 'color')
1379         rgb = hex_to_color(color)
1380         if rgb:
1381             event.markup += "\\with-color #(rgb-color %s %s %s)" % (rgb[0], rgb[1], rgb[2])
1382
1383     if hasattr(words, 'font-style'):
1384         font_style = { "italic": '\\italic' }.get(getattr(words, 'font-style'), '')
1385         if font_style:
1386             event.markup += font_style
1387
1388     # TODO: How should I best convert the font-family attribute?
1389
1390     # TODO: How can I represent the underline, overline and line-through
1391     #       attributes in LilyPond? Values of these attributes indicate
1392     #       the number of lines
1393
1394     return event
1395
1396
1397 # convert accordion-registration to lilypond.
1398 # Since lilypond does not have any built-in commands, we need to create
1399 # the markup commands manually and define our own variables.
1400 # Idea was taken from: http://lsr.dsi.unimi.it/LSR/Item?id=194
1401 def musicxml_accordion_to_markup(mxl_event):
1402     commandname = "accReg"
1403     command = ""
1404
1405     high = mxl_event.get_maybe_exist_named_child('accordion-high')
1406     if high:
1407         commandname += "H"
1408         command += """\\combine
1409           \\raise #2.5 \\musicglyph #\"accordion.dot\"
1410           """
1411     middle = mxl_event.get_maybe_exist_named_child('accordion-middle')
1412     if middle:
1413         # By default, use one dot (when no or invalid content is given). The
1414         # MusicXML spec is quiet about this case...
1415         txt = 1
1416         try:
1417           txt = string.atoi(middle.get_text())
1418         except ValueError:
1419             pass
1420         if txt == 3:
1421             commandname += "MMM"
1422             command += """\\combine
1423           \\raise #1.5 \\musicglyph #\"accordion.dot\"
1424           \\combine
1425           \\raise #1.5 \\translate #(cons 1 0) \\musicglyph #\"accordion.dot\"
1426           \\combine
1427           \\raise #1.5 \\translate #(cons -1 0) \\musicglyph #\"accordion.dot\"
1428           """
1429         elif txt == 2:
1430             commandname += "MM"
1431             command += """\\combine
1432           \\raise #1.5 \\translate #(cons 0.5 0) \\musicglyph #\"accordion.dot\"
1433           \\combine
1434           \\raise #1.5 \\translate #(cons -0.5 0) \\musicglyph #\"accordion.dot\"
1435           """
1436         elif not txt <= 0:
1437             commandname += "M"
1438             command += """\\combine
1439           \\raise #1.5 \\musicglyph #\"accordion.dot\"
1440           """
1441     low = mxl_event.get_maybe_exist_named_child('accordion-low')
1442     if low:
1443         commandname += "L"
1444         command += """\\combine
1445           \\raise #0.5 \musicglyph #\"accordion.dot\"
1446           """
1447
1448     command += "\musicglyph #\"accordion.discant\""
1449     command = "\\markup { \\normalsize %s }" % command
1450     # Define the newly built command \accReg[H][MMM][L]
1451     additional_definitions[commandname] = "%s = %s" % (commandname, command)
1452     needed_additional_definitions.append(commandname)
1453     return "\\%s" % commandname
1454
1455 def musicxml_accordion_to_ly(mxl_event):
1456     txt = musicxml_accordion_to_markup(mxl_event)
1457     if txt:
1458         ev = musicexp.MarkEvent(txt)
1459         return ev
1460     return
1461
1462
1463 def musicxml_rehearsal_to_ly_mark(mxl_event):
1464     text = mxl_event.get_text()
1465     if not text:
1466         return
1467     # default is boxed rehearsal marks!
1468     encl = "box"
1469     if hasattr(mxl_event, 'enclosure'):
1470         encl = {"none": None, "square": "box", "circle": "circle" }.get(mxl_event.enclosure, None)
1471     if encl:
1472         text = "\\%s { %s }" % (encl, text)
1473     ev = musicexp.MarkEvent("\\markup { %s }" % text)
1474     return ev
1475
1476 def musicxml_harp_pedals_to_ly(mxl_event):
1477     count = 0
1478     result = "\\harp-pedal #\""
1479     for t in mxl_event.get_named_children('pedal-tuning'):
1480       alter = t.get_named_child('pedal-alter')
1481       if alter:
1482         val = int(alter.get_text().strip())
1483         result += {1: "v", 0: "-", -1: "^"}.get(val, "")
1484       count += 1
1485       if count == 3:
1486         result += "|"
1487     ev = musicexp.MarkupEvent()
1488     ev.contents = result + "\""
1489     return ev
1490
1491 def musicxml_eyeglasses_to_ly(mxl_event):
1492     needed_additional_definitions.append("eyeglasses")
1493     return musicexp.MarkEvent("\\markup { \\eyeglasses }")
1494
1495 def next_non_hash_index(lst, pos):
1496     pos += 1
1497     while pos < len(lst) and isinstance(lst[pos], musicxml.Hash_text):
1498         pos += 1
1499     return pos
1500
1501 def musicxml_metronome_to_ly(mxl_event):
1502     children = mxl_event.get_all_children()
1503     if not children:
1504         return
1505
1506     index = -1
1507     index = next_non_hash_index(children, index)
1508     if isinstance(children[index], musicxml.BeatUnit):
1509         # first form of metronome-mark, using unit and beats/min or other unit
1510         ev = musicexp.TempoMark()
1511         if hasattr(mxl_event, 'parentheses'):
1512             ev.set_parentheses(mxl_event.parentheses == "yes")
1513
1514         d = musicexp.Duration()
1515         d.duration_log = utilities.musicxml_duration_to_log(
1516             children[index].get_text())
1517         index = next_non_hash_index(children, index)
1518         if isinstance(children[index], musicxml.BeatUnitDot):
1519             d.dots = 1
1520             index = next_non_hash_index(children, index)
1521         ev.set_base_duration(d)
1522         if isinstance(children[index], musicxml.BeatUnit):
1523             # Form "note = newnote"
1524             newd = musicexp.Duration()
1525             newd.duration_log = utilities.musicxml_duration_to_log(
1526                 children[index].get_text())
1527             index = next_non_hash_index(children, index)
1528             if isinstance(children[index], musicxml.BeatUnitDot):
1529                 newd.dots = 1
1530                 index = next_non_hash_index(children, index)
1531             ev.set_new_duration(newd)
1532         elif isinstance(children[index], musicxml.PerMinute):
1533             # Form "note = bpm"
1534             try:
1535                 beats = int(children[index].get_text())
1536                 ev.set_beats_per_minute(beats)
1537             except ValueError:
1538                 pass
1539         else:
1540             ly.warning(_("Unknown metronome mark, ignoring"))
1541             return
1542         return ev
1543     else:
1544         #TODO: Implement the other (more complex) way for tempo marks!
1545         ly.warning(_("Metronome marks with complex relations (<metronome-note> in MusicXML) are not yet implemented."))
1546         return
1547
1548 # translate directions into Events, possible values:
1549 #   -) string  (MarkEvent with that command)
1550 #   -) function (function(mxl_event) needs to return a full Event-derived object
1551 #   -) (class, name)  (like string, only that a different class than MarkEvent is used)
1552 directions_dict = {
1553     'accordion-registration' : musicxml_accordion_to_ly,
1554     'coda' : (musicexp.MusicGlyphMarkEvent, "coda"),
1555 #     'damp' : ???
1556 #     'damp-all' : ???
1557     'eyeglasses': musicxml_eyeglasses_to_ly,
1558     'harp-pedals' : musicxml_harp_pedals_to_ly,
1559 #     'image' : ???
1560     'metronome' : musicxml_metronome_to_ly,
1561     'rehearsal' : musicxml_rehearsal_to_ly_mark,
1562 #     'scordatura' : ???
1563     'segno' : (musicexp.MusicGlyphMarkEvent, "segno"),
1564     'words' : musicxml_words_to_lily_event,
1565 }
1566 directions_spanners = [ 'octave-shift', 'pedal', 'wedge', 'dashes', 'bracket' ]
1567
1568 def musicxml_direction_to_lily(n):
1569     # TODO: Handle the <staff> element!
1570     res = []
1571     # placement applies to all children!
1572     dir = None
1573     if hasattr(n, 'placement') and hasattr(options, 'convert_directions') and options.convert_directions:
1574         dir = musicxml_direction_to_indicator(n.placement)
1575     dirtype_children = []
1576     # TODO: The direction-type is used for grouping (e.g. dynamics with text),
1577     #       so we can't simply flatten them out!
1578     for dt in n.get_typed_children(musicxml.DirType):
1579         dirtype_children += dt.get_all_children()
1580
1581     for entry in dirtype_children:
1582         # brackets, dashes, octave shifts. pedal marks, hairpins etc. are spanners:
1583         if entry.get_name() in directions_spanners:
1584             event = musicxml_spanner_to_lily_event(entry)
1585             if event:
1586                 event.force_direction=dir
1587                 res.append(event)
1588             continue
1589
1590         # now treat all the "simple" ones, that can be translated using the dict
1591         ev = None
1592         tmp_tp = directions_dict.get(entry.get_name(), None)
1593         if isinstance(tmp_tp, str): # string means MarkEvent
1594             ev = musicexp.MarkEvent(tmp_tp)
1595         elif isinstance(tmp_tp, tuple): # tuple means (EventClass, "text")
1596             ev = tmp_tp[0](tmp_tp[1])
1597         elif tmp_tp:
1598             ev = tmp_tp(entry)
1599         if ev:
1600             # TODO: set the correct direction! Unfortunately, \mark in ly does
1601             #       not seem to support directions!
1602             ev.force_direction = dir
1603             res.append(ev)
1604             continue
1605
1606         if entry.get_name() == "dynamics":
1607             for dynentry in entry.get_all_children():
1608                 ev = musicxml_dynamics_to_lily_event(dynentry)
1609                 if ev:
1610                     ev.force_direction = dir
1611                     res.append(ev)
1612
1613     return res
1614
1615 notehead_styles_dict = {
1616     'slash': '\'slash',
1617     'triangle': '\'triangle',
1618     'diamond': '\'diamond',
1619     'square': '\'la', # TODO: Proper squared note head
1620     'cross': None, # TODO: + shaped note head
1621     'x': '\'cross',
1622     'circle-x': '\'xcircle',
1623     'inverted triangle': None, # TODO: Implement
1624     'arrow down': None, # TODO: Implement
1625     'arrow up': None, # TODO: Implement
1626     'slashed': None, # TODO: Implement
1627     'back slashed': None, # TODO: Implement
1628     'normal': None,
1629     'cluster': None, # TODO: Implement
1630     'none': '#f',
1631     'do': '\'do',
1632     're': '\'re',
1633     'mi': '\'mi',
1634     'fa': '\'fa',
1635     'so': None,
1636     'la': '\'la',
1637     'ti': '\'ti',
1638     }
1639
1640 def musicxml_chordpitch_to_lily(mxl_cpitch):
1641     r = musicexp.ChordPitch()
1642     r.alteration = mxl_cpitch.get_alteration()
1643     r.step = musicxml_step_to_lily(mxl_cpitch.get_step())
1644     return r
1645
1646 chordkind_dict = {
1647     'major': r'{}:5',
1648     'minor': r'{}:m5',
1649     'augmented': r'{}:aug5',
1650     'diminished': r'{}:dim5',
1651         # Sevenths:
1652     'dominant': r'{}:7',
1653     'dominant-seventh': r'{}:7',
1654     'major-seventh': r'{}:maj7',
1655     'minor-seventh': r'{}:m7',
1656     'diminished-seventh': r'{}:dim7',
1657     'augmented-seventh': r'{}:aug7',
1658     'half-diminished': r'{}:dim5m7',
1659     'major-minor': r'{}:maj7m5',
1660         # Sixths:
1661     'major-sixth': r'{}:6',
1662     'minor-sixth': r'{}:m6',
1663         # Ninths:
1664     'dominant-ninth': r'{}:9',
1665     'major-ninth': r'{}:maj9',
1666     'minor-ninth': r'{}:m9',
1667         # 11ths (usually as the basis for alteration):
1668     'dominant-11th': r'{}:11',
1669     'major-11th': r'{}:maj11',
1670     'minor-11th': r'{}:m11',
1671         # 13ths (usually as the basis for alteration):
1672     'dominant-13th': r'{}:13.11',
1673     'major-13th': r'{}:maj13.11',
1674     'minor-13th': r'{}:m13',
1675         # Suspended:
1676     'suspended-second': r'{}:sus2',
1677     'suspended-fourth': r'{}:sus4',
1678         # Functional sixths:
1679     # TODO
1680     #'Neapolitan': '???',
1681     #'Italian': '???',
1682     #'French': '???',
1683     #'German': '???',
1684         # Other:
1685     #'pedal': '???',(pedal-point bass)
1686     'power': r'\powerChords {}:1.5',
1687     #'Tristan': '???',
1688     'other': r'{}:1',
1689     'none': None,
1690 }
1691
1692 def musicxml_chordkind_to_lily(kind):
1693     res = chordkind_dict.get(kind, None)
1694     # Check for None, since a major chord is converted to ''
1695     if res == None:
1696         ly.warning(_("Unable to convert chord type %s to lilypond.") % kind)
1697     return res
1698
1699
1700 # Global variable for guitar string tunings
1701 string_tunings = None
1702
1703 def musicxml_get_string_tunings(lines):
1704     global string_tunings
1705     if (string_tunings == None):
1706         if not lines:
1707             lines = 6
1708         string_tunings = [musicexp.Pitch()] * lines
1709         for i in range(0, lines):
1710             p = musicexp.Pitch()
1711             p.step = musicxml_step_to_lily(((("E","A","D","G","B")*(lines/5+1))[0:lines])[i])
1712             p.octave = (([-2+int(x%5>1)+2*(x/5) for x in range(0,lines)][0:lines])[i])
1713             p.alteration = 0
1714             p._force_absolute_pitch = True
1715             string_tunings[i] = p
1716         string_tunings = string_tunings[::-1]
1717     return string_tunings[0:lines]
1718
1719 def musicxml_frame_to_lily_event(frame):
1720     ev = musicexp.FretEvent()
1721     ev.strings = frame.get_strings()
1722     ev.frets = frame.get_frets()
1723     #offset = frame.get_first_fret() - 1
1724     #offset = frame.get_first_fret()
1725     barre = []
1726     open_strings = range(1,ev.strings+1)
1727     for fn in frame.get_named_children('frame-note'):
1728         fret = fn.get_fret()
1729         if fret <= 0:
1730             fret = "o"
1731         el = [ fn.get_string(), fret ]
1732         fingering = fn.get_fingering()
1733         if fingering >= 0:
1734             el.append(fingering)
1735         ev.elements.append(el)
1736         open_strings.remove(fn.get_string())
1737         b = fn.get_barre()
1738         if b == 'start':
1739             barre.append(el[0]) # start string
1740             barre.append(el[1]) # fret
1741         elif b == 'stop':
1742             barre.insert(1, el[0]) # end string
1743     for string in open_strings:
1744         ev.elements.append([string, 'x'])
1745     ev.elements.sort()
1746     ev.elements.reverse()
1747     if barre:
1748         ev.barre = barre
1749     return ev
1750
1751 def musicxml_harmony_to_lily(n):
1752     res = []
1753     for f in n.get_named_children('frame'):
1754         ev = musicxml_frame_to_lily_event(f)
1755         if ev:
1756             res.append(ev)
1757     return res
1758
1759 def musicxml_harmony_to_lily_fretboards(n):
1760     res = []
1761     frame = n.get_maybe_exist_named_child('frame')
1762     if frame:
1763         strings = frame.get_strings()
1764         if not strings:
1765             strings = 6
1766         tunings = musicxml_get_string_tunings(strings)
1767         ev = musicexp.FretBoardEvent()
1768         #barre = []
1769         for fn in frame.get_named_children('frame-note'):
1770             fbn = musicexp.FretBoardNote()
1771             string = fn.get_string()
1772             fbn.string = string
1773             fingering = fn.get_fingering()
1774             if fingering >= 0:
1775                 fbn.fingering = fingering
1776             p = tunings[string-1].copy()
1777             p.add_semitones(fn.get_fret())
1778             fbn.pitch = p
1779             ev.append(fbn)
1780         res.append(ev)
1781     return res
1782
1783 def musicxml_harmony_to_lily_chordname(n):
1784     res = []
1785     root = n.get_maybe_exist_named_child('root')
1786     if root:
1787         ev = musicexp.ChordNameEvent()
1788         ev.root = musicxml_chordpitch_to_lily(root)
1789         kind = n.get_maybe_exist_named_child('kind')
1790         if kind:
1791             ev.kind = musicxml_chordkind_to_lily(kind.get_text())
1792             if not ev.kind:
1793                 return res
1794         bass = n.get_maybe_exist_named_child('bass')
1795         if bass:
1796             ev.bass = musicxml_chordpitch_to_lily(bass)
1797         inversion = n.get_maybe_exist_named_child('inversion')
1798         if inversion:
1799             # TODO: LilyPond does not support inversions, does it?
1800
1801             # Mail from Carl Sorensen on lilypond-devel, June 11, 2008:
1802             # 4. LilyPond supports the first inversion in the form of added
1803             # bass notes.  So the first inversion of C major would be c:/g.
1804             # To get the second inversion of C major, you would need to do
1805             # e:6-3-^5 or e:m6-^5.  However, both of these techniques
1806             # require you to know the chord and calculate either the fifth
1807             # pitch (for the first inversion) or the third pitch (for the
1808             # second inversion) so they may not be helpful for musicxml2ly.
1809             inversion_count = string.atoi(inversion.get_text())
1810             if inversion_count == 1:
1811               # TODO: Calculate the bass note for the inversion...
1812               pass
1813             pass
1814         for deg in n.get_named_children('degree'):
1815             d = musicexp.ChordModification()
1816             d.type = deg.get_type()
1817             d.step = deg.get_value()
1818             d.alteration = deg.get_alter()
1819             ev.add_modification(d)
1820         #TODO: convert the user-symbols attribute:
1821             #major: a triangle, like Unicode 25B3
1822             #minor: -, like Unicode 002D
1823             #augmented: +, like Unicode 002B
1824             #diminished: (degree), like Unicode 00B0
1825             #half-diminished: (o with slash), like Unicode 00F8
1826         if ev and ev.root:
1827             res.append(ev)
1828     return res
1829
1830 def musicxml_figured_bass_note_to_lily(n):
1831     res = musicexp.FiguredBassNote()
1832     suffix_dict = { 'sharp' : "+",
1833                     'flat' : "-",
1834                     'natural' : "!",
1835                     'double-sharp' : "++",
1836                     'flat-flat' : "--",
1837                     'sharp-sharp' : "++",
1838                     'slash' : "/" }
1839     prefix = n.get_maybe_exist_named_child('prefix')
1840     if prefix:
1841         res.set_prefix(suffix_dict.get(prefix.get_text(), ""))
1842     fnumber = n.get_maybe_exist_named_child('figure-number')
1843     if fnumber:
1844         res.set_number(fnumber.get_text())
1845     suffix = n.get_maybe_exist_named_child('suffix')
1846     if suffix:
1847         res.set_suffix(suffix_dict.get(suffix.get_text(), ""))
1848     if n.get_maybe_exist_named_child('extend'):
1849         # TODO: Implement extender lines (unfortunately, in lilypond you have
1850         #       to use \set useBassFigureExtenders = ##t, which turns them on
1851         #       globally, while MusicXML has a property for each note...
1852         #       I'm not sure there is a proper way to implement this cleanly
1853         #n.extend
1854         pass
1855     return res
1856
1857
1858
1859 def musicxml_figured_bass_to_lily(n):
1860     if not isinstance(n, musicxml.FiguredBass):
1861         return
1862     res = musicexp.FiguredBassEvent()
1863     for i in n.get_named_children('figure'):
1864         note = musicxml_figured_bass_note_to_lily(i)
1865         if note:
1866             res.append(note)
1867     dur = n.get_maybe_exist_named_child('duration')
1868     if dur:
1869         # apply the duration to res
1870         length = Rational(int(dur.get_text()), n._divisions) * Rational(1, 4)
1871         res.set_real_duration(length)
1872         duration = rational_to_lily_duration(length)
1873         if duration:
1874             res.set_duration(duration)
1875     if hasattr(n, 'parentheses') and n.parentheses == "yes":
1876         res.set_parentheses(True)
1877     return res
1878
1879 instrument_drumtype_dict = {
1880     'Acoustic Snare Drum': 'acousticsnare',
1881     'Side Stick': 'sidestick',
1882     'Open Triangle': 'opentriangle',
1883     'Mute Triangle': 'mutetriangle',
1884     'Tambourine': 'tambourine',
1885     'Bass Drum': 'bassdrum',
1886 }
1887
1888
1889
1890 def musicxml_lyrics_to_text(lyrics, ignoremelismata):
1891     # TODO: Implement text styles for lyrics syllables
1892     continued = False
1893     extended = False
1894     text = ''
1895     for e in lyrics.get_all_children():
1896         if isinstance(e, musicxml.Syllabic):
1897             continued = e.continued()
1898         elif isinstance(e, musicxml.Text):
1899             # We need to convert soft hyphens to -, otherwise the ascii codec as well
1900             # as lilypond will barf on that character
1901             text += string.replace(e.get_text(), u'\xad', '-')
1902         elif isinstance(e, musicxml.Elision):
1903             if text:
1904                 text += " "
1905             continued = False
1906             extended = False
1907         elif isinstance(e, musicxml.Extend):
1908             if text:
1909                 text += " "
1910             extended = True
1911
1912     if text == "-" and continued:
1913         return "--"
1914     elif text == "_" and extended:
1915         return "__"
1916     elif continued and text:
1917         if hasattr(options, 'convert_beaming') and options.convert_beaming:
1918             if (ignoremelismata == "on"):
1919                 return " \set ignoreMelismata = ##t " + utilities.escape_ly_output_string (text)
1920             elif (ignoremelismata == "off"):
1921                 return " " + utilities.escape_ly_output_string(text) + " -- \unset ignoreMelismata"
1922             else:
1923                 return " " + utilities.escape_ly_output_string(text) + " --"
1924         else:
1925             return " " + utilities.escape_ly_output_string(text) + " -- "
1926     elif continued:
1927         return "--"
1928     elif extended and text:
1929         return " " + utilities.escape_ly_output_string(text) + " __"
1930     elif extended:
1931         return "__"
1932     elif text:
1933         return " " + utilities.escape_ly_output_string(text)
1934     else:
1935         return ""
1936
1937 ## TODO
1938 class NegativeSkip:
1939     def __init__(self, here, dest):
1940         self.here = here
1941         self.dest = dest
1942
1943 class LilyPondVoiceBuilder:
1944     def __init__(self):
1945         self.elements = []
1946         self.pending_dynamics = []
1947         self.end_moment = Rational(0)
1948         self.begin_moment = Rational(0)
1949         self.pending_multibar = Rational(0)
1950         self.ignore_skips = False
1951         self.has_relevant_elements = False
1952         self.measure_length = Rational(4, 4)
1953         self.stay_here = False
1954
1955     def _insert_multibar(self):
1956         layout_information.set_context_item('Score', 'skipBars = ##t')
1957         r = musicexp.MultiMeasureRest()
1958         lenfrac = self.measure_length
1959         r.duration = rational_to_lily_duration(lenfrac)
1960         r.duration.factor *= self.pending_multibar / lenfrac
1961         self.elements.append(r)
1962         self.begin_moment = self.end_moment
1963         self.end_moment = self.begin_moment + self.pending_multibar
1964         self.pending_multibar = Rational(0)
1965
1966     def set_measure_length(self, mlen):
1967         if (mlen != self.measure_length) and self.pending_multibar:
1968             self._insert_multibar()
1969         self.measure_length = mlen
1970
1971     def add_multibar_rest(self, duration):
1972         self.pending_multibar += duration
1973
1974     def set_duration(self, duration):
1975         self.end_moment = self.begin_moment + duration
1976     def current_duration(self):
1977         return self.end_moment - self.begin_moment
1978
1979     def add_pending_dynamics(self):
1980         for d in self.pending_dynamics:
1981             self.elements.append(d)
1982         self.pending_dynamics = []
1983
1984     def add_music(self, music, duration, relevant=True):
1985         assert isinstance(music, musicexp.Music)
1986         if self.pending_multibar > Rational(0):
1987             self._insert_multibar()
1988
1989         self.has_relevant_elements = self.has_relevant_elements or relevant
1990
1991         if isinstance(music, musicexp.BarLine):
1992             if self.pending_dynamics:
1993                 for d in self.pending_dynamics:
1994                     if not isinstance(d, (musicexp.SpanEvent, musicexp.DynamicsEvent)):
1995                         index = self.pending_dynamics.index(d)
1996                         dyn = self.pending_dynamics.pop(index)
1997                         self.elements.append(dyn)
1998
1999         self.elements.append(music)
2000         self.begin_moment = self.end_moment
2001         self.set_duration(duration)
2002
2003         # Insert all pending dynamics right after the note/rest:
2004         if isinstance(music, musicexp.ChordEvent) and self.pending_dynamics:
2005             self.add_pending_dynamics()
2006
2007     # Insert some music command that does not affect the position in the measure
2008     def add_command(self, command, relevant=True):
2009         assert isinstance(command, musicexp.Music)
2010         if self.pending_multibar > Rational(0):
2011             self._insert_multibar()
2012         self.has_relevant_elements = self.has_relevant_elements or relevant
2013         self.elements.append(command)
2014     def add_barline(self, barline, relevant=False):
2015         # Insert only if we don't have a barline already
2016         # TODO: Implement proper merging of default barline and custom bar line
2017         has_relevant = self.has_relevant_elements
2018         if (not (self.elements) or
2019             not (isinstance (self.elements[-1], musicexp.BarLine)) or
2020             (self.pending_multibar > Rational(0))):
2021
2022             self.add_music(barline, Rational(0))
2023
2024         self.has_relevant_elements = has_relevant or relevant
2025
2026     def add_partial(self, command):
2027         self.ignore_skips = True
2028         # insert the partial, but restore relevant_elements (partial is not relevant)
2029         relevant = self.has_relevant_elements
2030         self.add_command(command)
2031         self.has_relevant_elements = relevant
2032
2033     def add_dynamics(self, dynamic):
2034         # store the dynamic item(s) until we encounter the next note/rest:
2035         self.pending_dynamics.append(dynamic)
2036
2037     def add_bar_check(self, number):
2038         # re/store has_relevant_elements, so that a barline alone does not
2039         # trigger output for figured bass, chord names
2040         b = musicexp.BarLine()
2041         b.bar_number = number
2042         self.add_barline(b)
2043
2044     def jumpto(self, moment):
2045         if (not self.stay_here):
2046             current_end = self.end_moment + self.pending_multibar
2047             diff = moment - current_end
2048
2049             if diff < Rational(0):
2050                 ly.warning(_('Negative skip %s (from position %s to %s)') %
2051                             (diff, current_end, moment))
2052                 diff = Rational(0)
2053
2054             if diff > Rational(0) and not(self.ignore_skips and moment == 0):
2055                 skip = musicexp.SkipEvent()
2056                 duration_factor = 1
2057                 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)
2058                 duration_dots = 0
2059                 # TODO: Use the time signature for skips, too. Problem: The skip
2060                 #       might not start at a measure boundary!
2061                 if duration_log > 0: # denominator is a power of 2...
2062                     if diff.numerator() == 3:
2063                         duration_log -= 1
2064                         duration_dots = 1
2065                     else:
2066                         duration_factor = Rational(diff.numerator())
2067                 else:
2068                     # for skips of a whole or more, simply use s1*factor
2069                     duration_log = 0
2070                     duration_factor = diff
2071                 skip.duration.duration_log = duration_log
2072                 skip.duration.factor = duration_factor
2073                 skip.duration.dots = duration_dots
2074
2075                 evc = musicexp.ChordEvent()
2076                 evc.elements.append(skip)
2077                 self.add_music(evc, diff, False)
2078
2079             if diff > Rational(0) and moment == 0:
2080                 self.ignore_skips = False
2081
2082     def last_event_chord(self, starting_at):
2083         value = None
2084
2085         # if the position matches, find the last ChordEvent, do not cross a bar line!
2086         at = len(self.elements) - 1
2087         while (at >= 0 and
2088                not isinstance(self.elements[at], musicexp.ChordEvent) and
2089                not isinstance(self.elements[at], musicexp.BarLine)):
2090             at -= 1
2091
2092         if (self.elements
2093             and at >= 0
2094             and isinstance(self.elements[at], musicexp.ChordEvent)
2095             and self.begin_moment == starting_at):
2096             value = self.elements[at]
2097         else:
2098             self.jumpto(starting_at)
2099             value = None
2100         return value
2101
2102     def correct_negative_skip(self, goto):
2103         self.end_moment = goto
2104         self.begin_moment = goto
2105         evc = musicexp.ChordEvent()
2106         self.elements.append(evc)
2107
2108 class VoiceData:
2109     def __init__(self):
2110         self.voicename = None
2111         self.voicedata = None
2112         self.ly_voice = None
2113         self.figured_bass = None
2114         self.chordnames = None
2115         self.fretboards = None
2116         self.lyrics_dict = {}
2117         self.lyrics_order = []
2118
2119 def measure_length_from_attributes(attr, current_measure_length):
2120     len = attr.get_measure_length()
2121     if not len:
2122         len = current_measure_length
2123     return len
2124
2125 def music_xml_voice_name_to_lily_name(part_id, name):
2126     str = "Part%sVoice%s" % (part_id, name)
2127     return musicxml_id_to_lily(str)
2128
2129 def music_xml_lyrics_name_to_lily_name(part_id, name, lyricsnr):
2130     str = music_xml_voice_name_to_lily_name(part_id, name)+("Lyrics%s" % lyricsnr)
2131     return musicxml_id_to_lily(str)
2132
2133 def music_xml_figuredbass_name_to_lily_name(part_id, voicename):
2134     str = music_xml_voice_name_to_lily_name(part_id, voicename)+"FiguredBass"
2135     return musicxml_id_to_lily(str)
2136
2137 def music_xml_chordnames_name_to_lily_name(part_id, voicename):
2138     str = music_xml_voice_name_to_lily_name(part_id, voicename)+"Chords"
2139     return musicxml_id_to_lily(str)
2140
2141 def music_xml_fretboards_name_to_lily_name(part_id, voicename):
2142     str = music_xml_voice_name_to_lily_name(part_id, voicename)+"FretBoards"
2143     return musicxml_id_to_lily(str)
2144
2145 def get_all_lyric_parts_in_voice(voice):
2146     '''
2147     Collect the indexes of all lyric parts in this voice.
2148     In case not all of the current lyric parts are active (a typical case would be
2149     a refrain/chorus), the current implementation inserts \skip-commands in the
2150     inactive parts to keep them in sync.
2151     '''
2152     all_lyric_parts = []
2153     for elem in voice._elements:
2154         lyrics = elem.get_typed_children(musicxml.Lyric)
2155         if lyrics:
2156             for lyric in lyrics:
2157                 index = lyric.number
2158                 if not index in all_lyric_parts:
2159                     all_lyric_parts.append(index)
2160     return all_lyric_parts
2161
2162
2163 def extract_lyrics(voice, lyric_key, lyrics_dict):
2164     curr_number = None
2165     result = []
2166
2167     def is_note(elem):
2168         return isinstance(elem, musicxml.Note)
2169
2170     def is_rest(elem):
2171         return elem.get_typed_children(musicxml.Rest)
2172
2173     def is_note_and_not_rest(elem):
2174         return is_note(elem) and not is_rest(elem)
2175
2176     def get_lyric_elements(note):
2177         return note.get_typed_children(musicxml.Lyric)
2178
2179     def has_lyric_belonging_to_lyric_part(note, lyric_part_id):
2180         lyric_elements = get_lyric_elements(note)
2181         lyric_numbers = [lyric.number for lyric in lyric_elements]
2182         return any([lyric_number == lyric_part_id for lyric_number in lyric_numbers])
2183
2184     for idx, elem in enumerate(voice._elements):
2185         lyrics = get_lyric_elements(elem)
2186         lyric_keys = [lyric.number for lyric in lyrics]
2187         note_has_lyric_belonging_to_lyric_part = lyric_key in lyric_keys
2188         # Current note has lyric with 'number' matching 'lyric_key'.
2189         if note_has_lyric_belonging_to_lyric_part:
2190             for lyric in lyrics:
2191                 if lyric.number == lyric_key:
2192                     text = musicxml_lyrics_to_text(lyric, None)
2193                     result.append(text)
2194         # Note has any lyric.
2195         elif get_lyric_elements(elem) and \
2196              not note_has_lyric_belonging_to_lyric_part:
2197             result.append('\skip1 ')
2198         # Note does not have any lyric attached to it.
2199         elif is_note_and_not_rest(elem):
2200             result.append('\skip1 ')
2201
2202     lyrics_dict[lyric_key].extend(result)
2203
2204 def musicxml_voice_to_lily_voice(voice):
2205     tuplet_events = []
2206     modes_found = {}
2207     lyrics = {}
2208     return_value = VoiceData()
2209     return_value.voicedata = voice
2210
2211     # First pitch needed for relative mode (if selected in command-line options)
2212     first_pitch = None
2213
2214     # Needed for melismata detection (ignore lyrics on those notes!):
2215     inside_slur = False
2216     is_tied = False
2217     is_chord = False
2218     is_beamed = False
2219     ignore_lyrics = False
2220
2221     current_staff = None
2222
2223     pending_figured_bass = []
2224     pending_chordnames = []
2225     pending_fretboards = []
2226
2227     # Make sure that the keys in the dict don't get reordered, since
2228     # we need the correct ordering of the lyrics stanzas! By default,
2229     # a dict will reorder its keys
2230     return_value.lyrics_order = voice.get_lyrics_numbers()
2231     for k in return_value.lyrics_order:
2232         lyrics[k] = []
2233
2234     voice_builder = LilyPondVoiceBuilder()
2235     figured_bass_builder = LilyPondVoiceBuilder()
2236     chordnames_builder = LilyPondVoiceBuilder()
2237     fretboards_builder = LilyPondVoiceBuilder()
2238     current_measure_length = Rational(4, 4)
2239     voice_builder.set_measure_length(current_measure_length)
2240     in_slur = False
2241
2242     all_lyric_parts = set(get_all_lyric_parts_in_voice(voice))
2243     if lyrics.keys():
2244         for number in lyrics.keys():
2245             extracted_lyrics = extract_lyrics(voice, number, lyrics)
2246
2247     for idx, n in enumerate(voice._elements):
2248         tie_started = False
2249         if n.get_name() == 'forward':
2250             continue
2251         staff = n.get_maybe_exist_named_child('staff')
2252         if staff:
2253             staff = staff.get_text()
2254             if current_staff and staff <> current_staff and not n.get_maybe_exist_named_child('chord'):
2255                 voice_builder.add_command(musicexp.StaffChange(staff))
2256             current_staff = staff
2257
2258         if isinstance(n, musicxml.Partial) and n.partial > 0:
2259             a = musicxml_partial_to_lily(n.partial)
2260             if a:
2261                 voice_builder.add_partial(a)
2262                 figured_bass_builder.add_partial(a)
2263                 chordnames_builder.add_partial(a)
2264                 fretboards_builder.add_partial(a)
2265             continue
2266
2267         is_chord = n.get_maybe_exist_named_child('chord')
2268         is_after_grace =(isinstance(n, musicxml.Note) and n.is_after_grace());
2269         if not is_chord and not is_after_grace:
2270             try:
2271                 voice_builder.jumpto(n._when)
2272                 figured_bass_builder.jumpto(n._when)
2273                 chordnames_builder.jumpto(n._when)
2274                 fretboards_builder.jumpto(n._when)
2275             except NegativeSkip, neg:
2276                 voice_builder.correct_negative_skip(n._when)
2277                 figured_bass_builder.correct_negative_skip(n._when)
2278                 chordnames_builder.correct_negative_skip(n._when)
2279                 fretboards_builder.correct_negative_skip(n._when)
2280                 n.message(_("Negative skip found: from %s to %s, difference is %s") % (neg.here, neg.dest, neg.dest - neg.here))
2281
2282         if isinstance(n, musicxml.Barline):
2283             barlines = n.to_lily_object()
2284             for a in barlines:
2285                 if isinstance(a, musicexp.BarLine):
2286                     voice_builder.add_barline(a)
2287                     figured_bass_builder.add_barline(a, False)
2288                     chordnames_builder.add_barline(a, False)
2289                     fretboards_builder.add_barline(a, False)
2290                 elif isinstance(a, RepeatMarker) or isinstance(a, EndingMarker):
2291                     voice_builder.add_command(a)
2292                     figured_bass_builder.add_barline(a, False)
2293                     chordnames_builder.add_barline(a, False)
2294                     fretboards_builder.add_barline(a, False)
2295             continue
2296
2297         if isinstance(n, musicxml.Print):
2298             for a in musicxml_print_to_lily(n):
2299                 voice_builder.add_command(a, False)
2300             continue
2301
2302         # Continue any multimeasure-rests before trying to add bar checks!
2303         # Don't handle new MM rests yet, because for them we want bar checks!
2304         rest = n.get_maybe_exist_typed_child(musicxml.Rest)
2305         if (rest and rest.is_whole_measure()
2306                  and voice_builder.pending_multibar > Rational(0)):
2307             voice_builder.add_multibar_rest(n._duration)
2308             continue
2309
2310         # print a bar check at the beginning of each measure!
2311         if n.is_first() and n._measure_position == Rational(0) and n != voice._elements[0]:
2312             try:
2313                 num = int(n.get_parent().number)
2314             except ValueError:
2315                 num = 0
2316             if num > 0:
2317                 voice_builder.add_bar_check(num)
2318                 figured_bass_builder.add_bar_check(num)
2319                 chordnames_builder.add_bar_check(num)
2320                 fretboards_builder.add_bar_check(num)
2321
2322         if isinstance(n, musicxml.Direction):
2323             # check if Direction already has been converted in another voice.
2324             if n.converted:
2325                 continue
2326             else:
2327                 n.converted = True
2328                 for direction in musicxml_direction_to_lily(n):
2329                     if direction.wait_for_note():
2330                         voice_builder.add_dynamics(direction)
2331                     else:
2332                         voice_builder.add_command(direction)
2333                 continue
2334
2335         # Start any new multimeasure rests
2336         if (rest and rest.is_whole_measure()):
2337             if pending_chordnames:
2338                 chordnames_builder.jumpto(n._when)
2339                 chordnames_builder.stay_here = True
2340             if pending_figured_bass:
2341                 figured_bass_builder.jumpto(n._when)
2342                 figured_bass_builder.stay_here = True
2343             if pending_fretboards:
2344                 fretboards_builder.jumpto(n._when)
2345                 fretboards_builder.stay_here = True
2346             voice_builder.add_multibar_rest(n._duration)
2347             continue
2348
2349         if isinstance(n, musicxml.Harmony):
2350             if (options.fretboards):
2351                 # Makes fretboard diagrams in a separate FretBoards voice
2352                 for a in musicxml_harmony_to_lily_fretboards(n):
2353                     pending_fretboards.append(a)
2354             else:
2355                 # Makes markup fretboard-diagrams inside the voice
2356                 for a in musicxml_harmony_to_lily(n):
2357                     if a.wait_for_note():
2358                         voice_builder.add_dynamics(a)
2359                     else:
2360                         voice_builder.add_command(a)
2361             for a in musicxml_harmony_to_lily_chordname(n):
2362                 pending_chordnames.append(a)
2363             continue
2364
2365         if isinstance(n, musicxml.FiguredBass):
2366             a = musicxml_figured_bass_to_lily(n)
2367             if a:
2368                 pending_figured_bass.append(a)
2369             continue
2370
2371         if isinstance(n, musicxml.Attributes):
2372             for a in musicxml_attributes_to_lily(n):
2373                 voice_builder.add_command(a)
2374             measure_length = measure_length_from_attributes(n, current_measure_length)
2375             if current_measure_length != measure_length:
2376                 current_measure_length = measure_length
2377                 voice_builder.set_measure_length(current_measure_length)
2378             continue
2379
2380         if not n.__class__.__name__ == 'Note':
2381             n.message(_('unexpected %s; expected %s or %s or %s') % (n, 'Note', 'Attributes', 'Barline'))
2382             continue
2383
2384 #        if not hasattr(conversion_settings, 'convert_rest_positions'):
2385 #            conversion_settings.convert_rest_positions = True
2386
2387         main_event = n.to_lily_object(
2388             convert_stem_directions=conversion_settings.convert_stem_directions,
2389             convert_rest_positions=conversion_settings.convert_rest_positions)
2390
2391         if main_event and not first_pitch:
2392             first_pitch = main_event.pitch
2393         # ignore lyrics for notes inside a slur, tie, chord or beam
2394         ignore_lyrics = is_tied or is_chord #or is_beamed or inside_slur
2395
2396         if main_event and hasattr(main_event, 'drum_type') and main_event.drum_type:
2397             modes_found['drummode'] = True
2398
2399         ev_chord = voice_builder.last_event_chord(n._when)
2400         if not ev_chord:
2401             ev_chord = musicexp.ChordEvent()
2402             voice_builder.add_music(ev_chord, n._duration)
2403
2404         # For grace notes:
2405         grace = n.get_maybe_exist_typed_child(musicxml.Grace)
2406         if n.is_grace():
2407             is_after_grace = ev_chord.has_elements() or n.is_after_grace();
2408             is_chord = n.get_maybe_exist_typed_child(musicxml.Chord)
2409
2410             grace_chord = None
2411
2412             # after-graces and other graces use different lists; Depending on
2413             # whether we have a chord or not, obtain either a new ChordEvent or
2414             # the previous one to create a chord
2415             if is_after_grace:
2416                 if ev_chord.after_grace_elements and n.get_maybe_exist_typed_child(musicxml.Chord):
2417                     grace_chord = ev_chord.after_grace_elements.get_last_event_chord()
2418                 if not grace_chord:
2419                     grace_chord = musicexp.ChordEvent()
2420                     ev_chord.append_after_grace(grace_chord)
2421             elif n.is_grace():
2422                 if ev_chord.grace_elements and n.get_maybe_exist_typed_child(musicxml.Chord):
2423                     grace_chord = ev_chord.grace_elements.get_last_event_chord()
2424                 if not grace_chord:
2425                     grace_chord = musicexp.ChordEvent()
2426                     ev_chord.append_grace(grace_chord)
2427
2428             if hasattr(grace, 'slash') and not is_after_grace:
2429                 # TODO: use grace_type = "appoggiatura" for slurred grace notes
2430                 if grace.slash == "yes":
2431                     ev_chord.grace_type = "acciaccatura"
2432             # now that we have inserted the chord into the grace music, insert
2433             # everything into that chord instead of the ev_chord
2434             ev_chord = grace_chord
2435             ev_chord.append(main_event)
2436             ignore_lyrics = True
2437         else:
2438             ev_chord.append(main_event)
2439             # When a note/chord has grace notes (duration==0), the duration of the
2440             # event chord is not yet known, but the event chord was already added
2441             # with duration 0. The following correct this when we hit the real note!
2442             if voice_builder.current_duration() == 0 and n._duration > 0:
2443                 voice_builder.set_duration(n._duration)
2444
2445         # if we have a figured bass, set its voice builder to the correct position
2446         # and insert the pending figures
2447         if pending_figured_bass:
2448             try:
2449                 figured_bass_builder.jumpto(n._when)
2450                 if (figured_bass_builder.stay_here):
2451                     figured_bass_builder.stay_here = False
2452             except NegativeSkip, neg:
2453                 pass
2454             for fb in pending_figured_bass:
2455                 # if a duration is given, use that, otherwise the one of the note
2456                 dur = fb.real_duration
2457                 if not dur:
2458                     dur = ev_chord.get_length()
2459                 if not fb.duration:
2460                     fb.duration = ev_chord.get_duration()
2461                 figured_bass_builder.add_music(fb, dur)
2462             pending_figured_bass = []
2463
2464         if pending_chordnames:
2465             try:
2466                 chordnames_builder.jumpto(n._when)
2467                 if (chordnames_builder.stay_here):
2468                     chordnames_builder.stay_here = False
2469             except NegativeSkip, neg:
2470                 pass
2471             for cn in pending_chordnames:
2472                 # Assign the duration of the EventChord
2473                 cn.duration = ev_chord.get_duration()
2474                 chordnames_builder.add_music(cn, ev_chord.get_length())
2475             pending_chordnames = []
2476
2477         if pending_fretboards:
2478             try:
2479                 fretboards_builder.jumpto(n._when)
2480                 if (fretboards_builder.stay_here):
2481                     fretboards_builder.stay_here = False
2482             except NegativeSkip, neg:
2483                 pass
2484             for fb in pending_fretboards:
2485                 # Assign the duration of the EventChord
2486                 fb.duration = ev_chord.get_duration()
2487                 fretboards_builder.add_music(fb, ev_chord.get_length())
2488             pending_fretboards = []
2489
2490         notations_children = n.get_typed_children(musicxml.Notations)
2491         tuplet_event = None
2492         span_events = []
2493
2494         # The <notation> element can have the following children (+ means implemented, ~ partially, - not):
2495         # +tied | +slur | +tuplet | glissando | slide |
2496         #    ornaments | technical | articulations | dynamics |
2497         #    +fermata | arpeggiate | non-arpeggiate |
2498         #    accidental-mark | other-notation
2499         for notations in notations_children:
2500             for tuplet_event in notations.get_tuplets():
2501                 time_mod = n.get_maybe_exist_typed_child(musicxml.Time_modification)
2502                 tuplet_events.append((ev_chord, tuplet_event, time_mod))
2503
2504             # First, close all open slurs, only then start any new slur
2505             # TODO: Record the number of the open slur to dtermine the correct
2506             #       closing slur!
2507             endslurs = [s for s in notations.get_named_children('slur')
2508                 if s.get_type() in ('stop')]
2509             if endslurs and not inside_slur:
2510                 endslurs[0].message(_('Encountered closing slur, but no slur is open'))
2511             elif endslurs:
2512                 if len(endslurs) > 1:
2513                     endslurs[0].message(_('Cannot have two simultaneous (closing) slurs'))
2514                 # record the slur status for the next note in the loop
2515                 inside_slur = False
2516                 lily_ev = musicxml_spanner_to_lily_event(endslurs[0])
2517                 ev_chord.append(lily_ev)
2518
2519             startslurs = [s for s in notations.get_named_children('slur')
2520                 if s.get_type() in('start')]
2521             if startslurs and inside_slur:
2522                 startslurs[0].message(_('Cannot have a slur inside another slur'))
2523             elif startslurs:
2524                 if len(startslurs) > 1:
2525                     startslurs[0].message(_('Cannot have two simultaneous slurs'))
2526                 # record the slur status for the next note in the loop
2527                 inside_slur = True
2528                 lily_ev = musicxml_spanner_to_lily_event(startslurs[0])
2529                 ev_chord.append(lily_ev)
2530
2531
2532             if not grace:
2533                 mxl_tie = notations.get_tie()
2534                 if mxl_tie and mxl_tie.type == 'start':
2535                     ev_chord.append(musicexp.TieEvent())
2536                     is_tied = True
2537                     tie_started = True
2538                 else:
2539                     is_tied = False
2540
2541             fermatas = notations.get_named_children('fermata')
2542             for a in fermatas:
2543                 ev = musicxml_fermata_to_lily_event(a)
2544                 if ev:
2545                     ev_chord.append(ev)
2546
2547             arpeggiate = notations.get_named_children('arpeggiate')
2548             for a in arpeggiate:
2549                 ev = musicxml_arpeggiate_to_lily_event(a)
2550                 if ev:
2551                     ev_chord.append(ev)
2552
2553             arpeggiate = notations.get_named_children('non-arpeggiate')
2554             for a in arpeggiate:
2555                 ev = musicxml_nonarpeggiate_to_lily_event(a)
2556                 if ev:
2557                     ev_chord.append(ev)
2558
2559             glissandos = notations.get_named_children('glissando')
2560             glissandos += notations.get_named_children('slide')
2561             for a in glissandos:
2562                 ev = musicxml_spanner_to_lily_event(a)
2563                 if ev:
2564                     ev_chord.append(ev)
2565
2566             # accidental-marks are direct children of <notation>!
2567             for a in notations.get_named_children('accidental-mark'):
2568                 ev = musicxml_articulation_to_lily_event(a)
2569                 if ev:
2570                     ev_chord.append(ev)
2571
2572             # Articulations can contain the following child elements:
2573             #         accent | strong-accent | staccato | tenuto |
2574             #         detached-legato | staccatissimo | spiccato |
2575             #         scoop | plop | doit | falloff | breath-mark |
2576             #         caesura | stress | unstress
2577             # Technical can contain the following child elements:
2578             #         up-bow | down-bow | harmonic | open-string |
2579             #         thumb-position | fingering | pluck | double-tongue |
2580             #         triple-tongue | stopped | snap-pizzicato | fret |
2581             #         string | hammer-on | pull-off | bend | tap | heel |
2582             #         toe | fingernails | other-technical
2583             # Ornaments can contain the following child elements:
2584             #         trill-mark | turn | delayed-turn | inverted-turn |
2585             #         shake | wavy-line | mordent | inverted-mordent |
2586             #         schleifer | tremolo | other-ornament, accidental-mark
2587             ornaments = notations.get_named_children('ornaments')
2588             ornaments += notations.get_named_children('articulations')
2589             ornaments += notations.get_named_children('technical')
2590
2591             for a in ornaments:
2592                 for ch in a.get_all_children():
2593                     ev = musicxml_articulation_to_lily_event(ch)
2594                     if ev:
2595                         ev_chord.append(ev)
2596
2597             dynamics = notations.get_named_children('dynamics')
2598             for a in dynamics:
2599                 for ch in a.get_all_children():
2600                     ev = musicxml_dynamics_to_lily_event(ch)
2601                     if ev:
2602                         ev_chord.append(ev)
2603
2604
2605         mxl_beams = [b for b in n.get_named_children('beam')
2606                      if (b.get_type() in('begin', 'end')
2607                          and b.is_primary())]
2608         if mxl_beams and not conversion_settings.ignore_beaming:
2609             beam_ev = musicxml_spanner_to_lily_event(mxl_beams[0])
2610             if beam_ev:
2611                 ev_chord.append(beam_ev)
2612                 if beam_ev.span_direction == -1: # beam and thus melisma starts here
2613                     is_beamed = True
2614                 elif beam_ev.span_direction == 1: # beam and thus melisma ends here
2615                     is_beamed = False
2616
2617         # Assume that a <tie> element only lasts for one note.
2618         # This might not be correct MusicXML interpretation, but works for
2619         # most cases and fixes broken files, which have the end tag missing
2620         if is_tied and not tie_started:
2621             is_tied = False
2622
2623     # force trailing mm rests to be written out.
2624     # voice_builder.add_music (musicexp.ChordEvent(), Rational(0))
2625     if hasattr(options, 'shift_meter') and options.shift_meter:
2626         for event in voice_builder.elements:
2627             if isinstance(event, musicexp.TimeSignatureChange):
2628                 sd = []
2629                 for i in range(0,5):
2630                     sd.append(musicexp.ShiftDurations())
2631                     sd[i].set_shift_durations_parameters(event)
2632                 break;
2633
2634     ly_voice = group_tuplets(voice_builder.elements, tuplet_events)
2635     ly_voice = group_repeats(ly_voice)
2636
2637     seq_music = musicexp.SequentialMusic()
2638
2639     if 'drummode' in modes_found.keys():
2640         ## \key <pitch> barfs in drummode.
2641         ly_voice = [e for e in ly_voice
2642                     if not isinstance(e, musicexp.KeySignatureChange)]
2643
2644     seq_music.elements = ly_voice
2645     for k in lyrics.keys():
2646         return_value.lyrics_dict[k] = musicexp.Lyrics()
2647         return_value.lyrics_dict[k].lyrics_syllables = lyrics[k]
2648
2649
2650     if len(modes_found) > 1:
2651        ly.warning(_('cannot simultaneously have more than one mode: %s') % modes_found.keys())
2652
2653     if hasattr(options, 'shift_meter') and options.shift_meter:
2654         sd[-1].element = seq_music
2655         seq_music = sd[-1]
2656         sd.pop()
2657
2658     if hasattr(options, 'relative') and options.relative:
2659         v = musicexp.RelativeMusic()
2660         v.element = seq_music
2661         v.basepitch = first_pitch
2662         seq_music = v
2663
2664     return_value.ly_voice = seq_music
2665     for mode in modes_found.keys():
2666         v = musicexp.ModeChangingMusicWrapper()
2667         v.element = seq_music
2668         v.mode = mode
2669         return_value.ly_voice = v
2670
2671     # create \figuremode { figured bass elements }
2672     if figured_bass_builder.has_relevant_elements:
2673         fbass_music = musicexp.SequentialMusic()
2674         fbass_music.elements = group_repeats(figured_bass_builder.elements)
2675         v = musicexp.ModeChangingMusicWrapper()
2676         v.mode = 'figuremode'
2677         v.element = fbass_music
2678         if hasattr(options, 'shift_meter') and options.shift_meter:
2679             sd[-1].element = v
2680             v = sd[-1]
2681             sd.pop()
2682         return_value.figured_bass = v
2683
2684     # create \chordmode { chords }
2685     if chordnames_builder.has_relevant_elements:
2686         cname_music = musicexp.SequentialMusic()
2687         cname_music.elements = group_repeats(chordnames_builder.elements)
2688         v = musicexp.ModeChangingMusicWrapper()
2689         v.mode = 'chordmode'
2690         v.element = cname_music
2691         if hasattr(options, 'shift_meter') and options.shift_meter:
2692             sd[-1].element = v
2693             v = sd[-1]
2694             sd.pop()
2695         return_value.chordnames = v
2696
2697     # create diagrams for FretBoards engraver
2698     if fretboards_builder.has_relevant_elements:
2699         fboard_music = musicexp.SequentialMusic()
2700         fboard_music.elements = group_repeats(fretboards_builder.elements)
2701         v = musicexp.MusicWrapper()
2702         v.element = fboard_music
2703         if hasattr(options, 'shift_meter') and options.shift_meter:
2704             sd[-1].element = v
2705             v = sd[-1]
2706             sd.pop()
2707         return_value.fretboards = v
2708
2709     # coll = []
2710     # pending = []
2711
2712     # for elt in return_value.ly_voice.element.elements:
2713     #     if isinstance(elt, musicexp.TimeScaledMusic):
2714     #         print elt.element.elements
2715     #         pending.append(elt)
2716     #     else:
2717     #         coll.append(elt)
2718
2719     # if pending:
2720     #     coll.extend(pending)
2721
2722     # return_value.ly_voice.element.elements = coll
2723
2724     return return_value
2725
2726 def musicxml_id_to_lily(id):
2727     digits = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five',
2728               'Six', 'Seven', 'Eight', 'Nine', 'Ten']
2729
2730     for digit in digits:
2731         d = digits.index(digit)
2732         id = re.sub('%d' % d, digit, id)
2733
2734     id = re.sub('[^a-zA-Z]', 'X', id)
2735     return id
2736
2737 def voices_in_part(part):
2738     """Return a Name -> Voice dictionary for PART"""
2739     part.interpret()
2740     part.extract_voices()
2741     voices = part.get_voices()
2742     part_info = part.get_staff_attributes()
2743
2744     return (voices, part_info)
2745
2746 def voices_in_part_in_parts(parts):
2747     """return a Part -> Name -> Voice dictionary"""
2748     # don't crash if Part doesn't have an id (that's invalid MusicXML,
2749     # but such files are out in the wild!)
2750     dictionary = {}
2751     for p in parts:
2752         voices = voices_in_part(p)
2753         if (hasattr(p, "id")):
2754              dictionary[p.id] = voices
2755         else:
2756              # TODO: extract correct part id from other sources
2757              dictionary[None] = voices
2758     return dictionary;
2759
2760
2761 def get_all_voices(parts):
2762     all_voices = voices_in_part_in_parts(parts)
2763
2764     all_ly_voices = {}
2765     all_ly_staffinfo = {}
2766     for p, (name_voice, staff_info) in all_voices.items():
2767
2768         part_ly_voices = {}
2769         for n, v in name_voice.items():
2770             ly.progress(_("Converting to LilyPond expressions..."), True)
2771             # musicxml_voice_to_lily_voice returns (lily_voice, {nr->lyrics, nr->lyrics})
2772             voice = musicxml_voice_to_lily_voice(v)
2773             part_ly_voices[n] = voice
2774
2775         all_ly_voices[p] = part_ly_voices
2776         all_ly_staffinfo[p] = staff_info
2777
2778     return (all_ly_voices, all_ly_staffinfo)
2779
2780
2781 def option_parser():
2782     p = ly.get_option_parser(usage=_("musicxml2ly [OPTION]... FILE.xml"),
2783                              description=
2784 _("""Convert MusicXML from FILE.xml to LilyPond input.
2785 If the given filename is -, musicxml2ly reads from the command line.
2786 """), add_help_option=False)
2787
2788     p.add_option("-h", "--help",
2789                  action="help",
2790                  help=_("show this help and exit"))
2791
2792     p.version = ('%prog (LilyPond) ' + lilypond_version + '\n\n'
2793 +
2794 _ ("""Copyright (c) 2005--2015 by
2795     Han-Wen Nienhuys <hanwen@xs4all.nl>,
2796     Jan Nieuwenhuizen <janneke@gnu.org> and
2797     Reinhold Kainhofer <reinhold@kainhofer.com>
2798     Patrick L. Schmidt <pls@philomelos.net>
2799 """
2800 +
2801 """
2802 This program is free software.  It is covered by the GNU General Public
2803 License and you are welcome to change it and/or distribute copies of it
2804 under certain conditions.  Invoke as `%s --warranty' for more
2805 information.""") % 'lilypond')
2806
2807     p.add_option("--version",
2808                  action="version",
2809                  help=_ ("show version number and exit"))
2810
2811     p.add_option('-v', '--verbose',
2812                   action="callback",
2813                   callback=ly.handle_loglevel_option,
2814                   callback_args=("DEBUG",),
2815                   help=_ ("be verbose"))
2816
2817     p.add_option('', '--lxml',
2818                   action="store_true",
2819                   default=False,
2820                   dest="use_lxml",
2821                   help=_ ("use lxml.etree; uses less memory and cpu time"))
2822
2823     p.add_option('-z', '--compressed',
2824                   action="store_true",
2825                   dest='compressed',
2826                   default=False,
2827                   help=_ ("input file is a zip-compressed MusicXML file"))
2828
2829     p.add_option('-r', '--relative',
2830                   action="store_true",
2831                   default=True,
2832                   dest="relative",
2833                   help=_ ("convert pitches in relative mode (default)"))
2834
2835     p.add_option('-a', '--absolute',
2836                   action="store_false",
2837                   dest="relative",
2838                   help=_ ("convert pitches in absolute mode"))
2839
2840     p.add_option('-l', '--language',
2841                   metavar=_ ("LANG"),
2842                   action="store",
2843                   help=_ ("use LANG for pitch names, e.g. 'deutsch' for note names in German"))
2844
2845     p.add_option("--loglevel",
2846                   help=_ ("Print log messages according to LOGLEVEL "
2847                           "(NONE, ERROR, WARNING, PROGRESS (default), DEBUG)"),
2848                   metavar=_ ("LOGLEVEL"),
2849                   action='callback',
2850                   callback=ly.handle_loglevel_option,
2851                   type='string')
2852
2853     p.add_option('--nd', '--no-articulation-directions',
2854                   action="store_false",
2855                   default=True,
2856                   dest="convert_directions",
2857                   help=_ ("do not convert directions (^, _ or -) for articulations, dynamics, etc."))
2858
2859     p.add_option('--nrp', '--no-rest-positions',
2860                   action="store_false",
2861                   default=True,
2862                   dest="convert_rest_positions",
2863                   help=_ ("do not convert exact vertical positions of rests"))
2864
2865     p.add_option('--nsb', '--no-system-breaks',
2866                   action="store_false",
2867                   default=True,
2868                   dest="convert_system_breaks",
2869                   help=_ ("ignore system breaks"))
2870
2871     p.add_option('--npb', '--no-page-breaks',
2872                   action="store_false",
2873                   default=True,
2874                   dest="convert_page_breaks",
2875                   help=_ ("ignore page breaks"))
2876
2877     p.add_option('--npm', '--no-page-margins',
2878                   action="store_false",
2879                   default=True,
2880                   dest="convert_page_margins",
2881                   help=_ ("ignore page margins"))
2882
2883     p.add_option('--npl', '--no-page-layout',
2884                   action="store_false",
2885                   default=True,
2886                   dest="convert_page_layout",
2887                   help=_ ("do not convert the exact page layout and breaks (shortcut for \"--nsb --npb --npm\" options)"))
2888
2889     p.add_option('--nsd', '--no-stem-directions',
2890                   action="store_false",
2891                   default=True,
2892                   dest="convert_stem_directions",
2893                   help=_ ("ignore stem directions from MusicXML, use lilypond's automatic stemming instead"))
2894
2895     p.add_option('--nb', '--no-beaming',
2896                   action="store_false",
2897                   default=True,
2898                   dest="convert_beaming",
2899                   help=_ ("do not convert beaming information, use lilypond's automatic beaming instead"))
2900
2901     p.add_option('-o', '--output',
2902                   metavar=_ ("FILE"),
2903                   action="store",
2904                   default=None,
2905                   type='string',
2906                   dest='output_name',
2907                   help=_ ("set output filename to FILE, stdout if -"))
2908
2909     p.add_option('-m', '--midi',
2910                   action="store_true",
2911                   default=False,
2912                   dest="midi",
2913                   help=_("activate midi-block in .ly file"))
2914
2915     # transpose function
2916     p.add_option('--transpose',
2917                   metavar=_ ("TOPITCH"),
2918                   action="store",
2919                   dest="transpose",
2920                   help=_ ("set pitch to transpose by the interval between pitch 'c' and TOPITCH"))
2921
2922     # time signature changing function
2923     p.add_option('--sm', '--shift-meter',
2924                   metavar=_ ("BEATS/BEATTYPE"),
2925                   action="store",
2926                   dest="shift_meter",
2927                   help=_ ("change the length|duration of notes as a function of a given time signature to make the score look faster or slower, (eg. '4/4' or '2/2')"))
2928
2929     # switch tabstaff clef
2930     p.add_option('--tc', '--tab-clef',
2931                   metavar=_ ("TABCLEFNAME"),
2932                   action="store",
2933                   dest="tab_clef",
2934                   help=_ ("switch between two versions of tab clefs (\"tab\" and \"moderntab\")"))
2935
2936     # StringNumber stencil on/off
2937     p.add_option('--sn', '--string-numbers',
2938                   metavar=_ ("t[rue]/f[alse]"),
2939                   action="store",
2940                   dest="string_numbers",
2941                   help=_ ("deactivate string number stencil with --string-numbers f[alse]. Default is t[rue]"))
2942
2943     # StringNumber stencil on/off
2944     p.add_option('--fb', '--fretboards',
2945                   action="store_true",
2946                   default=False,
2947                   dest="fretboards",
2948                   help=_("converts '<frame>' events to a separate FretBoards voice instead of markups"))
2949
2950     p.add_option_group('',
2951                         description=(
2952             _ ("Report bugs via %s")
2953             % 'http://post.gmane.org/post.php'
2954             '?group=gmane.comp.gnu.lilypond.bugs') + '\n')
2955     return p
2956
2957 def print_voice_definitions (printer, part_list, voices):
2958     for part in part_list:
2959         part_id = part.id
2960         nv_dict = voices.get (part_id, {})
2961         for (name, voice) in nv_dict.items ():
2962             k = music_xml_voice_name_to_lily_name (part_id, name)
2963             printer.dump ('%s = ' % k)
2964             voice.ly_voice.print_ly (printer)
2965             printer.newline()
2966             if voice.chordnames:
2967                 cnname = music_xml_chordnames_name_to_lily_name (part_id, name)
2968                 printer.dump ('%s = ' % cnname)
2969                 voice.chordnames.print_ly (printer)
2970                 printer.newline()
2971             for l in voice.lyrics_order:
2972                 lname = music_xml_lyrics_name_to_lily_name (part_id, name, l)
2973                 printer.dump ('%s = ' % lname)
2974                 voice.lyrics_dict[l].print_ly (printer)
2975                 printer.newline()
2976             if voice.figured_bass:
2977                 fbname = music_xml_figuredbass_name_to_lily_name (part_id, name)
2978                 printer.dump ('%s = ' % fbname)
2979                 voice.figured_bass.print_ly (printer)
2980                 printer.newline()
2981             if voice.fretboards:
2982                 fbdname = music_xml_fretboards_name_to_lily_name (part_id, name)
2983                 printer.dump ('%s = ' % fbdname)
2984                 voice.fretboards.print_ly (printer)
2985                 printer.newline()
2986
2987 def uniq_list(l):
2988     return dict([(elt, 1) for elt in l]).keys()
2989
2990 # format the information about the staff in the form
2991 #     [staffid,
2992 #         [
2993 #            [voiceid1, [lyricsid11, lyricsid12,...], figuredbassid1],
2994 #            [voiceid2, [lyricsid21, lyricsid22,...], figuredbassid2],
2995 #            ...
2996 #         ]
2997 #     ]
2998 # raw_voices is of the form [(voicename, lyricsids, havefiguredbass)*]
2999 def format_staff_info(part_id, staff_id, raw_voices):
3000     voices = []
3001     for (v, lyricsids, figured_bass, chordnames, fretboards) in raw_voices:
3002         voice_name = music_xml_voice_name_to_lily_name(part_id, v)
3003         voice_lyrics = [music_xml_lyrics_name_to_lily_name(part_id, v, l)
3004                    for l in lyricsids]
3005         figured_bass_name = ''
3006         if figured_bass:
3007             figured_bass_name = music_xml_figuredbass_name_to_lily_name(part_id, v)
3008         chordnames_name = ''
3009         if chordnames:
3010             chordnames_name = music_xml_chordnames_name_to_lily_name(part_id, v)
3011         fretboards_name = ''
3012         if fretboards:
3013             fretboards_name = music_xml_fretboards_name_to_lily_name(part_id, v)
3014         voices.append([voice_name, voice_lyrics, figured_bass_name, chordnames_name, fretboards_name])
3015     return [staff_id, voices]
3016
3017 def update_score_setup(score_structure, part_list, voices, parts):
3018     for part_definition in part_list:
3019         part_id = part_definition.id
3020         nv_dict = voices.get(part_id)
3021         if not nv_dict:
3022             ly.warning(_('unknown part in part-list: %s') % part_id)
3023             continue
3024
3025         staves = reduce(lambda x, y: x + y,
3026                 [voice.voicedata._staves.keys()
3027                  for voice in nv_dict.values()],
3028                 [])
3029         staves_info = []
3030         if len(staves) > 1:
3031             staves_info = []
3032             staves = uniq_list(staves)
3033             staves.sort()
3034             for s in staves:
3035                 thisstaff_raw_voices = [(voice_name, voice.lyrics_order, voice.figured_bass, voice.chordnames, voice.fretboards)
3036                     for (voice_name, voice) in nv_dict.items()
3037                     if voice.voicedata._start_staff == s]
3038                 staves_info.append(format_staff_info(part_id, s, thisstaff_raw_voices))
3039         else:
3040             thisstaff_raw_voices = [(voice_name, voice.lyrics_order, voice.figured_bass, voice.chordnames, voice.fretboards)
3041                 for (voice_name, voice) in nv_dict.items()]
3042             staves_info.append(format_staff_info(part_id, None, thisstaff_raw_voices))
3043         score_structure.set_part_information(part_id, staves_info)
3044
3045     sounds = []
3046     for part in parts:
3047         for measure in part.get_typed_children(musicxml.Measure):
3048             for sound in measure.get_typed_children(musicxml.Sound):
3049                 sounds.append(sound)
3050             for direction in measure.get_typed_children(musicxml.Direction):
3051                 for sound in direction.get_typed_children(musicxml.Sound):
3052                     sounds.append(sound)
3053
3054     score_structure.set_tempo('100')
3055     if len(sounds) != 0:
3056         for sound in sounds:
3057             if (sound.get_tempo() != None and sound.get_tempo() != ""):
3058                 score_structure.set_tempo(sound.get_tempo())
3059                 break
3060
3061
3062 # Set global values in the \layout block, like auto-beaming etc.
3063 def update_layout_information():
3064     if not conversion_settings.ignore_beaming and layout_information:
3065         layout_information.set_context_item('Score', 'autoBeaming = ##f')
3066     if musicexp.get_string_numbers() == "f":
3067         layout_information.set_context_item('Score', '\\override StringNumber #\'stencil = ##f')
3068
3069 #  \n\t\t\t\t\\override StringNumber #\'stencil = ##f
3070
3071 def print_ly_preamble(printer, filename):
3072     printer.dump_version(lilypond_version)
3073     printer.print_verbatim('% automatically converted by musicxml2ly from ' + filename)
3074     printer.newline()
3075     printer.dump(r'\pointAndClickOff')
3076     printer.newline()
3077     if options.midi:
3078         printer.newline()
3079         printer.dump(r'\include "articulate.ly"')
3080         printer.newline()
3081
3082 def print_ly_additional_definitions(printer, filename=None):
3083     if needed_additional_definitions:
3084         printer.newline()
3085         printer.print_verbatim('%% additional definitions required by the score:')
3086         printer.newline()
3087     for a in set(needed_additional_definitions):
3088         printer.print_verbatim(additional_definitions.get(a, ''))
3089         printer.newline()
3090     printer.newline()
3091
3092 # Read in the tree from the given I/O object (either file or string) and
3093 # demarshall it using the classes from the musicxml.py file
3094 def read_xml(io_object, use_lxml):
3095     if use_lxml:
3096         import lxml.etree
3097         tree = lxml.etree.parse(io_object)
3098         mxl_tree = musicxml.lxml_demarshal_node(tree.getroot())
3099         return mxl_tree
3100     else:
3101         from xml.dom import minidom, Node
3102         doc = minidom.parse(io_object)
3103         node = doc.documentElement
3104         return musicxml.minidom_demarshal_node(node)
3105     return None
3106
3107
3108 def read_musicxml(filename, compressed, use_lxml):
3109     raw_string = None
3110     if compressed:
3111         if filename == "-":
3112              ly.progress(_("Input is compressed, extracting raw MusicXML data from stdin"), True)
3113              # unfortunately, zipfile.ZipFile can't read directly from
3114              # stdin, so copy everything from stdin to a temp file and read
3115              # that. TemporaryFile() will remove the file when it is closed.
3116              tmp = tempfile.TemporaryFile()
3117              sys.stdin = os.fdopen(sys.stdin.fileno(), 'rb', 0) # Make sys.stdin binary
3118              bytes_read = sys.stdin.read(8192)
3119              while bytes_read:
3120                  for b in bytes_read:
3121                      tmp.write(b)
3122                  bytes_read = sys.stdin.read(8192)
3123              z = zipfile.ZipFile(tmp, "r")
3124         else:
3125             ly.progress(_("Input file %s is compressed, extracting raw MusicXML data") % filename, True)
3126             z = zipfile.ZipFile(filename, "r")
3127         container_xml = z.read("META-INF/container.xml")
3128         if not container_xml:
3129             return None
3130         container = read_xml(StringIO.StringIO(container_xml), use_lxml)
3131         if not container:
3132             return None
3133         rootfiles = container.get_maybe_exist_named_child('rootfiles')
3134         if not rootfiles:
3135             return None
3136         rootfile_list = rootfiles.get_named_children('rootfile')
3137         mxml_file = None
3138         if len(rootfile_list) > 0:
3139             mxml_file = getattr(rootfile_list[0], 'full-path', None)
3140         if mxml_file:
3141             raw_string = z.read(mxml_file)
3142
3143     if raw_string:
3144         io_object = StringIO.StringIO(raw_string)
3145     elif filename == "-":
3146         io_object = sys.stdin
3147     else:
3148         io_object = filename
3149
3150     return read_xml(io_object, use_lxml)
3151
3152
3153 def convert(filename, options):
3154     if filename == "-":
3155         ly.progress(_("Reading MusicXML from Standard input ..."), True)
3156     else:
3157         ly.progress(_("Reading MusicXML from %s ...") % filename, True)
3158
3159     tree = read_musicxml(filename, options.compressed, options.use_lxml)
3160     score_information = extract_score_information(tree)
3161     paper_information = extract_paper_information(tree)
3162
3163     parts = tree.get_typed_children(musicxml.Part)
3164     (voices, staff_info) = get_all_voices(parts)
3165
3166     score = None
3167     mxl_pl = tree.get_maybe_exist_typed_child(musicxml.Part_list)
3168     if mxl_pl:
3169         score = extract_score_structure(mxl_pl, staff_info)
3170         part_list = mxl_pl.get_named_children("score-part")
3171
3172     # score information is contained in the <work>, <identification> or <movement-title> tags
3173     update_score_setup(score, part_list, voices, parts)
3174     # After the conversion, update the list of settings for the \layout block
3175     update_layout_information()
3176
3177     if not options.output_name:
3178         options.output_name = os.path.basename(filename)
3179         options.output_name = os.path.splitext(options.output_name)[0]
3180     elif re.match(".*\.ly", options.output_name):
3181         options.output_name = os.path.splitext(options.output_name)[0]
3182
3183
3184     #defs_ly_name = options.output_name + '-defs.ly'
3185     if (options.output_name == "-"):
3186       output_ly_name = 'Standard output'
3187     else:
3188       output_ly_name = options.output_name + '.ly'
3189     ly.progress(_("Output to `%s'") % output_ly_name, True)
3190     printer = musicexp.Output_printer()
3191     #ly.progress(_("Output to `%s'") % defs_ly_name, True)
3192     if (options.output_name == "-"):
3193       printer.set_file(codecs.getwriter("utf-8")(sys.stdout))
3194     else:
3195       printer.set_file(codecs.open(output_ly_name, 'wb', encoding='utf-8'))
3196     print_ly_preamble(printer, filename)
3197     print_ly_additional_definitions(printer, filename)
3198     if score_information:
3199         score_information.print_ly(printer)
3200     if paper_information and conversion_settings.convert_page_layout:
3201         paper_information.print_ly(printer)
3202     if layout_information:
3203         layout_information.print_ly(printer)
3204     print_voice_definitions(printer, part_list, voices)
3205
3206     printer.newline()
3207     printer.dump("% The score definition")
3208     printer.newline()
3209     score.print_ly(printer)
3210     printer.newline()
3211
3212     # Syntax update to current version
3213     if (options.output_name != "-"):
3214         version = os.popen("lilypond --version | head -1 | cut -d' ' -f3").read().strip()
3215         ly.progress(_("Converting to current version (%s) notations ..." % version), True)
3216         os.system("convert-ly -e %s 2> /dev/null" % utilities.escape_ly_output_string(output_ly_name))
3217
3218     return voices
3219
3220 def get_existing_filename_with_extension(filename, ext):
3221     if os.path.exists(filename):
3222         return filename
3223     newfilename = filename + "." + ext
3224     if os.path.exists(newfilename):
3225         return newfilename;
3226     newfilename = filename + ext
3227     if os.path.exists(newfilename):
3228         return newfilename;
3229     return ''
3230
3231
3232 def main():
3233     opt_parser = option_parser()
3234
3235     global options
3236     (options, args) = opt_parser.parse_args()
3237     if not args:
3238         opt_parser.print_usage()
3239         sys.exit(2)
3240
3241     # midi-block option
3242     if options.midi:
3243         musicexp.set_create_midi(options.midi)
3244
3245     # transpose function
3246     if options.transpose:
3247         musicexp.set_transpose(options.transpose)
3248
3249     # tab clef option
3250     if options.tab_clef:
3251         musicexp.set_tab_clef(options.tab_clef)
3252
3253     # string numbers option
3254     if options.string_numbers:
3255         musicexp.set_string_numbers(options.string_numbers)
3256
3257     if options.language:
3258         musicexp.set_pitch_language(options.language)
3259         needed_additional_definitions.append(options.language)
3260         additional_definitions[options.language] = "\\language \"%s\"\n" % options.language
3261
3262     conversion_settings.ignore_beaming = not options.convert_beaming
3263     conversion_settings.convert_page_layout = options.convert_page_layout
3264     if conversion_settings.convert_page_layout:
3265         conversion_settings.convert_system_breaks = options.convert_system_breaks
3266         conversion_settings.convert_page_breaks = options.convert_page_breaks
3267         conversion_settings.convert_page_margins = options.convert_page_margins
3268     else:
3269         conversion_settings.convert_system_breaks = False
3270         conversion_settings.convert_page_breaks = False
3271         conversion_settings.convert_page_margins = False
3272     conversion_settings.convert_stem_directions = options.convert_stem_directions
3273
3274     # Allow the user to leave out the .xml or xml on the filename
3275     basefilename = args[0].decode('utf-8')
3276     if basefilename == "-": # Read from stdin
3277         filename = "-"
3278     else:
3279         filename = get_existing_filename_with_extension(basefilename, "xml")
3280         if not filename:
3281             filename = get_existing_filename_with_extension(basefilename, "mxl")
3282             options.compressed = True
3283     if filename and filename.endswith("mxl"):
3284         options.compressed = True
3285
3286     if filename and (filename == "-" or os.path.exists(filename)):
3287         voices = convert(filename, options)
3288     else:
3289         ly.error(_("Unable to find input file %s") % basefilename)
3290         sys.exit(1)
3291
3292 if __name__ == '__main__':
3293     main()