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