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