]> git.donarmstrong.com Git - lilypond.git/blob - scripts/musicxml2ly.py
Convert miscellaneous description elements to texidoc items.
[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         seq.elements = music_list[i1:last]
766
767         tsm.element = seq
768
769         new_list.append(tsm)
770         #TODO: Handle nested tuplets!!!!
771
772     new_list.extend(music_list[last:])
773     return new_list
774
775
776 def musicxml_clef_to_lily(attributes):
777     change = musicexp.ClefChange()
778     (change.type, change.position, change.octave) = attributes.get_clef_information()
779     return change
780
781 def musicxml_time_to_lily(attributes):
782     change = musicexp.TimeSignatureChange()
783     # time signature function
784     if hasattr(options, 'shift_meter') and options.shift_meter:
785         tmp_meter = options.shift_meter.split("/",1)
786         sig = [int(tmp_meter[0]), int(tmp_meter[1])]
787         change.originalFractions = attributes.get_time_signature()
788     else:
789         sig = attributes.get_time_signature()
790     if not sig:
791         return None
792     change.fractions = sig
793
794     time_elm = attributes.get_maybe_exist_named_child('time')
795     if time_elm and hasattr(time_elm, 'symbol'):
796         change.style = { 'single-number': "'single-digit",
797                          'cut': None,
798                          'common': None,
799                          'normal': "'()"}.get(time_elm.symbol, "'()")
800     else:
801         change.style = "'()"
802
803     # TODO: Handle senza-misura measures
804     # TODO: Handle hidden time signatures(print-object="no")
805     # TODO: What shall we do if the symbol clashes with the sig? e.g. "cut"
806     #       with 3/8 or "single-number" with(2+3)/8 or 3/8+2/4?
807     return change
808
809 def musicxml_key_to_lily(attributes):
810     key_sig = attributes.get_key_signature()
811     if not key_sig or not(isinstance(key_sig, list) or isinstance(key_sig, tuple)):
812         ly.warning(_("Unable to extract key signature!"))
813         return None
814
815     change = musicexp.KeySignatureChange()
816
817     if len(key_sig) == 2 and not isinstance(key_sig[0], list):
818         # standard key signature,(fifths, mode)
819         (fifths, mode) = key_sig
820         change.mode = mode
821
822         start_pitch = musicexp.Pitch()
823         start_pitch.octave = 0
824         try:
825             (n, a) = {
826                 'major'     : (0, 0),
827                 'minor'     : (5, 0),
828                 'ionian'    : (0, 0),
829                 'dorian'    : (1, 0),
830                 'phrygian'  : (2, 0),
831                 'lydian'    : (3, 0),
832                 'mixolydian': (4, 0),
833                 'aeolian'   : (5, 0),
834                 'locrian'   : (6, 0),
835                 }[mode]
836             start_pitch.step = n
837             start_pitch.alteration = a
838         except  KeyError:
839             ly.warning(_("unknown mode %s, expecting 'major' or 'minor' "
840                 "or a church mode!") % mode)
841
842         fifth = musicexp.Pitch()
843         fifth.step = 4
844         if fifths < 0:
845             fifths *= -1
846             fifth.step *= -1
847             fifth.normalize()
848         for x in range(fifths):
849             start_pitch = start_pitch.transposed(fifth)
850         change.tonic = start_pitch
851
852     else:
853         # Non-standard key signature of the form [[step,alter<,octave>],...]
854         # MusicXML contains C,D,E,F,G,A,B as steps, lily uses 0-7, so convert
855         alterations = []
856         for k in key_sig:
857             k[0] = musicxml_step_to_lily(k[0])
858             alterations.append(k)
859         change.non_standard_alterations = alterations
860     return change
861
862 def musicxml_transpose_to_lily(attributes):
863     transpose = attributes.get_transposition()
864     if not transpose:
865         return None
866
867     shift = musicexp.Pitch()
868     octave_change = transpose.get_maybe_exist_named_child('octave-change')
869     if octave_change:
870         shift.octave = string.atoi(octave_change.get_text())
871     chromatic_shift = string.atoi(transpose.get_named_child('chromatic').get_text())
872     chromatic_shift_normalized = chromatic_shift % 12;
873     (shift.step, shift.alteration) = [
874         (0, 0), (0, 1), (1, 0), (2, -1), (2, 0),
875         (3, 0), (3, 1), (4, 0), (5, -1), (5, 0),
876         (6, -1), (6, 0)][chromatic_shift_normalized];
877
878     shift.octave += (chromatic_shift - chromatic_shift_normalized) / 12
879
880     diatonic = transpose.get_maybe_exist_named_child('diatonic')
881     if diatonic:
882         diatonic_step = string.atoi(diatonic.get_text()) % 7
883         if diatonic_step != shift.step:
884             # We got the alter incorrect!
885             old_semitones = shift.semitones()
886             shift.step = diatonic_step
887             new_semitones = shift.semitones()
888             shift.alteration += old_semitones - new_semitones
889
890     transposition = musicexp.Transposition()
891     transposition.pitch = musicexp.Pitch().transposed(shift)
892     return transposition
893
894 def musicxml_staff_details_to_lily(attributes):
895     details = attributes.get_maybe_exist_named_child('staff-details')
896     if not details:
897         return None
898
899     ## TODO: Handle staff-type, staff-lines, staff-tuning, capo, staff-size
900     ret = []
901
902     stafflines = details.get_maybe_exist_named_child('staff-lines')
903     if stafflines:
904         lines = string.atoi(stafflines.get_text());
905         lines_event = musicexp.StaffLinesEvent(lines);
906         ret.append(lines_event);
907
908     return ret;
909
910
911 def musicxml_attributes_to_lily(attrs):
912     elts = []
913     attr_dispatch = {
914         'clef': musicxml_clef_to_lily,
915         'time': musicxml_time_to_lily,
916         'key': musicxml_key_to_lily,
917         'transpose': musicxml_transpose_to_lily,
918         'staff-details': musicxml_staff_details_to_lily,
919     }
920     for (k, func) in attr_dispatch.items():
921         children = attrs.get_named_children(k)
922         if children:
923             ev = func(attrs)
924             if isinstance(ev, list):
925               for e in ev:
926                 elts.append(e)
927             elif ev:
928                 elts.append(ev)
929
930     return elts
931
932 def extract_display_text(el):
933     child = el.get_maybe_exist_named_child("display-text")
934     if child:
935         return child.get_text()
936     else:
937         return False
938
939
940 def musicxml_print_to_lily(el):
941     # TODO: Implement other print attributes
942     #  <!ELEMENT print (page-layout?, system-layout?, staff-layout*,
943     #          measure-layout?, measure-numbering?, part-name-display?,
944     #          part-abbreviation-display?)>
945     #  <!ATTLIST print
946     #      staff-spacing %tenths; #IMPLIED
947     #      new-system %yes-no; #IMPLIED
948     #      new-page %yes-no-number; #IMPLIED
949     #      blank-page NMTOKEN #IMPLIED
950     #      page-number CDATA #IMPLIED
951     #  >
952     elts = []
953     if (hasattr(el, "new-system") and conversion_settings.convert_system_breaks):
954         val = getattr(el, "new-system")
955         if (val == "yes"):
956             elts.append(musicexp.Break("break"))
957     if (hasattr(el, "new-page") and conversion_settings.convert_page_breaks):
958         val = getattr(el, "new-page")
959         if (val == "yes"):
960             elts.append(musicexp.Break("pageBreak"))
961     child = el.get_maybe_exist_named_child("part-name-display")
962     if child:
963         elts.append(musicexp.SetEvent("Staff.instrumentName",
964                                         "\"%s\"" % extract_display_text(child)))
965     child = el.get_maybe_exist_named_child("part-abbreviation-display")
966     if child:
967         elts.append(musicexp.SetEvent("Staff.shortInstrumentName",
968                                         "\"%s\"" % extract_display_text(child)))
969     return elts
970
971
972 class Marker(musicexp.Music):
973     def __init__(self):
974         self.direction = 0
975         self.event = None
976     def print_ly(self, printer):
977         ly.warning(_("Encountered unprocessed marker %s\n") % self)
978         pass
979     def ly_expression(self):
980         return ""
981 class RepeatMarker(Marker):
982     def __init__(self):
983         Marker.__init__(self)
984         self.times = 0
985 class EndingMarker(Marker):
986     pass
987
988
989 spanner_event_dict = {
990     'beam' : musicexp.BeamEvent,
991     'dashes' : musicexp.TextSpannerEvent,
992     'bracket' : musicexp.BracketSpannerEvent,
993     'glissando' : musicexp.GlissandoEvent,
994     'octave-shift' : musicexp.OctaveShiftEvent,
995     'pedal' : musicexp.PedalEvent,
996     'slide' : musicexp.GlissandoEvent,
997     'slur' : musicexp.SlurEvent,
998     'wavy-line' : musicexp.TextSpannerEvent,
999     'wedge' : musicexp.HairpinEvent
1000 }
1001 spanner_type_dict = {
1002     'start':-1,
1003     'begin':-1,
1004     'crescendo':-1,
1005     'decreschendo':-1,
1006     'diminuendo':-1,
1007     'continue': 0,
1008     'change': 0,
1009     'up':-1,
1010     'down':-1,
1011     'stop': 1,
1012     'end' : 1
1013 }
1014
1015 def musicxml_spanner_to_lily_event(mxl_event):
1016     ev = None
1017
1018     name = mxl_event.get_name()
1019     func = spanner_event_dict.get(name)
1020     if func:
1021         ev = func()
1022     else:
1023         ly.warning(_('unknown span event %s') % mxl_event)
1024
1025     if name == "wavy-line":
1026         ev.style=OrnamenthasWhat(mxl_event)
1027
1028     type = mxl_event.get_type()
1029     span_direction = spanner_type_dict.get(type)
1030     # really check for None, because some types will be translated to 0, which
1031     # would otherwise also lead to the unknown span warning
1032     if span_direction != None:
1033         ev.span_direction = span_direction
1034     else:
1035         ly.warning(_('unknown span type %s for %s') %(type, name))
1036
1037     ev.set_span_type(type)
1038     ev.line_type = getattr(mxl_event, 'line-type', 'solid')
1039
1040     # assign the size, which is used for octave-shift, etc.
1041     ev.size = mxl_event.get_size()
1042
1043     return ev
1044
1045 def musicxml_direction_to_indicator(direction):
1046     return { "above": 1, "upright": 1, "up": 1, "below":-1, "downright":-1, "down":-1, "inverted":-1 }.get(direction, 0)
1047
1048 def musicxml_fermata_to_lily_event(mxl_event):
1049
1050     ev = musicexp.ArticulationEvent()
1051     txt = mxl_event.get_text()
1052
1053     # The contents of the element defined the shape, possible are normal, angled and square
1054     ev.type = { "angled": "shortfermata", "square": "longfermata" }.get(txt, "fermata")
1055     fermata_types= { "angled": "shortfermata",
1056                      "square": "longfermata" }
1057
1058     # MusicXML fermata types can be specified in two different ways:
1059     # 1. <fermata>angled</fermata> and
1060     # 2. <fermata type="angled"/> -- both need to be handled.
1061     if hasattr(mxl_event, 'type'):
1062         fermata_type = fermata_types.get(mxl_event.type, 'fermata')
1063     else:
1064         fermata_type = fermata_types.get(mxl_event.get_text(), 'fermata')
1065
1066     ev.type = fermata_type
1067
1068     if hasattr(mxl_event, 'type'):
1069         dir = musicxml_direction_to_indicator(mxl_event.type)
1070         if dir and options.convert_directions:
1071             ev.force_direction = dir
1072     return ev
1073
1074 def musicxml_arpeggiate_to_lily_event(mxl_event):
1075     ev = musicexp.ArpeggioEvent()
1076     ev.direction = musicxml_direction_to_indicator(getattr(mxl_event, 'direction', None))
1077     return ev
1078
1079 def musicxml_nonarpeggiate_to_lily_event(mxl_event):
1080     ev = musicexp.ArpeggioEvent()
1081     ev.non_arpeggiate = True
1082     ev.direction = musicxml_direction_to_indicator(getattr(mxl_event, 'direction', None))
1083     return ev
1084
1085 def musicxml_tremolo_to_lily_event(mxl_event):
1086     ev = musicexp.TremoloEvent()
1087     txt = mxl_event.get_text()
1088     if txt:
1089       ev.strokes = txt
1090     else:
1091       # This is supposed to be a default for empty tremolo elements
1092       # TODO: Add empty tremolo element to test cases in tremolo.xml
1093       # TODO: Test empty tremolo element
1094       # TODO: Consideration: Is 3 really a reasonable default?
1095       ev.strokes = "3"
1096     return ev
1097
1098 def musicxml_falloff_to_lily_event(mxl_event):
1099     ev = musicexp.BendEvent()
1100     ev.alter = -4
1101     return ev
1102
1103 def musicxml_doit_to_lily_event(mxl_event):
1104     ev = musicexp.BendEvent()
1105     ev.alter = 4
1106     return ev
1107
1108 def musicxml_bend_to_lily_event(mxl_event):
1109     ev = musicexp.BendEvent()
1110     ev.alter = mxl_event.bend_alter()
1111     return ev
1112
1113 def musicxml_caesura_to_lily_event(mxl_event):
1114     ev = musicexp.MarkupEvent()
1115     # FIXME: default to straight or curved caesura?
1116     ev.contents = "\\musicglyph #\"scripts.caesura.straight\""
1117     ev.force_direction = 1
1118     return ev
1119
1120 def musicxml_fingering_event(mxl_event):
1121     ev = musicexp.ShortArticulationEvent()
1122     ev.type = mxl_event.get_text()
1123     return ev
1124
1125 def musicxml_string_event(mxl_event):
1126     ev = musicexp.NoDirectionArticulationEvent()
1127     ev.type = mxl_event.get_text()
1128     return ev
1129
1130 def musicxml_accidental_mark(mxl_event):
1131     ev = musicexp.MarkupEvent()
1132     contents = { "sharp": "\\sharp",
1133       "natural": "\\natural",
1134       "flat": "\\flat",
1135       "double-sharp": "\\doublesharp",
1136       "sharp-sharp": "\\sharp\\sharp",
1137       "flat-flat": "\\flat\\flat",
1138       "flat-flat": "\\doubleflat",
1139       "natural-sharp": "\\natural\\sharp",
1140       "natural-flat": "\\natural\\flat",
1141       "quarter-flat": "\\semiflat",
1142       "quarter-sharp": "\\semisharp",
1143       "three-quarters-flat": "\\sesquiflat",
1144       "three-quarters-sharp": "\\sesquisharp",
1145     }.get(mxl_event.get_text())
1146     if contents:
1147         ev.contents = contents
1148         return ev
1149     else:
1150         return None
1151
1152 # translate articulations, ornaments and other notations into ArticulationEvents
1153 # possible values:
1154 #   -) string  (ArticulationEvent with that name)
1155 #   -) function (function(mxl_event) needs to return a full ArticulationEvent-derived object
1156 #   -) (class, name)  (like string, only that a different class than ArticulationEvent is used)
1157 # TODO: Some translations are missing!
1158 articulations_dict = {
1159     "accent": (musicexp.ShortArticulationEvent, ">"), # or "accent"
1160     "accidental-mark": musicxml_accidental_mark,
1161     "bend": musicxml_bend_to_lily_event,
1162     "breath-mark": (musicexp.NoDirectionArticulationEvent, "breathe"),
1163     "caesura": musicxml_caesura_to_lily_event,
1164     #"delayed-turn": "?",
1165     "detached-legato": (musicexp.ShortArticulationEvent, "_"), # or "portato"
1166     "doit": musicxml_doit_to_lily_event,
1167     #"double-tongue": "?",
1168     "down-bow": "downbow",
1169     "falloff": musicxml_falloff_to_lily_event,
1170     "fingering": musicxml_fingering_event,
1171     #"fingernails": "?",
1172     #"fret": "?",
1173     #"hammer-on": "?",
1174     "harmonic": "flageolet",
1175     #"heel": "?",
1176     "inverted-mordent": "prall",
1177     "inverted-turn": "reverseturn",
1178     "mordent": "mordent",
1179     "open-string": "open",
1180     #"plop": "?",
1181     #"pluck": "?",
1182     #"pull-off": "?",
1183     #"schleifer": "?",
1184     #"scoop": "?",
1185     #"shake": "?",
1186     "snap-pizzicato": "snappizzicato",
1187     #"spiccato": "?",
1188     "staccatissimo": (musicexp.ShortArticulationEvent, "|"), # or "staccatissimo"
1189     "staccato": (musicexp.ShortArticulationEvent, "."), # or "staccato"
1190     "stopped": (musicexp.ShortArticulationEvent, "+"), # or "stopped"
1191     #"stress": "?",
1192     "string": musicxml_string_event,
1193     "strong-accent": (musicexp.ShortArticulationEvent, "^"), # or "marcato"
1194     #"tap": "?",
1195     "tenuto": (musicexp.ShortArticulationEvent, "-"), # or "tenuto"
1196     "thumb-position": "thumb",
1197     #"toe": "?",
1198     "turn": "turn",
1199     "tremolo": musicxml_tremolo_to_lily_event,
1200     "trill-mark": "trill",
1201     #"triple-tongue": "?",
1202     #"unstress": "?"
1203     "up-bow": "upbow",
1204     #"wavy-line": "?",
1205 }
1206 articulation_spanners = [ "wavy-line" ]
1207
1208 def OrnamenthasWhat(mxl_event):
1209     wavy = trilly = ignore = start = stop = False
1210     for i in mxl_event._parent._children:
1211             if i._name == "wavy-line": wavy = True
1212             elif i._name == "trill-mark": trilly = True
1213             try:
1214                 if i.type == "continue": ignore = True
1215                 elif i.type == "start": start = True
1216                 elif i.type == "stop": stop = True
1217             except:
1218                 pass
1219     if start == True:
1220         if wavy == True and trilly == False: musicexp.whatOrnament = "wave"
1221         else: musicexp.whatOrnament = "trill"
1222     if ignore == True: return "ignore"
1223     elif stop == True: return "stop"
1224     elif wavy == True and trilly == True: return "trill and wave"
1225     elif wavy == True: return "wave"
1226     elif trilly == True: return "trill"
1227
1228 def OrnamenthasWavyline(mxl_event):
1229     for i in mxl_event._parent._children:
1230             if i._name == "wavy-line": return True
1231     return False
1232
1233
1234 def musicxml_articulation_to_lily_event(mxl_event):
1235     # wavy-line elements are treated as trill spanners, not as articulation ornaments
1236     if mxl_event.get_name() in articulation_spanners:
1237         return musicxml_spanner_to_lily_event(mxl_event)
1238
1239     tmp_tp = articulations_dict.get(mxl_event.get_name())
1240     if OrnamenthasWavyline(mxl_event):
1241         return
1242     if not tmp_tp:
1243         return
1244
1245     if isinstance(tmp_tp, str):
1246         ev = musicexp.ArticulationEvent()
1247         ev.type = tmp_tp
1248     elif isinstance(tmp_tp, tuple):
1249         ev = tmp_tp[0]()
1250         ev.type = tmp_tp[1]
1251     else:
1252         ev = tmp_tp(mxl_event)
1253
1254     # Some articulations use the type attribute, other the placement...
1255     dir = None
1256     if hasattr(mxl_event, 'type') and hasattr(options, 'convert_directions') and options.convert_directions:
1257         dir = musicxml_direction_to_indicator(mxl_event.type)
1258     if hasattr(mxl_event, 'placement') and hasattr(options, 'convert_directions') and options.convert_directions:
1259         dir = musicxml_direction_to_indicator(mxl_event.placement)
1260     if dir:
1261         ev.force_direction = dir
1262     return ev
1263
1264
1265
1266 def musicxml_dynamics_to_lily_event(dynentry):
1267     dynamics_available = (
1268         "ppppp", "pppp", "ppp", "pp", "p", "mp", "mf",
1269         "f", "ff", "fff", "ffff", "fp", "sf", "sff", "sp", "spp", "sfz", "rfz")
1270     dynamicsname = dynentry.get_name()
1271     if dynamicsname == "other-dynamics":
1272         dynamicsname = dynentry.get_text()
1273     if not dynamicsname or dynamicsname == "#text":
1274         return None
1275
1276     if not dynamicsname in dynamics_available:
1277         # Get rid of - in tag names (illegal in ly tags!)
1278         dynamicstext = dynamicsname
1279         dynamicsname = string.replace(dynamicsname, "-", "")
1280         additional_definitions[dynamicsname] = dynamicsname + \
1281               " = #(make-dynamic-script \"" + dynamicstext + "\")"
1282         needed_additional_definitions.append(dynamicsname)
1283     event = musicexp.DynamicsEvent()
1284     event.type = dynamicsname
1285     return event
1286
1287 # Convert single-color two-byte strings to numbers 0.0 - 1.0
1288 def hexcolorval_to_nr(hex_val):
1289     try:
1290         v = int(hex_val, 16)
1291         if v == 255:
1292             v = 256
1293         return v / 256.
1294     except ValueError:
1295         return 0.
1296
1297 def hex_to_color(hex_val):
1298     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)
1299     if res:
1300         return map(lambda x: hexcolorval_to_nr(x), res.group(2, 3, 4))
1301     else:
1302         return None
1303
1304 def font_size_number_to_lily_command(size):
1305     d = {
1306         (0, 8): r'\teeny',
1307         (8, 10): r'\tiny',
1308         (10, 12): r'\small',
1309         (12, 16): r'',
1310         (16, 24): r'\large',
1311         (24, float('inf')): r'\huge',
1312     }
1313     result = None
1314     for r in d.keys():
1315         if r[0] <= size < r[1]:
1316             result = d[r]
1317             break
1318     return result
1319
1320 def font_size_word_to_lily_command(size):
1321     font_size_dict = {
1322         "xx-small": '\\teeny',
1323         "x-small": '\\tiny',
1324         "small": '\\small',
1325         "medium": '',
1326         "large": '\\large',
1327         "x-large": '\\huge',
1328         "xx-large": '\\larger\\huge'
1329         }
1330     return font_size_dict.get(size, '')
1331
1332 def get_font_size(size):
1333     try:
1334         size = float(size)
1335         return font_size_number_to_lily_command(size)
1336     except ValueError:
1337         return font_size_word_to_lily_command(size)
1338
1339 def musicxml_words_to_lily_event(words):
1340     event = musicexp.TextEvent()
1341     text = words.get_text()
1342     text = re.sub('^ *\n? *', '', text) #remove white spaces and line breaks before text
1343     text = re.sub(' *\n? *$', '', text) #remove white spaces and line breaks before text
1344     event.text = text
1345
1346     if hasattr(words, 'default-y') and hasattr(options, 'convert_directions') and options.convert_directions:
1347         offset = getattr(words, 'default-y')
1348         try:
1349             off = string.atoi(offset)
1350             if off > 0:
1351                 event.force_direction = 1
1352             else:
1353                 event.force_direction = -1
1354         except ValueError:
1355             event.force_direction = 0
1356
1357     if hasattr(words, 'font-weight'):
1358         font_weight = { "normal": '', "bold": '\\bold' }.get(getattr(words, 'font-weight'), '')
1359         if font_weight:
1360             event.markup += font_weight
1361
1362     if hasattr(words, 'font-size'):
1363         size = getattr(words, 'font-size')
1364         # font_size = font_size_dict.get(size, '')
1365         font_size = get_font_size(size)
1366         if font_size:
1367             event.markup += font_size
1368
1369     if hasattr(words, 'color'):
1370         color = getattr(words, 'color')
1371         rgb = hex_to_color(color)
1372         if rgb:
1373             event.markup += "\\with-color #(rgb-color %s %s %s)" % (rgb[0], rgb[1], rgb[2])
1374
1375     if hasattr(words, 'font-style'):
1376         font_style = { "italic": '\\italic' }.get(getattr(words, 'font-style'), '')
1377         if font_style:
1378             event.markup += font_style
1379
1380     # TODO: How should I best convert the font-family attribute?
1381
1382     # TODO: How can I represent the underline, overline and line-through
1383     #       attributes in LilyPond? Values of these attributes indicate
1384     #       the number of lines
1385
1386     return event
1387
1388
1389 # convert accordion-registration to lilypond.
1390 # Since lilypond does not have any built-in commands, we need to create
1391 # the markup commands manually and define our own variables.
1392 # Idea was taken from: http://lsr.dsi.unimi.it/LSR/Item?id=194
1393 def musicxml_accordion_to_markup(mxl_event):
1394     commandname = "accReg"
1395     command = ""
1396
1397     high = mxl_event.get_maybe_exist_named_child('accordion-high')
1398     if high:
1399         commandname += "H"
1400         command += """\\combine
1401           \\raise #2.5 \\musicglyph #\"accordion.dot\"
1402           """
1403     middle = mxl_event.get_maybe_exist_named_child('accordion-middle')
1404     if middle:
1405         # By default, use one dot (when no or invalid content is given). The
1406         # MusicXML spec is quiet about this case...
1407         txt = 1
1408         try:
1409           txt = string.atoi(middle.get_text())
1410         except ValueError:
1411             pass
1412         if txt == 3:
1413             commandname += "MMM"
1414             command += """\\combine
1415           \\raise #1.5 \\musicglyph #\"accordion.dot\"
1416           \\combine
1417           \\raise #1.5 \\translate #(cons 1 0) \\musicglyph #\"accordion.dot\"
1418           \\combine
1419           \\raise #1.5 \\translate #(cons -1 0) \\musicglyph #\"accordion.dot\"
1420           """
1421         elif txt == 2:
1422             commandname += "MM"
1423             command += """\\combine
1424           \\raise #1.5 \\translate #(cons 0.5 0) \\musicglyph #\"accordion.dot\"
1425           \\combine
1426           \\raise #1.5 \\translate #(cons -0.5 0) \\musicglyph #\"accordion.dot\"
1427           """
1428         elif not txt <= 0:
1429             commandname += "M"
1430             command += """\\combine
1431           \\raise #1.5 \\musicglyph #\"accordion.dot\"
1432           """
1433     low = mxl_event.get_maybe_exist_named_child('accordion-low')
1434     if low:
1435         commandname += "L"
1436         command += """\\combine
1437           \\raise #0.5 \musicglyph #\"accordion.dot\"
1438           """
1439
1440     command += "\musicglyph #\"accordion.discant\""
1441     command = "\\markup { \\normalsize %s }" % command
1442     # Define the newly built command \accReg[H][MMM][L]
1443     additional_definitions[commandname] = "%s = %s" % (commandname, command)
1444     needed_additional_definitions.append(commandname)
1445     return "\\%s" % commandname
1446
1447 def musicxml_accordion_to_ly(mxl_event):
1448     txt = musicxml_accordion_to_markup(mxl_event)
1449     if txt:
1450         ev = musicexp.MarkEvent(txt)
1451         return ev
1452     return
1453
1454
1455 def musicxml_rehearsal_to_ly_mark(mxl_event):
1456     text = mxl_event.get_text()
1457     if not text:
1458         return
1459     # default is boxed rehearsal marks!
1460     encl = "box"
1461     if hasattr(mxl_event, 'enclosure'):
1462         encl = {"none": None, "square": "box", "circle": "circle" }.get(mxl_event.enclosure, None)
1463     if encl:
1464         text = "\\%s { %s }" % (encl, text)
1465     ev = musicexp.MarkEvent("\\markup { %s }" % text)
1466     return ev
1467
1468 def musicxml_harp_pedals_to_ly(mxl_event):
1469     count = 0
1470     result = "\\harp-pedal #\""
1471     for t in mxl_event.get_named_children('pedal-tuning'):
1472       alter = t.get_named_child('pedal-alter')
1473       if alter:
1474         val = int(alter.get_text().strip())
1475         result += {1: "v", 0: "-", -1: "^"}.get(val, "")
1476       count += 1
1477       if count == 3:
1478         result += "|"
1479     ev = musicexp.MarkupEvent()
1480     ev.contents = result + "\""
1481     return ev
1482
1483 def musicxml_eyeglasses_to_ly(mxl_event):
1484     needed_additional_definitions.append("eyeglasses")
1485     return musicexp.MarkEvent("\\markup { \\eyeglasses }")
1486
1487 def next_non_hash_index(lst, pos):
1488     pos += 1
1489     while pos < len(lst) and isinstance(lst[pos], musicxml.Hash_text):
1490         pos += 1
1491     return pos
1492
1493 def musicxml_metronome_to_ly(mxl_event):
1494     children = mxl_event.get_all_children()
1495     if not children:
1496         return
1497
1498     index = -1
1499     index = next_non_hash_index(children, index)
1500     if isinstance(children[index], musicxml.BeatUnit):
1501         # first form of metronome-mark, using unit and beats/min or other unit
1502         ev = musicexp.TempoMark()
1503         if hasattr(mxl_event, 'parentheses'):
1504             ev.set_parentheses(mxl_event.parentheses == "yes")
1505
1506         d = musicexp.Duration()
1507         d.duration_log = utilities.musicxml_duration_to_log(
1508             children[index].get_text())
1509         index = next_non_hash_index(children, index)
1510         if isinstance(children[index], musicxml.BeatUnitDot):
1511             d.dots = 1
1512             index = next_non_hash_index(children, index)
1513         ev.set_base_duration(d)
1514         if isinstance(children[index], musicxml.BeatUnit):
1515             # Form "note = newnote"
1516             newd = musicexp.Duration()
1517             newd.duration_log = utilities.musicxml_duration_to_log(
1518                 children[index].get_text())
1519             index = next_non_hash_index(children, index)
1520             if isinstance(children[index], musicxml.BeatUnitDot):
1521                 newd.dots = 1
1522                 index = next_non_hash_index(children, index)
1523             ev.set_new_duration(newd)
1524         elif isinstance(children[index], musicxml.PerMinute):
1525             # Form "note = bpm"
1526             try:
1527                 beats = int(children[index].get_text())
1528                 ev.set_beats_per_minute(beats)
1529             except ValueError:
1530                 pass
1531         else:
1532             ly.warning(_("Unknown metronome mark, ignoring"))
1533             return
1534         return ev
1535     else:
1536         #TODO: Implement the other (more complex) way for tempo marks!
1537         ly.warning(_("Metronome marks with complex relations (<metronome-note> in MusicXML) are not yet implemented."))
1538         return
1539
1540 # translate directions into Events, possible values:
1541 #   -) string  (MarkEvent with that command)
1542 #   -) function (function(mxl_event) needs to return a full Event-derived object
1543 #   -) (class, name)  (like string, only that a different class than MarkEvent is used)
1544 directions_dict = {
1545     'accordion-registration' : musicxml_accordion_to_ly,
1546     'coda' : (musicexp.MusicGlyphMarkEvent, "coda"),
1547 #     'damp' : ???
1548 #     'damp-all' : ???
1549     'eyeglasses': musicxml_eyeglasses_to_ly,
1550     'harp-pedals' : musicxml_harp_pedals_to_ly,
1551 #     'image' : ???
1552     'metronome' : musicxml_metronome_to_ly,
1553     'rehearsal' : musicxml_rehearsal_to_ly_mark,
1554 #     'scordatura' : ???
1555     'segno' : (musicexp.MusicGlyphMarkEvent, "segno"),
1556     'words' : musicxml_words_to_lily_event,
1557 }
1558 directions_spanners = [ 'octave-shift', 'pedal', 'wedge', 'dashes', 'bracket' ]
1559
1560 def musicxml_direction_to_lily(n):
1561     # TODO: Handle the <staff> element!
1562     res = []
1563     # placement applies to all children!
1564     dir = None
1565     if hasattr(n, 'placement') and hasattr(options, 'convert_directions') and options.convert_directions:
1566         dir = musicxml_direction_to_indicator(n.placement)
1567     dirtype_children = []
1568     # TODO: The direction-type is used for grouping (e.g. dynamics with text),
1569     #       so we can't simply flatten them out!
1570     for dt in n.get_typed_children(musicxml.DirType):
1571         dirtype_children += dt.get_all_children()
1572
1573     for entry in dirtype_children:
1574         # brackets, dashes, octave shifts. pedal marks, hairpins etc. are spanners:
1575         if entry.get_name() in directions_spanners:
1576             event = musicxml_spanner_to_lily_event(entry)
1577             if event:
1578                 event.force_direction=dir
1579                 res.append(event)
1580             continue
1581
1582         # now treat all the "simple" ones, that can be translated using the dict
1583         ev = None
1584         tmp_tp = directions_dict.get(entry.get_name(), None)
1585         if isinstance(tmp_tp, str): # string means MarkEvent
1586             ev = musicexp.MarkEvent(tmp_tp)
1587         elif isinstance(tmp_tp, tuple): # tuple means (EventClass, "text")
1588             ev = tmp_tp[0](tmp_tp[1])
1589         elif tmp_tp:
1590             ev = tmp_tp(entry)
1591         if ev:
1592             # TODO: set the correct direction! Unfortunately, \mark in ly does
1593             #       not seem to support directions!
1594             ev.force_direction = dir
1595             res.append(ev)
1596             continue
1597
1598         if entry.get_name() == "dynamics":
1599             for dynentry in entry.get_all_children():
1600                 ev = musicxml_dynamics_to_lily_event(dynentry)
1601                 if ev:
1602                     ev.force_direction = dir
1603                     res.append(ev)
1604
1605     return res
1606
1607 notehead_styles_dict = {
1608     'slash': '\'slash',
1609     'triangle': '\'triangle',
1610     'diamond': '\'diamond',
1611     'square': '\'la', # TODO: Proper squared note head
1612     'cross': None, # TODO: + shaped note head
1613     'x': '\'cross',
1614     'circle-x': '\'xcircle',
1615     'inverted triangle': None, # TODO: Implement
1616     'arrow down': None, # TODO: Implement
1617     'arrow up': None, # TODO: Implement
1618     'slashed': None, # TODO: Implement
1619     'back slashed': None, # TODO: Implement
1620     'normal': None,
1621     'cluster': None, # TODO: Implement
1622     'none': '#f',
1623     'do': '\'do',
1624     're': '\'re',
1625     'mi': '\'mi',
1626     'fa': '\'fa',
1627     'so': None,
1628     'la': '\'la',
1629     'ti': '\'ti',
1630     }
1631
1632 def musicxml_chordpitch_to_lily(mxl_cpitch):
1633     r = musicexp.ChordPitch()
1634     r.alteration = mxl_cpitch.get_alteration()
1635     r.step = musicxml_step_to_lily(mxl_cpitch.get_step())
1636     return r
1637
1638 chordkind_dict = {
1639     'major': r'{}:5',
1640     'minor': r'{}:m5',
1641     'augmented': r'{}:aug5',
1642     'diminished': r'{}:dim5',
1643         # Sevenths:
1644     'dominant': r'{}:7',
1645     'dominant-seventh': r'{}:7',
1646     'major-seventh': r'{}:maj7',
1647     'minor-seventh': r'{}:m7',
1648     'diminished-seventh': r'{}:dim7',
1649     'augmented-seventh': r'{}:aug7',
1650     'half-diminished': r'{}:dim5m7',
1651     'major-minor': r'{}:maj7m5',
1652         # Sixths:
1653     'major-sixth': r'{}:6',
1654     'minor-sixth': r'{}:m6',
1655         # Ninths:
1656     'dominant-ninth': r'{}:9',
1657     'major-ninth': r'{}:maj9',
1658     'minor-ninth': r'{}:m9',
1659         # 11ths (usually as the basis for alteration):
1660     'dominant-11th': r'{}:11',
1661     'major-11th': r'{}:maj11',
1662     'minor-11th': r'{}:m11',
1663         # 13ths (usually as the basis for alteration):
1664     'dominant-13th': r'{}:13.11',
1665     'major-13th': r'{}:maj13.11',
1666     'minor-13th': r'{}:m13',
1667         # Suspended:
1668     'suspended-second': r'{}:sus2',
1669     'suspended-fourth': r'{}:sus4',
1670         # Functional sixths:
1671     # TODO
1672     #'Neapolitan': '???',
1673     #'Italian': '???',
1674     #'French': '???',
1675     #'German': '???',
1676         # Other:
1677     #'pedal': '???',(pedal-point bass)
1678     'power': r'\powerChords {}:1.5',
1679     #'Tristan': '???',
1680     'other': r'{}:1',
1681     'none': None,
1682 }
1683
1684 def musicxml_chordkind_to_lily(kind):
1685     res = chordkind_dict.get(kind, None)
1686     # Check for None, since a major chord is converted to ''
1687     if res == None:
1688         ly.warning(_("Unable to convert chord type %s to lilypond.") % kind)
1689     return res
1690
1691
1692 # Global variable for guitar string tunings
1693 string_tunings = None
1694
1695 def musicxml_get_string_tunings(lines):
1696     global string_tunings
1697     if (string_tunings == None):
1698         if not lines:
1699             lines = 6
1700         string_tunings = [musicexp.Pitch()] * lines
1701         for i in range(0, lines):
1702             p = musicexp.Pitch()
1703             p.step = musicxml_step_to_lily(((("E","A","D","G","B")*(lines/5+1))[0:lines])[i])
1704             p.octave = (([-2+int(x%5>1)+2*(x/5) for x in range(0,lines)][0:lines])[i])
1705             p.alteration = 0
1706             p._force_absolute_pitch = True
1707             string_tunings[i] = p
1708         string_tunings = string_tunings[::-1]
1709     return string_tunings[0:lines]
1710
1711 def musicxml_frame_to_lily_event(frame):
1712     ev = musicexp.FretEvent()
1713     ev.strings = frame.get_strings()
1714     ev.frets = frame.get_frets()
1715     #offset = frame.get_first_fret() - 1
1716     #offset = frame.get_first_fret()
1717     barre = []
1718     open_strings = range(1,ev.strings+1)
1719     for fn in frame.get_named_children('frame-note'):
1720         fret = fn.get_fret()
1721         if fret <= 0:
1722             fret = "o"
1723         el = [ fn.get_string(), fret ]
1724         fingering = fn.get_fingering()
1725         if fingering >= 0:
1726             el.append(fingering)
1727         ev.elements.append(el)
1728         open_strings.remove(fn.get_string())
1729         b = fn.get_barre()
1730         if b == 'start':
1731             barre.append(el[0]) # start string
1732             barre.append(el[1]) # fret
1733         elif b == 'stop':
1734             barre.insert(1, el[0]) # end string
1735     for string in open_strings:
1736         ev.elements.append([string, 'x'])
1737     ev.elements.sort()
1738     ev.elements.reverse()
1739     if barre:
1740         ev.barre = barre
1741     return ev
1742
1743 def musicxml_harmony_to_lily(n):
1744     res = []
1745     for f in n.get_named_children('frame'):
1746         ev = musicxml_frame_to_lily_event(f)
1747         if ev:
1748             res.append(ev)
1749     return res
1750
1751 def musicxml_harmony_to_lily_fretboards(n):
1752     res = []
1753     frame = n.get_maybe_exist_named_child('frame')
1754     if frame:
1755         strings = frame.get_strings()
1756         if not strings:
1757             strings = 6
1758         tunings = musicxml_get_string_tunings(strings)
1759         ev = musicexp.FretBoardEvent()
1760         #barre = []
1761         for fn in frame.get_named_children('frame-note'):
1762             fbn = musicexp.FretBoardNote()
1763             string = fn.get_string()
1764             fbn.string = string
1765             fingering = fn.get_fingering()
1766             if fingering >= 0:
1767                 fbn.fingering = fingering
1768             p = tunings[string-1].copy()
1769             p.add_semitones(fn.get_fret())
1770             fbn.pitch = p
1771             ev.append(fbn)
1772         res.append(ev)
1773     return res
1774
1775 def musicxml_harmony_to_lily_chordname(n):
1776     res = []
1777     root = n.get_maybe_exist_named_child('root')
1778     if root:
1779         ev = musicexp.ChordNameEvent()
1780         ev.root = musicxml_chordpitch_to_lily(root)
1781         kind = n.get_maybe_exist_named_child('kind')
1782         if kind:
1783             ev.kind = musicxml_chordkind_to_lily(kind.get_text())
1784             if not ev.kind:
1785                 return res
1786         bass = n.get_maybe_exist_named_child('bass')
1787         if bass:
1788             ev.bass = musicxml_chordpitch_to_lily(bass)
1789         inversion = n.get_maybe_exist_named_child('inversion')
1790         if inversion:
1791             # TODO: LilyPond does not support inversions, does it?
1792
1793             # Mail from Carl Sorensen on lilypond-devel, June 11, 2008:
1794             # 4. LilyPond supports the first inversion in the form of added
1795             # bass notes.  So the first inversion of C major would be c:/g.
1796             # To get the second inversion of C major, you would need to do
1797             # e:6-3-^5 or e:m6-^5.  However, both of these techniques
1798             # require you to know the chord and calculate either the fifth
1799             # pitch (for the first inversion) or the third pitch (for the
1800             # second inversion) so they may not be helpful for musicxml2ly.
1801             inversion_count = string.atoi(inversion.get_text())
1802             if inversion_count == 1:
1803               # TODO: Calculate the bass note for the inversion...
1804               pass
1805             pass
1806         for deg in n.get_named_children('degree'):
1807             d = musicexp.ChordModification()
1808             d.type = deg.get_type()
1809             d.step = deg.get_value()
1810             d.alteration = deg.get_alter()
1811             ev.add_modification(d)
1812         #TODO: convert the user-symbols attribute:
1813             #major: a triangle, like Unicode 25B3
1814             #minor: -, like Unicode 002D
1815             #augmented: +, like Unicode 002B
1816             #diminished: (degree), like Unicode 00B0
1817             #half-diminished: (o with slash), like Unicode 00F8
1818         if ev and ev.root:
1819             res.append(ev)
1820     return res
1821
1822 def musicxml_figured_bass_note_to_lily(n):
1823     res = musicexp.FiguredBassNote()
1824     suffix_dict = { 'sharp' : "+",
1825                     'flat' : "-",
1826                     'natural' : "!",
1827                     'double-sharp' : "++",
1828                     'flat-flat' : "--",
1829                     'sharp-sharp' : "++",
1830                     'slash' : "/" }
1831     prefix = n.get_maybe_exist_named_child('prefix')
1832     if prefix:
1833         res.set_prefix(suffix_dict.get(prefix.get_text(), ""))
1834     fnumber = n.get_maybe_exist_named_child('figure-number')
1835     if fnumber:
1836         res.set_number(fnumber.get_text())
1837     suffix = n.get_maybe_exist_named_child('suffix')
1838     if suffix:
1839         res.set_suffix(suffix_dict.get(suffix.get_text(), ""))
1840     if n.get_maybe_exist_named_child('extend'):
1841         # TODO: Implement extender lines (unfortunately, in lilypond you have
1842         #       to use \set useBassFigureExtenders = ##t, which turns them on
1843         #       globally, while MusicXML has a property for each note...
1844         #       I'm not sure there is a proper way to implement this cleanly
1845         #n.extend
1846         pass
1847     return res
1848
1849
1850
1851 def musicxml_figured_bass_to_lily(n):
1852     if not isinstance(n, musicxml.FiguredBass):
1853         return
1854     res = musicexp.FiguredBassEvent()
1855     for i in n.get_named_children('figure'):
1856         note = musicxml_figured_bass_note_to_lily(i)
1857         if note:
1858             res.append(note)
1859     dur = n.get_maybe_exist_named_child('duration')
1860     if dur:
1861         # apply the duration to res
1862         length = Rational(int(dur.get_text()), n._divisions) * Rational(1, 4)
1863         res.set_real_duration(length)
1864         duration = rational_to_lily_duration(length)
1865         if duration:
1866             res.set_duration(duration)
1867     if hasattr(n, 'parentheses') and n.parentheses == "yes":
1868         res.set_parentheses(True)
1869     return res
1870
1871 instrument_drumtype_dict = {
1872     'Acoustic Snare Drum': 'acousticsnare',
1873     'Side Stick': 'sidestick',
1874     'Open Triangle': 'opentriangle',
1875     'Mute Triangle': 'mutetriangle',
1876     'Tambourine': 'tambourine',
1877     'Bass Drum': 'bassdrum',
1878 }
1879
1880
1881
1882 def musicxml_lyrics_to_text(lyrics, ignoremelismata):
1883     # TODO: Implement text styles for lyrics syllables
1884     continued = False
1885     extended = False
1886     text = ''
1887     for e in lyrics.get_all_children():
1888         if isinstance(e, musicxml.Syllabic):
1889             continued = e.continued()
1890         elif isinstance(e, musicxml.Text):
1891             # We need to convert soft hyphens to -, otherwise the ascii codec as well
1892             # as lilypond will barf on that character
1893             text += string.replace(e.get_text(), u'\xad', '-')
1894         elif isinstance(e, musicxml.Elision):
1895             if text:
1896                 text += " "
1897             continued = False
1898             extended = False
1899         elif isinstance(e, musicxml.Extend):
1900             if text:
1901                 text += " "
1902             extended = True
1903
1904     if text == "-" and continued:
1905         return "--"
1906     elif text == "_" and extended:
1907         return "__"
1908     elif continued and text:
1909         if hasattr(options, 'convert_beaming') and options.convert_beaming:
1910             if (ignoremelismata == "on"):
1911                 return " \set ignoreMelismata = ##t " + utilities.escape_ly_output_string (text)
1912             elif (ignoremelismata == "off"):
1913                 return " " + utilities.escape_ly_output_string(text) + " -- \unset ignoreMelismata"
1914             else:
1915                 return " " + utilities.escape_ly_output_string(text) + " --"
1916         else:
1917             return " " + utilities.escape_ly_output_string(text) + " -- "
1918     elif continued:
1919         return "--"
1920     elif extended and text:
1921         return " " + utilities.escape_ly_output_string(text) + " __"
1922     elif extended:
1923         return "__"
1924     elif text:
1925         return " " + utilities.escape_ly_output_string(text)
1926     else:
1927         return ""
1928
1929 ## TODO
1930 class NegativeSkip:
1931     def __init__(self, here, dest):
1932         self.here = here
1933         self.dest = dest
1934
1935 class LilyPondVoiceBuilder:
1936     def __init__(self):
1937         self.elements = []
1938         self.pending_dynamics = []
1939         self.end_moment = Rational(0)
1940         self.begin_moment = Rational(0)
1941         self.pending_multibar = Rational(0)
1942         self.ignore_skips = False
1943         self.has_relevant_elements = False
1944         self.measure_length = Rational(4, 4)
1945         self.stay_here = False
1946
1947     def _insert_multibar(self):
1948         layout_information.set_context_item('Score', 'skipBars = ##t')
1949         r = musicexp.MultiMeasureRest()
1950         lenfrac = self.measure_length
1951         r.duration = rational_to_lily_duration(lenfrac)
1952         r.duration.factor *= self.pending_multibar / lenfrac
1953         self.elements.append(r)
1954         self.begin_moment = self.end_moment
1955         self.end_moment = self.begin_moment + self.pending_multibar
1956         self.pending_multibar = Rational(0)
1957
1958     def set_measure_length(self, mlen):
1959         if (mlen != self.measure_length) and self.pending_multibar:
1960             self._insert_multibar()
1961         self.measure_length = mlen
1962
1963     def add_multibar_rest(self, duration):
1964         self.pending_multibar += duration
1965
1966     def set_duration(self, duration):
1967         self.end_moment = self.begin_moment + duration
1968     def current_duration(self):
1969         return self.end_moment - self.begin_moment
1970
1971     def add_pending_dynamics(self):
1972         for d in self.pending_dynamics:
1973             self.elements.append(d)
1974         self.pending_dynamics = []
1975
1976     def add_music(self, music, duration, relevant=True):
1977         assert isinstance(music, musicexp.Music)
1978         if self.pending_multibar > Rational(0):
1979             self._insert_multibar()
1980
1981         self.has_relevant_elements = self.has_relevant_elements or relevant
1982
1983         if isinstance(music, musicexp.BarLine):
1984             if self.pending_dynamics:
1985                 for d in self.pending_dynamics:
1986                     if not isinstance(d, (musicexp.SpanEvent, musicexp.DynamicsEvent)):
1987                         index = self.pending_dynamics.index(d)
1988                         dyn = self.pending_dynamics.pop(index)
1989                         self.elements.append(dyn)
1990
1991         self.elements.append(music)
1992         self.begin_moment = self.end_moment
1993         self.set_duration(duration)
1994
1995         # Insert all pending dynamics right after the note/rest:
1996         if isinstance(music, musicexp.ChordEvent) and self.pending_dynamics:
1997             self.add_pending_dynamics()
1998
1999     # Insert some music command that does not affect the position in the measure
2000     def add_command(self, command, relevant=True):
2001         assert isinstance(command, musicexp.Music)
2002         if self.pending_multibar > Rational(0):
2003             self._insert_multibar()
2004         self.has_relevant_elements = self.has_relevant_elements or relevant
2005         self.elements.append(command)
2006     def add_barline(self, barline, relevant=False):
2007         # Insert only if we don't have a barline already
2008         # TODO: Implement proper merging of default barline and custom bar line
2009         has_relevant = self.has_relevant_elements
2010         if (not (self.elements) or
2011             not (isinstance (self.elements[-1], musicexp.BarLine)) or
2012             (self.pending_multibar > Rational(0))):
2013
2014             self.add_music(barline, Rational(0))
2015
2016         self.has_relevant_elements = has_relevant or relevant
2017
2018     def add_partial(self, command):
2019         self.ignore_skips = True
2020         # insert the partial, but restore relevant_elements (partial is not relevant)
2021         relevant = self.has_relevant_elements
2022         self.add_command(command)
2023         self.has_relevant_elements = relevant
2024
2025     def add_dynamics(self, dynamic):
2026         # store the dynamic item(s) until we encounter the next note/rest:
2027         self.pending_dynamics.append(dynamic)
2028
2029     def add_bar_check(self, number):
2030         # re/store has_relevant_elements, so that a barline alone does not
2031         # trigger output for figured bass, chord names
2032         b = musicexp.BarLine()
2033         b.bar_number = number
2034         self.add_barline(b)
2035
2036     def jumpto(self, moment):
2037         if (not self.stay_here):
2038             current_end = self.end_moment + self.pending_multibar
2039             diff = moment - current_end
2040
2041             if diff < Rational(0):
2042                 ly.warning(_('Negative skip %s (from position %s to %s)') %
2043                             (diff, current_end, moment))
2044                 diff = Rational(0)
2045
2046             if diff > Rational(0) and not(self.ignore_skips and moment == 0):
2047                 skip = musicexp.SkipEvent()
2048                 duration_factor = 1
2049                 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)
2050                 duration_dots = 0
2051                 # TODO: Use the time signature for skips, too. Problem: The skip
2052                 #       might not start at a measure boundary!
2053                 if duration_log > 0: # denominator is a power of 2...
2054                     if diff.numerator() == 3:
2055                         duration_log -= 1
2056                         duration_dots = 1
2057                     else:
2058                         duration_factor = Rational(diff.numerator())
2059                 else:
2060                     # for skips of a whole or more, simply use s1*factor
2061                     duration_log = 0
2062                     duration_factor = diff
2063                 skip.duration.duration_log = duration_log
2064                 skip.duration.factor = duration_factor
2065                 skip.duration.dots = duration_dots
2066
2067                 evc = musicexp.ChordEvent()
2068                 evc.elements.append(skip)
2069                 self.add_music(evc, diff, False)
2070
2071             if diff > Rational(0) and moment == 0:
2072                 self.ignore_skips = False
2073
2074     def last_event_chord(self, starting_at):
2075         value = None
2076
2077         # if the position matches, find the last ChordEvent, do not cross a bar line!
2078         at = len(self.elements) - 1
2079         while (at >= 0 and
2080                not isinstance(self.elements[at], musicexp.ChordEvent) and
2081                not isinstance(self.elements[at], musicexp.BarLine)):
2082             at -= 1
2083
2084         if (self.elements
2085             and at >= 0
2086             and isinstance(self.elements[at], musicexp.ChordEvent)
2087             and self.begin_moment == starting_at):
2088             value = self.elements[at]
2089         else:
2090             self.jumpto(starting_at)
2091             value = None
2092         return value
2093
2094     def correct_negative_skip(self, goto):
2095         self.end_moment = goto
2096         self.begin_moment = goto
2097         evc = musicexp.ChordEvent()
2098         self.elements.append(evc)
2099
2100 class VoiceData:
2101     def __init__(self):
2102         self.voicename = None
2103         self.voicedata = None
2104         self.ly_voice = None
2105         self.figured_bass = None
2106         self.chordnames = None
2107         self.fretboards = None
2108         self.lyrics_dict = {}
2109         self.lyrics_order = []
2110
2111 def measure_length_from_attributes(attr, current_measure_length):
2112     len = attr.get_measure_length()
2113     if not len:
2114         len = current_measure_length
2115     return len
2116
2117 def music_xml_voice_name_to_lily_name(part_id, name):
2118     str = "Part%sVoice%s" % (part_id, name)
2119     return musicxml_id_to_lily(str)
2120
2121 def music_xml_lyrics_name_to_lily_name(part_id, name, lyricsnr):
2122     str = music_xml_voice_name_to_lily_name(part_id, name)+("Lyrics%s" % lyricsnr)
2123     return musicxml_id_to_lily(str)
2124
2125 def music_xml_figuredbass_name_to_lily_name(part_id, voicename):
2126     str = music_xml_voice_name_to_lily_name(part_id, voicename)+"FiguredBass"
2127     return musicxml_id_to_lily(str)
2128
2129 def music_xml_chordnames_name_to_lily_name(part_id, voicename):
2130     str = music_xml_voice_name_to_lily_name(part_id, voicename)+"Chords"
2131     return musicxml_id_to_lily(str)
2132
2133 def music_xml_fretboards_name_to_lily_name(part_id, voicename):
2134     str = music_xml_voice_name_to_lily_name(part_id, voicename)+"FretBoards"
2135     return musicxml_id_to_lily(str)
2136
2137 def get_all_lyric_parts_in_voice(voice):
2138     '''
2139     Collect the indexes of all lyric parts in this voice.
2140     In case not all of the current lyric parts are active (a typical case would be
2141     a refrain/chorus), the current implementation inserts \skip-commands in the
2142     inactive parts to keep them in sync.
2143     '''
2144     all_lyric_parts = []
2145     for elem in voice._elements:
2146         lyrics = elem.get_typed_children(musicxml.Lyric)
2147         if lyrics:
2148             for lyric in lyrics:
2149                 index = lyric.number
2150                 if not index in all_lyric_parts:
2151                     all_lyric_parts.append(index)
2152     return all_lyric_parts
2153
2154
2155 def extract_lyrics(voice, lyric_key, lyrics_dict):
2156     curr_number = None
2157     result = []
2158
2159     def is_note(elem):
2160         return isinstance(elem, musicxml.Note)
2161
2162     def is_rest(elem):
2163         return elem.get_typed_children(musicxml.Rest)
2164
2165     def is_note_and_not_rest(elem):
2166         return is_note(elem) and not is_rest(elem)
2167
2168     def get_lyric_elements(note):
2169         return note.get_typed_children(musicxml.Lyric)
2170
2171     def has_lyric_belonging_to_lyric_part(note, lyric_part_id):
2172         lyric_elements = get_lyric_elements(note)
2173         lyric_numbers = [lyric.number for lyric in lyric_elements]
2174         return any([lyric_number == lyric_part_id for lyric_number in lyric_numbers])
2175
2176     for idx, elem in enumerate(voice._elements):
2177         lyrics = get_lyric_elements(elem)
2178         lyric_keys = [lyric.number for lyric in lyrics]
2179         note_has_lyric_belonging_to_lyric_part = lyric_key in lyric_keys
2180         # Current note has lyric with 'number' matching 'lyric_key'.
2181         if note_has_lyric_belonging_to_lyric_part:
2182             for lyric in lyrics:
2183                 if lyric.number == lyric_key:
2184                     text = musicxml_lyrics_to_text(lyric, None)
2185                     result.append(text)
2186         # Note has any lyric.
2187         elif get_lyric_elements(elem) and \
2188              not note_has_lyric_belonging_to_lyric_part:
2189             result.append('\skip1 ')
2190         # Note does not have any lyric attached to it.
2191         elif is_note_and_not_rest(elem):
2192             result.append('\skip1 ')
2193
2194     lyrics_dict[lyric_key].extend(result)
2195
2196 def musicxml_voice_to_lily_voice(voice):
2197     tuplet_events = []
2198     modes_found = {}
2199     lyrics = {}
2200     return_value = VoiceData()
2201     return_value.voicedata = voice
2202
2203     # First pitch needed for relative mode (if selected in command-line options)
2204     first_pitch = None
2205
2206     # Needed for melismata detection (ignore lyrics on those notes!):
2207     inside_slur = False
2208     is_tied = False
2209     is_chord = False
2210     is_beamed = False
2211     ignore_lyrics = False
2212
2213     current_staff = None
2214
2215     pending_figured_bass = []
2216     pending_chordnames = []
2217     pending_fretboards = []
2218
2219     # Make sure that the keys in the dict don't get reordered, since
2220     # we need the correct ordering of the lyrics stanzas! By default,
2221     # a dict will reorder its keys
2222     return_value.lyrics_order = voice.get_lyrics_numbers()
2223     for k in return_value.lyrics_order:
2224         lyrics[k] = []
2225
2226     voice_builder = LilyPondVoiceBuilder()
2227     figured_bass_builder = LilyPondVoiceBuilder()
2228     chordnames_builder = LilyPondVoiceBuilder()
2229     fretboards_builder = LilyPondVoiceBuilder()
2230     current_measure_length = Rational(4, 4)
2231     voice_builder.set_measure_length(current_measure_length)
2232     in_slur = False
2233
2234     all_lyric_parts = set(get_all_lyric_parts_in_voice(voice))
2235     if lyrics.keys():
2236         for number in lyrics.keys():
2237             extracted_lyrics = extract_lyrics(voice, number, lyrics)
2238
2239     for idx, n in enumerate(voice._elements):
2240         tie_started = False
2241         if n.get_name() == 'forward':
2242             continue
2243         staff = n.get_maybe_exist_named_child('staff')
2244         if staff:
2245             staff = staff.get_text()
2246             if current_staff and staff <> current_staff and not n.get_maybe_exist_named_child('chord'):
2247                 voice_builder.add_command(musicexp.StaffChange(staff))
2248             current_staff = staff
2249
2250         if isinstance(n, musicxml.Partial) and n.partial > 0:
2251             a = musicxml_partial_to_lily(n.partial)
2252             if a:
2253                 voice_builder.add_partial(a)
2254                 figured_bass_builder.add_partial(a)
2255                 chordnames_builder.add_partial(a)
2256                 fretboards_builder.add_partial(a)
2257             continue
2258
2259         is_chord = n.get_maybe_exist_named_child('chord')
2260         is_after_grace =(isinstance(n, musicxml.Note) and n.is_after_grace());
2261         if not is_chord and not is_after_grace:
2262             try:
2263                 voice_builder.jumpto(n._when)
2264                 figured_bass_builder.jumpto(n._when)
2265                 chordnames_builder.jumpto(n._when)
2266                 fretboards_builder.jumpto(n._when)
2267             except NegativeSkip, neg:
2268                 voice_builder.correct_negative_skip(n._when)
2269                 figured_bass_builder.correct_negative_skip(n._when)
2270                 chordnames_builder.correct_negative_skip(n._when)
2271                 fretboards_builder.correct_negative_skip(n._when)
2272                 n.message(_("Negative skip found: from %s to %s, difference is %s") % (neg.here, neg.dest, neg.dest - neg.here))
2273
2274         if isinstance(n, musicxml.Barline):
2275             barlines = n.to_lily_object()
2276             for a in barlines:
2277                 if isinstance(a, musicexp.BarLine):
2278                     voice_builder.add_barline(a)
2279                     figured_bass_builder.add_barline(a, False)
2280                     chordnames_builder.add_barline(a, False)
2281                     fretboards_builder.add_barline(a, False)
2282                 elif isinstance(a, RepeatMarker) or isinstance(a, EndingMarker):
2283                     voice_builder.add_command(a)
2284                     figured_bass_builder.add_barline(a, False)
2285                     chordnames_builder.add_barline(a, False)
2286                     fretboards_builder.add_barline(a, False)
2287             continue
2288
2289         if isinstance(n, musicxml.Print):
2290             for a in musicxml_print_to_lily(n):
2291                 voice_builder.add_command(a, False)
2292             continue
2293
2294         # Continue any multimeasure-rests before trying to add bar checks!
2295         # Don't handle new MM rests yet, because for them we want bar checks!
2296         rest = n.get_maybe_exist_typed_child(musicxml.Rest)
2297         if (rest and rest.is_whole_measure()
2298                  and voice_builder.pending_multibar > Rational(0)):
2299             voice_builder.add_multibar_rest(n._duration)
2300             continue
2301
2302         # print a bar check at the beginning of each measure!
2303         if n.is_first() and n._measure_position == Rational(0) and n != voice._elements[0]:
2304             try:
2305                 num = int(n.get_parent().number)
2306             except ValueError:
2307                 num = 0
2308             if num > 0:
2309                 voice_builder.add_bar_check(num)
2310                 figured_bass_builder.add_bar_check(num)
2311                 chordnames_builder.add_bar_check(num)
2312                 fretboards_builder.add_bar_check(num)
2313
2314         if isinstance(n, musicxml.Direction):
2315             # check if Direction already has been converted in another voice.
2316             if n.converted:
2317                 continue
2318             else:
2319                 n.converted = True
2320                 for direction in musicxml_direction_to_lily(n):
2321                     if direction.wait_for_note():
2322                         voice_builder.add_dynamics(direction)
2323                     else:
2324                         voice_builder.add_command(direction)
2325                 continue
2326
2327         # Start any new multimeasure rests
2328         if (rest and rest.is_whole_measure()):
2329             if pending_chordnames:
2330                 chordnames_builder.jumpto(n._when)
2331                 chordnames_builder.stay_here = True
2332             if pending_figured_bass:
2333                 figured_bass_builder.jumpto(n._when)
2334                 figured_bass_builder.stay_here = True
2335             if pending_fretboards:
2336                 fretboards_builder.jumpto(n._when)
2337                 fretboards_builder.stay_here = True
2338             voice_builder.add_multibar_rest(n._duration)
2339             continue
2340
2341         if isinstance(n, musicxml.Harmony):
2342             if (options.fretboards):
2343                 # Makes fretboard diagrams in a separate FretBoards voice
2344                 for a in musicxml_harmony_to_lily_fretboards(n):
2345                     pending_fretboards.append(a)
2346             else:
2347                 # Makes markup fretboard-diagrams inside the voice
2348                 for a in musicxml_harmony_to_lily(n):
2349                     if a.wait_for_note():
2350                         voice_builder.add_dynamics(a)
2351                     else:
2352                         voice_builder.add_command(a)
2353             for a in musicxml_harmony_to_lily_chordname(n):
2354                 pending_chordnames.append(a)
2355             continue
2356
2357         if isinstance(n, musicxml.FiguredBass):
2358             a = musicxml_figured_bass_to_lily(n)
2359             if a:
2360                 pending_figured_bass.append(a)
2361             continue
2362
2363         if isinstance(n, musicxml.Attributes):
2364             for a in musicxml_attributes_to_lily(n):
2365                 voice_builder.add_command(a)
2366             measure_length = measure_length_from_attributes(n, current_measure_length)
2367             if current_measure_length != measure_length:
2368                 current_measure_length = measure_length
2369                 voice_builder.set_measure_length(current_measure_length)
2370             continue
2371
2372         if not n.__class__.__name__ == 'Note':
2373             n.message(_('unexpected %s; expected %s or %s or %s') % (n, 'Note', 'Attributes', 'Barline'))
2374             continue
2375
2376 #        if not hasattr(conversion_settings, 'convert_rest_positions'):
2377 #            conversion_settings.convert_rest_positions = True
2378
2379         main_event = n.to_lily_object(
2380             convert_stem_directions=conversion_settings.convert_stem_directions,
2381             convert_rest_positions=conversion_settings.convert_rest_positions)
2382
2383         if main_event and not first_pitch:
2384             first_pitch = main_event.pitch
2385         # ignore lyrics for notes inside a slur, tie, chord or beam
2386         ignore_lyrics = is_tied or is_chord #or is_beamed or inside_slur
2387
2388         if main_event and hasattr(main_event, 'drum_type') and main_event.drum_type:
2389             modes_found['drummode'] = True
2390
2391         ev_chord = voice_builder.last_event_chord(n._when)
2392         if not ev_chord:
2393             ev_chord = musicexp.ChordEvent()
2394             voice_builder.add_music(ev_chord, n._duration)
2395
2396         # For grace notes:
2397         grace = n.get_maybe_exist_typed_child(musicxml.Grace)
2398         if n.is_grace():
2399             is_after_grace = ev_chord.has_elements() or n.is_after_grace();
2400             is_chord = n.get_maybe_exist_typed_child(musicxml.Chord)
2401
2402             grace_chord = None
2403
2404             # after-graces and other graces use different lists; Depending on
2405             # whether we have a chord or not, obtain either a new ChordEvent or
2406             # the previous one to create a chord
2407             if is_after_grace:
2408                 if ev_chord.after_grace_elements and n.get_maybe_exist_typed_child(musicxml.Chord):
2409                     grace_chord = ev_chord.after_grace_elements.get_last_event_chord()
2410                 if not grace_chord:
2411                     grace_chord = musicexp.ChordEvent()
2412                     ev_chord.append_after_grace(grace_chord)
2413             elif n.is_grace():
2414                 if ev_chord.grace_elements and n.get_maybe_exist_typed_child(musicxml.Chord):
2415                     grace_chord = ev_chord.grace_elements.get_last_event_chord()
2416                 if not grace_chord:
2417                     grace_chord = musicexp.ChordEvent()
2418                     ev_chord.append_grace(grace_chord)
2419
2420             if hasattr(grace, 'slash') and not is_after_grace:
2421                 # TODO: use grace_type = "appoggiatura" for slurred grace notes
2422                 if grace.slash == "yes":
2423                     ev_chord.grace_type = "acciaccatura"
2424             # now that we have inserted the chord into the grace music, insert
2425             # everything into that chord instead of the ev_chord
2426             ev_chord = grace_chord
2427             ev_chord.append(main_event)
2428             ignore_lyrics = True
2429         else:
2430             ev_chord.append(main_event)
2431             # When a note/chord has grace notes (duration==0), the duration of the
2432             # event chord is not yet known, but the event chord was already added
2433             # with duration 0. The following correct this when we hit the real note!
2434             if voice_builder.current_duration() == 0 and n._duration > 0:
2435                 voice_builder.set_duration(n._duration)
2436
2437         # if we have a figured bass, set its voice builder to the correct position
2438         # and insert the pending figures
2439         if pending_figured_bass:
2440             try:
2441                 figured_bass_builder.jumpto(n._when)
2442                 if (figured_bass_builder.stay_here):
2443                     figured_bass_builder.stay_here = False
2444             except NegativeSkip, neg:
2445                 pass
2446             for fb in pending_figured_bass:
2447                 # if a duration is given, use that, otherwise the one of the note
2448                 dur = fb.real_duration
2449                 if not dur:
2450                     dur = ev_chord.get_length()
2451                 if not fb.duration:
2452                     fb.duration = ev_chord.get_duration()
2453                 figured_bass_builder.add_music(fb, dur)
2454             pending_figured_bass = []
2455
2456         if pending_chordnames:
2457             try:
2458                 chordnames_builder.jumpto(n._when)
2459                 if (chordnames_builder.stay_here):
2460                     chordnames_builder.stay_here = False
2461             except NegativeSkip, neg:
2462                 pass
2463             for cn in pending_chordnames:
2464                 # Assign the duration of the EventChord
2465                 cn.duration = ev_chord.get_duration()
2466                 chordnames_builder.add_music(cn, ev_chord.get_length())
2467             pending_chordnames = []
2468
2469         if pending_fretboards:
2470             try:
2471                 fretboards_builder.jumpto(n._when)
2472                 if (fretboards_builder.stay_here):
2473                     fretboards_builder.stay_here = False
2474             except NegativeSkip, neg:
2475                 pass
2476             for fb in pending_fretboards:
2477                 # Assign the duration of the EventChord
2478                 fb.duration = ev_chord.get_duration()
2479                 fretboards_builder.add_music(fb, ev_chord.get_length())
2480             pending_fretboards = []
2481
2482         notations_children = n.get_typed_children(musicxml.Notations)
2483         tuplet_event = None
2484         span_events = []
2485
2486         # The <notation> element can have the following children (+ means implemented, ~ partially, - not):
2487         # +tied | +slur | +tuplet | glissando | slide |
2488         #    ornaments | technical | articulations | dynamics |
2489         #    +fermata | arpeggiate | non-arpeggiate |
2490         #    accidental-mark | other-notation
2491         for notations in notations_children:
2492             for tuplet_event in notations.get_tuplets():
2493                 time_mod = n.get_maybe_exist_typed_child(musicxml.Time_modification)
2494                 tuplet_events.append((ev_chord, tuplet_event, time_mod))
2495
2496             # First, close all open slurs, only then start any new slur
2497             # TODO: Record the number of the open slur to dtermine the correct
2498             #       closing slur!
2499             endslurs = [s for s in notations.get_named_children('slur')
2500                 if s.get_type() in ('stop')]
2501             if endslurs and not inside_slur:
2502                 endslurs[0].message(_('Encountered closing slur, but no slur is open'))
2503             elif endslurs:
2504                 if len(endslurs) > 1:
2505                     endslurs[0].message(_('Cannot have two simultaneous (closing) slurs'))
2506                 # record the slur status for the next note in the loop
2507                 inside_slur = False
2508                 lily_ev = musicxml_spanner_to_lily_event(endslurs[0])
2509                 ev_chord.append(lily_ev)
2510
2511             startslurs = [s for s in notations.get_named_children('slur')
2512                 if s.get_type() in('start')]
2513             if startslurs and inside_slur:
2514                 startslurs[0].message(_('Cannot have a slur inside another slur'))
2515             elif startslurs:
2516                 if len(startslurs) > 1:
2517                     startslurs[0].message(_('Cannot have two simultaneous slurs'))
2518                 # record the slur status for the next note in the loop
2519                 inside_slur = True
2520                 lily_ev = musicxml_spanner_to_lily_event(startslurs[0])
2521                 ev_chord.append(lily_ev)
2522
2523
2524             if not grace:
2525                 mxl_tie = notations.get_tie()
2526                 if mxl_tie and mxl_tie.type == 'start':
2527                     ev_chord.append(musicexp.TieEvent())
2528                     is_tied = True
2529                     tie_started = True
2530                 else:
2531                     is_tied = False
2532
2533             fermatas = notations.get_named_children('fermata')
2534             for a in fermatas:
2535                 ev = musicxml_fermata_to_lily_event(a)
2536                 if ev:
2537                     ev_chord.append(ev)
2538
2539             arpeggiate = notations.get_named_children('arpeggiate')
2540             for a in arpeggiate:
2541                 ev = musicxml_arpeggiate_to_lily_event(a)
2542                 if ev:
2543                     ev_chord.append(ev)
2544
2545             arpeggiate = notations.get_named_children('non-arpeggiate')
2546             for a in arpeggiate:
2547                 ev = musicxml_nonarpeggiate_to_lily_event(a)
2548                 if ev:
2549                     ev_chord.append(ev)
2550
2551             glissandos = notations.get_named_children('glissando')
2552             glissandos += notations.get_named_children('slide')
2553             for a in glissandos:
2554                 ev = musicxml_spanner_to_lily_event(a)
2555                 if ev:
2556                     ev_chord.append(ev)
2557
2558             # accidental-marks are direct children of <notation>!
2559             for a in notations.get_named_children('accidental-mark'):
2560                 ev = musicxml_articulation_to_lily_event(a)
2561                 if ev:
2562                     ev_chord.append(ev)
2563
2564             # Articulations can contain the following child elements:
2565             #         accent | strong-accent | staccato | tenuto |
2566             #         detached-legato | staccatissimo | spiccato |
2567             #         scoop | plop | doit | falloff | breath-mark |
2568             #         caesura | stress | unstress
2569             # Technical can contain the following child elements:
2570             #         up-bow | down-bow | harmonic | open-string |
2571             #         thumb-position | fingering | pluck | double-tongue |
2572             #         triple-tongue | stopped | snap-pizzicato | fret |
2573             #         string | hammer-on | pull-off | bend | tap | heel |
2574             #         toe | fingernails | other-technical
2575             # Ornaments can contain the following child elements:
2576             #         trill-mark | turn | delayed-turn | inverted-turn |
2577             #         shake | wavy-line | mordent | inverted-mordent |
2578             #         schleifer | tremolo | other-ornament, accidental-mark
2579             ornaments = notations.get_named_children('ornaments')
2580             ornaments += notations.get_named_children('articulations')
2581             ornaments += notations.get_named_children('technical')
2582
2583             for a in ornaments:
2584                 for ch in a.get_all_children():
2585                     ev = musicxml_articulation_to_lily_event(ch)
2586                     if ev:
2587                         ev_chord.append(ev)
2588
2589             dynamics = notations.get_named_children('dynamics')
2590             for a in dynamics:
2591                 for ch in a.get_all_children():
2592                     ev = musicxml_dynamics_to_lily_event(ch)
2593                     if ev:
2594                         ev_chord.append(ev)
2595
2596
2597         mxl_beams = [b for b in n.get_named_children('beam')
2598                      if (b.get_type() in('begin', 'end')
2599                          and b.is_primary())]
2600         if mxl_beams and not conversion_settings.ignore_beaming:
2601             beam_ev = musicxml_spanner_to_lily_event(mxl_beams[0])
2602             if beam_ev:
2603                 ev_chord.append(beam_ev)
2604                 if beam_ev.span_direction == -1: # beam and thus melisma starts here
2605                     is_beamed = True
2606                 elif beam_ev.span_direction == 1: # beam and thus melisma ends here
2607                     is_beamed = False
2608
2609         # Assume that a <tie> element only lasts for one note.
2610         # This might not be correct MusicXML interpretation, but works for
2611         # most cases and fixes broken files, which have the end tag missing
2612         if is_tied and not tie_started:
2613             is_tied = False
2614
2615     # force trailing mm rests to be written out.
2616     # voice_builder.add_music (musicexp.ChordEvent(), Rational(0))
2617     if hasattr(options, 'shift_meter') and options.shift_meter:
2618         for event in voice_builder.elements:
2619             if isinstance(event, musicexp.TimeSignatureChange):
2620                 sd = []
2621                 for i in range(0,5):
2622                     sd.append(musicexp.ShiftDurations())
2623                     sd[i].set_shift_durations_parameters(event)
2624                 break;
2625
2626     ly_voice = group_tuplets(voice_builder.elements, tuplet_events)
2627     ly_voice = group_repeats(ly_voice)
2628
2629     seq_music = musicexp.SequentialMusic()
2630
2631     if 'drummode' in modes_found.keys():
2632         ## \key <pitch> barfs in drummode.
2633         ly_voice = [e for e in ly_voice
2634                     if not isinstance(e, musicexp.KeySignatureChange)]
2635
2636     seq_music.elements = ly_voice
2637     for k in lyrics.keys():
2638         return_value.lyrics_dict[k] = musicexp.Lyrics()
2639         return_value.lyrics_dict[k].lyrics_syllables = lyrics[k]
2640
2641
2642     if len(modes_found) > 1:
2643        ly.warning(_('cannot simultaneously have more than one mode: %s') % modes_found.keys())
2644
2645     if hasattr(options, 'shift_meter') and options.shift_meter:
2646         sd[-1].element = seq_music
2647         seq_music = sd[-1]
2648         sd.pop()
2649
2650     if hasattr(options, 'relative') and options.relative:
2651         v = musicexp.RelativeMusic()
2652         v.element = seq_music
2653         v.basepitch = first_pitch
2654         seq_music = v
2655
2656     return_value.ly_voice = seq_music
2657     for mode in modes_found.keys():
2658         v = musicexp.ModeChangingMusicWrapper()
2659         v.element = seq_music
2660         v.mode = mode
2661         return_value.ly_voice = v
2662
2663     # create \figuremode { figured bass elements }
2664     if figured_bass_builder.has_relevant_elements:
2665         fbass_music = musicexp.SequentialMusic()
2666         fbass_music.elements = group_repeats(figured_bass_builder.elements)
2667         v = musicexp.ModeChangingMusicWrapper()
2668         v.mode = 'figuremode'
2669         v.element = fbass_music
2670         if hasattr(options, 'shift_meter') and options.shift_meter:
2671             sd[-1].element = v
2672             v = sd[-1]
2673             sd.pop()
2674         return_value.figured_bass = v
2675
2676     # create \chordmode { chords }
2677     if chordnames_builder.has_relevant_elements:
2678         cname_music = musicexp.SequentialMusic()
2679         cname_music.elements = group_repeats(chordnames_builder.elements)
2680         v = musicexp.ModeChangingMusicWrapper()
2681         v.mode = 'chordmode'
2682         v.element = cname_music
2683         if hasattr(options, 'shift_meter') and options.shift_meter:
2684             sd[-1].element = v
2685             v = sd[-1]
2686             sd.pop()
2687         return_value.chordnames = v
2688
2689     # create diagrams for FretBoards engraver
2690     if fretboards_builder.has_relevant_elements:
2691         fboard_music = musicexp.SequentialMusic()
2692         fboard_music.elements = group_repeats(fretboards_builder.elements)
2693         v = musicexp.MusicWrapper()
2694         v.element = fboard_music
2695         if hasattr(options, 'shift_meter') and options.shift_meter:
2696             sd[-1].element = v
2697             v = sd[-1]
2698             sd.pop()
2699         return_value.fretboards = v
2700
2701     # coll = []
2702     # pending = []
2703
2704     # for elt in return_value.ly_voice.element.elements:
2705     #     if isinstance(elt, musicexp.TimeScaledMusic):
2706     #         print elt.element.elements
2707     #         pending.append(elt)
2708     #     else:
2709     #         coll.append(elt)
2710
2711     # if pending:
2712     #     coll.extend(pending)
2713
2714     # return_value.ly_voice.element.elements = coll
2715
2716     return return_value
2717
2718 def musicxml_id_to_lily(id):
2719     digits = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five',
2720               'Six', 'Seven', 'Eight', 'Nine', 'Ten']
2721
2722     for digit in digits:
2723         d = digits.index(digit)
2724         id = re.sub('%d' % d, digit, id)
2725
2726     id = re.sub('[^a-zA-Z]', 'X', id)
2727     return id
2728
2729 def voices_in_part(part):
2730     """Return a Name -> Voice dictionary for PART"""
2731     part.interpret()
2732     part.extract_voices()
2733     voices = part.get_voices()
2734     part_info = part.get_staff_attributes()
2735
2736     return (voices, part_info)
2737
2738 def voices_in_part_in_parts(parts):
2739     """return a Part -> Name -> Voice dictionary"""
2740     # don't crash if Part doesn't have an id (that's invalid MusicXML,
2741     # but such files are out in the wild!)
2742     dictionary = {}
2743     for p in parts:
2744         voices = voices_in_part(p)
2745         if (hasattr(p, "id")):
2746              dictionary[p.id] = voices
2747         else:
2748              # TODO: extract correct part id from other sources
2749              dictionary[None] = voices
2750     return dictionary;
2751
2752
2753 def get_all_voices(parts):
2754     all_voices = voices_in_part_in_parts(parts)
2755
2756     all_ly_voices = {}
2757     all_ly_staffinfo = {}
2758     for p, (name_voice, staff_info) in all_voices.items():
2759
2760         part_ly_voices = {}
2761         for n, v in name_voice.items():
2762             ly.progress(_("Converting to LilyPond expressions..."), True)
2763             # musicxml_voice_to_lily_voice returns (lily_voice, {nr->lyrics, nr->lyrics})
2764             voice = musicxml_voice_to_lily_voice(v)
2765             part_ly_voices[n] = voice
2766
2767         all_ly_voices[p] = part_ly_voices
2768         all_ly_staffinfo[p] = staff_info
2769
2770     return (all_ly_voices, all_ly_staffinfo)
2771
2772
2773 def option_parser():
2774     p = ly.get_option_parser(usage=_("musicxml2ly [OPTION]... FILE.xml"),
2775                              description=
2776 _("""Convert MusicXML from FILE.xml to LilyPond input.
2777 If the given filename is -, musicxml2ly reads from the command line.
2778 """), add_help_option=False)
2779
2780     p.add_option("-h", "--help",
2781                  action="help",
2782                  help=_("show this help and exit"))
2783
2784     p.version = ('%prog (LilyPond) ' + lilypond_version + '\n\n'
2785 +
2786 _ ("""Copyright (c) 2005--2015 by
2787     Han-Wen Nienhuys <hanwen@xs4all.nl>,
2788     Jan Nieuwenhuizen <janneke@gnu.org> and
2789     Reinhold Kainhofer <reinhold@kainhofer.com>
2790     Patrick L. Schmidt <pls@philomelos.net>
2791 """
2792 +
2793 """
2794 This program is free software.  It is covered by the GNU General Public
2795 License and you are welcome to change it and/or distribute copies of it
2796 under certain conditions.  Invoke as `%s --warranty' for more
2797 information.""") % 'lilypond')
2798
2799     p.add_option("--version",
2800                  action="version",
2801                  help=_ ("show version number and exit"))
2802
2803     p.add_option('-v', '--verbose',
2804                   action="callback",
2805                   callback=ly.handle_loglevel_option,
2806                   callback_args=("DEBUG",),
2807                   help=_ ("be verbose"))
2808
2809     p.add_option('', '--lxml',
2810                   action="store_true",
2811                   default=False,
2812                   dest="use_lxml",
2813                   help=_ ("use lxml.etree; uses less memory and cpu time"))
2814
2815     p.add_option('-z', '--compressed',
2816                   action="store_true",
2817                   dest='compressed',
2818                   default=False,
2819                   help=_ ("input file is a zip-compressed MusicXML file"))
2820
2821     p.add_option('-r', '--relative',
2822                   action="store_true",
2823                   default=True,
2824                   dest="relative",
2825                   help=_ ("convert pitches in relative mode (default)"))
2826
2827     p.add_option('-a', '--absolute',
2828                   action="store_false",
2829                   dest="relative",
2830                   help=_ ("convert pitches in absolute mode"))
2831
2832     p.add_option('-l', '--language',
2833                   metavar=_ ("LANG"),
2834                   action="store",
2835                   help=_ ("use LANG for pitch names, e.g. 'deutsch' for note names in German"))
2836
2837     p.add_option("--loglevel",
2838                   help=_ ("Print log messages according to LOGLEVEL "
2839                           "(NONE, ERROR, WARNING, PROGRESS (default), DEBUG)"),
2840                   metavar=_ ("LOGLEVEL"),
2841                   action='callback',
2842                   callback=ly.handle_loglevel_option,
2843                   type='string')
2844
2845     p.add_option('--nd', '--no-articulation-directions',
2846                   action="store_false",
2847                   default=True,
2848                   dest="convert_directions",
2849                   help=_ ("do not convert directions (^, _ or -) for articulations, dynamics, etc."))
2850
2851     p.add_option('--nrp', '--no-rest-positions',
2852                   action="store_false",
2853                   default=True,
2854                   dest="convert_rest_positions",
2855                   help=_ ("do not convert exact vertical positions of rests"))
2856
2857     p.add_option('--nsb', '--no-system-breaks',
2858                   action="store_false",
2859                   default=True,
2860                   dest="convert_system_breaks",
2861                   help=_ ("ignore system breaks"))
2862
2863     p.add_option('--npb', '--no-page-breaks',
2864                   action="store_false",
2865                   default=True,
2866                   dest="convert_page_breaks",
2867                   help=_ ("ignore page breaks"))
2868
2869     p.add_option('--npm', '--no-page-margins',
2870                   action="store_false",
2871                   default=True,
2872                   dest="convert_page_margins",
2873                   help=_ ("ignore page margins"))
2874
2875     p.add_option('--npl', '--no-page-layout',
2876                   action="store_false",
2877                   default=True,
2878                   dest="convert_page_layout",
2879                   help=_ ("do not convert the exact page layout and breaks (shortcut for \"--nsb --npb --npm\" options)"))
2880
2881     p.add_option('--nsd', '--no-stem-directions',
2882                   action="store_false",
2883                   default=True,
2884                   dest="convert_stem_directions",
2885                   help=_ ("ignore stem directions from MusicXML, use lilypond's automatic stemming instead"))
2886
2887     p.add_option('--nb', '--no-beaming',
2888                   action="store_false",
2889                   default=True,
2890                   dest="convert_beaming",
2891                   help=_ ("do not convert beaming information, use lilypond's automatic beaming instead"))
2892
2893     p.add_option('-o', '--output',
2894                   metavar=_ ("FILE"),
2895                   action="store",
2896                   default=None,
2897                   type='string',
2898                   dest='output_name',
2899                   help=_ ("set output filename to FILE, stdout if -"))
2900
2901     p.add_option('-m', '--midi',
2902                   action="store_true",
2903                   default=False,
2904                   dest="midi",
2905                   help=_("activate midi-block in .ly file"))
2906
2907     # transpose function
2908     p.add_option('--transpose',
2909                   metavar=_ ("TOPITCH"),
2910                   action="store",
2911                   dest="transpose",
2912                   help=_ ("set pitch to transpose by the interval between pitch 'c' and TOPITCH"))
2913
2914     # time signature changing function
2915     p.add_option('--sm', '--shift-meter',
2916                   metavar=_ ("BEATS/BEATTYPE"),
2917                   action="store",
2918                   dest="shift_meter",
2919                   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')"))
2920
2921     # switch tabstaff clef
2922     p.add_option('--tc', '--tab-clef',
2923                   metavar=_ ("TABCLEFNAME"),
2924                   action="store",
2925                   dest="tab_clef",
2926                   help=_ ("switch between two versions of tab clefs (\"tab\" and \"moderntab\")"))
2927
2928     # StringNumber stencil on/off
2929     p.add_option('--sn', '--string-numbers',
2930                   metavar=_ ("t[rue]/f[alse]"),
2931                   action="store",
2932                   dest="string_numbers",
2933                   help=_ ("deactivate string number stencil with --string-numbers f[alse]. Default is t[rue]"))
2934
2935     # StringNumber stencil on/off
2936     p.add_option('--fb', '--fretboards',
2937                   action="store_true",
2938                   default=False,
2939                   dest="fretboards",
2940                   help=_("converts '<frame>' events to a separate FretBoards voice instead of markups"))
2941
2942     p.add_option_group('',
2943                         description=(
2944             _ ("Report bugs via %s")
2945             % 'http://post.gmane.org/post.php'
2946             '?group=gmane.comp.gnu.lilypond.bugs') + '\n')
2947     return p
2948
2949 def print_voice_definitions (printer, part_list, voices):
2950     for part in part_list:
2951         part_id = part.id
2952         nv_dict = voices.get (part_id, {})
2953         for (name, voice) in nv_dict.items ():
2954             k = music_xml_voice_name_to_lily_name (part_id, name)
2955             printer.dump ('%s = ' % k)
2956             voice.ly_voice.print_ly (printer)
2957             printer.newline()
2958             if voice.chordnames:
2959                 cnname = music_xml_chordnames_name_to_lily_name (part_id, name)
2960                 printer.dump ('%s = ' % cnname)
2961                 voice.chordnames.print_ly (printer)
2962                 printer.newline()
2963             for l in voice.lyrics_order:
2964                 lname = music_xml_lyrics_name_to_lily_name (part_id, name, l)
2965                 printer.dump ('%s = ' % lname)
2966                 voice.lyrics_dict[l].print_ly (printer)
2967                 printer.newline()
2968             if voice.figured_bass:
2969                 fbname = music_xml_figuredbass_name_to_lily_name (part_id, name)
2970                 printer.dump ('%s = ' % fbname)
2971                 voice.figured_bass.print_ly (printer)
2972                 printer.newline()
2973             if voice.fretboards:
2974                 fbdname = music_xml_fretboards_name_to_lily_name (part_id, name)
2975                 printer.dump ('%s = ' % fbdname)
2976                 voice.fretboards.print_ly (printer)
2977                 printer.newline()
2978
2979 def uniq_list(l):
2980     return dict([(elt, 1) for elt in l]).keys()
2981
2982 # format the information about the staff in the form
2983 #     [staffid,
2984 #         [
2985 #            [voiceid1, [lyricsid11, lyricsid12,...], figuredbassid1],
2986 #            [voiceid2, [lyricsid21, lyricsid22,...], figuredbassid2],
2987 #            ...
2988 #         ]
2989 #     ]
2990 # raw_voices is of the form [(voicename, lyricsids, havefiguredbass)*]
2991 def format_staff_info(part_id, staff_id, raw_voices):
2992     voices = []
2993     for (v, lyricsids, figured_bass, chordnames, fretboards) in raw_voices:
2994         voice_name = music_xml_voice_name_to_lily_name(part_id, v)
2995         voice_lyrics = [music_xml_lyrics_name_to_lily_name(part_id, v, l)
2996                    for l in lyricsids]
2997         figured_bass_name = ''
2998         if figured_bass:
2999             figured_bass_name = music_xml_figuredbass_name_to_lily_name(part_id, v)
3000         chordnames_name = ''
3001         if chordnames:
3002             chordnames_name = music_xml_chordnames_name_to_lily_name(part_id, v)
3003         fretboards_name = ''
3004         if fretboards:
3005             fretboards_name = music_xml_fretboards_name_to_lily_name(part_id, v)
3006         voices.append([voice_name, voice_lyrics, figured_bass_name, chordnames_name, fretboards_name])
3007     return [staff_id, voices]
3008
3009 def update_score_setup(score_structure, part_list, voices, parts):
3010     for part_definition in part_list:
3011         part_id = part_definition.id
3012         nv_dict = voices.get(part_id)
3013         if not nv_dict:
3014             ly.warning(_('unknown part in part-list: %s') % part_id)
3015             continue
3016
3017         staves = reduce(lambda x, y: x + y,
3018                 [voice.voicedata._staves.keys()
3019                  for voice in nv_dict.values()],
3020                 [])
3021         staves_info = []
3022         if len(staves) > 1:
3023             staves_info = []
3024             staves = uniq_list(staves)
3025             staves.sort()
3026             for s in staves:
3027                 thisstaff_raw_voices = [(voice_name, voice.lyrics_order, voice.figured_bass, voice.chordnames, voice.fretboards)
3028                     for (voice_name, voice) in nv_dict.items()
3029                     if voice.voicedata._start_staff == s]
3030                 staves_info.append(format_staff_info(part_id, s, thisstaff_raw_voices))
3031         else:
3032             thisstaff_raw_voices = [(voice_name, voice.lyrics_order, voice.figured_bass, voice.chordnames, voice.fretboards)
3033                 for (voice_name, voice) in nv_dict.items()]
3034             staves_info.append(format_staff_info(part_id, None, thisstaff_raw_voices))
3035         score_structure.set_part_information(part_id, staves_info)
3036
3037     sounds = []
3038     for part in parts:
3039         for measure in part.get_typed_children(musicxml.Measure):
3040             for sound in measure.get_typed_children(musicxml.Sound):
3041                 sounds.append(sound)
3042             for direction in measure.get_typed_children(musicxml.Direction):
3043                 for sound in direction.get_typed_children(musicxml.Sound):
3044                     sounds.append(sound)
3045
3046     score_structure.set_tempo('100')
3047     if len(sounds) != 0:
3048         for sound in sounds:
3049             if (sound.get_tempo() != None and sound.get_tempo() != ""):
3050                 score_structure.set_tempo(sound.get_tempo())
3051                 break
3052
3053
3054 # Set global values in the \layout block, like auto-beaming etc.
3055 def update_layout_information():
3056     if not conversion_settings.ignore_beaming and layout_information:
3057         layout_information.set_context_item('Score', 'autoBeaming = ##f')
3058     if musicexp.get_string_numbers() == "f":
3059         layout_information.set_context_item('Score', '\\override StringNumber #\'stencil = ##f')
3060
3061 #  \n\t\t\t\t\\override StringNumber #\'stencil = ##f
3062
3063 def print_ly_preamble(printer, filename):
3064     printer.dump_version(lilypond_version)
3065     printer.print_verbatim('% automatically converted by musicxml2ly from ' + filename)
3066     printer.newline()
3067     printer.dump(r'\pointAndClickOff')
3068     printer.newline()
3069     if options.midi:
3070         printer.newline()
3071         printer.dump(r'\include "articulate.ly"')
3072         printer.newline()
3073
3074 def print_ly_additional_definitions(printer, filename=None):
3075     if needed_additional_definitions:
3076         printer.newline()
3077         printer.print_verbatim('%% additional definitions required by the score:')
3078         printer.newline()
3079     for a in set(needed_additional_definitions):
3080         printer.print_verbatim(additional_definitions.get(a, ''))
3081         printer.newline()
3082     printer.newline()
3083
3084 # Read in the tree from the given I/O object (either file or string) and
3085 # demarshall it using the classes from the musicxml.py file
3086 def read_xml(io_object, use_lxml):
3087     if use_lxml:
3088         import lxml.etree
3089         tree = lxml.etree.parse(io_object)
3090         mxl_tree = musicxml.lxml_demarshal_node(tree.getroot())
3091         return mxl_tree
3092     else:
3093         from xml.dom import minidom, Node
3094         doc = minidom.parse(io_object)
3095         node = doc.documentElement
3096         return musicxml.minidom_demarshal_node(node)
3097     return None
3098
3099
3100 def read_musicxml(filename, compressed, use_lxml):
3101     raw_string = None
3102     if compressed:
3103         if filename == "-":
3104              ly.progress(_("Input is compressed, extracting raw MusicXML data from stdin"), True)
3105              # unfortunately, zipfile.ZipFile can't read directly from
3106              # stdin, so copy everything from stdin to a temp file and read
3107              # that. TemporaryFile() will remove the file when it is closed.
3108              tmp = tempfile.TemporaryFile()
3109              sys.stdin = os.fdopen(sys.stdin.fileno(), 'rb', 0) # Make sys.stdin binary
3110              bytes_read = sys.stdin.read(8192)
3111              while bytes_read:
3112                  for b in bytes_read:
3113                      tmp.write(b)
3114                  bytes_read = sys.stdin.read(8192)
3115              z = zipfile.ZipFile(tmp, "r")
3116         else:
3117             ly.progress(_("Input file %s is compressed, extracting raw MusicXML data") % filename, True)
3118             z = zipfile.ZipFile(filename, "r")
3119         container_xml = z.read("META-INF/container.xml")
3120         if not container_xml:
3121             return None
3122         container = read_xml(StringIO.StringIO(container_xml), use_lxml)
3123         if not container:
3124             return None
3125         rootfiles = container.get_maybe_exist_named_child('rootfiles')
3126         if not rootfiles:
3127             return None
3128         rootfile_list = rootfiles.get_named_children('rootfile')
3129         mxml_file = None
3130         if len(rootfile_list) > 0:
3131             mxml_file = getattr(rootfile_list[0], 'full-path', None)
3132         if mxml_file:
3133             raw_string = z.read(mxml_file)
3134
3135     if raw_string:
3136         io_object = StringIO.StringIO(raw_string)
3137     elif filename == "-":
3138         io_object = sys.stdin
3139     else:
3140         io_object = filename
3141
3142     return read_xml(io_object, use_lxml)
3143
3144
3145 def convert(filename, options):
3146     if filename == "-":
3147         ly.progress(_("Reading MusicXML from Standard input ..."), True)
3148     else:
3149         ly.progress(_("Reading MusicXML from %s ...") % filename, True)
3150
3151     tree = read_musicxml(filename, options.compressed, options.use_lxml)
3152     score_information = extract_score_information(tree)
3153     paper_information = extract_paper_information(tree)
3154
3155     parts = tree.get_typed_children(musicxml.Part)
3156     (voices, staff_info) = get_all_voices(parts)
3157
3158     score = None
3159     mxl_pl = tree.get_maybe_exist_typed_child(musicxml.Part_list)
3160     if mxl_pl:
3161         score = extract_score_structure(mxl_pl, staff_info)
3162         part_list = mxl_pl.get_named_children("score-part")
3163
3164     # score information is contained in the <work>, <identification> or <movement-title> tags
3165     update_score_setup(score, part_list, voices, parts)
3166     # After the conversion, update the list of settings for the \layout block
3167     update_layout_information()
3168
3169     if not options.output_name:
3170         options.output_name = os.path.basename(filename)
3171         options.output_name = os.path.splitext(options.output_name)[0]
3172     elif re.match(".*\.ly", options.output_name):
3173         options.output_name = os.path.splitext(options.output_name)[0]
3174
3175
3176     #defs_ly_name = options.output_name + '-defs.ly'
3177     if (options.output_name == "-"):
3178       output_ly_name = 'Standard output'
3179     else:
3180       output_ly_name = options.output_name + '.ly'
3181     ly.progress(_("Output to `%s'") % output_ly_name, True)
3182     printer = musicexp.Output_printer()
3183     #ly.progress(_("Output to `%s'") % defs_ly_name, True)
3184     if (options.output_name == "-"):
3185       printer.set_file(codecs.getwriter("utf-8")(sys.stdout))
3186     else:
3187       printer.set_file(codecs.open(output_ly_name, 'wb', encoding='utf-8'))
3188     print_ly_preamble(printer, filename)
3189     print_ly_additional_definitions(printer, filename)
3190     if score_information:
3191         score_information.print_ly(printer)
3192     if paper_information and conversion_settings.convert_page_layout:
3193         paper_information.print_ly(printer)
3194     if layout_information:
3195         layout_information.print_ly(printer)
3196     print_voice_definitions(printer, part_list, voices)
3197
3198     printer.newline()
3199     printer.dump("% The score definition")
3200     printer.newline()
3201     score.print_ly(printer)
3202     printer.newline()
3203
3204     # Syntax update to current version
3205     if (options.output_name != "-"):
3206         version = os.popen("lilypond --version | head -1 | cut -d' ' -f3").read().strip()
3207         ly.progress(_("Converting to current version (%s) notations ..." % version), True)
3208         os.system("convert-ly -e %s 2> /dev/null" % utilities.escape_ly_output_string(output_ly_name))
3209
3210     return voices
3211
3212 def get_existing_filename_with_extension(filename, ext):
3213     if os.path.exists(filename):
3214         return filename
3215     newfilename = filename + "." + ext
3216     if os.path.exists(newfilename):
3217         return newfilename;
3218     newfilename = filename + ext
3219     if os.path.exists(newfilename):
3220         return newfilename;
3221     return ''
3222
3223
3224 def main():
3225     opt_parser = option_parser()
3226
3227     global options
3228     (options, args) = opt_parser.parse_args()
3229     if not args:
3230         opt_parser.print_usage()
3231         sys.exit(2)
3232
3233     # midi-block option
3234     if options.midi:
3235         musicexp.set_create_midi(options.midi)
3236
3237     # transpose function
3238     if options.transpose:
3239         musicexp.set_transpose(options.transpose)
3240
3241     # tab clef option
3242     if options.tab_clef:
3243         musicexp.set_tab_clef(options.tab_clef)
3244
3245     # string numbers option
3246     if options.string_numbers:
3247         musicexp.set_string_numbers(options.string_numbers)
3248
3249     if options.language:
3250         musicexp.set_pitch_language(options.language)
3251         needed_additional_definitions.append(options.language)
3252         additional_definitions[options.language] = "\\language \"%s\"\n" % options.language
3253
3254     conversion_settings.ignore_beaming = not options.convert_beaming
3255     conversion_settings.convert_page_layout = options.convert_page_layout
3256     if conversion_settings.convert_page_layout:
3257         conversion_settings.convert_system_breaks = options.convert_system_breaks
3258         conversion_settings.convert_page_breaks = options.convert_page_breaks
3259         conversion_settings.convert_page_margins = options.convert_page_margins
3260     else:
3261         conversion_settings.convert_system_breaks = False
3262         conversion_settings.convert_page_breaks = False
3263         conversion_settings.convert_page_margins = False
3264     conversion_settings.convert_stem_directions = options.convert_stem_directions
3265
3266     # Allow the user to leave out the .xml or xml on the filename
3267     basefilename = args[0].decode('utf-8')
3268     if basefilename == "-": # Read from stdin
3269         filename = "-"
3270     else:
3271         filename = get_existing_filename_with_extension(basefilename, "xml")
3272         if not filename:
3273             filename = get_existing_filename_with_extension(basefilename, "mxl")
3274             options.compressed = True
3275     if filename and filename.endswith("mxl"):
3276         options.compressed = True
3277
3278     if filename and (filename == "-" or os.path.exists(filename)):
3279         voices = convert(filename, options)
3280     else:
3281         ly.error(_("Unable to find input file %s") % basefilename)
3282         sys.exit(1)
3283
3284 if __name__ == '__main__':
3285     main()