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